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 <errno.h>
|
2020-08-27 19:48:13 +02:00
|
|
|
|
#include <pwd.h>
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
#include <sys/stat.h>
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
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"
|
2021-01-15 20:39:45 +00:00
|
|
|
|
#include "debug.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;
|
|
|
|
|
|
|
2021-09-25 02:46:22 +01:00
|
|
|
|
#define cube6(r, g) \
|
|
|
|
|
|
r|g|0x00, r|g|0x5f, r|g|0x87, r|g|0xaf, r|g|0xd7, r|g|0xff
|
|
|
|
|
|
|
|
|
|
|
|
#define cube36(r) \
|
|
|
|
|
|
cube6(r, 0x0000), \
|
|
|
|
|
|
cube6(r, 0x5f00), \
|
|
|
|
|
|
cube6(r, 0x8700), \
|
|
|
|
|
|
cube6(r, 0xaf00), \
|
|
|
|
|
|
cube6(r, 0xd700), \
|
|
|
|
|
|
cube6(r, 0xff00)
|
|
|
|
|
|
|
|
|
|
|
|
static const uint32_t default_color_table[256] = {
|
|
|
|
|
|
// 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,
|
|
|
|
|
|
|
2021-09-25 02:46:22 +01:00
|
|
|
|
// 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,
|
2021-09-25 02:46:22 +01:00
|
|
|
|
|
|
|
|
|
|
// 6x6x6 RGB cube
|
2021-09-25 22:01:03 +01:00
|
|
|
|
// (color channels = i ? i*40+55 : 0, where i = 0..5)
|
2021-09-25 02:46:22 +01:00
|
|
|
|
cube36(0x000000),
|
|
|
|
|
|
cube36(0x5f0000),
|
|
|
|
|
|
cube36(0x870000),
|
|
|
|
|
|
cube36(0xaf0000),
|
|
|
|
|
|
cube36(0xd70000),
|
|
|
|
|
|
cube36(0xff0000),
|
|
|
|
|
|
|
|
|
|
|
|
// 24 shades of gray
|
2021-09-25 22:01:03 +01:00
|
|
|
|
// (color channels = i*10+8, where i = 0..23)
|
2021-09-25 02:46:22 +01:00
|
|
|
|
0x080808, 0x121212, 0x1c1c1c, 0x262626,
|
|
|
|
|
|
0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
|
|
|
|
|
|
0x585858, 0x626262, 0x6c6c6c, 0x767676,
|
|
|
|
|
|
0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
|
|
|
|
|
|
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6,
|
|
|
|
|
|
0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
|
2019-07-21 11:06:28 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
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-09-10 18:17:47 +02:00
|
|
|
|
[BIND_ACTION_SCROLLBACK_UP_PAGE] = "scrollback-up-page",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_UP_HALF_PAGE] = "scrollback-up-half-page",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_UP_LINE] = "scrollback-up-line",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE] = "scrollback-down-half-page",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_DOWN_LINE] = "scrollback-down-line",
|
2020-03-09 20:03:04 +01:00
|
|
|
|
[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",
|
2021-02-04 20:55:08 +01:00
|
|
|
|
[BIND_ACTION_SHOW_URLS_COPY] = "show-urls-copy",
|
2021-02-04 20:39:13 +01:00
|
|
|
|
[BIND_ACTION_SHOW_URLS_LAUNCH] = "show-urls-launch",
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
|
|
|
|
|
/* Mouse-specific actions */
|
|
|
|
|
|
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
2020-08-11 10:17:19 +02:00
|
|
|
|
[BIND_ACTION_SELECT_BEGIN_BLOCK] = "select-begin-block",
|
|
|
|
|
|
[BIND_ACTION_SELECT_EXTEND] = "select-extend",
|
2021-01-06 11:11:46 +01:00
|
|
|
|
[BIND_ACTION_SELECT_EXTEND_CHAR_WISE] = "select-extend-character-wise",
|
2020-08-11 09:55:33 +02:00
|
|
|
|
[BIND_ACTION_SELECT_WORD] = "select-word",
|
|
|
|
|
|
[BIND_ACTION_SELECT_WORD_WS] = "select-word-whitespace",
|
2020-08-11 10:17:19 +02:00
|
|
|
|
[BIND_ACTION_SELECT_ROW] = "select-row",
|
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");
|
|
|
|
|
|
|
2021-05-07 16:41:10 +02:00
|
|
|
|
static void NOINLINE PRINTF(5)
|
|
|
|
|
|
log_and_notify(struct config *conf, enum log_class log_class,
|
|
|
|
|
|
const char *file, int lineno, const char *fmt, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
enum user_notification_kind kind;
|
|
|
|
|
|
|
|
|
|
|
|
switch (log_class) {
|
|
|
|
|
|
case LOG_CLASS_WARNING: kind = USER_NOTIFICATION_WARNING; break;
|
|
|
|
|
|
case LOG_CLASS_ERROR: kind = USER_NOTIFICATION_ERROR; break;
|
|
|
|
|
|
|
|
|
|
|
|
case LOG_CLASS_INFO:
|
|
|
|
|
|
case LOG_CLASS_DEBUG:
|
2021-06-26 22:15:09 +01:00
|
|
|
|
case LOG_CLASS_NONE:
|
|
|
|
|
|
default:
|
|
|
|
|
|
BUG("unsupported log class: %d", (int)log_class);
|
|
|
|
|
|
return;
|
2021-05-07 16:41:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
va_list va1, va2;
|
|
|
|
|
|
va_start(va1, fmt);
|
|
|
|
|
|
va_copy(va2, va1);
|
|
|
|
|
|
|
|
|
|
|
|
log_msg_va(log_class, LOG_MODULE, file, lineno, fmt, va1);
|
|
|
|
|
|
|
|
|
|
|
|
char *text = xvasprintf(fmt, va2);
|
|
|
|
|
|
tll_push_back(
|
|
|
|
|
|
conf->notifications,
|
|
|
|
|
|
((struct user_notification){.kind = kind, .text = text}));
|
|
|
|
|
|
|
|
|
|
|
|
va_end(va2);
|
|
|
|
|
|
va_end(va1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void NOINLINE PRINTF(5)
|
|
|
|
|
|
log_errno_and_notify(struct config *conf, enum log_class log_class,
|
|
|
|
|
|
const char *file, int lineno, const char *fmt, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
int errno_copy = errno;
|
|
|
|
|
|
|
|
|
|
|
|
va_list va1, va2, va3;
|
|
|
|
|
|
va_start(va1, fmt);
|
|
|
|
|
|
va_copy(va2, va1);
|
|
|
|
|
|
va_copy(va3, va2);
|
|
|
|
|
|
|
|
|
|
|
|
log_errno_provided_va(
|
|
|
|
|
|
log_class, LOG_MODULE, file, lineno, errno_copy, fmt, va1);
|
|
|
|
|
|
|
|
|
|
|
|
int len = vsnprintf(NULL, 0, fmt, va2);
|
|
|
|
|
|
int errno_len = snprintf(NULL, 0, ": %s", strerror(errno_copy));
|
|
|
|
|
|
|
|
|
|
|
|
char *text = xmalloc(len + errno_len + 1);
|
|
|
|
|
|
vsnprintf(text, len + errno_len + 1, fmt, va3);
|
|
|
|
|
|
snprintf(&text[len], errno_len + 1, ": %s", strerror(errno_copy));
|
|
|
|
|
|
|
|
|
|
|
|
tll_push_back(
|
|
|
|
|
|
conf->notifications,
|
|
|
|
|
|
((struct user_notification){
|
|
|
|
|
|
.kind = USER_NOTIFICATION_ERROR, .text = text}));
|
|
|
|
|
|
|
|
|
|
|
|
va_end(va3);
|
|
|
|
|
|
va_end(va2);
|
|
|
|
|
|
va_end(va1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_ERR(...) \
|
|
|
|
|
|
log_and_notify(conf, LOG_CLASS_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_WARN(...) \
|
|
|
|
|
|
log_and_notify(conf, LOG_CLASS_WARNING, __FILE__, __LINE__, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_ERRNO(...) \
|
|
|
|
|
|
log_errno_and_notify(conf, LOG_CLASS_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
2020-07-31 17:07:14 +02:00
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
struct config_file {
|
|
|
|
|
|
char *path; /* Full, absolute, path */
|
|
|
|
|
|
int fd; /* FD of file, O_RDONLY */
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct path_component {
|
|
|
|
|
|
const char *component;
|
|
|
|
|
|
int fd;
|
|
|
|
|
|
};
|
|
|
|
|
|
typedef tll(struct path_component) path_components_t;
|
|
|
|
|
|
|
2021-06-30 19:10:37 +02:00
|
|
|
|
static void NOINLINE
|
2020-08-27 19:48:13 +02:00
|
|
|
|
path_component_add(path_components_t *components, const char *comp, int fd)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(comp != NULL);
|
|
|
|
|
|
xassert(fd >= 0);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
|
|
|
|
|
|
struct path_component pc = {.component = comp, .fd = fd};
|
|
|
|
|
|
tll_push_back(*components, pc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 19:10:37 +02:00
|
|
|
|
static void NOINLINE
|
2020-08-27 19:48:13 +02:00
|
|
|
|
path_component_destroy(struct path_component *component)
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(component->fd >= 0);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
close(component->fd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 19:10:37 +02:00
|
|
|
|
static void NOINLINE
|
2020-08-27 19:48:13 +02:00
|
|
|
|
path_components_destroy(path_components_t *components)
|
|
|
|
|
|
{
|
|
|
|
|
|
tll_foreach(*components, it) {
|
|
|
|
|
|
path_component_destroy(&it->item);
|
|
|
|
|
|
tll_remove(*components, it);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct config_file
|
|
|
|
|
|
path_components_to_config_file(const path_components_t *components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tll_length(*components) == 0)
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
tll_foreach(*components, it)
|
|
|
|
|
|
len += strlen(it->item.component) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
char *path = malloc(len);
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
tll_foreach(*components, it) {
|
|
|
|
|
|
strcpy(&path[idx], it->item.component);
|
|
|
|
|
|
idx += strlen(it->item.component);
|
|
|
|
|
|
path[idx++] = '/';
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
2020-08-27 19:48:13 +02:00
|
|
|
|
path[idx - 1] = '\0'; /* Strip last ’/’ */
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
int fd_copy = dup(tll_back(*components).fd);
|
|
|
|
|
|
if (fd_copy < 0) {
|
|
|
|
|
|
free(path);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
return (struct config_file){.path = path, .fd = fd_copy};
|
|
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
|
return (struct config_file){.path = NULL, .fd = -1};
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
static const char *
|
|
|
|
|
|
get_user_home_dir(void)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2020-08-27 19:48:13 +02:00
|
|
|
|
const struct passwd *passwd = getpwuid(getuid());
|
|
|
|
|
|
if (passwd == NULL)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
return NULL;
|
2020-08-27 19:48:13 +02:00
|
|
|
|
return passwd->pw_dir;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
try_open_file(path_components_t *components, const char *name)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2020-08-27 19:48:13 +02:00
|
|
|
|
int parent_fd = tll_back(*components).fd;
|
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
|
struct stat st;
|
2020-08-27 19:48:13 +02:00
|
|
|
|
if (fstatat(parent_fd, name, &st, 0) == 0 && S_ISREG(st.st_mode)) {
|
|
|
|
|
|
int fd = openat(parent_fd, name, O_RDONLY);
|
|
|
|
|
|
if (fd >= 0) {
|
|
|
|
|
|
path_component_add(components, name, fd);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct config_file
|
2021-03-16 09:54:32 +01:00
|
|
|
|
open_config(void)
|
2020-08-27 19:48:13 +02:00
|
|
|
|
{
|
|
|
|
|
|
struct config_file ret = {.path = NULL, .fd = -1};
|
|
|
|
|
|
|
|
|
|
|
|
path_components_t components = tll_init();
|
|
|
|
|
|
|
|
|
|
|
|
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
|
|
|
|
|
|
const char *user_home_dir = get_user_home_dir();
|
|
|
|
|
|
char *xdg_config_dirs_copy = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Use XDG_CONFIG_HOME, or ~/.config */
|
|
|
|
|
|
if (xdg_config_home != NULL) {
|
|
|
|
|
|
int fd = open(xdg_config_home, O_RDONLY);
|
|
|
|
|
|
if (fd >= 0)
|
|
|
|
|
|
path_component_add(&components, xdg_config_home, fd);
|
|
|
|
|
|
} else if (user_home_dir != NULL) {
|
|
|
|
|
|
int home_fd = open(user_home_dir, O_RDONLY);
|
|
|
|
|
|
if (home_fd >= 0) {
|
|
|
|
|
|
int config_fd = openat(home_fd, ".config", O_RDONLY);
|
|
|
|
|
|
if (config_fd >= 0) {
|
|
|
|
|
|
path_component_add(&components, user_home_dir, home_fd);
|
|
|
|
|
|
path_component_add(&components, ".config", config_fd);
|
|
|
|
|
|
} else
|
|
|
|
|
|
close(home_fd);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* First look for foot/foot.ini */
|
|
|
|
|
|
if (tll_length(components) > 0) {
|
|
|
|
|
|
int foot_fd = openat(tll_back(components).fd, "foot", O_RDONLY);
|
|
|
|
|
|
if (foot_fd >= 0) {
|
|
|
|
|
|
path_component_add(&components, "foot", foot_fd);
|
|
|
|
|
|
|
|
|
|
|
|
if (try_open_file(&components, "foot.ini"))
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
|
|
struct path_component pc = tll_pop_back(components);
|
|
|
|
|
|
path_component_destroy(&pc);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Finally, try foot/foot.ini in all XDG_CONFIG_DIRS */
|
|
|
|
|
|
const char *xdg_config_dirs = getenv("XDG_CONFIG_DIRS");
|
|
|
|
|
|
xdg_config_dirs_copy = xdg_config_dirs != NULL
|
|
|
|
|
|
? strdup(xdg_config_dirs) : NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (xdg_config_dirs_copy != NULL) {
|
|
|
|
|
|
for (char *save = NULL,
|
|
|
|
|
|
*xdg_dir = strtok_r(xdg_config_dirs_copy, ":", &save);
|
|
|
|
|
|
xdg_dir != NULL;
|
|
|
|
|
|
xdg_dir = strtok_r(NULL, ":", &save))
|
|
|
|
|
|
{
|
|
|
|
|
|
path_components_destroy(&components);
|
|
|
|
|
|
|
|
|
|
|
|
int xdg_fd = open(xdg_dir, O_RDONLY);
|
|
|
|
|
|
if (xdg_fd < 0)
|
|
|
|
|
|
continue;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
int foot_fd = openat(xdg_fd, "foot", O_RDONLY);
|
|
|
|
|
|
if (foot_fd < 0) {
|
|
|
|
|
|
close(xdg_fd);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(tll_length(components) == 0);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
path_component_add(&components, xdg_dir, xdg_fd);
|
|
|
|
|
|
path_component_add(&components, "foot", foot_fd);
|
|
|
|
|
|
|
|
|
|
|
|
if (try_open_file(&components, "foot.ini"))
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
path_components_destroy(&components);
|
|
|
|
|
|
free(xdg_config_dirs_copy);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
done:
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(tll_length(components) > 0);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
ret = path_components_to_config_file(&components);
|
|
|
|
|
|
goto out;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
static int
|
|
|
|
|
|
wccmp(const void *_a, const void *_b)
|
|
|
|
|
|
{
|
|
|
|
|
|
const wchar_t *a = _a;
|
|
|
|
|
|
const wchar_t *b = _b;
|
|
|
|
|
|
return *a - *b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-08 14:56:11 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
str_has_prefix(const char *str, const char *prefix)
|
|
|
|
|
|
{
|
|
|
|
|
|
return strncmp(str, prefix, strlen(prefix)) == 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2020-03-11 16:10:14 +01:00
|
|
|
|
str_to_bool(const char *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
return strcasecmp(s, "on") == 0 ||
|
|
|
|
|
|
strcasecmp(s, "true") == 0 ||
|
|
|
|
|
|
strcasecmp(s, "yes") == 0 ||
|
|
|
|
|
|
strtoul(s, NULL, 0) > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
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
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2019-08-15 18:15:43 +02:00
|
|
|
|
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';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-02-13 11:42:21 +01:00
|
|
|
|
str_to_wchars(const char *s, wchar_t **res, struct config *conf,
|
|
|
|
|
|
const char *path, int lineno,
|
|
|
|
|
|
const char *section, const char *key)
|
|
|
|
|
|
{
|
|
|
|
|
|
*res = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
size_t chars = mbstowcs(NULL, s, 0);
|
|
|
|
|
|
if (chars == (size_t)-1) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [%s]: %s: invalid string: %s",
|
|
|
|
|
|
path, lineno, section, key, s);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*res = xmalloc((chars + 1) * sizeof(wchar_t));
|
|
|
|
|
|
mbstowcs(*res, s, chars + 1);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-01-07 17:00:13 +01:00
|
|
|
|
str_to_color(const char *s, uint32_t *color, bool allow_alpha,
|
|
|
|
|
|
struct config *conf, const char *path, int lineno,
|
2020-07-31 17:07:14 +02:00
|
|
|
|
const char *section, const char *key)
|
2019-07-29 20:13:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
unsigned long value;
|
|
|
|
|
|
if (!str_to_ulong(s, 16, &value)) {
|
2021-06-20 13:55:12 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-01-07 17:00:13 +01:00
|
|
|
|
"%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) {
|
2021-01-07 17:00:13 +01:00
|
|
|
|
LOG_AND_NOTIFY_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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-02-06 11:10:40 +01:00
|
|
|
|
str_to_two_colors(const char *s, uint32_t *first, uint32_t *second,
|
|
|
|
|
|
bool allow_alpha, struct config *conf, const char *path,
|
|
|
|
|
|
int lineno, const char *section, const char *key)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* TODO: do this without strdup() */
|
|
|
|
|
|
char *value_copy = xstrdup(s);
|
|
|
|
|
|
const char *first_as_str = strtok(value_copy, " ");
|
|
|
|
|
|
const char *second_as_str = strtok(NULL, " ");
|
|
|
|
|
|
|
|
|
|
|
|
if (first_as_str == NULL || second_as_str == NULL ||
|
|
|
|
|
|
!str_to_color(first_as_str, first, allow_alpha, conf, path, lineno, section, key) ||
|
|
|
|
|
|
!str_to_color(second_as_str, second, allow_alpha, conf, path, lineno, section, key))
|
|
|
|
|
|
{
|
|
|
|
|
|
free(value_copy);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(value_copy);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-01-11 19:37:05 +01:00
|
|
|
|
str_to_pt_or_px(const char *s, struct pt_or_px *res, struct config *conf,
|
2021-01-07 17:00:58 +01:00
|
|
|
|
const char *path, int lineno, const char *section, const char *key)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t len = s != NULL ? strlen(s) : 0;
|
|
|
|
|
|
if (len >= 2 && s[len - 2] == 'p' && s[len - 1] == 'x') {
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
long value = strtol(s, &end, 10);
|
|
|
|
|
|
if (!(errno == 0 && end == s + len - 2)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [%s]: %s: "
|
|
|
|
|
|
"expected an integer directly followed by 'px', got '%s'",
|
|
|
|
|
|
path, lineno, section, key, s);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
res->pt = 0;
|
|
|
|
|
|
res->px = value;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
double value;
|
|
|
|
|
|
if (!str_to_double(s, &value)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [%s]: %s: expected a decimal value, got '%s'",
|
|
|
|
|
|
path, lineno, section, key, s);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
res->pt = value;
|
|
|
|
|
|
res->px = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
static struct config_font_list NOINLINE
|
|
|
|
|
|
str_to_fonts(const char *s, struct config *conf, const char *path, int lineno,
|
|
|
|
|
|
const char *section, const char *key)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
struct config_font *fonts = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
char *copy = xstrdup(s);
|
|
|
|
|
|
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] == '\0')
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
struct config_font font_data;
|
|
|
|
|
|
if (!config_font_parse(font, &font_data)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [%s]: %s: invalid font specification: %s",
|
|
|
|
|
|
path, lineno, section, key, font);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count + 1 > size) {
|
|
|
|
|
|
size += 4;
|
|
|
|
|
|
fonts = xrealloc(fonts, size * sizeof(fonts[0]));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
xassert(count + 1 <= size);
|
|
|
|
|
|
fonts[count++] = font_data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
return (struct config_font_list){.arr = fonts, .count = count};
|
|
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
free(fonts);
|
|
|
|
|
|
return (struct config_font_list){.arr = NULL, .count = 0};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-20 14:17:31 +02:00
|
|
|
|
static void NOINLINE
|
2021-06-18 16:18:41 +02:00
|
|
|
|
free_argv(struct argv *argv)
|
2021-06-20 14:17:31 +02:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (argv->args == NULL)
|
2021-06-20 14:17:31 +02:00
|
|
|
|
return;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
for (char **a = argv->args; *a != NULL; a++)
|
|
|
|
|
|
free(*a);
|
|
|
|
|
|
free(argv->args);
|
|
|
|
|
|
argv->args = NULL;
|
2021-06-20 14:17:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void NOINLINE
|
2021-06-18 16:18:41 +02:00
|
|
|
|
clone_argv(struct argv *dst, const struct argv *src)
|
2021-06-20 14:17:31 +02:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (src->args == NULL) {
|
|
|
|
|
|
dst->args = NULL;
|
2021-06-20 14:17:31 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t count = 0;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
for (char **args = src->args; *args != NULL; args++)
|
2021-06-20 14:17:31 +02:00
|
|
|
|
count++;
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
dst->args = xmalloc((count + 1) * sizeof(dst->args[0]));
|
|
|
|
|
|
for (char **args_src = src->args, **args_dst = dst->args;
|
|
|
|
|
|
*args_src != NULL; args_src++,
|
|
|
|
|
|
args_dst++)
|
2021-06-20 14:17:31 +02:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
*args_dst = xstrdup(*args_src);
|
2021-06-20 14:17:31 +02:00
|
|
|
|
}
|
2021-06-18 16:18:41 +02:00
|
|
|
|
dst->args[count] = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
spawn_template_free(struct config_spawn_template *template)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_argv(&template->argv);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
spawn_template_clone(struct config_spawn_template *dst,
|
|
|
|
|
|
const struct config_spawn_template *src)
|
|
|
|
|
|
{
|
|
|
|
|
|
clone_argv(&dst->argv, &src->argv);
|
2021-06-20 14:17:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-01-31 14:27:13 +01:00
|
|
|
|
str_to_spawn_template(struct config *conf,
|
|
|
|
|
|
const char *s, struct config_spawn_template *template,
|
|
|
|
|
|
const char *path, int lineno, const char *section,
|
|
|
|
|
|
const char *key)
|
|
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_free(template);
|
2021-01-31 14:27:13 +01:00
|
|
|
|
|
|
|
|
|
|
char **argv = NULL;
|
|
|
|
|
|
|
2021-06-20 14:17:31 +02:00
|
|
|
|
if (!tokenize_cmdline(s, &argv)) {
|
2021-01-31 14:27:13 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [%s]: %s: syntax error in command line",
|
|
|
|
|
|
path, lineno, section, key);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
template->argv.args = argv;
|
2021-01-31 14:27:13 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
static void
|
|
|
|
|
|
deprecated_url_option(struct config *conf,
|
|
|
|
|
|
const char *old_name, const char *new_name,
|
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"deprecated: %s:%d: [default]: %s: use '%s' in section '[url]' instead",
|
|
|
|
|
|
path, lineno, old_name, new_name);
|
|
|
|
|
|
|
|
|
|
|
|
const char fmt[] =
|
|
|
|
|
|
"%s:%d: \033[1m%s\033[22m, use \033[1m%s\033[22m in the \033[1m[url]\033[22m section instead";
|
|
|
|
|
|
char *text = xasprintf(fmt, path, lineno, old_name, new_name);
|
|
|
|
|
|
|
|
|
|
|
|
struct user_notification deprecation = {
|
|
|
|
|
|
.kind = USER_NOTIFICATION_DEPRECATED,
|
|
|
|
|
|
.text = text,
|
|
|
|
|
|
};
|
|
|
|
|
|
tll_push_back(conf->notifications, deprecation);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-28 17:44:13 +02:00
|
|
|
|
static bool parse_config_file(
|
|
|
|
|
|
FILE *f, struct config *conf, const char *path, bool errors_are_fatal);
|
|
|
|
|
|
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2021-05-28 17:44:13 +02:00
|
|
|
|
if (strcmp(key, "include") == 0) {
|
2021-07-30 14:44:16 +02:00
|
|
|
|
char *_include_path = NULL;
|
|
|
|
|
|
const char *include_path = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (value[0] == '~' && value[1] == '/') {
|
|
|
|
|
|
const char *home_dir = get_user_home_dir();
|
|
|
|
|
|
|
2021-07-30 14:47:45 +02:00
|
|
|
|
if (home_dir == NULL) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO(
|
|
|
|
|
|
"%s:%d: [default]: include: %s: failed to expand '~'",
|
|
|
|
|
|
path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-31 00:39:51 +02:00
|
|
|
|
_include_path = xasprintf("%s/%s", home_dir, value + 2);
|
2021-07-30 14:44:16 +02:00
|
|
|
|
include_path = _include_path;
|
|
|
|
|
|
} else
|
|
|
|
|
|
include_path = value;
|
2021-05-28 17:44:13 +02:00
|
|
|
|
|
|
|
|
|
|
if (include_path[0] != '/') {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-07-30 14:47:45 +02:00
|
|
|
|
"%s:%d: [default]: include: %s: not an absolute path",
|
2021-05-28 17:44:13 +02:00
|
|
|
|
path, lineno, include_path);
|
2021-07-30 14:44:16 +02:00
|
|
|
|
free(_include_path);
|
2021-05-28 17:44:13 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FILE *include = fopen(include_path, "r");
|
|
|
|
|
|
|
|
|
|
|
|
if (include == NULL) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO(
|
2021-07-30 14:47:45 +02:00
|
|
|
|
"%s:%d: [default]: include: %s: failed to open",
|
2021-05-28 17:44:13 +02:00
|
|
|
|
path, lineno, include_path);
|
2021-07-30 14:44:16 +02:00
|
|
|
|
free(_include_path);
|
2021-05-28 17:44:13 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ret = parse_config_file(
|
|
|
|
|
|
include, conf, include_path, errors_are_fatal);
|
|
|
|
|
|
fclose(include);
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("imported sub-configuration from %s", include_path);
|
2021-07-30 14:44:16 +02:00
|
|
|
|
free(_include_path);
|
2021-05-28 17:44:13 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "term") == 0) {
|
2019-07-18 14:29:40 +02:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-04 17:59:40 +02:00
|
|
|
|
else if (strcmp(key, "locked-title") == 0)
|
|
|
|
|
|
conf->locked_title = str_to_bool(value);
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-08 19:53:36 +01:00
|
|
|
|
else if (strcmp(key, "initial-window-size-pixels") == 0) {
|
2019-08-23 17:26:41 +02:00
|
|
|
|
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(
|
2021-05-28 17:44:13 +02:00
|
|
|
|
"%s:%d: [default]: initial-window-size-pixels: "
|
2020-09-08 19:17:29 +02:00
|
|
|
|
"expected WIDTHxHEIGHT, where both are positive integers, "
|
|
|
|
|
|
"got '%s'", path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->size.type = CONF_SIZE_PX;
|
2020-11-30 02:24:38 +00:00
|
|
|
|
conf->size.width = width;
|
|
|
|
|
|
conf->size.height = height;
|
2020-09-08 19:17:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "initial-window-size-chars") == 0) {
|
|
|
|
|
|
unsigned width, height;
|
|
|
|
|
|
if (sscanf(value, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-05-28 17:44:13 +02:00
|
|
|
|
"%s:%d: [default]: initial-window-size-chars: "
|
2020-09-08 19:17:29 +02:00
|
|
|
|
"expected WIDTHxHEIGHT, where both are positive integers, "
|
|
|
|
|
|
"got '%s'", path, lineno, value);
|
2019-08-23 17:26:41 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-08 19:17:29 +02:00
|
|
|
|
conf->size.type = CONF_SIZE_CELLS;
|
2020-11-30 02:24:38 +00:00
|
|
|
|
conf->size.width = width;
|
|
|
|
|
|
conf->size.height = height;
|
2019-08-23 17:26:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-15 19:00:56 +01:00
|
|
|
|
else if (strcmp(key, "pad") == 0) {
|
|
|
|
|
|
unsigned x, y;
|
2021-01-06 11:17:29 +01:00
|
|
|
|
char mode[16] = {0};
|
|
|
|
|
|
|
|
|
|
|
|
int ret = sscanf(value, "%ux%u %15s", &x, &y, mode);
|
|
|
|
|
|
bool center = strcasecmp(mode, "center") == 0;
|
|
|
|
|
|
bool invalid_mode = !center && mode[0] != '\0';
|
|
|
|
|
|
|
|
|
|
|
|
if ((ret != 2 && ret != 3) || invalid_mode) {
|
2020-07-31 17:07:14 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-01-06 11:17:29 +01:00
|
|
|
|
"%s:%d: [default]: pad: expected PAD_XxPAD_Y [center], "
|
2020-07-31 17:07:14 +02:00
|
|
|
|
"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;
|
2021-01-06 11:17:29 +01:00
|
|
|
|
conf->center = center;
|
2020-02-15 19:00:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-21 15:14:43 +01:00
|
|
|
|
else if (strcmp(key, "resize-delay-ms") == 0) {
|
|
|
|
|
|
unsigned long ms;
|
|
|
|
|
|
if (!str_to_ulong(value, 10, &ms)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [default]: resize-delay-ms: "
|
|
|
|
|
|
"expected an integer, got '%s'",
|
|
|
|
|
|
path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->resize_delay_ms = ms;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-17 21:57:08 +02:00
|
|
|
|
else if (strcmp(key, "bold-text-in-bright") == 0) {
|
|
|
|
|
|
if (strcmp(value, "palette-based") == 0) {
|
|
|
|
|
|
conf->bold_in_bright.enabled = true;
|
|
|
|
|
|
conf->bold_in_bright.palette_based = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
conf->bold_in_bright.enabled = str_to_bool(value);
|
|
|
|
|
|
conf->bold_in_bright.palette_based = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-11-14 11:21:51 +01:00
|
|
|
|
|
2021-05-07 04:40:07 -05:00
|
|
|
|
else if (strcmp(key, "bell") == 0) {
|
2021-05-20 17:51:04 +02:00
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"deprecated: %s:%d: [default]: bell: "
|
|
|
|
|
|
"set actions in section '[bell]' instead", path, lineno);
|
2021-05-07 04:40:07 -05:00
|
|
|
|
|
2021-05-20 17:51:04 +02:00
|
|
|
|
const char fmt[] =
|
|
|
|
|
|
"%s:%d: \033[1mbell\033[22m, use \033[1murgent\033[22m in "
|
|
|
|
|
|
"the \033[1m[bell]\033[22m section instead";
|
2021-05-07 04:40:07 -05:00
|
|
|
|
|
|
|
|
|
|
struct user_notification deprecation = {
|
|
|
|
|
|
.kind = USER_NOTIFICATION_DEPRECATED,
|
2021-05-20 17:51:04 +02:00
|
|
|
|
.text = xasprintf(fmt, path, lineno),
|
2021-05-07 04:40:07 -05:00
|
|
|
|
};
|
|
|
|
|
|
tll_push_back(conf->notifications, deprecation);
|
|
|
|
|
|
|
2021-05-07 07:16:48 -05:00
|
|
|
|
if (strcmp(value, "set-urgency") == 0) {
|
|
|
|
|
|
memset(&conf->bell, 0, sizeof(conf->bell));
|
2021-05-07 04:40:07 -05:00
|
|
|
|
conf->bell.urgent = true;
|
2021-05-07 07:16:48 -05:00
|
|
|
|
}
|
|
|
|
|
|
else if (strcmp(value, "notify") == 0) {
|
|
|
|
|
|
memset(&conf->bell, 0, sizeof(conf->bell));
|
2021-05-07 04:40:07 -05:00
|
|
|
|
conf->bell.notify = true;
|
2021-05-07 07:16:48 -05:00
|
|
|
|
}
|
|
|
|
|
|
else if (strcmp(value, "none") == 0) {
|
|
|
|
|
|
memset(&conf->bell, 0, sizeof(conf->bell));
|
|
|
|
|
|
}
|
2021-05-07 04:40:07 -05:00
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s%d: [default]: bell: "
|
|
|
|
|
|
"expected either 'set-urgency', 'notify' or 'none'",
|
|
|
|
|
|
path, lineno);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-20 21:04:47 +02:00
|
|
|
|
else if (strcmp(key, "font") == 0 ||
|
|
|
|
|
|
strcmp(key, "font-bold") == 0 ||
|
|
|
|
|
|
strcmp(key, "font-italic") == 0 ||
|
|
|
|
|
|
strcmp(key, "font-bold-italic") == 0)
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t idx =
|
|
|
|
|
|
strcmp(key, "font") == 0 ? 0 :
|
|
|
|
|
|
strcmp(key, "font-bold") == 0 ? 1 :
|
|
|
|
|
|
strcmp(key, "font-italic") == 0 ? 2 : 3;
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
config_font_list_destroy(&conf->fonts[idx]);
|
|
|
|
|
|
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
struct config_font *fonts = NULL;
|
2020-10-20 21:04:47 +02:00
|
|
|
|
|
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++;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
if (font[0] == '\0')
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
struct config_font font_data;
|
|
|
|
|
|
if (!config_font_parse(font, &font_data)) {
|
2021-07-22 23:21:31 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [default]: %s: invalid font specification: %s",
|
|
|
|
|
|
path, lineno, key, font);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
free(copy);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count + 1 > size) {
|
|
|
|
|
|
size += 4;
|
|
|
|
|
|
fonts = xrealloc(fonts, size * sizeof(fonts[0]));
|
2020-12-15 18:55:27 +01:00
|
|
|
|
}
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
xassert(count + 1 <= size);
|
|
|
|
|
|
fonts[count++] = font_data;
|
2019-12-05 19:33:54 +01:00
|
|
|
|
}
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
conf->fonts[idx].count = count;
|
|
|
|
|
|
conf->fonts[idx].arr = fonts;
|
2019-07-30 18:04:28 +02:00
|
|
|
|
free(copy);
|
2019-07-18 14:29:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-07 11:16:02 +01:00
|
|
|
|
else if (strcmp(key, "line-height") == 0) {
|
2021-01-07 17:00:58 +01:00
|
|
|
|
if (!str_to_pt_or_px(value, &conf->line_height,
|
|
|
|
|
|
conf, path, lineno, "default", "line-height"))
|
2021-01-07 11:16:02 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "letter-spacing") == 0) {
|
2021-01-07 17:00:58 +01:00
|
|
|
|
if (!str_to_pt_or_px(value, &conf->letter_spacing,
|
|
|
|
|
|
conf, path, lineno, "default", "letter-spacing"))
|
2021-01-07 11:16:02 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "horizontal-letter-offset") == 0) {
|
2021-01-07 17:00:58 +01:00
|
|
|
|
if (!str_to_pt_or_px(
|
|
|
|
|
|
value, &conf->horizontal_letter_offset,
|
|
|
|
|
|
conf, path, lineno, "default", "horizontal-letter-offset"))
|
2021-01-07 11:16:02 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "vertical-letter-offset") == 0) {
|
2021-01-07 17:00:58 +01:00
|
|
|
|
if (!str_to_pt_or_px(
|
2021-03-01 13:44:46 +01:00
|
|
|
|
value, &conf->vertical_letter_offset,
|
2021-01-07 17:00:58 +01:00
|
|
|
|
conf, path, lineno, "default", "vertical-letter-offset"))
|
2021-01-07 11:16:02 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-17 17:52:38 +02:00
|
|
|
|
else if (strcmp(key, "underline-offset") == 0) {
|
|
|
|
|
|
if (!str_to_pt_or_px(
|
|
|
|
|
|
value, &conf->underline_offset,
|
|
|
|
|
|
conf, path, lineno, "default", "underline-offset"))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
conf->use_custom_underline_offset = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-17 12:05:22 +01:00
|
|
|
|
else if (strcmp(key, "dpi-aware") == 0) {
|
|
|
|
|
|
if (strcmp(value, "auto") == 0)
|
|
|
|
|
|
conf->dpi_aware = DPI_AWARE_AUTO;
|
|
|
|
|
|
else
|
|
|
|
|
|
conf->dpi_aware = str_to_bool(value) ? DPI_AWARE_YES : DPI_AWARE_NO;
|
|
|
|
|
|
}
|
2020-11-17 17:59:31 +01: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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-09 19:44:23 +02:00
|
|
|
|
else if (strcmp(key, "word-delimiters") == 0) {
|
2021-02-13 11:42:21 +01:00
|
|
|
|
wchar_t *word_delimiters;
|
|
|
|
|
|
if (!str_to_wchars(value, &word_delimiters, conf, path, lineno,
|
|
|
|
|
|
"default", "word-delimiters"))
|
|
|
|
|
|
{
|
2020-10-09 19:44:23 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
free(conf->word_delimiters);
|
2021-02-13 11:42:21 +01:00
|
|
|
|
conf->word_delimiters = word_delimiters;
|
2021-02-13 11:42:40 +01:00
|
|
|
|
}
|
2020-10-09 19:44:23 +02:00
|
|
|
|
|
2021-02-13 11:42:40 +01:00
|
|
|
|
else if (strcmp(key, "jump-label-letters") == 0) {
|
2021-05-20 17:56:56 +02:00
|
|
|
|
deprecated_url_option(
|
|
|
|
|
|
conf, "jump-label-letters", "label-letters", path, lineno);
|
|
|
|
|
|
|
2021-02-13 11:42:40 +01:00
|
|
|
|
wchar_t *letters;
|
|
|
|
|
|
if (!str_to_wchars(value, &letters, conf, path, lineno,
|
2021-05-20 17:56:56 +02:00
|
|
|
|
"default", "label-letters"))
|
2021-02-13 11:42:40 +01:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2021-05-20 17:56:56 +02:00
|
|
|
|
free(conf->url.label_letters);
|
|
|
|
|
|
conf->url.label_letters = letters;
|
2020-10-09 19:44:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-08 19:19:55 +01:00
|
|
|
|
else if (strcmp(key, "notify") == 0) {
|
2021-01-31 14:27:13 +01:00
|
|
|
|
if (!str_to_spawn_template(conf, value, &conf->notify, path, lineno,
|
|
|
|
|
|
"default", "notify"))
|
|
|
|
|
|
{
|
2020-12-08 19:19:55 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2021-01-31 14:27:13 +01:00
|
|
|
|
}
|
2020-12-08 19:19:55 +01:00
|
|
|
|
|
2021-01-31 14:27:13 +01:00
|
|
|
|
else if (strcmp(key, "url-launch") == 0) {
|
2021-05-20 17:56:56 +02:00
|
|
|
|
deprecated_url_option(
|
|
|
|
|
|
conf, "url-launch", "launch", path, lineno);
|
|
|
|
|
|
|
|
|
|
|
|
if (!str_to_spawn_template(conf, value, &conf->url.launch, path, lineno,
|
2021-01-31 14:27:13 +01:00
|
|
|
|
"default", "url-launch"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2020-12-08 19:19:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 11:26:03 +01:00
|
|
|
|
else if (strcmp(key, "selection-target") == 0) {
|
2021-01-20 17:57:44 +01:00
|
|
|
|
static const char values[][12] = {
|
2021-01-16 15:39:44 +01:00
|
|
|
|
[SELECTION_TARGET_NONE] = "none",
|
|
|
|
|
|
[SELECTION_TARGET_PRIMARY] = "primary",
|
|
|
|
|
|
[SELECTION_TARGET_CLIPBOARD] = "clipboard",
|
|
|
|
|
|
[SELECTION_TARGET_BOTH] = "both",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(values); i++) {
|
|
|
|
|
|
if (strcasecmp(value, values[i]) == 0) {
|
|
|
|
|
|
conf->selection_target = i;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2021-01-16 11:26:03 +01:00
|
|
|
|
}
|
2021-01-16 15:39:44 +01:00
|
|
|
|
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [default]: %s: invalid 'selection-target'; "
|
|
|
|
|
|
"must be one of 'none', 'primary', 'clipboard' or 'both",
|
|
|
|
|
|
path, lineno, value);
|
|
|
|
|
|
return false;
|
2021-01-16 11:26:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-14 21:29:22 +01:00
|
|
|
|
else if (strcmp(key, "osc8-underline") == 0) {
|
2021-05-20 17:56:56 +02:00
|
|
|
|
deprecated_url_option(
|
|
|
|
|
|
conf, "osc8-underline", "osc8-underline", path, lineno);
|
|
|
|
|
|
|
2021-02-14 21:29:22 +01:00
|
|
|
|
if (strcmp(value, "url-mode") == 0)
|
2021-05-20 17:56:56 +02:00
|
|
|
|
conf->url.osc8_underline = OSC8_UNDERLINE_URL_MODE;
|
2021-02-14 21:29:22 +01:00
|
|
|
|
else if (strcmp(value, "always") == 0)
|
2021-05-20 17:56:56 +02:00
|
|
|
|
conf->url.osc8_underline = OSC8_UNDERLINE_ALWAYS;
|
2021-02-14 21:29:22 +01:00
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%u: [default]: %s: invalid 'osc8-underline'; "
|
|
|
|
|
|
"must be one of 'url-mode', or 'always'", path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-09 23:19:20 +02:00
|
|
|
|
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
|
|
|
|
|
conf->box_drawings_uses_font_glyphs = str_to_bool(value);
|
|
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-29 04:12:55 -05:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_bell(const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2021-04-29 04:12:55 -05:00
|
|
|
|
{
|
|
|
|
|
|
if (strcmp(key, "urgent") == 0)
|
|
|
|
|
|
conf->bell.urgent = str_to_bool(value);
|
|
|
|
|
|
else if (strcmp(key, "notify") == 0)
|
|
|
|
|
|
conf->bell.notify = str_to_bool(value);
|
|
|
|
|
|
else if (strcmp(key, "command") == 0) {
|
|
|
|
|
|
if (!str_to_spawn_template(conf, value, &conf->bell.command, path, lineno, "bell", key))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (strcmp(key, "command-focused") == 0)
|
|
|
|
|
|
conf->bell.command_focused = str_to_bool(value);
|
|
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [bell]: %s: invalid key", path, lineno, key);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_scrollback(const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-07-29 17:41:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
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-08-23 18:50:11 +02:00
|
|
|
|
"'none', 'fixed' or 'relative'",
|
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;
|
|
|
|
|
|
|
2020-08-22 10:56:53 +02:00
|
|
|
|
size_t len = mbstowcs(NULL, value, 0);
|
2020-08-25 19:39:17 +01:00
|
|
|
|
if (len == (size_t)-1) {
|
2020-08-22 10:56:53 +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-10-09 19:44:23 +02:00
|
|
|
|
mbstowcs(conf->scrollback.indicator.text, value, len + 1);
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_url(const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2021-05-20 17:56:56 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (strcmp(key, "launch") == 0) {
|
|
|
|
|
|
if (!str_to_spawn_template(conf, value, &conf->url.launch, path, lineno,
|
|
|
|
|
|
"url", "launch"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "label-letters") == 0) {
|
|
|
|
|
|
wchar_t *letters;
|
|
|
|
|
|
if (!str_to_wchars(value, &letters, conf, path, lineno, "url", "letters"))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
free(conf->url.label_letters);
|
|
|
|
|
|
conf->url.label_letters = letters;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "osc8-underline") == 0) {
|
|
|
|
|
|
if (strcmp(value, "url-mode") == 0)
|
|
|
|
|
|
conf->url.osc8_underline = OSC8_UNDERLINE_URL_MODE;
|
|
|
|
|
|
else if (strcmp(value, "always") == 0)
|
|
|
|
|
|
conf->url.osc8_underline = OSC8_UNDERLINE_ALWAYS;
|
|
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%u: [url]: %s: invalid 'osc8-underline'; "
|
|
|
|
|
|
"must be one of 'url-mode', or 'always'", path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:58:06 +02:00
|
|
|
|
else if (strcmp(key, "protocols") == 0) {
|
|
|
|
|
|
for (size_t i = 0; i < conf->url.prot_count; i++)
|
|
|
|
|
|
free(conf->url.protocols[i]);
|
|
|
|
|
|
free(conf->url.protocols);
|
|
|
|
|
|
|
|
|
|
|
|
conf->url.max_prot_len = 0;
|
|
|
|
|
|
conf->url.prot_count = 0;
|
|
|
|
|
|
conf->url.protocols = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
char *copy = xstrdup(value);
|
|
|
|
|
|
|
2021-05-20 20:45:01 +02:00
|
|
|
|
for (char *prot = strtok(copy, ",");
|
2021-05-20 17:58:06 +02:00
|
|
|
|
prot != NULL;
|
2021-05-20 20:45:01 +02:00
|
|
|
|
prot = strtok(NULL, ","))
|
2021-05-20 17:58:06 +02:00
|
|
|
|
{
|
2021-05-20 20:45:01 +02:00
|
|
|
|
|
|
|
|
|
|
/* Strip leading whitespace */
|
|
|
|
|
|
while (isspace(*prot))
|
|
|
|
|
|
prot++;
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace */
|
|
|
|
|
|
size_t len = strlen(prot);
|
|
|
|
|
|
while (len > 0 && isspace(prot[len - 1]))
|
|
|
|
|
|
prot[--len] = '\0';
|
|
|
|
|
|
|
2021-05-20 17:58:06 +02:00
|
|
|
|
size_t chars = mbstowcs(NULL, prot, 0);
|
|
|
|
|
|
if (chars == (size_t)-1) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO(
|
|
|
|
|
|
"%s:%u: [url]: protocols: invalid protocol name: %s",
|
|
|
|
|
|
path, lineno, prot);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->url.prot_count++;
|
|
|
|
|
|
conf->url.protocols = xrealloc(
|
|
|
|
|
|
conf->url.protocols,
|
|
|
|
|
|
conf->url.prot_count * sizeof(conf->url.protocols[0]));
|
|
|
|
|
|
|
|
|
|
|
|
size_t idx = conf->url.prot_count - 1;
|
|
|
|
|
|
conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(wchar_t));
|
|
|
|
|
|
mbstowcs(conf->url.protocols[idx], prot, chars + 1);
|
|
|
|
|
|
wcscpy(&conf->url.protocols[idx][chars], L"://");
|
|
|
|
|
|
|
2021-05-20 20:45:01 +02:00
|
|
|
|
chars += 3; /* Include the "://" */
|
|
|
|
|
|
if (chars > conf->url.max_prot_len)
|
|
|
|
|
|
conf->url.max_prot_len = chars;
|
2021-05-20 17:58:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
else if (strcmp(key, "uri-characters") == 0) {
|
|
|
|
|
|
wchar_t *uri_characters;
|
|
|
|
|
|
if (!str_to_wchars(value, &uri_characters, conf, path, lineno,
|
|
|
|
|
|
"url", "uri-characters"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(conf->url.uri_characters);
|
|
|
|
|
|
|
|
|
|
|
|
qsort(
|
|
|
|
|
|
uri_characters,
|
|
|
|
|
|
wcslen(uri_characters),
|
|
|
|
|
|
sizeof(uri_characters[0]),
|
|
|
|
|
|
&wccmp);
|
|
|
|
|
|
conf->url.uri_characters = uri_characters;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [url]: %s: invalid key", path, lineno, key);
|
|
|
|
|
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2019-07-21 11:31:16 +02:00
|
|
|
|
{
|
2021-05-08 14:56:11 +01:00
|
|
|
|
size_t key_len = strlen(key);
|
|
|
|
|
|
uint8_t last_digit = (unsigned char)key[key_len - 1] - '0';
|
2019-07-21 11:31:16 +02:00
|
|
|
|
uint32_t *color = NULL;
|
|
|
|
|
|
|
2021-05-08 02:18:45 -05:00
|
|
|
|
if (isdigit(key[0])) {
|
|
|
|
|
|
unsigned long index;
|
|
|
|
|
|
if (!str_to_ulong(key, 0, &index)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [colors]: invalid numeric key", path, lineno);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (index >= ALEN(conf->colors.table)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [colors]: numeric key out of range", path, lineno);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
color = &conf->colors.table[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-08 14:56:11 +01:00
|
|
|
|
else if (key_len == 8 && str_has_prefix(key, "regular") && last_digit < 8)
|
|
|
|
|
|
color = &conf->colors.table[last_digit];
|
|
|
|
|
|
|
|
|
|
|
|
else if (key_len == 7 && str_has_prefix(key, "bright") && last_digit < 8)
|
|
|
|
|
|
color = &conf->colors.table[8 + last_digit];
|
|
|
|
|
|
|
2021-05-08 02:18:45 -05:00
|
|
|
|
else if (strcmp(key, "foreground") == 0) color = &conf->colors.fg;
|
2019-07-21 11:31:16 +02:00
|
|
|
|
else if (strcmp(key, "background") == 0) color = &conf->colors.bg;
|
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;
|
2021-02-06 11:10:40 +01:00
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "jump-labels") == 0) {
|
|
|
|
|
|
if (!str_to_two_colors(
|
|
|
|
|
|
value, &conf->colors.jump_label.fg, &conf->colors.jump_label.bg,
|
|
|
|
|
|
false, conf, path, lineno, "colors", "jump-labels"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.jump_label = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "urls") == 0) {
|
|
|
|
|
|
if (!str_to_color(value, &conf->colors.url, false,
|
|
|
|
|
|
conf, path, lineno, "colors", "urls"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.url = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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.) {
|
2021-05-28 17:44:13 +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;
|
2021-01-07 17:00:13 +01:00
|
|
|
|
if (!str_to_color(value, &color_value, false, conf, 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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2019-07-22 20:15:14 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (strcmp(key, "style") == 0) {
|
|
|
|
|
|
if (strcmp(value, "block") == 0)
|
|
|
|
|
|
conf->cursor.style = CURSOR_BLOCK;
|
2021-04-30 20:31:47 +02:00
|
|
|
|
else if (strcmp(value, "beam") == 0 || strcmp(value, "bar") == 0)
|
|
|
|
|
|
conf->cursor.style = CURSOR_BEAM;
|
2019-07-22 20:15:14 +02:00
|
|
|
|
else if (strcmp(value, "underline") == 0)
|
|
|
|
|
|
conf->cursor.style = CURSOR_UNDERLINE;
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
2021-04-30 20:31:47 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: style: one of block, beam or underline",
|
|
|
|
|
|
path, lineno);
|
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) {
|
2021-02-06 11:10:40 +01:00
|
|
|
|
if (!str_to_two_colors(
|
|
|
|
|
|
value, &conf->cursor.color.text, &conf->cursor.color.cursor,
|
|
|
|
|
|
false, conf, path, lineno, "cursor", "color"))
|
2019-07-23 18:54:58 +02:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-06 11:10:40 +01:00
|
|
|
|
conf->cursor.color.text |= 1u << 31;
|
|
|
|
|
|
conf->cursor.color.cursor |= 1u << 31;
|
2019-07-23 18:54:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-30 20:31:47 +02:00
|
|
|
|
else if (strcmp(key, "beam-thickness") == 0) {
|
|
|
|
|
|
if (!str_to_pt_or_px(
|
|
|
|
|
|
value, &conf->cursor.beam_thickness,
|
|
|
|
|
|
conf, path, lineno, "cursor", "beam-thickness"))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-18 18:52:10 +02:00
|
|
|
|
else if (strcmp(key, "underline-thickness") == 0) {
|
|
|
|
|
|
if (!str_to_pt_or_px(
|
|
|
|
|
|
value, &conf->cursor.underline_thickness,
|
|
|
|
|
|
conf, path, lineno, "cursor", "underline-thickness"))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-08-04 07:33:15 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (strcmp(key, "hide-when-typing") == 0)
|
|
|
|
|
|
conf->mouse.hide_when_typing = str_to_bool(value);
|
|
|
|
|
|
|
2020-09-15 19:09:00 +02:00
|
|
|
|
else if (strcmp(key, "alternate-scroll-mode") == 0)
|
|
|
|
|
|
conf->mouse.alternate_scroll_mode = str_to_bool(value);
|
|
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-03-02 18:42:49 +01:00
|
|
|
|
{
|
|
|
|
|
|
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;
|
2020-10-10 11:04:17 +02:00
|
|
|
|
else if (strcmp(value, "none") == 0)
|
|
|
|
|
|
conf->csd.preferred = CONF_CSD_PREFER_NONE;
|
2020-03-02 18:42:49 +01:00
|
|
|
|
else {
|
2020-10-10 11:04:17 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: csd.preferred: expected either "
|
|
|
|
|
|
"'server', 'client' or 'none'", path, lineno);
|
2020-03-02 18:42:49 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
else if (strcmp(key, "font") == 0) {
|
|
|
|
|
|
struct config_font_list new_list = str_to_fonts(
|
|
|
|
|
|
value, conf, path, lineno, "csd", "font");
|
|
|
|
|
|
|
|
|
|
|
|
if (new_list.arr == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
config_font_list_destroy(&conf->csd.font);
|
|
|
|
|
|
conf->csd.font = new_list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
2021-06-20 13:55:12 +02:00
|
|
|
|
if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "color"))
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-20 10:44:50 +02:00
|
|
|
|
else if (strcmp(key, "button-color") == 0) {
|
|
|
|
|
|
uint32_t color;
|
2021-06-20 13:55:12 +02:00
|
|
|
|
if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-color"))
|
2021-06-20 10:44:50 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.buttons_set = true;
|
|
|
|
|
|
conf->csd.color.buttons = color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
|
else if (strcmp(key, "button-minimize-color") == 0) {
|
|
|
|
|
|
uint32_t color;
|
2021-06-20 13:55:12 +02:00
|
|
|
|
if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-minimize-color"))
|
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;
|
2021-06-20 13:55:12 +02:00
|
|
|
|
if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-maximize-color"))
|
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;
|
2021-06-20 13:55:12 +02:00
|
|
|
|
if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-close-color"))
|
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-08-11 09:55:33 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [csd]: %s: invalid action",
|
|
|
|
|
|
path, lineno, key);
|
2020-03-08 14:08:48 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Struct that holds temporary key/mouse binding parsed data */
|
|
|
|
|
|
struct key_combo {
|
|
|
|
|
|
char *text; /* Raw text, e.g. "Control+Shift+V" */
|
|
|
|
|
|
struct config_key_modifiers modifiers;
|
|
|
|
|
|
union {
|
|
|
|
|
|
xkb_keysym_t sym; /* Key converted to an XKB symbol, e.g. XKB_KEY_V */
|
|
|
|
|
|
struct {
|
|
|
|
|
|
int button;
|
|
|
|
|
|
int count;
|
|
|
|
|
|
} m;
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
struct key_combo_list {
|
|
|
|
|
|
size_t count;
|
|
|
|
|
|
struct key_combo *combos;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void NOINLINE
|
|
|
|
|
|
free_key_combo_list(struct key_combo_list *key_combos)
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < key_combos->count; i++)
|
|
|
|
|
|
free(key_combos->combos[i].text);
|
|
|
|
|
|
free(key_combos->combos);
|
|
|
|
|
|
key_combos->count = 0;
|
|
|
|
|
|
key_combos->combos = NULL;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-12 17:16:35 +01:00
|
|
|
|
static bool
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
parse_modifiers(struct config *conf, const char *text, size_t len,
|
|
|
|
|
|
struct config_key_modifiers *modifiers, const char *path, unsigned lineno)
|
2020-03-12 17:16:35 +01:00
|
|
|
|
{
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
bool ret = false;
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
2020-08-23 07:42:20 +02:00
|
|
|
|
*modifiers = (struct config_key_modifiers){0};
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
char *copy = xstrndup(text, len);
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
for (char *tok_ctx = NULL, *key = strtok_r(copy, "+", &tok_ctx);
|
|
|
|
|
|
key != NULL;
|
|
|
|
|
|
key = strtok_r(NULL, "+", &tok_ctx))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (strcmp(key, XKB_MOD_NAME_SHIFT) == 0)
|
|
|
|
|
|
modifiers->shift = true;
|
|
|
|
|
|
else if (strcmp(key, XKB_MOD_NAME_CTRL) == 0)
|
|
|
|
|
|
modifiers->ctrl = true;
|
|
|
|
|
|
else if (strcmp(key, XKB_MOD_NAME_ALT) == 0)
|
|
|
|
|
|
modifiers->alt = true;
|
|
|
|
|
|
else if (strcmp(key, XKB_MOD_NAME_LOGO) == 0)
|
|
|
|
|
|
modifiers->meta = true;
|
|
|
|
|
|
else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s: not a valid modifier name",
|
|
|
|
|
|
path, lineno, key);
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
}
|
2020-07-30 18:57:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
ret = true;
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
out:
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static bool
|
2021-06-17 18:15:29 +02:00
|
|
|
|
parse_key_combos(struct config *conf, const char *combos,
|
|
|
|
|
|
struct key_combo_list *key_combos,
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
const char *section, const char *option,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
xassert(key_combos != NULL);
|
|
|
|
|
|
xassert(key_combos->count == 0 && key_combos->combos == NULL);
|
|
|
|
|
|
|
|
|
|
|
|
size_t size = 0; /* Size of ‘combos’ array in the key-combo list */
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
char *copy = xstrdup(combos);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
for (char *tok_ctx = NULL, *combo = strtok_r(copy, " ", &tok_ctx);
|
|
|
|
|
|
combo != NULL;
|
|
|
|
|
|
combo = strtok_r(NULL, " ", &tok_ctx))
|
|
|
|
|
|
{
|
2020-08-23 07:42:20 +02:00
|
|
|
|
struct config_key_modifiers modifiers = {0};
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
char *key = strrchr(combo, '+');
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
if (key == NULL) {
|
|
|
|
|
|
/* No modifiers */
|
|
|
|
|
|
key = combo;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_modifiers(conf, combo, key - combo, &modifiers, path, lineno))
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
key++; /* Skip past the '+' */
|
|
|
|
|
|
}
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
#if 0
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
if (modifiers.shift && strlen(key) == 1 && (*key >= 'A' && *key <= 'Z')) {
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"%s:%d: [%s]: %s: %s: "
|
|
|
|
|
|
"upper case keys not supported with explicit 'Shift' modifier",
|
|
|
|
|
|
path, lineno, section, option, combo);
|
|
|
|
|
|
user_notification_add(
|
|
|
|
|
|
&conf->notifications, USER_NOTIFICATION_DEPRECATED,
|
|
|
|
|
|
"%s:%d: [%s]: %s: \033[1m%s\033[m: "
|
|
|
|
|
|
"shifted keys not supported with explicit \033[1mShift\033[m "
|
|
|
|
|
|
"modifier",
|
|
|
|
|
|
path, lineno, section, option, combo);
|
2021-02-28 11:38:33 +01:00
|
|
|
|
*key = *key - 'A' + 'a';
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
}
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
#endif
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Translate key name to symbol */
|
|
|
|
|
|
xkb_keysym_t sym = xkb_keysym_from_name(key, 0);
|
|
|
|
|
|
if (sym == XKB_KEY_NoSymbol) {
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [%s]: %s: ]%s: key is not a valid XKB key name",
|
|
|
|
|
|
path, lineno, section, option, key);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
if (key_combos->count + 1 > size) {
|
|
|
|
|
|
size += 4;
|
|
|
|
|
|
key_combos->combos = xrealloc(
|
|
|
|
|
|
key_combos->combos, size * sizeof(key_combos->combos[0]));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
xassert(key_combos->count + 1 <= size);
|
|
|
|
|
|
key_combos->combos[key_combos->count++] = (struct key_combo){
|
|
|
|
|
|
.text = xstrdup(combo),
|
|
|
|
|
|
.modifiers = modifiers,
|
|
|
|
|
|
.sym = sym,
|
|
|
|
|
|
};
|
2020-03-12 17:16:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free(copy);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
return true;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
|
|
|
|
|
err:
|
2021-06-17 18:15:29 +02:00
|
|
|
|
free_key_combo_list(key_combos);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free(copy);
|
|
|
|
|
|
return false;
|
2020-03-12 17:16:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-09 22:40:53 +02:00
|
|
|
|
static bool
|
2021-02-07 14:46:29 +01:00
|
|
|
|
has_key_binding_collisions(struct config *conf,
|
|
|
|
|
|
int action, const char *const action_map[],
|
2021-06-17 18:15:29 +02:00
|
|
|
|
const struct config_key_binding_list *bindings,
|
|
|
|
|
|
const struct key_combo_list *key_combos,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
const char *path, unsigned lineno)
|
2020-08-09 22:40:53 +02:00
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t j = 0; j < bindings->count; j++) {
|
|
|
|
|
|
const struct config_key_binding *combo1 = &bindings->arr[j];
|
|
|
|
|
|
|
|
|
|
|
|
if (combo1->action == BIND_ACTION_NONE)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (combo1->action == action)
|
2020-12-05 11:21:17 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < key_combos->count; i++) {
|
|
|
|
|
|
const struct key_combo *combo2 = &key_combos->combos[i];
|
|
|
|
|
|
|
|
|
|
|
|
const struct config_key_modifiers *mods1 = &combo1->modifiers;
|
|
|
|
|
|
const struct config_key_modifiers *mods2 = &combo2->modifiers;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
2020-11-06 19:26:55 +01:00
|
|
|
|
bool shift = mods1->shift == mods2->shift;
|
|
|
|
|
|
bool alt = mods1->alt == mods2->alt;
|
|
|
|
|
|
bool ctrl = mods1->ctrl == mods2->ctrl;
|
|
|
|
|
|
bool meta = mods1->meta == mods2->meta;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
bool sym = combo1->sym == combo2->sym;
|
2020-11-06 19:26:55 +01:00
|
|
|
|
|
|
|
|
|
|
if (shift && alt && ctrl && meta && sym) {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
bool has_pipe = combo1->pipe.argv.args != NULL;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
|
2021-06-17 18:15:29 +02:00
|
|
|
|
path, lineno, combo2->text,
|
|
|
|
|
|
action_map[combo1->action],
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
has_pipe ? " [" : "",
|
2021-06-18 16:18:41 +02:00
|
|
|
|
has_pipe ? combo1->pipe.argv.args[0] : "",
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
has_pipe ? "]" : "");
|
|
|
|
|
|
return true;
|
2020-08-09 22:40:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2020-08-09 22:40:53 +02:00
|
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
|
static int
|
|
|
|
|
|
argv_compare(char *const *argv1, char *const *argv2)
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(argv1 != NULL);
|
|
|
|
|
|
xassert(argv2 != NULL);
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-09 13:52:33 +00:00
|
|
|
|
BUG("unexpected loop break");
|
2020-07-30 18:57:21 +02:00
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Parses a key binding value on the form
|
|
|
|
|
|
* "[cmd-to-exec arg1 arg2] Mods+Key"
|
|
|
|
|
|
*
|
|
|
|
|
|
* and extracts 'cmd-to-exec' and its arguments.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Input:
|
|
|
|
|
|
* - value: raw string, on the form mention above
|
|
|
|
|
|
* - cmd: pointer to string to will be allocated and filled with
|
|
|
|
|
|
* 'cmd-to-exec arg1 arg2'
|
|
|
|
|
|
* - argv: point to array of string. Array will be allocated. Will be
|
|
|
|
|
|
* filled with {'cmd-to-exec', 'arg1', 'arg2', NULL}
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns:
|
|
|
|
|
|
* - ssize_t, number of bytes to strip from 'value' to remove the '[]'
|
|
|
|
|
|
* enclosed cmd and its arguments, including any subsequent
|
|
|
|
|
|
* whitespace characters. I.e. if 'value' is "[cmd] BTN_RIGHT", the
|
|
|
|
|
|
* return value is 6 (strlen("[cmd] ")).
|
|
|
|
|
|
* - cmd: allocated string containing "cmd arg1 arg2...". Caller frees.
|
|
|
|
|
|
* - argv: allocatd array containing {"cmd", "arg1", "arg2", NULL}. Caller frees.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static ssize_t
|
2021-06-18 16:18:41 +02:00
|
|
|
|
pipe_argv_from_string(const char *value, char ***argv,
|
2020-11-06 19:27:46 +01:00
|
|
|
|
struct config *conf,
|
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
|
{
|
|
|
|
|
|
*argv = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (value[0] != '[')
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
const char *pipe_cmd_end = strrchr(value, ']');
|
|
|
|
|
|
if (pipe_cmd_end == NULL) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: unclosed '['", path, lineno);
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t pipe_len = pipe_cmd_end - value - 1;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
char *cmd = xstrndup(&value[1], pipe_len);
|
2020-11-06 19:27:46 +01:00
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (!tokenize_cmdline(cmd, argv)) {
|
2020-11-06 19:27:46 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: syntax error in command line", path, lineno);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
free(cmd);
|
2020-11-06 19:27:46 +01:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
2020-09-10 18:17:47 +02:00
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
ssize_t remove_len = pipe_cmd_end + 1 - value;
|
|
|
|
|
|
value = pipe_cmd_end + 1;
|
|
|
|
|
|
while (isspace(*value)) {
|
|
|
|
|
|
value++;
|
|
|
|
|
|
remove_len++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
free(cmd);
|
2020-11-06 19:27:46 +01:00
|
|
|
|
return remove_len;
|
2020-09-10 18:17:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 19:10:37 +02:00
|
|
|
|
static void NOINLINE
|
2021-06-29 18:08:15 +02:00
|
|
|
|
remove_action_from_key_bindings_list(struct config_key_binding_list *bindings,
|
|
|
|
|
|
int action, char **pipe_argv)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t remove_first_idx = 0;
|
|
|
|
|
|
size_t remove_count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < bindings->count; i++) {
|
|
|
|
|
|
struct config_key_binding *binding = &bindings->arr[i];
|
|
|
|
|
|
|
|
|
|
|
|
if (binding->action == action &&
|
|
|
|
|
|
((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
|
|
|
|
|
|
(binding->pipe.argv.args != NULL && pipe_argv != NULL &&
|
|
|
|
|
|
argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (remove_count++ == 0)
|
|
|
|
|
|
remove_first_idx = i;
|
|
|
|
|
|
|
|
|
|
|
|
xassert(remove_first_idx + remove_count - 1 == i);
|
|
|
|
|
|
|
|
|
|
|
|
if (binding->pipe.master_copy)
|
|
|
|
|
|
free_argv(&binding->pipe.argv);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (remove_count == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
size_t move_count = bindings->count - (remove_first_idx + remove_count);
|
|
|
|
|
|
|
|
|
|
|
|
memmove(
|
|
|
|
|
|
&bindings->arr[remove_first_idx],
|
|
|
|
|
|
&bindings->arr[remove_first_idx + remove_count],
|
|
|
|
|
|
move_count * sizeof(bindings->arr[0]));
|
|
|
|
|
|
bindings->count -= remove_count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
parse_key_binding_section(
|
|
|
|
|
|
const char *section, const char *key, const char *value,
|
|
|
|
|
|
int action_count, const char *const action_map[static action_count],
|
2021-06-17 18:15:29 +02:00
|
|
|
|
struct config_key_binding_list *bindings,
|
2021-02-07 14:46:29 +01:00
|
|
|
|
struct config *conf, const char *path, unsigned lineno)
|
2020-03-09 20:03:04 +01:00
|
|
|
|
{
|
2020-11-06 19:27:46 +01:00
|
|
|
|
char **pipe_argv;
|
2020-07-15 17:28:09 +02:00
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
ssize_t pipe_remove_len = pipe_argv_from_string(
|
2021-06-18 16:18:41 +02:00
|
|
|
|
value, &pipe_argv, conf, path, lineno);
|
2020-07-30 18:53:51 +02:00
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
if (pipe_remove_len < 0)
|
|
|
|
|
|
return false;
|
2020-07-15 17:28:09 +02:00
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
value += pipe_remove_len;
|
2020-07-15 13:32:31 +02:00
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
for (int action = 0; action < action_count; action++) {
|
|
|
|
|
|
if (action_map[action] == NULL)
|
2020-03-12 10:46:27 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
if (strcmp(key, action_map[action]) != 0)
|
2020-03-09 20:03:04 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Unset binding */
|
2020-03-18 14:52:04 +01:00
|
|
|
|
if (strcasecmp(value, "none") == 0) {
|
2021-06-29 18:08:15 +02:00
|
|
|
|
remove_action_from_key_bindings_list(bindings, action, pipe_argv);
|
2020-07-30 18:53:51 +02:00
|
|
|
|
free(pipe_argv);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2020-03-09 20:03:04 +01:00
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
struct key_combo_list key_combos = {0};
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
if (!parse_key_combos(
|
|
|
|
|
|
conf, value, &key_combos, section, key, path, lineno) ||
|
2021-02-07 14:46:29 +01:00
|
|
|
|
has_key_binding_collisions(
|
2021-04-05 14:33:44 +02:00
|
|
|
|
conf, action, action_map, bindings, &key_combos,
|
2021-02-07 14:46:29 +01:00
|
|
|
|
path, lineno))
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
{
|
2020-07-30 18:53:51 +02:00
|
|
|
|
free(pipe_argv);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free_key_combo_list(&key_combos);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-29 18:08:15 +02:00
|
|
|
|
remove_action_from_key_bindings_list(bindings, action, pipe_argv);
|
2020-07-15 16:39:07 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Emit key bindings */
|
2021-06-17 18:15:29 +02:00
|
|
|
|
size_t ofs = bindings->count;
|
|
|
|
|
|
bindings->count += key_combos.count;
|
|
|
|
|
|
bindings->arr = xrealloc(
|
|
|
|
|
|
bindings->arr, bindings->count * sizeof(bindings->arr[0]));
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
bool first = true;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < key_combos.count; i++) {
|
|
|
|
|
|
const struct key_combo *combo = &key_combos.combos[i];
|
2021-02-07 14:46:29 +01:00
|
|
|
|
struct config_key_binding binding = {
|
2020-07-15 16:39:07 +02:00
|
|
|
|
.action = action,
|
2021-06-17 18:15:29 +02:00
|
|
|
|
.modifiers = combo->modifiers,
|
|
|
|
|
|
.sym = combo->sym,
|
2020-07-30 18:53:51 +02:00
|
|
|
|
.pipe = {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.argv = {
|
|
|
|
|
|
.args = pipe_argv,
|
|
|
|
|
|
},
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
.master_copy = first,
|
2020-07-30 18:53:51 +02:00
|
|
|
|
},
|
2020-07-15 16:39:07 +02:00
|
|
|
|
};
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
/* TODO: we could re-use free:d slots */
|
|
|
|
|
|
bindings->arr[ofs + i] = binding;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
first = false;
|
2020-07-15 16:39:07 +02:00
|
|
|
|
}
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
|
|
|
|
|
free_key_combo_list(&key_combos);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [%s]: %s: invalid action",
|
|
|
|
|
|
path, lineno, section, key);
|
2020-09-08 19:09:36 +02:00
|
|
|
|
free(pipe_argv);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
return false;
|
2021-02-07 14:46:29 +01:00
|
|
|
|
}
|
2020-03-09 20:03:04 +01:00
|
|
|
|
|
2021-06-29 18:08:15 +02:00
|
|
|
|
UNITTEST
|
|
|
|
|
|
{
|
|
|
|
|
|
enum test_actions {
|
|
|
|
|
|
TEST_ACTION_NONE,
|
|
|
|
|
|
TEST_ACTION_FOO,
|
|
|
|
|
|
TEST_ACTION_BAR,
|
|
|
|
|
|
TEST_ACTION_COUNT,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const char *const map[] = {
|
|
|
|
|
|
[TEST_ACTION_NONE] = NULL,
|
|
|
|
|
|
[TEST_ACTION_FOO] = "foo",
|
|
|
|
|
|
[TEST_ACTION_BAR] = "bar",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct config_key_binding_list bindings = {0};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ADD foo=Escape
|
|
|
|
|
|
*
|
|
|
|
|
|
* This verifies we can bind a single key combo to an action.
|
|
|
|
|
|
*/
|
|
|
|
|
|
xassert(parse_key_binding_section(
|
|
|
|
|
|
"", "foo", "Escape", ALEN(map), map, &bindings, &conf, "", 0));
|
|
|
|
|
|
xassert(bindings.count == 1);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[0].sym == XKB_KEY_Escape);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ADD bar=Control+g Control+Shift+x
|
|
|
|
|
|
*
|
|
|
|
|
|
* This verifies we can bind multiple key combos to an action.
|
|
|
|
|
|
*/
|
|
|
|
|
|
xassert(parse_key_binding_section(
|
|
|
|
|
|
"", "bar", "Control+g Control+Shift+x", ALEN(map), map,
|
|
|
|
|
|
&bindings, &conf, "", 0));
|
|
|
|
|
|
xassert(bindings.count == 3);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[1].action == TEST_ACTION_BAR);
|
|
|
|
|
|
xassert(bindings.arr[1].sym == XKB_KEY_g);
|
|
|
|
|
|
xassert(bindings.arr[1].modifiers.ctrl);
|
|
|
|
|
|
xassert(bindings.arr[2].action == TEST_ACTION_BAR);
|
|
|
|
|
|
xassert(bindings.arr[2].sym == XKB_KEY_x);
|
|
|
|
|
|
xassert(bindings.arr[2].modifiers.ctrl && bindings.arr[2].modifiers.shift);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* REPLACE foo with foo=Mod+v Shift+q
|
|
|
|
|
|
*
|
|
|
|
|
|
* This verifies we can update a single-combo action with multiple
|
|
|
|
|
|
* key combos.
|
|
|
|
|
|
*/
|
|
|
|
|
|
xassert(parse_key_binding_section(
|
|
|
|
|
|
"", "foo", "Mod1+v Shift+q", ALEN(map), map,
|
|
|
|
|
|
&bindings, &conf, "", 0));
|
|
|
|
|
|
xassert(bindings.count == 4);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_BAR);
|
|
|
|
|
|
xassert(bindings.arr[1].action == TEST_ACTION_BAR);
|
|
|
|
|
|
xassert(bindings.arr[2].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[2].sym == XKB_KEY_v);
|
|
|
|
|
|
xassert(bindings.arr[2].modifiers.alt);
|
|
|
|
|
|
xassert(bindings.arr[3].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[3].sym == XKB_KEY_q);
|
|
|
|
|
|
xassert(bindings.arr[3].modifiers.shift);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* REMOVE bar
|
|
|
|
|
|
*/
|
|
|
|
|
|
xassert(parse_key_binding_section(
|
|
|
|
|
|
"", "bar", "none", ALEN(map), map, &bindings, &conf, "", 0));
|
|
|
|
|
|
xassert(bindings.count == 2);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[1].action == TEST_ACTION_FOO);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* REMOVE foo
|
|
|
|
|
|
*/
|
|
|
|
|
|
xassert(parse_key_binding_section(
|
|
|
|
|
|
"", "foo", "none", ALEN(map), map, &bindings, &conf, "", 0));
|
|
|
|
|
|
xassert(bindings.count == 0);
|
|
|
|
|
|
|
|
|
|
|
|
free(bindings.arr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_key_bindings(
|
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
return parse_key_binding_section(
|
|
|
|
|
|
"key-bindings", key, value, BIND_ACTION_KEY_COUNT, binding_action_map,
|
|
|
|
|
|
&conf->bindings.key, conf, path, lineno);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-29 17:27:01 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_search_bindings(
|
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-07-29 17:27:01 +02:00
|
|
|
|
{
|
2021-04-05 14:50:21 +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",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
|
|
|
|
|
|
"search binding action map size mismatch");
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
return parse_key_binding_section(
|
|
|
|
|
|
"search-bindings", key, value, BIND_ACTION_SEARCH_COUNT,
|
|
|
|
|
|
search_binding_action_map, &conf->bindings.search, conf, path, lineno);
|
2020-07-29 17:27:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-30 12:43:59 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_url_bindings(
|
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2021-01-30 12:43:59 +01:00
|
|
|
|
{
|
2021-04-05 14:50:21 +02:00
|
|
|
|
static const char *const url_binding_action_map[] = {
|
|
|
|
|
|
[BIND_ACTION_URL_NONE] = NULL,
|
|
|
|
|
|
[BIND_ACTION_URL_CANCEL] = "cancel",
|
|
|
|
|
|
[BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL] = "toggle-url-visible",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static_assert(ALEN(url_binding_action_map) == BIND_ACTION_URL_COUNT,
|
|
|
|
|
|
"URL binding action map size mismatch");
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
return parse_key_binding_section(
|
|
|
|
|
|
"url-bindings", key, value, BIND_ACTION_URL_COUNT,
|
|
|
|
|
|
url_binding_action_map, &conf->bindings.url, conf, path, lineno);
|
2021-01-30 12:43:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static bool
|
2021-06-17 18:15:29 +02:00
|
|
|
|
parse_mouse_combos(struct config *conf, const char *combos,
|
|
|
|
|
|
struct key_combo_list *key_combos,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
xassert(key_combos != NULL);
|
|
|
|
|
|
xassert(key_combos->count == 0 && key_combos->combos == NULL);
|
|
|
|
|
|
|
|
|
|
|
|
size_t size = 0; /* Size of the ‘combos’ array in key_combos */
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
|
|
|
|
|
char *copy = xstrdup(combos);
|
|
|
|
|
|
|
|
|
|
|
|
for (char *tok_ctx = NULL, *combo = strtok_r(copy, " ", &tok_ctx);
|
|
|
|
|
|
combo != NULL;
|
|
|
|
|
|
combo = strtok_r(NULL, " ", &tok_ctx))
|
|
|
|
|
|
{
|
2020-08-23 07:42:20 +02:00
|
|
|
|
struct config_key_modifiers modifiers = {0};
|
2020-08-10 19:39:37 +02:00
|
|
|
|
char *key = strrchr(combo, '+');
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
|
|
|
|
|
if (key == NULL) {
|
|
|
|
|
|
/* No modifiers */
|
|
|
|
|
|
key = combo;
|
|
|
|
|
|
} else {
|
2020-08-10 19:39:37 +02:00
|
|
|
|
*key = '\0';
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
if (!parse_modifiers(conf, combo, key - combo, &modifiers, path, lineno))
|
|
|
|
|
|
goto err;
|
2020-08-11 10:44:27 +02:00
|
|
|
|
if (modifiers.shift) {
|
2020-09-09 19:04:24 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: Shift cannot be used in mouse bindings",
|
|
|
|
|
|
path, lineno);
|
2020-08-11 10:44:27 +02:00
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
key++; /* Skip past the '+' */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-06 19:28:38 +01:00
|
|
|
|
size_t count = 1;
|
2020-08-10 19:39:37 +02:00
|
|
|
|
{
|
|
|
|
|
|
char *_count = strrchr(key, '-');
|
|
|
|
|
|
if (_count != NULL) {
|
|
|
|
|
|
*_count = '\0';
|
|
|
|
|
|
_count++;
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
char *end;
|
|
|
|
|
|
unsigned long value = strtoul(_count, &end, 10);
|
|
|
|
|
|
if (_count[0] == '\0' || *end != '\0' || errno != 0) {
|
|
|
|
|
|
if (errno != 0)
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO(
|
|
|
|
|
|
"%s:%d: %s: invalid click count", path, lineno, _count);
|
|
|
|
|
|
else
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: %s: invalid click count", path, lineno, _count);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
count = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *name;
|
|
|
|
|
|
int code;
|
|
|
|
|
|
} 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},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int button = 0;
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(map); i++) {
|
|
|
|
|
|
if (strcmp(key, map[i].name) == 0) {
|
|
|
|
|
|
button = map[i].code;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (button == 0) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s: invalid mouse button name", path, lineno, key);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct key_combo new = {
|
|
|
|
|
|
.text = xstrdup(combo),
|
|
|
|
|
|
.modifiers = modifiers,
|
|
|
|
|
|
.m = {
|
|
|
|
|
|
.button = button,
|
2020-08-10 19:39:37 +02:00
|
|
|
|
.count = count,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
},
|
|
|
|
|
|
};
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
if (key_combos->count + 1 > size) {
|
|
|
|
|
|
size += 4;
|
|
|
|
|
|
key_combos->combos = xrealloc(
|
|
|
|
|
|
key_combos->combos, size * sizeof(key_combos->combos[0]));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
xassert(key_combos->count + 1 <= size);
|
|
|
|
|
|
key_combos->combos[key_combos->count++] = new;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
err:
|
2021-06-17 18:15:29 +02:00
|
|
|
|
free_key_combo_list(key_combos);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free(copy);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
2021-06-17 18:15:29 +02:00
|
|
|
|
has_mouse_binding_collisions(struct config *conf, const struct key_combo_list *key_combos,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t j = 0; j < conf->bindings.mouse.count; j++) {
|
|
|
|
|
|
const struct config_mouse_binding *combo1 = &conf->bindings.mouse.arr[j];
|
|
|
|
|
|
if (combo1->action == BIND_ACTION_NONE)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < key_combos->count; i++) {
|
|
|
|
|
|
const struct key_combo *combo2 = &key_combos->combos[i];
|
|
|
|
|
|
|
|
|
|
|
|
const struct config_key_modifiers *mods1 = &combo1->modifiers;
|
|
|
|
|
|
const struct config_key_modifiers *mods2 = &combo2->modifiers;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
2020-11-06 19:26:55 +01:00
|
|
|
|
bool shift = mods1->shift == mods2->shift;
|
|
|
|
|
|
bool alt = mods1->alt == mods2->alt;
|
|
|
|
|
|
bool ctrl = mods1->ctrl == mods2->ctrl;
|
|
|
|
|
|
bool meta = mods1->meta == mods2->meta;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
bool button = combo1->button == combo2->m.button;
|
|
|
|
|
|
bool count = combo1->count == combo2->m.count;
|
2020-11-06 19:26:55 +01:00
|
|
|
|
|
|
|
|
|
|
if (shift && alt && ctrl && meta && button && count) {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
bool has_pipe = combo1->pipe.argv.args != NULL;
|
2020-11-06 19:29:23 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
|
2021-06-17 18:15:29 +02:00
|
|
|
|
path, lineno, combo2->text,
|
|
|
|
|
|
binding_action_map[combo1->action],
|
2020-11-06 19:29:23 +01:00
|
|
|
|
has_pipe ? " [" : "",
|
2021-06-18 16:18:41 +02:00
|
|
|
|
has_pipe ? combo1->pipe.argv.args[0] : "",
|
2020-11-06 19:29:23 +01:00
|
|
|
|
has_pipe ? "]" : "");
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-03-12 10:20:05 +01:00
|
|
|
|
{
|
2020-11-06 19:29:23 +01:00
|
|
|
|
char **pipe_argv;
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t pipe_remove_len = pipe_argv_from_string(
|
2021-06-18 16:18:41 +02:00
|
|
|
|
value, &pipe_argv, conf, path, lineno);
|
2020-11-06 19:29:23 +01:00
|
|
|
|
|
|
|
|
|
|
if (pipe_remove_len < 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
value += pipe_remove_len;
|
|
|
|
|
|
|
2020-08-11 09:55:33 +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-03-12 10:20:05 +01:00
|
|
|
|
if (strcmp(key, binding_action_map[action]) != 0)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Unset binding */
|
|
|
|
|
|
if (strcasecmp(value, "none") == 0) {
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
|
|
|
|
|
struct config_mouse_binding *binding =
|
|
|
|
|
|
&conf->bindings.mouse.arr[i];
|
|
|
|
|
|
|
|
|
|
|
|
if (binding->action == action) {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (binding->pipe.master_copy)
|
|
|
|
|
|
free_argv(&binding->pipe.argv);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
binding->action = BIND_ACTION_NONE;
|
2020-11-06 19:29:23 +01:00
|
|
|
|
}
|
2020-07-15 16:39:07 +02:00
|
|
|
|
}
|
2020-11-06 19:29:23 +01:00
|
|
|
|
free(pipe_argv);
|
2020-03-12 10:46:27 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
struct key_combo_list key_combos = {0};
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
if (!parse_mouse_combos(conf, value, &key_combos, path, lineno) ||
|
|
|
|
|
|
has_mouse_binding_collisions(conf, &key_combos, path, lineno))
|
|
|
|
|
|
{
|
2020-11-06 19:29:23 +01:00
|
|
|
|
free(pipe_argv);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free_key_combo_list(&key_combos);
|
2020-08-09 22:40:53 +02:00
|
|
|
|
return false;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
2020-08-09 22:40:53 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Remove existing bindings for this action */
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
|
|
|
|
|
struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i];
|
|
|
|
|
|
|
|
|
|
|
|
if (binding->action == action &&
|
2021-06-18 16:18:41 +02:00
|
|
|
|
((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
|
|
|
|
|
|
(binding->pipe.argv.args != NULL && pipe_argv != NULL &&
|
|
|
|
|
|
argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
|
2020-11-06 19:29:23 +01:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (binding->pipe.master_copy)
|
|
|
|
|
|
free_argv(&binding->pipe.argv);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
binding->action = BIND_ACTION_NONE;
|
2020-08-09 22:40:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/* Emit mouse bindings */
|
2021-06-17 18:15:29 +02:00
|
|
|
|
size_t ofs = conf->bindings.mouse.count;
|
|
|
|
|
|
conf->bindings.mouse.count += key_combos.count;
|
|
|
|
|
|
conf->bindings.mouse.arr = xrealloc(
|
|
|
|
|
|
conf->bindings.mouse.arr,
|
|
|
|
|
|
conf->bindings.mouse.count * sizeof(conf->bindings.mouse.arr[0]));
|
|
|
|
|
|
|
2020-11-06 19:29:23 +01:00
|
|
|
|
bool first = true;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < key_combos.count; i++) {
|
|
|
|
|
|
const struct key_combo *combo = &key_combos.combos[i];
|
2020-08-09 22:40:53 +02:00
|
|
|
|
struct config_mouse_binding binding = {
|
|
|
|
|
|
.action = action,
|
2021-06-17 18:15:29 +02:00
|
|
|
|
.modifiers = combo->modifiers,
|
|
|
|
|
|
.button = combo->m.button,
|
|
|
|
|
|
.count = combo->m.count,
|
2020-11-06 19:29:23 +01:00
|
|
|
|
.pipe = {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.argv = {
|
|
|
|
|
|
.args = pipe_argv,
|
|
|
|
|
|
},
|
2020-11-06 19:29:23 +01:00
|
|
|
|
.master_copy = first,
|
|
|
|
|
|
},
|
2020-08-09 22:40:53 +02:00
|
|
|
|
};
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
conf->bindings.mouse.arr[ofs + i] = binding;
|
2020-11-06 19:29:23 +01:00
|
|
|
|
first = false;
|
2020-08-09 22:40:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
free_key_combo_list(&key_combos);
|
2020-08-09 22:40:53 +02:00
|
|
|
|
return true;
|
2020-03-12 10:20:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [mouse-bindings]: %s: invalid key", path, lineno, key);
|
2020-11-06 19:29:23 +01:00
|
|
|
|
free(pipe_argv);
|
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,
|
2021-05-28 17:44:13 +02:00
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal)
|
2020-03-17 16:46:54 +01:00
|
|
|
|
{
|
2020-09-13 17:59:56 +02:00
|
|
|
|
if (strcmp(key, "scaling-filter") == 0) {
|
2021-05-08 16:50:49 +01:00
|
|
|
|
static const char filters[][12] = {
|
|
|
|
|
|
[FCFT_SCALING_FILTER_NONE] = "none",
|
|
|
|
|
|
[FCFT_SCALING_FILTER_NEAREST] = "nearest",
|
|
|
|
|
|
[FCFT_SCALING_FILTER_BILINEAR] = "bilinear",
|
|
|
|
|
|
[FCFT_SCALING_FILTER_CUBIC] = "cubic",
|
|
|
|
|
|
[FCFT_SCALING_FILTER_LANCZOS3] = "lanczos3",
|
2020-09-13 17:59:56 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(filters); i++) {
|
2021-05-08 16:50:49 +01:00
|
|
|
|
if (strcmp(value, filters[i]) == 0) {
|
|
|
|
|
|
conf->tweak.fcft_filter = i;
|
|
|
|
|
|
LOG_WARN("tweak: scaling-filter=%s", filters[i]);
|
2020-09-13 17:59:56 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [tweak]: %s: invalid 'scaling-filter' value, "
|
|
|
|
|
|
"expected one of 'none', 'nearest', 'bilinear', 'cubic' or "
|
|
|
|
|
|
"'lanczos3'", path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-15 11:45:27 +02:00
|
|
|
|
else if (strcmp(key, "overflowing-glyphs") == 0) {
|
|
|
|
|
|
conf->tweak.overflowing_glyphs = str_to_bool(value);
|
|
|
|
|
|
if (!conf->tweak.overflowing_glyphs)
|
|
|
|
|
|
LOG_WARN("tweak: disabled overflowing glyphs");
|
2021-05-31 17:10:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-12 19:52:52 +02:00
|
|
|
|
else if (strcmp(key, "damage-whole-window") == 0) {
|
2020-09-06 17:52:07 +02:00
|
|
|
|
conf->tweak.damage_whole_window = str_to_bool(value);
|
2020-09-12 19:52:52 +02:00
|
|
|
|
if (conf->tweak.damage_whole_window)
|
|
|
|
|
|
LOG_WARN("tweak: damage whole window");
|
|
|
|
|
|
}
|
2020-09-06 17:52:07 +02:00
|
|
|
|
|
2020-08-20 19:25:35 +02:00
|
|
|
|
else if (strcmp(key, "grapheme-shaping") == 0) {
|
|
|
|
|
|
conf->tweak.grapheme_shaping = str_to_bool(value);
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined(FOOT_GRAPHEME_CLUSTERING)
|
|
|
|
|
|
if (conf->tweak.grapheme_shaping) {
|
|
|
|
|
|
LOG_AND_NOTIFY_WARN(
|
|
|
|
|
|
"%s:%d: [tweak]: "
|
|
|
|
|
|
"grapheme-shaping enabled but foot was not compiled with "
|
|
|
|
|
|
"support for it", path, lineno);
|
|
|
|
|
|
conf->tweak.grapheme_shaping = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
if (conf->tweak.grapheme_shaping && !conf->can_shape_grapheme) {
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"%s:%d [tweak]: "
|
|
|
|
|
|
"grapheme-shaping enabled but fcft was not compiled with "
|
|
|
|
|
|
"support for it", path, lineno);
|
|
|
|
|
|
|
|
|
|
|
|
/* Keep it enabled though - this will cause us to do
|
|
|
|
|
|
* grapheme-clustering at least */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (conf->tweak.grapheme_shaping)
|
|
|
|
|
|
LOG_WARN("tweak: grapheme shaping");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 18:00:33 +02:00
|
|
|
|
else if (strcmp(key, "grapheme-width-method") == 0) {
|
2021-07-01 08:00:23 +02:00
|
|
|
|
if (strcmp(value, "double-width") == 0)
|
|
|
|
|
|
conf->tweak.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE;
|
2021-06-30 18:00:33 +02:00
|
|
|
|
else if (strcmp(value, "wcswidth") == 0)
|
|
|
|
|
|
conf->tweak.grapheme_width_method = GRAPHEME_WIDTH_WCSWIDTH;
|
|
|
|
|
|
|
|
|
|
|
|
LOG_WARN("%s:%d [tweak]: grapheme-width-method=%s", path, lineno, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-03 17:37:44 +02:00
|
|
|
|
else if (strcmp(key, "render-timer") == 0) {
|
2020-08-13 18:35:17 +02:00
|
|
|
|
if (strcmp(value, "none") == 0) {
|
|
|
|
|
|
conf->tweak.render_timer_osd = false;
|
|
|
|
|
|
conf->tweak.render_timer_log = false;
|
|
|
|
|
|
} else if (strcmp(value, "osd") == 0) {
|
|
|
|
|
|
conf->tweak.render_timer_osd = true;
|
|
|
|
|
|
conf->tweak.render_timer_log = false;
|
|
|
|
|
|
} else if (strcmp(value, "log") == 0) {
|
|
|
|
|
|
conf->tweak.render_timer_osd = false;
|
|
|
|
|
|
conf->tweak.render_timer_log = true;
|
|
|
|
|
|
} else if (strcmp(value, "both") == 0) {
|
|
|
|
|
|
conf->tweak.render_timer_osd = true;
|
|
|
|
|
|
conf->tweak.render_timer_log = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [tweak]: %s: invalid 'render-timer' value, "
|
|
|
|
|
|
"expected one of 'none', 'osd', 'log' or 'both'",
|
|
|
|
|
|
path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "delayed-render-lower") == 0) {
|
2020-03-17 16:46:54 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-08 10:27:35 +01:00
|
|
|
|
else if (strcmp(key, "box-drawing-base-thickness") == 0) {
|
|
|
|
|
|
double base_thickness;
|
|
|
|
|
|
if (!str_to_double(value, &base_thickness)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
|
"%s:%d: [tweak]: box-drawing-base-thickness: "
|
|
|
|
|
|
"expected a decimal value, got '%s'", path, lineno, value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->tweak.box_drawing_base_thickness = base_thickness;
|
|
|
|
|
|
LOG_WARN("tweak: box-drawing-base-thickness=%f",
|
|
|
|
|
|
conf->tweak.box_drawing_base_thickness);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-05 22:24:25 +02:00
|
|
|
|
else if (strcmp(key, "box-drawing-solid-shades") == 0) {
|
|
|
|
|
|
conf->tweak.box_drawing_solid_shades = str_to_bool(value);
|
|
|
|
|
|
|
|
|
|
|
|
if (!conf->tweak.box_drawing_solid_shades)
|
|
|
|
|
|
LOG_WARN("tweak: box-drawing-solid-shades=%s",
|
|
|
|
|
|
conf->tweak.box_drawing_solid_shades ? "yes" : "no");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-31 19:42:22 +02:00
|
|
|
|
else if (strcmp(key, "font-monospace-warn") == 0)
|
|
|
|
|
|
conf->tweak.font_monospace_warn = str_to_bool(value);
|
|
|
|
|
|
|
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
|
2021-06-11 04:40:08 -05:00
|
|
|
|
parse_key_value(char *kv, char **section, char **key, char **value)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
/*strip leading whitespace*/
|
|
|
|
|
|
while (*kv && isspace(*kv))
|
|
|
|
|
|
++kv;
|
|
|
|
|
|
|
|
|
|
|
|
if (section != NULL)
|
|
|
|
|
|
*section = NULL;
|
|
|
|
|
|
*key = kv;
|
|
|
|
|
|
*value = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
size_t kvlen = strlen(kv);
|
|
|
|
|
|
for (size_t i = 0; i < kvlen; ++i) {
|
|
|
|
|
|
if (kv[i] == '.') {
|
|
|
|
|
|
if (section != NULL && *section == NULL) {
|
|
|
|
|
|
*section = kv;
|
|
|
|
|
|
kv[i] = '\0';
|
|
|
|
|
|
*key = &kv[i + 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (kv[i] == '=') {
|
|
|
|
|
|
if (section != NULL && *section == NULL)
|
|
|
|
|
|
*section = "main";
|
|
|
|
|
|
kv[i] = '\0';
|
|
|
|
|
|
*value = &kv[i + 1];
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (*value == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace from key (leading stripped earlier) */
|
|
|
|
|
|
{
|
|
|
|
|
|
xassert(!isspace(**key));
|
|
|
|
|
|
|
|
|
|
|
|
char *end = *key + strlen(*key) - 1;
|
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
|
end--;
|
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip leading+trailing whitespace from valueue */
|
|
|
|
|
|
{
|
|
|
|
|
|
while (isspace(**value))
|
|
|
|
|
|
++*value;
|
|
|
|
|
|
|
|
|
|
|
|
if (*value[0] != '\0') {
|
|
|
|
|
|
char *end = *value + strlen(*value) - 1;
|
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
|
end--;
|
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum section {
|
|
|
|
|
|
SECTION_MAIN,
|
|
|
|
|
|
SECTION_BELL,
|
|
|
|
|
|
SECTION_SCROLLBACK,
|
|
|
|
|
|
SECTION_URL,
|
|
|
|
|
|
SECTION_COLORS,
|
|
|
|
|
|
SECTION_CURSOR,
|
|
|
|
|
|
SECTION_MOUSE,
|
|
|
|
|
|
SECTION_CSD,
|
|
|
|
|
|
SECTION_KEY_BINDINGS,
|
|
|
|
|
|
SECTION_SEARCH_BINDINGS,
|
|
|
|
|
|
SECTION_URL_BINDINGS,
|
|
|
|
|
|
SECTION_MOUSE_BINDINGS,
|
|
|
|
|
|
SECTION_TWEAK,
|
|
|
|
|
|
SECTION_COUNT,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Function pointer, called for each key/value line */
|
|
|
|
|
|
typedef bool (*parser_fun_t)(
|
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
|
const char *path, unsigned lineno, bool errors_are_fatal);
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
parser_fun_t fun;
|
|
|
|
|
|
const char *name;
|
|
|
|
|
|
} section_info[] = {
|
|
|
|
|
|
[SECTION_MAIN] = {&parse_section_main, "main"},
|
|
|
|
|
|
[SECTION_BELL] = {&parse_section_bell, "bell"},
|
|
|
|
|
|
[SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"},
|
|
|
|
|
|
[SECTION_URL] = {&parse_section_url, "url"},
|
|
|
|
|
|
[SECTION_COLORS] = {&parse_section_colors, "colors"},
|
|
|
|
|
|
[SECTION_CURSOR] = {&parse_section_cursor, "cursor"},
|
|
|
|
|
|
[SECTION_MOUSE] = {&parse_section_mouse, "mouse"},
|
|
|
|
|
|
[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_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"},
|
|
|
|
|
|
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
|
|
|
|
|
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
|
|
|
|
|
|
|
|
|
|
|
static enum section
|
|
|
|
|
|
str_to_section(const char *str)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (enum section section = SECTION_MAIN; section < SECTION_COUNT; ++section) {
|
|
|
|
|
|
if (strcmp(str, section_info[section].name) == 0)
|
|
|
|
|
|
return section;
|
|
|
|
|
|
}
|
|
|
|
|
|
return SECTION_COUNT;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_are_fatal)
|
|
|
|
|
|
{
|
|
|
|
|
|
enum section section = SECTION_MAIN;
|
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;
|
|
|
|
|
|
|
2021-01-03 14:08:25 -05:00
|
|
|
|
/* Split up into key/value pair + trailing comment separated by blank */
|
|
|
|
|
|
char *key_value = line;
|
|
|
|
|
|
char *comment = line;
|
|
|
|
|
|
while (comment[0] != '\0') {
|
|
|
|
|
|
const char c = comment[0];
|
|
|
|
|
|
comment++;
|
|
|
|
|
|
if (isblank(c) && comment[0] == '#') {
|
|
|
|
|
|
comment[0] = '\0'; /* Terminate key/value pair */
|
|
|
|
|
|
comment++;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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';
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
section = str_to_section(&key_value[1]);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
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-10-07 18:34:48 +02:00
|
|
|
|
if (section >= SECTION_COUNT) {
|
|
|
|
|
|
/* Last section name was invalid; ignore all keys in it */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
char *key, *value;
|
|
|
|
|
|
if (!parse_key_value(key_value, NULL, &key, &value)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: syntax error: %s", path, lineno, key_value);
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
break;
|
2020-07-28 19:55:09 +02:00
|
|
|
|
}
|
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
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(section >= 0 && section < SECTION_COUNT);
|
2020-10-07 18:34:48 +02:00
|
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
|
parser_fun_t section_parser = section_info[section].fun;
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(section_parser != NULL);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2021-05-28 17:44:13 +02:00
|
|
|
|
if (!section_parser(key, value, conf, path, lineno, errors_are_fatal))
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-17 19:17:58 +02:00
|
|
|
|
#define m_none {0}
|
|
|
|
|
|
#define m_alt {.alt = true}
|
|
|
|
|
|
#define m_ctrl {.ctrl = true}
|
|
|
|
|
|
#define m_shift {.shift = true}
|
|
|
|
|
|
#define m_ctrl_shift {.ctrl = true, .shift = true}
|
2021-05-07 17:18:46 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
add_default_key_bindings(struct config *conf)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
static const struct config_key_binding bindings[] = {
|
2021-06-17 19:17:58 +02:00
|
|
|
|
{BIND_ACTION_SCROLLBACK_UP_PAGE, m_shift, XKB_KEY_Page_Up},
|
|
|
|
|
|
{BIND_ACTION_SCROLLBACK_DOWN_PAGE, m_shift, XKB_KEY_Page_Down},
|
|
|
|
|
|
{BIND_ACTION_CLIPBOARD_COPY, m_ctrl_shift, XKB_KEY_c},
|
|
|
|
|
|
{BIND_ACTION_CLIPBOARD_PASTE, m_ctrl_shift, XKB_KEY_v},
|
|
|
|
|
|
{BIND_ACTION_PRIMARY_PASTE, m_shift, XKB_KEY_Insert},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_START, m_ctrl_shift, XKB_KEY_r},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_plus},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_equal},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_KP_Add},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_DOWN, m_ctrl, XKB_KEY_minus},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_DOWN, m_ctrl, XKB_KEY_KP_Subtract},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, XKB_KEY_0},
|
|
|
|
|
|
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, XKB_KEY_KP_0},
|
|
|
|
|
|
{BIND_ACTION_SPAWN_TERMINAL, m_ctrl_shift, XKB_KEY_n},
|
|
|
|
|
|
{BIND_ACTION_SHOW_URLS_LAUNCH, m_ctrl_shift, XKB_KEY_u},
|
2021-06-17 18:15:29 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
conf->bindings.key.count = ALEN(bindings);
|
|
|
|
|
|
conf->bindings.key.arr = xmalloc(sizeof(bindings));
|
|
|
|
|
|
memcpy(conf->bindings.key.arr, bindings, sizeof(bindings));
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:46 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
add_default_search_bindings(struct config *conf)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
static const struct config_key_binding bindings[] = {
|
2021-06-17 19:17:58 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_CANCEL, m_ctrl, XKB_KEY_c},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_CANCEL, m_ctrl, XKB_KEY_g},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_CANCEL, m_none, XKB_KEY_Escape},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_COMMIT, m_none, XKB_KEY_Return},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_FIND_PREV, m_ctrl, XKB_KEY_r},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_FIND_NEXT, m_ctrl, XKB_KEY_s},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_LEFT, m_none, XKB_KEY_Left},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_LEFT, m_ctrl, XKB_KEY_b},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m_ctrl, XKB_KEY_Left},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m_alt, XKB_KEY_b},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_RIGHT, m_none, XKB_KEY_Right},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_RIGHT, m_ctrl, XKB_KEY_f},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m_ctrl, XKB_KEY_Right},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m_alt, XKB_KEY_f},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_HOME, m_none, XKB_KEY_Home},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_HOME, m_ctrl, XKB_KEY_a},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_END, m_none, XKB_KEY_End},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EDIT_END, m_ctrl, XKB_KEY_e},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_PREV, m_none, XKB_KEY_BackSpace},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m_ctrl, XKB_KEY_BackSpace},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m_alt, XKB_KEY_BackSpace},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_NEXT, m_none, XKB_KEY_Delete},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m_ctrl, XKB_KEY_Delete},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m_alt, XKB_KEY_d},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_WORD, m_ctrl, XKB_KEY_w},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_WORD_WS, m_ctrl_shift, XKB_KEY_w},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, XKB_KEY_v},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, XKB_KEY_y},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_PRIMARY_PASTE, m_shift, XKB_KEY_Insert},
|
2021-06-17 18:15:29 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
conf->bindings.search.count = ALEN(bindings);
|
|
|
|
|
|
conf->bindings.search.arr = xmalloc(sizeof(bindings));
|
|
|
|
|
|
memcpy(conf->bindings.search.arr, bindings, sizeof(bindings));
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-30 12:43:59 +01:00
|
|
|
|
static void
|
|
|
|
|
|
add_default_url_bindings(struct config *conf)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
static const struct config_key_binding bindings[] = {
|
2021-06-17 19:17:58 +02:00
|
|
|
|
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_c},
|
|
|
|
|
|
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_g},
|
|
|
|
|
|
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_d},
|
|
|
|
|
|
{BIND_ACTION_URL_CANCEL, m_none, XKB_KEY_Escape},
|
|
|
|
|
|
{BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL, m_none, XKB_KEY_t},
|
2021-06-17 18:15:29 +02:00
|
|
|
|
};
|
2021-05-07 17:18:46 +02:00
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
conf->bindings.url.count = ALEN(bindings);
|
|
|
|
|
|
conf->bindings.url.arr = xmalloc(sizeof(bindings));
|
|
|
|
|
|
memcpy(conf->bindings.url.arr, bindings, sizeof(bindings));
|
2021-01-30 12:43:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
add_default_mouse_bindings(struct config *conf)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
static const struct config_mouse_binding bindings[] = {
|
2021-06-17 19:17:58 +02:00
|
|
|
|
{BIND_ACTION_PRIMARY_PASTE, m_none, BTN_MIDDLE, 1},
|
|
|
|
|
|
{BIND_ACTION_SELECT_BEGIN, m_none, BTN_LEFT, 1},
|
|
|
|
|
|
{BIND_ACTION_SELECT_BEGIN_BLOCK, m_ctrl, BTN_LEFT, 1},
|
|
|
|
|
|
{BIND_ACTION_SELECT_EXTEND, m_none, BTN_RIGHT, 1},
|
|
|
|
|
|
{BIND_ACTION_SELECT_EXTEND_CHAR_WISE, m_ctrl, BTN_RIGHT, 1},
|
|
|
|
|
|
{BIND_ACTION_SELECT_WORD, m_none, BTN_LEFT, 2},
|
|
|
|
|
|
{BIND_ACTION_SELECT_WORD_WS, m_ctrl, BTN_LEFT, 2},
|
|
|
|
|
|
{BIND_ACTION_SELECT_ROW, m_none, BTN_LEFT, 3},
|
2021-06-17 18:15:29 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
conf->bindings.mouse.count = ALEN(bindings);
|
|
|
|
|
|
conf->bindings.mouse.arr = xmalloc(sizeof(bindings));
|
|
|
|
|
|
memcpy(conf->bindings.mouse.arr, bindings, sizeof(bindings));
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
static void NOINLINE
|
|
|
|
|
|
config_font_list_clone(struct config_font_list *dst,
|
|
|
|
|
|
const struct config_font_list *src)
|
|
|
|
|
|
{
|
|
|
|
|
|
dst->count = src->count;
|
|
|
|
|
|
dst->arr = xmalloc(dst->count * sizeof(dst->arr[0]));
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < dst->count; j++) {
|
|
|
|
|
|
dst->arr[j].pt_size = src->arr[j].pt_size;
|
|
|
|
|
|
dst->arr[j].px_size = src->arr[j].px_size;
|
|
|
|
|
|
dst->arr[j].pattern = xstrdup(src->arr[j].pattern);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-17 10:12:14 +02:00
|
|
|
|
bool
|
2020-09-08 19:17:29 +02:00
|
|
|
|
config_load(struct config *conf, const char *conf_path,
|
2021-06-11 04:40:08 -05:00
|
|
|
|
user_notifications_t *initial_user_notifications,
|
|
|
|
|
|
config_override_t *overrides, bool errors_are_fatal)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2019-07-17 10:12:14 +02:00
|
|
|
|
bool ret = false;
|
2020-08-20 19:25:35 +02:00
|
|
|
|
enum fcft_capabilities fcft_caps = fcft_capabilities();
|
2019-07-17 10:12:14 +02:00
|
|
|
|
|
|
|
|
|
|
*conf = (struct config) {
|
terminfo: install to $datadir/foot/terminfo by default, append to TERMINFO_DIRS
As of 2021-07-31, ncurses ships its own version of foot’s terminfo.
Since:
* It doesn’t have the non-standard Sync,Tc,setrgbf,setrgbb
capabilities.
* It doesn’t set hs,fsl,dsl (statusbar).
* We want to be able to update our termminfo without waiting for an
ncurses release.
* Foot should be installable and usable on Linux systems that doesn’t
have the latest ncurses.
we still want to ship our own version. We can however not install it
to the default terminfo location (e.g. /usr/share/terminfo), since it
will collide with the ncurses provided files.
Our options are to either rename our terminfo to something else, or to
keep the name, but install our terminfo files somewhere else.
The first option would be the easy one. However, I think it makes
sense to use the same name. For example, a user that SSH’s into a
remote system that does *not* have our own version installed,
but *does* have the ncurses one, will gracefully fall back to that
one, which is better than manually having to set
e.g. TERM=xterm-256color.
Now, if we want to use the same name, we need to install it somewhere
else. But where? And how do we ensure our version is preferred over
the ncurses one?
I opted to $datadir/foot/terminfo (e.g. /usr/share/foot/terminfo) by
default. It makes it namespaced to foot (i.e. we’re not introducing a
new “standard” terminfo location), thus guaranteeing it wont collide
with ncurses.
To enable applications to find it, we export TERMINFO_DIRS. This is a
list of *additional* directories to search for terminfo files. If it’s
already defined, we *append* to it.
The nice thing with this is, if there’s no terminfo in that
location (e.g. when you SSH into a remote), the default terminfo
location is *also* searched. But only *after* having searched through
TERMINFO_DIRS.
In short: our version is preferred, but the ncurses one (or an older
version of our terminfo package!) will be used if ours cannot be
found.
To enable packagers full control over the new behavior, the existing
meson command line options have been modified, and a new option added:
-Dterminfo=disabled|enabled|auto: *build* and *install* the terminfo
files.
-Dcustom-terminfo-install-location=<path>: *where* the terminfo files
are expected to be found.
This *needs* to be set *even* if -Dterminfo=disabled. For example, if
the packaging script builds and packages the terminfo files separate
from the regular foot build. The path is *relative to $prefix*, and
defaults to $datadir/foot/terminfo.
This is the value that will be appended to TERMINFO_DIRS. Note that
you can set it to ‘no’, in which case foot will *not* set/modify
TERMINFO_DIRS. Only do this if you don’t intend to package foot’s
terminfo files at all (i.e. you plan on using the ncurses ones only).
-Ddefault-terminfo=foot. Allows overriding the default TERM
value. This should only be changed if the target platform doesn’t
support terminfo files.
Closes #671
2021-08-07 16:42:51 +02:00
|
|
|
|
.term = xstrdup(FOOT_DEFAULT_TERM),
|
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-10-09 19:44:23 +02:00
|
|
|
|
.word_delimiters = xwcsdup(L",│`|:\"'()[]{}<>"),
|
2020-09-08 19:17:29 +02:00
|
|
|
|
.size = {
|
|
|
|
|
|
.type = CONF_SIZE_PX,
|
2020-11-30 02:24:38 +00:00
|
|
|
|
.width = 700,
|
|
|
|
|
|
.height = 500,
|
2020-09-08 19:17:29 +02:00
|
|
|
|
},
|
2020-02-15 19:00:56 +01:00
|
|
|
|
.pad_x = 2,
|
|
|
|
|
|
.pad_y = 2,
|
2021-01-21 15:14:43 +01:00
|
|
|
|
.resize_delay_ms = 100,
|
2021-04-17 21:57:08 +02:00
|
|
|
|
.bold_in_bright = {
|
|
|
|
|
|
.enabled = false,
|
|
|
|
|
|
.palette_based = false,
|
|
|
|
|
|
},
|
2020-03-26 19:39:12 +01:00
|
|
|
|
.startup_mode = STARTUP_WINDOWED,
|
2021-06-17 18:15:29 +02:00
|
|
|
|
.fonts = {{0}},
|
2021-06-17 17:52:38 +02:00
|
|
|
|
.line_height = {.pt = 0, .px = -1},
|
|
|
|
|
|
.letter_spacing = {.pt = 0, .px = 0},
|
|
|
|
|
|
.horizontal_letter_offset = {.pt = 0, .px = 0},
|
|
|
|
|
|
.vertical_letter_offset = {.pt = 0, .px = 0},
|
|
|
|
|
|
.use_custom_underline_offset = false,
|
2021-04-09 23:19:20 +02:00
|
|
|
|
.box_drawings_uses_font_glyphs = false,
|
2020-12-17 12:05:22 +01:00
|
|
|
|
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
|
2021-04-29 04:12:55 -05:00
|
|
|
|
.bell = {
|
|
|
|
|
|
.urgent = false,
|
|
|
|
|
|
.notify = false,
|
|
|
|
|
|
.command = {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.argv = {.args = NULL},
|
2021-04-29 04:12:55 -05:00
|
|
|
|
},
|
|
|
|
|
|
.command_focused = false,
|
|
|
|
|
|
},
|
2021-05-20 17:56:56 +02:00
|
|
|
|
.url = {
|
|
|
|
|
|
.label_letters = xwcsdup(L"sadfjklewcmpgh"),
|
2021-08-09 18:25:36 +02:00
|
|
|
|
.uri_characters = xwcsdup(L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"),
|
2021-05-20 17:56:56 +02:00
|
|
|
|
.osc8_underline = OSC8_UNDERLINE_URL_MODE,
|
|
|
|
|
|
},
|
2020-08-20 19:25:35 +02:00
|
|
|
|
.can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
|
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-09-29 09:50:17 +02:00
|
|
|
|
.multiplier = 3.,
|
2020-07-25 14:31:45 +02:00
|
|
|
|
},
|
2019-07-21 11:06:28 +02:00
|
|
|
|
.colors = {
|
|
|
|
|
|
.fg = default_foreground,
|
|
|
|
|
|
.bg = default_background,
|
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 */
|
2021-02-06 10:35:48 +01:00
|
|
|
|
.use_custom = {
|
|
|
|
|
|
.selection = false,
|
2021-02-06 11:10:40 +01:00
|
|
|
|
.jump_label = false,
|
|
|
|
|
|
.url = false,
|
2021-02-06 10:35:48 +01:00
|
|
|
|
},
|
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,
|
|
|
|
|
|
},
|
2021-04-30 20:31:47 +02:00
|
|
|
|
.beam_thickness = {.pt = 1.5},
|
2021-05-18 18:52:10 +02:00
|
|
|
|
.underline_thickness = {.pt = 0., .px = -1},
|
2019-07-22 20:15:14 +02:00
|
|
|
|
},
|
2020-08-04 07:33:15 +02:00
|
|
|
|
.mouse = {
|
|
|
|
|
|
.hide_when_typing = false,
|
2020-09-15 19:09:00 +02:00
|
|
|
|
.alternate_scroll_mode = true,
|
2020-08-04 07:33:15 +02:00
|
|
|
|
},
|
2020-03-02 18:42:49 +01:00
|
|
|
|
.csd = {
|
|
|
|
|
|
.preferred = CONF_CSD_PREFER_SERVER,
|
2021-07-22 23:21:31 +02:00
|
|
|
|
.font = {0},
|
2020-03-02 18:42:49 +01:00
|
|
|
|
.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,
|
2021-01-16 11:26:03 +01:00
|
|
|
|
.selection_target = SELECTION_TARGET_PRIMARY,
|
2020-02-03 19:58:32 +01:00
|
|
|
|
.hold_at_exit = false,
|
2020-12-08 19:19:55 +01:00
|
|
|
|
.notify = {
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.argv = {.args = NULL},
|
2020-12-08 19:19:55 +01:00
|
|
|
|
},
|
2020-03-17 16:46:54 +01:00
|
|
|
|
|
|
|
|
|
|
.tweak = {
|
2020-09-13 17:59:56 +02:00
|
|
|
|
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
2021-06-15 11:45:27 +02:00
|
|
|
|
.overflowing_glyphs = true,
|
2020-08-20 19:25:35 +02:00
|
|
|
|
.grapheme_shaping = false,
|
2021-07-01 08:00:23 +02:00
|
|
|
|
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
2020-03-17 16:46:54 +01:00
|
|
|
|
.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-08-13 18:35:17 +02:00
|
|
|
|
.render_timer_osd = false,
|
|
|
|
|
|
.render_timer_log = false,
|
2020-09-06 17:52:07 +02:00
|
|
|
|
.damage_whole_window = false,
|
2021-01-11 13:06:48 +01:00
|
|
|
|
.box_drawing_base_thickness = 0.04,
|
2021-05-05 22:24:25 +02:00
|
|
|
|
.box_drawing_solid_shades = true,
|
2021-08-31 19:42:22 +02:00
|
|
|
|
.font_monospace_warn = true,
|
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
|
|
|
|
};
|
|
|
|
|
|
|
2021-09-25 02:46:22 +01:00
|
|
|
|
memcpy(conf->colors.table, default_color_table, sizeof(default_color_table));
|
2021-04-07 08:07:43 +02:00
|
|
|
|
|
2021-06-20 14:17:31 +02:00
|
|
|
|
tokenize_cmdline("notify-send -a ${app-id} -i ${app-id} ${title} ${body}",
|
2021-06-18 16:18:41 +02:00
|
|
|
|
&conf->notify.argv.args);
|
|
|
|
|
|
tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args);
|
2021-01-31 14:27:13 +01:00
|
|
|
|
|
2021-05-20 17:58:06 +02:00
|
|
|
|
static const wchar_t *url_protocols[] = {
|
|
|
|
|
|
L"http://",
|
|
|
|
|
|
L"https://",
|
|
|
|
|
|
L"ftp://",
|
|
|
|
|
|
L"ftps://",
|
|
|
|
|
|
L"file://",
|
|
|
|
|
|
L"gemini://",
|
|
|
|
|
|
L"gopher://",
|
|
|
|
|
|
};
|
|
|
|
|
|
conf->url.protocols = xmalloc(
|
|
|
|
|
|
ALEN(url_protocols) * sizeof(conf->url.protocols[0]));
|
|
|
|
|
|
conf->url.prot_count = ALEN(url_protocols);
|
|
|
|
|
|
conf->url.max_prot_len = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(url_protocols); i++) {
|
|
|
|
|
|
size_t len = wcslen(url_protocols[i]);
|
|
|
|
|
|
if (len > conf->url.max_prot_len)
|
|
|
|
|
|
conf->url.max_prot_len = len;
|
|
|
|
|
|
conf->url.protocols[i] = xwcsdup(url_protocols[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
qsort(
|
|
|
|
|
|
conf->url.uri_characters,
|
|
|
|
|
|
wcslen(conf->url.uri_characters),
|
|
|
|
|
|
sizeof(conf->url.uri_characters[0]),
|
|
|
|
|
|
&wccmp);
|
|
|
|
|
|
|
2021-02-07 14:52:04 +01:00
|
|
|
|
tll_foreach(*initial_user_notifications, it) {
|
2020-09-08 19:17:29 +02:00
|
|
|
|
tll_push_back(conf->notifications, it->item);
|
2021-02-07 14:52:04 +01:00
|
|
|
|
tll_remove(*initial_user_notifications, it);
|
|
|
|
|
|
}
|
2020-09-08 19:17:29 +02:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
add_default_key_bindings(conf);
|
|
|
|
|
|
add_default_search_bindings(conf);
|
2021-01-30 12:43:59 +01:00
|
|
|
|
add_default_url_bindings(conf);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
add_default_mouse_bindings(conf);
|
2020-07-15 16:39:07 +02:00
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
struct config_file conf_file = {.path = NULL, .fd = -1};
|
|
|
|
|
|
if (conf_path != NULL) {
|
|
|
|
|
|
int fd = open(conf_path, O_RDONLY);
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO("%s: failed to open", conf_path);
|
2020-07-31 17:07:14 +02:00
|
|
|
|
ret = !errors_are_fatal;
|
2019-12-17 19:08:43 +01:00
|
|
|
|
goto out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
conf_file.path = xstrdup(conf_path);
|
|
|
|
|
|
conf_file.fd = fd;
|
|
|
|
|
|
} else {
|
2021-03-16 09:54:32 +01:00
|
|
|
|
conf_file = open_config();
|
2020-08-27 19:48:13 +02:00
|
|
|
|
if (conf_file.fd < 0) {
|
2021-06-10 20:40:01 +02:00
|
|
|
|
LOG_WARN("no configuration found, using defaults");
|
2020-08-27 19:48:13 +02:00
|
|
|
|
ret = !errors_are_fatal;
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
}
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(conf_file.path != NULL);
|
|
|
|
|
|
xassert(conf_file.fd >= 0);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
LOG_INFO("loading configuration from %s", conf_file.path);
|
2019-08-12 19:31:21 +02:00
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
FILE *f = fdopen(conf_file.fd, "r");
|
2019-07-16 11:52:22 +02:00
|
|
|
|
if (f == NULL) {
|
2020-08-27 19:48:13 +02:00
|
|
|
|
LOG_AND_NOTIFY_ERRNO("%s: failed to open", conf_file.path);
|
2020-07-31 17:07:14 +02:00
|
|
|
|
ret = !errors_are_fatal;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
goto out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
ret = parse_config_file(f, conf, conf_file.path, errors_are_fatal) &&
|
|
|
|
|
|
config_override_apply(conf, overrides, errors_are_fatal);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
2021-02-06 10:35:48 +01:00
|
|
|
|
conf->colors.use_custom.selection =
|
2020-08-12 18:53:32 +02:00
|
|
|
|
conf->colors.selection_fg >> 24 == 0 &&
|
|
|
|
|
|
conf->colors.selection_bg >> 24 == 0;
|
|
|
|
|
|
|
2020-02-28 18:33:30 +01:00
|
|
|
|
out:
|
2021-06-17 18:15:29 +02:00
|
|
|
|
if (ret && conf->fonts[0].count == 0) {
|
2020-12-15 18:55:27 +01:00
|
|
|
|
struct config_font font;
|
|
|
|
|
|
if (!config_font_parse("monospace", &font)) {
|
|
|
|
|
|
LOG_ERR("failed to load font 'monospace' - no fonts installed?");
|
|
|
|
|
|
ret = false;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
conf->fonts[0].count = 1;
|
|
|
|
|
|
conf->fonts[0].arr = malloc(sizeof(font));
|
|
|
|
|
|
conf->fonts[0].arr[0] = font;
|
|
|
|
|
|
}
|
2020-12-15 18:55:27 +01:00
|
|
|
|
}
|
2019-12-17 19:08:43 +01:00
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
if (ret && conf->csd.font.count == 0)
|
|
|
|
|
|
config_font_list_clone(&conf->csd.font, &conf->fonts[0]);
|
|
|
|
|
|
|
2021-06-29 18:08:15 +02:00
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
for (size_t i = 0; i < conf->bindings.key.count; i++)
|
|
|
|
|
|
xassert(conf->bindings.key.arr[i].action != BIND_ACTION_NONE);
|
|
|
|
|
|
for (size_t i = 0; i < conf->bindings.search.count; i++)
|
|
|
|
|
|
xassert(conf->bindings.search.arr[i].action != BIND_ACTION_SEARCH_NONE);
|
|
|
|
|
|
for (size_t i = 0; i < conf->bindings.url.count; i++)
|
|
|
|
|
|
xassert(conf->bindings.url.arr[i].action != BIND_ACTION_URL_NONE);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-08-27 19:48:13 +02:00
|
|
|
|
free(conf_file.path);
|
2020-08-27 21:11:03 +02:00
|
|
|
|
if (conf_file.fd >= 0)
|
2020-08-27 19:48:13 +02:00
|
|
|
|
close(conf_file.fd);
|
|
|
|
|
|
|
2019-07-17 10:12:14 +02:00
|
|
|
|
return ret;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
bool
|
|
|
|
|
|
config_override_apply(struct config *conf, config_override_t *overrides, bool errors_are_fatal)
|
|
|
|
|
|
{
|
|
|
|
|
|
int i = -1;
|
|
|
|
|
|
tll_foreach(*overrides, it) {
|
|
|
|
|
|
++i;
|
|
|
|
|
|
char *section_str, *key, *value;
|
|
|
|
|
|
if (!parse_key_value(it->item, §ion_str, &key, &value)) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("syntax error: %s", it->item);
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum section section = str_to_section(section_str);
|
|
|
|
|
|
if (section == SECTION_COUNT) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR("override: invalid section name: %s", section_str);
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
parser_fun_t section_parser = section_info[section].fun;
|
|
|
|
|
|
xassert(section_parser != NULL);
|
|
|
|
|
|
|
|
|
|
|
|
if (!section_parser(key, value, conf, "override", i, errors_are_fatal)) {
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
static void
|
2021-02-07 14:46:29 +01:00
|
|
|
|
binding_pipe_free(struct config_binding_pipe *pipe)
|
|
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
if (pipe->master_copy)
|
|
|
|
|
|
free_argv(&pipe->argv);
|
2021-02-07 14:46:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2021-06-18 16:18:41 +02:00
|
|
|
|
binding_pipe_clone(struct config_binding_pipe *dst,
|
|
|
|
|
|
const struct config_binding_pipe *src)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
xassert(src->master_copy);
|
|
|
|
|
|
clone_argv(&dst->argv, &src->argv);
|
2021-02-07 14:46:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
static void NOINLINE
|
2021-06-17 18:15:29 +02:00
|
|
|
|
key_binding_list_free(struct config_key_binding_list *bindings)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < bindings->count; i++)
|
2021-06-18 16:18:41 +02:00
|
|
|
|
binding_pipe_free(&bindings->arr[i].pipe);
|
|
|
|
|
|
free(bindings->arr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void NOINLINE
|
|
|
|
|
|
key_binding_list_clone(struct config_key_binding_list *dst,
|
|
|
|
|
|
const struct config_key_binding_list *src)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct argv *last_master_argv = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
dst->count = src->count;
|
|
|
|
|
|
dst->arr = xmalloc(src->count * sizeof(dst->arr[0]));
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < src->count; i++) {
|
|
|
|
|
|
const struct config_key_binding *old = &src->arr[i];
|
|
|
|
|
|
struct config_key_binding *new = &dst->arr[i];
|
|
|
|
|
|
|
|
|
|
|
|
*new = *old;
|
|
|
|
|
|
|
|
|
|
|
|
if (old->pipe.argv.args == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (old->pipe.master_copy) {
|
|
|
|
|
|
binding_pipe_clone(&new->pipe, &old->pipe);
|
|
|
|
|
|
last_master_argv = &new->pipe.argv;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xassert(last_master_argv != NULL);
|
|
|
|
|
|
new->pipe.argv = *last_master_argv;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
mouse_binding_list_free(struct config_mouse_binding_list *bindings)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (size_t i = 0; i < bindings->count; i++)
|
|
|
|
|
|
binding_pipe_free(&bindings->arr[i].pipe);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
free(bindings->arr);
|
2021-02-07 14:46:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
static void NOINLINE
|
|
|
|
|
|
mouse_binding_list_clone(struct config_mouse_binding_list *dst,
|
|
|
|
|
|
const struct config_mouse_binding_list *src)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct argv *last_master_argv = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
dst->count = src->count;
|
|
|
|
|
|
dst->arr = xmalloc(src->count * sizeof(dst->arr[0]));
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < src->count; i++) {
|
|
|
|
|
|
const struct config_mouse_binding *old = &src->arr[i];
|
|
|
|
|
|
struct config_mouse_binding *new = &dst->arr[i];
|
|
|
|
|
|
|
|
|
|
|
|
*new = *old;
|
|
|
|
|
|
|
|
|
|
|
|
if (old->pipe.argv.args == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (old->pipe.master_copy) {
|
|
|
|
|
|
binding_pipe_clone(&new->pipe, &old->pipe);
|
|
|
|
|
|
last_master_argv = &new->pipe.argv;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xassert(last_master_argv != NULL);
|
|
|
|
|
|
new->pipe.argv = *last_master_argv;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct config *
|
|
|
|
|
|
config_clone(const struct config *old)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config *conf = xmalloc(sizeof(*conf));
|
|
|
|
|
|
*conf = *old;
|
|
|
|
|
|
|
|
|
|
|
|
conf->term = xstrdup(old->term);
|
|
|
|
|
|
conf->shell = xstrdup(old->shell);
|
|
|
|
|
|
conf->title = xstrdup(old->title);
|
|
|
|
|
|
conf->app_id = xstrdup(old->app_id);
|
|
|
|
|
|
conf->word_delimiters = xwcsdup(old->word_delimiters);
|
|
|
|
|
|
conf->scrollback.indicator.text = xwcsdup(old->scrollback.indicator.text);
|
|
|
|
|
|
conf->server_socket_path = xstrdup(old->server_socket_path);
|
|
|
|
|
|
spawn_template_clone(&conf->bell.command, &old->bell.command);
|
|
|
|
|
|
spawn_template_clone(&conf->notify, &old->notify);
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
for (size_t i = 0; i < ALEN(conf->fonts); i++)
|
|
|
|
|
|
config_font_list_clone(&conf->fonts[i], &old->fonts[i]);
|
|
|
|
|
|
config_font_list_clone(&conf->csd.font, &old->csd.font);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
|
|
|
|
|
|
conf->url.label_letters = xwcsdup(old->url.label_letters);
|
2021-08-09 18:25:36 +02:00
|
|
|
|
conf->url.uri_characters = xwcsdup(old->url.uri_characters);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_clone(&conf->url.launch, &old->url.launch);
|
|
|
|
|
|
conf->url.protocols = xmalloc(
|
|
|
|
|
|
old->url.prot_count * sizeof(conf->url.protocols[0]));
|
|
|
|
|
|
for (size_t i = 0; i < old->url.prot_count; i++)
|
|
|
|
|
|
conf->url.protocols[i] = xwcsdup(old->url.protocols[i]);
|
|
|
|
|
|
|
|
|
|
|
|
key_binding_list_clone(&conf->bindings.key, &old->bindings.key);
|
|
|
|
|
|
key_binding_list_clone(&conf->bindings.search, &old->bindings.search);
|
|
|
|
|
|
key_binding_list_clone(&conf->bindings.url, &old->bindings.url);
|
|
|
|
|
|
mouse_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
|
|
|
|
|
|
|
|
|
|
|
|
conf->notifications.length = 0;
|
|
|
|
|
|
conf->notifications.head = conf->notifications.tail = 0;
|
|
|
|
|
|
tll_foreach(old->notifications, it) {
|
|
|
|
|
|
struct user_notification notif = {
|
|
|
|
|
|
.kind = it->item.kind,
|
|
|
|
|
|
.text = xstrdup(it->item.text),
|
|
|
|
|
|
};
|
|
|
|
|
|
tll_push_back(conf->notifications, notif);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 21:06:21 +02:00
|
|
|
|
UNITTEST
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config original;
|
|
|
|
|
|
user_notifications_t nots = tll_init();
|
|
|
|
|
|
config_override_t overrides = tll_init();
|
|
|
|
|
|
|
|
|
|
|
|
bool ret = config_load(&original, "/dev/null", ¬s, &overrides, false);
|
|
|
|
|
|
xassert(ret);
|
|
|
|
|
|
|
|
|
|
|
|
struct config *clone = config_clone(&original);
|
|
|
|
|
|
xassert(clone != NULL);
|
|
|
|
|
|
xassert(clone != &original);
|
|
|
|
|
|
|
|
|
|
|
|
config_free(original);
|
|
|
|
|
|
config_free(*clone);
|
|
|
|
|
|
free(clone);
|
|
|
|
|
|
|
|
|
|
|
|
tll_free(overrides);
|
|
|
|
|
|
tll_free(nots);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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-10-09 19:44:23 +02:00
|
|
|
|
free(conf.word_delimiters);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_free(&conf.bell.command);
|
2020-07-28 19:56:53 +02:00
|
|
|
|
free(conf.scrollback.indicator.text);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_free(&conf.notify);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < ALEN(conf.fonts); i++)
|
|
|
|
|
|
config_font_list_destroy(&conf.fonts[i]);
|
2019-12-14 12:59:06 +01:00
|
|
|
|
free(conf.server_socket_path);
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
config_font_list_destroy(&conf.csd.font);
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
free(conf.url.label_letters);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_free(&conf.url.launch);
|
2021-05-20 17:58:06 +02:00
|
|
|
|
for (size_t i = 0; i < conf.url.prot_count; i++)
|
|
|
|
|
|
free(conf.url.protocols[i]);
|
|
|
|
|
|
free(conf.url.protocols);
|
2021-08-09 18:25:36 +02:00
|
|
|
|
free(conf.url.uri_characters);
|
2021-05-20 17:58:06 +02:00
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
key_binding_list_free(&conf.bindings.key);
|
|
|
|
|
|
key_binding_list_free(&conf.bindings.search);
|
|
|
|
|
|
key_binding_list_free(&conf.bindings.url);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
mouse_binding_list_free(&conf.bindings.mouse);
|
2020-07-15 16:39:07 +02:00
|
|
|
|
|
2020-09-08 19:17:29 +02:00
|
|
|
|
user_notifications_free(&conf.notifications);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
2020-12-15 18:55:27 +01:00
|
|
|
|
bool
|
|
|
|
|
|
config_font_parse(const char *pattern, struct config_font *font)
|
2020-07-07 10:44:55 +02:00
|
|
|
|
{
|
|
|
|
|
|
FcPattern *pat = FcNameParse((const FcChar8 *)pattern);
|
2020-12-15 18:55:27 +01:00
|
|
|
|
if (pat == NULL)
|
|
|
|
|
|
return false;
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2020-12-15 18:55:27 +01:00
|
|
|
|
*font = (struct config_font){
|
2020-07-07 10:44:55 +02:00
|
|
|
|
.pattern = stripped_pattern,
|
|
|
|
|
|
.pt_size = pt_size,
|
2020-12-15 18:55:27 +01:00
|
|
|
|
.px_size = px_size
|
|
|
|
|
|
};
|
|
|
|
|
|
return true;
|
2020-07-07 10:44:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-06-17 18:15:29 +02:00
|
|
|
|
config_font_list_destroy(struct config_font_list *font_list)
|
2020-07-07 10:44:55 +02:00
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < font_list->count; i++)
|
|
|
|
|
|
free(font_list->arr[i].pattern);
|
|
|
|
|
|
free(font_list->arr);
|
|
|
|
|
|
font_list->count = 0;
|
|
|
|
|
|
font_list->arr = NULL;
|
2020-07-07 10:44:55 +02:00
|
|
|
|
}
|
2021-08-31 19:42:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
check_if_font_is_monospaced(const char *pattern,
|
|
|
|
|
|
user_notifications_t *notifications)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct fcft_font *f = fcft_from_name(
|
|
|
|
|
|
1, (const char *[]){pattern}, ":size=8");
|
|
|
|
|
|
|
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
static const wchar_t chars[] = {L'a', L'i', L'l', L'M', L'W'};
|
|
|
|
|
|
|
|
|
|
|
|
bool is_monospaced = true;
|
|
|
|
|
|
int last_width = -1;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) {
|
|
|
|
|
|
const struct fcft_glyph *g = fcft_glyph_rasterize(
|
|
|
|
|
|
f, chars[i], FCFT_SUBPIXEL_NONE);
|
|
|
|
|
|
|
|
|
|
|
|
if (g == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (last_width >= 0 && g->advance.x != last_width) {
|
|
|
|
|
|
LOG_WARN("%s: font does not appear to be monospace; "
|
|
|
|
|
|
"check your config, or disable this warning by "
|
|
|
|
|
|
"setting [tweak].font-monospace-warn=no",
|
|
|
|
|
|
pattern);
|
|
|
|
|
|
|
|
|
|
|
|
user_notification_add(
|
|
|
|
|
|
notifications,
|
|
|
|
|
|
USER_NOTIFICATION_WARNING,
|
|
|
|
|
|
"%s: font does not appear to be monospace; "
|
|
|
|
|
|
"check your config, or disable this warning by "
|
2021-08-31 20:01:35 +02:00
|
|
|
|
"setting \033[1m[tweak].font-monospace-warn=no\033[22m",
|
2021-08-31 19:42:22 +02:00
|
|
|
|
pattern);
|
|
|
|
|
|
|
|
|
|
|
|
is_monospaced = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
last_width = g->advance.x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fcft_destroy(f);
|
|
|
|
|
|
return is_monospaced;
|
|
|
|
|
|
}
|