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"
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
#include "char32.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
|
#include "debug.h"
|
2020-03-09 20:03:04 +01:00
|
|
|
|
#include "input.h"
|
key-binding: new API, for handling sets of key bindings
Up until now, our Wayland seats have been tracking key bindings. This
makes sense, since the seat’s keymap determines how the key bindings
are resolved.
However, tying bindings to the seat/keymap alone isn’t enough, since
we also depend on the current configuration (i.e. user settings) when
resolving a key binding.
This means configurations that doesn’t match the wayland object’s
configuration, currently don’t resolve key bindings correctly. This
applies to footclients where the user has overridden key bindings on
the command line (e.g. --override key-bindings.foo=bar).
Thus, to correctly resolve key bindings, each set of key bindings must
be tied *both* to a seat/keymap, *and* a configuration.
This patch introduces a key-binding manager, with an API to
add/remove/lookup, and load/unload keymaps from sets of key bindings.
In the API, sets are tied to a seat and terminal instance, since this
makes the most sense (we need to instantiate, or incref a set whenever
a new terminal instance is created). Internally, the set is tied to a
seat and the terminal’s configuration.
Sets are *added* when a new seat is added, and when a new terminal
instance is created. Since there can only be one instance of each
seat, sets are always removed when a seat is removed.
Terminals on the other hand can re-use the same configuration (and
typically do). Thus, sets ref-count the configuration. In other words,
when instantiating a new terminal, we may not have to instantiate a
new set of key bindings, but can often be incref:ed instead.
Whenever the keymap changes on a seat, all key bindings sets
associated with that seat reloads (re-resolves) their key bindings.
Closes #931
2022-04-17 15:39:51 +02:00
|
|
|
|
#include "key-binding.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-08-04 23:28:16 +01:00
|
|
|
|
#include "xmalloc.h"
|
2021-11-20 15:23:24 +00:00
|
|
|
|
#include "xsnprintf.h"
|
2020-03-09 20:03:04 +01:00
|
|
|
|
|
2023-07-14 10:11:30 +02:00
|
|
|
|
static const uint32_t default_foreground = 0xffffff;
|
|
|
|
|
|
static const uint32_t default_background = 0x242424;
|
2019-07-21 11:06:28 +02:00
|
|
|
|
|
2021-12-22 20:21:46 +01:00
|
|
|
|
static const size_t min_csd_border_width = 5;
|
|
|
|
|
|
|
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
|
2023-07-14 10:11:30 +02:00
|
|
|
|
0x242424,
|
2023-07-18 16:18:53 +02:00
|
|
|
|
0xf62b5a,
|
|
|
|
|
|
0x47b413,
|
|
|
|
|
|
0xe3c401,
|
|
|
|
|
|
0x24acd4,
|
|
|
|
|
|
0xf2affd,
|
|
|
|
|
|
0x13c299,
|
2023-07-14 10:11:30 +02:00
|
|
|
|
0xe6e6e6,
|
2019-07-21 11:06:28 +02:00
|
|
|
|
|
2021-09-25 02:46:22 +01:00
|
|
|
|
// Bright
|
2023-07-14 10:11:30 +02:00
|
|
|
|
0x616161,
|
2023-07-18 16:18:53 +02:00
|
|
|
|
0xff4d51,
|
|
|
|
|
|
0x35d450,
|
|
|
|
|
|
0xe9e836,
|
|
|
|
|
|
0x5dc5f8,
|
|
|
|
|
|
0xfeabf2,
|
|
|
|
|
|
0x24dfc4,
|
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,
|
2021-10-24 11:49:37 +02:00
|
|
|
|
[BIND_ACTION_NOOP] = "noop",
|
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",
|
2022-02-23 19:03:54 +01:00
|
|
|
|
[BIND_ACTION_SCROLLBACK_HOME] = "scrollback-home",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_END] = "scrollback-end",
|
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",
|
2022-03-22 19:07:06 +01:00
|
|
|
|
[BIND_ACTION_SHOW_URLS_PERSISTENT] = "show-urls-persistent",
|
2022-02-06 19:36:44 +01:00
|
|
|
|
[BIND_ACTION_TEXT_BINDING] = "text-binding",
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
|
[BIND_ACTION_PROMPT_PREV] = "prompt-prev",
|
|
|
|
|
|
[BIND_ACTION_PROMPT_NEXT] = "prompt-next",
|
2022-07-28 18:09:16 +02:00
|
|
|
|
[BIND_ACTION_UNICODE_INPUT] = "unicode-input",
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
|
|
|
|
|
/* Mouse-specific actions */
|
2023-09-18 16:36:39 +02:00
|
|
|
|
[BIND_ACTION_SCROLLBACK_UP_MOUSE] = "scrollback-up-mouse",
|
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_DOWN_MOUSE] = "scrollback-down-mouse",
|
2020-08-11 09:55:33 +02:00
|
|
|
|
[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",
|
selection: add support for selecting the contents of a quote
This patch changes the default of triple clicking, from selecting the
current logical row, to first trying to select the contents of the
quote under the cursor, and if failing to find a quote, selecting the
current row (like before).
This is implemented by adding a new key binding, 'select-quote'.
It will search for surrounding quote characters, and if one is found
on each side of the cursor, the quote is selected. If not, the entire
row is selected instead.
Subsequent selection operations will behave as if the selection is
either a word selection (a quote was found), or a row selection (no
quote found).
Escaped quote characters are not supported: "foo \" bar" will match
'foo \', and not 'foo " bar'.
Mismatched quotes are not custom handled. They will simply not match.
Nested quotes ("123 'abc def' 456") are supported.
Closes #1364
2023-09-19 16:23:34 +02:00
|
|
|
|
[BIND_ACTION_SELECT_QUOTE] = "select-quote",
|
2020-08-11 10:17:19 +02:00
|
|
|
|
[BIND_ACTION_SELECT_ROW] = "select-row",
|
2020-03-09 20:03:04 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2021-12-05 15:19:22 +01: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",
|
2023-09-26 17:54:03 +02:00
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_CHAR] = "extend-char",
|
2021-12-05 15:19:22 +01:00
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_WORD] = "extend-to-word-boundary",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_WORD_WS] = "extend-to-next-whitespace",
|
2023-09-26 17:54:03 +02:00
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_LINE_DOWN] = "extend-line-down",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_CHAR] = "extend-backward-char",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD] = "extend-backward-to-word-boundary",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD_WS] = "extend-backward-to-next-whitespace",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_LINE_UP] = "extend-line-up",
|
2021-12-05 15:19:22 +01:00
|
|
|
|
[BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
|
2022-07-28 18:09:16 +02:00
|
|
|
|
[BIND_ACTION_SEARCH_UNICODE_INPUT] = "unicode-input",
|
2023-09-26 17:54:03 +02:00
|
|
|
|
[BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE] = "scrollback-up-page",
|
|
|
|
|
|
[BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page",
|
2021-12-05 15:19:22 +01: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",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2020-03-12 09:34:09 +01:00
|
|
|
|
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
|
|
|
|
|
|
"binding action map size mismatch");
|
2021-12-05 15:19:22 +01:00
|
|
|
|
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
|
|
|
|
|
|
"search binding action map size mismatch");
|
|
|
|
|
|
static_assert(ALEN(url_binding_action_map) == BIND_ACTION_URL_COUNT,
|
|
|
|
|
|
"URL binding action map size mismatch");
|
2020-03-12 09:34:09 +01:00
|
|
|
|
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct context {
|
|
|
|
|
|
struct config *conf;
|
|
|
|
|
|
const char *section;
|
|
|
|
|
|
const char *key;
|
|
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
|
|
|
|
const char *path;
|
|
|
|
|
|
unsigned lineno;
|
|
|
|
|
|
|
|
|
|
|
|
bool errors_are_fatal;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
static const enum user_notification_kind log_class_to_notify_kind[LOG_CLASS_COUNT] = {
|
|
|
|
|
|
[LOG_CLASS_WARNING] = USER_NOTIFICATION_WARNING,
|
|
|
|
|
|
[LOG_CLASS_ERROR] = USER_NOTIFICATION_ERROR,
|
|
|
|
|
|
};
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
static void NOINLINE VPRINTF(5)
|
|
|
|
|
|
log_and_notify_va(struct config *conf, enum log_class log_class,
|
|
|
|
|
|
const char *file, int lineno, const char *fmt, va_list va)
|
|
|
|
|
|
{
|
|
|
|
|
|
xassert(log_class < ALEN(log_class_to_notify_kind));
|
|
|
|
|
|
enum user_notification_kind kind = log_class_to_notify_kind[log_class];
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
if (kind == 0) {
|
2021-06-26 22:15:09 +01:00
|
|
|
|
BUG("unsupported log class: %d", (int)log_class);
|
|
|
|
|
|
return;
|
2021-05-07 16:41:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
char *formatted_msg = xvasprintf(fmt, va);
|
2021-11-06 14:22:26 +01:00
|
|
|
|
log_msg(log_class, LOG_MODULE, file, lineno, "%s", formatted_msg);
|
2021-11-14 15:08:47 +00:00
|
|
|
|
user_notification_add(&conf->notifications, kind, formatted_msg);
|
2021-11-05 17:13:39 +01:00
|
|
|
|
}
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01: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, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
va_list va;
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
|
log_and_notify_va(conf, log_class, file, lineno, fmt, va);
|
|
|
|
|
|
va_end(va);
|
2021-05-07 16:41:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void NOINLINE PRINTF(5)
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_contextual(struct context *ctx, enum log_class log_class,
|
|
|
|
|
|
const char *file, int lineno, const char *fmt, ...)
|
2021-05-07 16:41:10 +02:00
|
|
|
|
{
|
2021-11-05 17:13:39 +01:00
|
|
|
|
va_list va;
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
|
char *formatted_msg = xvasprintf(fmt, va);
|
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
2022-01-29 23:10:26 +05:30
|
|
|
|
bool print_dot = ctx->key != NULL;
|
|
|
|
|
|
bool print_colon = ctx->value != NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (!print_dot)
|
|
|
|
|
|
ctx->key = "";
|
|
|
|
|
|
|
|
|
|
|
|
if (!print_colon)
|
|
|
|
|
|
ctx->value = "";
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_and_notify(
|
2022-01-29 23:10:26 +05:30
|
|
|
|
ctx->conf, log_class, file, lineno, "%s:%d: [%s]%s%s%s%s: %s",
|
|
|
|
|
|
ctx->path, ctx->lineno, ctx->section, print_dot ? "." : "",
|
|
|
|
|
|
ctx->key, print_colon ? ": " : "", ctx->value, formatted_msg);
|
2021-11-05 17:13:39 +01:00
|
|
|
|
free(formatted_msg);
|
|
|
|
|
|
}
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
static void NOINLINE VPRINTF(4)
|
|
|
|
|
|
log_and_notify_errno_va(struct config *conf, const char *file, int lineno,
|
|
|
|
|
|
const char *fmt, va_list va)
|
|
|
|
|
|
{
|
|
|
|
|
|
int errno_copy = errno;
|
|
|
|
|
|
char *formatted_msg = xvasprintf(fmt, va);
|
|
|
|
|
|
log_and_notify(
|
|
|
|
|
|
conf, LOG_CLASS_ERROR, file, lineno,
|
|
|
|
|
|
"%s: %s", formatted_msg, strerror(errno_copy));
|
|
|
|
|
|
free(formatted_msg);
|
|
|
|
|
|
}
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
static void NOINLINE PRINTF(4)
|
|
|
|
|
|
log_and_notify_errno(struct config *conf, const char *file, int lineno,
|
|
|
|
|
|
const char *fmt, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
va_list va;
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
|
log_and_notify_errno_va(conf, file, lineno, fmt, va);
|
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
}
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
static void NOINLINE PRINTF(4)
|
|
|
|
|
|
log_contextual_errno(struct context *ctx, const char *file, int lineno,
|
|
|
|
|
|
const char *fmt, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
va_list va;
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
|
char *formatted_msg = xvasprintf(fmt, va);
|
|
|
|
|
|
va_end(va);
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_and_notify_errno(
|
|
|
|
|
|
ctx->conf, file, lineno, "%s:%d: [%s].%s: %s: %s",
|
|
|
|
|
|
ctx->path, ctx->lineno, ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
formatted_msg);
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
free(formatted_msg);
|
2021-05-07 16:41:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
#define LOG_CONTEXTUAL_ERR(...) \
|
|
|
|
|
|
log_contextual(ctx, LOG_CLASS_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_CONTEXTUAL_WARN(...) \
|
|
|
|
|
|
log_contextual(ctx, LOG_CLASS_WARNING, __FILE__, __LINE__, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_CONTEXTUAL_ERRNO(...) \
|
|
|
|
|
|
log_contextual_errno(ctx, __FILE__, __LINE__, __VA_ARGS__)
|
|
|
|
|
|
|
2021-05-07 16:41:10 +02:00
|
|
|
|
#define LOG_AND_NOTIFY_ERR(...) \
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_and_notify(conf, LOG_CLASS_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_WARN(...) \
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_and_notify(conf, LOG_CLASS_WARNING, __FILE__, __LINE__, __VA_ARGS__)
|
2021-05-07 16:41:10 +02:00
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_ERRNO(...) \
|
2021-11-05 17:13:39 +01:00
|
|
|
|
log_and_notify_errno(conf, __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 */
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct config_file
|
2021-03-16 09:54:32 +01:00
|
|
|
|
open_config(void)
|
2020-08-27 19:48:13 +02:00
|
|
|
|
{
|
2022-04-12 10:45:31 +02:00
|
|
|
|
char *path = NULL;
|
2020-08-27 19:48:13 +02:00
|
|
|
|
struct config_file ret = {.path = NULL, .fd = -1};
|
|
|
|
|
|
|
|
|
|
|
|
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
|
2022-04-12 10:45:31 +02:00
|
|
|
|
const char *xdg_config_dirs = getenv("XDG_CONFIG_DIRS");
|
2022-05-24 18:18:15 +02:00
|
|
|
|
const char *home_dir = getenv("HOME");
|
2020-08-27 19:48:13 +02:00
|
|
|
|
char *xdg_config_dirs_copy = NULL;
|
|
|
|
|
|
|
2022-04-12 10:45:31 +02:00
|
|
|
|
/* First, check XDG_CONFIG_HOME (or .config, if unset) */
|
|
|
|
|
|
if (xdg_config_home != NULL && xdg_config_home[0] != '\0')
|
|
|
|
|
|
path = xasprintf("%s/foot/foot.ini", xdg_config_home);
|
|
|
|
|
|
else if (home_dir != NULL)
|
|
|
|
|
|
path = xasprintf("%s/.config/foot/foot.ini", home_dir);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
|
2022-04-12 10:45:31 +02:00
|
|
|
|
if (path != NULL) {
|
|
|
|
|
|
LOG_DBG("checking for %s", path);
|
|
|
|
|
|
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
|
2022-04-12 10:45:31 +02:00
|
|
|
|
if (fd >= 0) {
|
|
|
|
|
|
ret = (struct config_file) {.path = path, .fd = fd};
|
|
|
|
|
|
path = NULL;
|
|
|
|
|
|
goto done;
|
2020-08-27 19:48:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-06 20:54:04 +02:00
|
|
|
|
xdg_config_dirs_copy = xdg_config_dirs != NULL && xdg_config_dirs[0] != '\0'
|
2022-04-06 18:44:09 +02:00
|
|
|
|
? strdup(xdg_config_dirs)
|
|
|
|
|
|
: strdup("/etc/xdg");
|
2020-08-27 19:48:13 +02:00
|
|
|
|
|
2022-04-12 10:45:31 +02:00
|
|
|
|
if (xdg_config_dirs_copy == NULL || xdg_config_dirs_copy[0] == '\0')
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
|
|
for (const char *conf_dir = strtok(xdg_config_dirs_copy, ":");
|
|
|
|
|
|
conf_dir != NULL;
|
|
|
|
|
|
conf_dir = strtok(NULL, ":"))
|
|
|
|
|
|
{
|
|
|
|
|
|
free(path);
|
|
|
|
|
|
path = xasprintf("%s/foot/foot.ini", conf_dir);
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DBG("checking for %s", path);
|
|
|
|
|
|
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
|
|
|
|
if (fd >= 0) {
|
|
|
|
|
|
ret = (struct config_file){.path = path, .fd = fd};
|
|
|
|
|
|
path = NULL;
|
|
|
|
|
|
goto done;
|
2020-08-27 19:48:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-12 10:45:31 +02:00
|
|
|
|
done:
|
2020-08-27 19:48:13 +02:00
|
|
|
|
free(xdg_config_dirs_copy);
|
2022-04-12 10:45:31 +02:00
|
|
|
|
free(path);
|
2020-08-27 19:48:13 +02:00
|
|
|
|
return ret;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
static int
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
c32cmp_single(const void *_a, const void *_b)
|
2021-08-09 18:25:36 +02:00
|
|
|
|
{
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
const char32_t *a = _a;
|
|
|
|
|
|
const char32_t *b = _b;
|
2021-08-09 18:25:36 +02:00
|
|
|
|
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
|
2021-11-06 11:25:55 +01:00
|
|
|
|
value_to_bool(struct context *ctx, bool *res)
|
2020-03-11 16:10:14 +01:00
|
|
|
|
{
|
2021-11-06 11:25:55 +01:00
|
|
|
|
static const char *const yes[] = {"on", "true", "yes", "1"};
|
|
|
|
|
|
static const char *const no[] = {"off", "false", "no", "0"};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(yes); i++) {
|
|
|
|
|
|
if (strcasecmp(ctx->value, yes[i]) == 0) {
|
|
|
|
|
|
*res = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(no); i++) {
|
|
|
|
|
|
if (strcasecmp(ctx->value, no[i]) == 0) {
|
|
|
|
|
|
*res = false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid boolean value");
|
|
|
|
|
|
return false;
|
2020-03-11 16:10:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 11:25:55 +01:00
|
|
|
|
|
2021-11-06 12:01:57 +01: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-11-05 14:08:25 +01:00
|
|
|
|
static bool NOINLINE
|
2021-11-06 12:01:57 +01:00
|
|
|
|
str_to_uint32(const char *s, int base, uint32_t *res)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned long v;
|
|
|
|
|
|
bool ret = str_to_ulong(s, base, &v);
|
|
|
|
|
|
if (v > UINT32_MAX)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
*res = v;
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
str_to_uint16(const char *s, int base, uint16_t *res)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned long v;
|
|
|
|
|
|
bool ret = str_to_ulong(s, base, &v);
|
|
|
|
|
|
if (v > UINT16_MAX)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
*res = v;
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
value_to_uint16(struct context *ctx, int base, uint16_t *res)
|
2021-11-05 14:08:25 +01:00
|
|
|
|
{
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!str_to_uint16(ctx->value, base, res)) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
|
|
|
|
|
"invalid integer value, or outside range 0-%u", UINT16_MAX);
|
2021-11-05 17:18:33 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2021-11-06 12:01:57 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2021-11-05 17:18:33 +01:00
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
value_to_uint32(struct context *ctx, int base, uint32_t *res)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!str_to_uint32(ctx->value, base, res)){
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
|
|
|
|
|
"invalid integer value, or outside range 0-%u", UINT32_MAX);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2021-11-05 17:18:33 +01:00
|
|
|
|
return true;
|
2021-11-05 14:08:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:32:20 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
value_to_dimensions(struct context *ctx, uint32_t *x, uint32_t *y)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sscanf(ctx->value, "%ux%u", x, y) != 2) {
|
2022-04-06 20:04:19 +02:00
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid dimensions (must be in the form AxB)");
|
2021-11-06 12:32:20 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2023-07-26 16:12:36 +02:00
|
|
|
|
value_to_float(struct context *ctx, float *res)
|
2019-08-15 18:15:43 +02:00
|
|
|
|
{
|
2021-11-05 14:13:30 +01:00
|
|
|
|
const char *s = ctx->value;
|
|
|
|
|
|
|
2019-08-15 18:15:43 +02:00
|
|
|
|
if (s == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
*res = strtof(s, &end);
|
2021-11-05 17:20:17 +01:00
|
|
|
|
if (!(errno == 0 && *end == '\0')) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid decimal value");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
2019-08-15 18:15:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
value_to_str(struct context *ctx, char **res)
|
|
|
|
|
|
{
|
2022-12-01 15:00:44 +01:00
|
|
|
|
char *copy = xstrdup(ctx->value);
|
|
|
|
|
|
char *end = copy + strlen(copy) - 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* Un-quote
|
|
|
|
|
|
*
|
|
|
|
|
|
* Note: this is very simple; we only support the *entire* value
|
|
|
|
|
|
* being quoted. That is, no mid-value quotes. Both double and
|
|
|
|
|
|
* single quotes are supported.
|
|
|
|
|
|
*
|
|
|
|
|
|
* - key="value" OK
|
|
|
|
|
|
* - key=abc "quote" def NOT OK
|
|
|
|
|
|
* - key=’value’ OK
|
|
|
|
|
|
*
|
|
|
|
|
|
* Finally, we support escaping the quote character, and the
|
|
|
|
|
|
* escape character itself:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - key="value \"quotes\""
|
|
|
|
|
|
* - key="backslash: \\"
|
|
|
|
|
|
*
|
|
|
|
|
|
* ONLY the "current" quote character can be escaped:
|
|
|
|
|
|
*
|
|
|
|
|
|
* key="value \'" NOt OK (both backslash and single quote is kept)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if ((copy[0] == '"' && *end == '"') ||
|
|
|
|
|
|
(copy[0] == '\'' && *end == '\''))
|
|
|
|
|
|
{
|
|
|
|
|
|
const char quote = copy[0];
|
|
|
|
|
|
*end = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
memmove(copy, copy + 1, end - copy);
|
|
|
|
|
|
|
|
|
|
|
|
/* Un-escape */
|
|
|
|
|
|
for (char *p = copy; *p != '\0'; p++) {
|
|
|
|
|
|
if (p[0] == '\\' && (p[1] == '\\' || p[1] == quote)) {
|
|
|
|
|
|
memmove(p, p + 1, end - p);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
free(*res);
|
2022-12-01 15:00:44 +01:00
|
|
|
|
*res = copy;
|
2021-11-06 11:32:11 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
value_to_wchars(struct context *ctx, char32_t **res)
|
2021-02-13 11:42:21 +01:00
|
|
|
|
{
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
char32_t *s = ambstoc32(ctx->value);
|
|
|
|
|
|
if (s == NULL) {
|
2022-03-22 19:07:06 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid string value");
|
2021-02-13 11:42:21 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 20:32:35 +01:00
|
|
|
|
free(*res);
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
*res = s;
|
2021-02-13 11:42:21 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
value_to_enum(struct context *ctx, const char **value_map, int *res)
|
2021-11-05 18:11:53 +01:00
|
|
|
|
{
|
|
|
|
|
|
size_t str_len = 0;
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (; value_map[count] != NULL; count++) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
if (strcasecmp(value_map[count], ctx->value) == 0) {
|
|
|
|
|
|
*res = count;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2021-11-05 18:11:53 +01:00
|
|
|
|
str_len += strlen(value_map[count]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const size_t size = str_len + count * 4 + 1;
|
2021-11-20 15:23:24 +00:00
|
|
|
|
char valid_values[512];
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
xassert(size < sizeof(valid_values));
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++)
|
2021-11-20 15:23:24 +00:00
|
|
|
|
idx += xsnprintf(&valid_values[idx], size - idx, "'%s', ", value_map[i]);
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
|
valid_values[idx - 2] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("not one of %s", valid_values);
|
2021-11-06 13:39:59 +01:00
|
|
|
|
*res = -1;
|
|
|
|
|
|
return false;
|
2021-11-05 18:11:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-11-05 14:08:25 +01:00
|
|
|
|
value_to_color(struct context *ctx, uint32_t *color, bool allow_alpha)
|
2019-07-29 20:13:26 +02:00
|
|
|
|
{
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!str_to_uint32(ctx->value, 16, color)) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid color value");
|
2019-07-23 18:54:58 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!allow_alpha && (*color & 0xff000000) != 0) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("color value must not have an alpha component");
|
2020-03-02 18:42:49 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-11-05 14:08:25 +01:00
|
|
|
|
value_to_two_colors(struct context *ctx,
|
|
|
|
|
|
uint32_t *first, uint32_t *second, bool allow_alpha)
|
2021-02-06 11:10:40 +01:00
|
|
|
|
{
|
2021-11-05 14:08:25 +01:00
|
|
|
|
bool ret = false;
|
|
|
|
|
|
const char *original_value = ctx->value;
|
|
|
|
|
|
|
2021-02-06 11:10:40 +01:00
|
|
|
|
/* TODO: do this without strdup() */
|
2021-11-05 14:08:25 +01:00
|
|
|
|
char *value_copy = xstrdup(ctx->value);
|
2021-02-06 11:10:40 +01:00
|
|
|
|
const char *first_as_str = strtok(value_copy, " ");
|
|
|
|
|
|
const char *second_as_str = strtok(NULL, " ");
|
|
|
|
|
|
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (first_as_str == NULL || second_as_str == NULL) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid double color value");
|
2021-11-05 14:08:25 +01:00
|
|
|
|
goto out;
|
2021-02-06 11:10:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 14:08:25 +01:00
|
|
|
|
ctx->value = first_as_str;
|
|
|
|
|
|
if (!value_to_color(ctx, first, allow_alpha))
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
|
|
ctx->value = second_as_str;
|
|
|
|
|
|
if (!value_to_color(ctx, second, allow_alpha))
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
|
|
|
|
out:
|
2021-02-06 11:10:40 +01:00
|
|
|
|
free(value_copy);
|
2021-11-05 14:08:25 +01:00
|
|
|
|
ctx->value = original_value;
|
|
|
|
|
|
return ret;
|
2021-02-06 11:10:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-07 17:18:27 +02:00
|
|
|
|
static bool NOINLINE
|
2021-11-05 14:13:30 +01:00
|
|
|
|
value_to_pt_or_px(struct context *ctx, struct pt_or_px *res)
|
2021-01-07 17:00:58 +01:00
|
|
|
|
{
|
2021-11-05 14:13:30 +01:00
|
|
|
|
const char *s = ctx->value;
|
|
|
|
|
|
|
2021-01-07 17:00:58 +01:00
|
|
|
|
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);
|
2022-12-17 10:25:16 +01:00
|
|
|
|
if (!(len > 2 && errno == 0 && end == s + len - 2)) {
|
2022-04-06 20:04:19 +02:00
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid px value (must be in the form 12px)");
|
2021-01-07 17:00:58 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
res->pt = 0;
|
|
|
|
|
|
res->px = value;
|
|
|
|
|
|
} else {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
float value;
|
2023-07-26 16:12:36 +02:00
|
|
|
|
if (!value_to_float(ctx, &value))
|
2021-01-07 17:00:58 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
res->pt = value;
|
|
|
|
|
|
res->px = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
static struct config_font_list NOINLINE
|
2021-11-05 14:17:42 +01:00
|
|
|
|
value_to_fonts(struct context *ctx)
|
2021-07-22 23:21:31 +02:00
|
|
|
|
{
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
struct config_font *fonts = NULL;
|
|
|
|
|
|
|
2021-11-05 14:17:42 +01:00
|
|
|
|
char *copy = xstrdup(ctx->value);
|
2021-07-22 23:21:31 +02:00
|
|
|
|
for (const char *font = strtok(copy, ",");
|
|
|
|
|
|
font != NULL;
|
|
|
|
|
|
font = strtok(NULL, ","))
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Trim spaces, strictly speaking not necessary, but looks nice :) */
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while (isspace(font[0]))
|
2021-07-22 23:21:31 +02:00
|
|
|
|
font++;
|
|
|
|
|
|
|
|
|
|
|
|
if (font[0] == '\0')
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
struct config_font font_data;
|
|
|
|
|
|
if (!config_font_parse(font, &font_data)) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
ctx->value = font;
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid font specification");
|
2021-07-22 23:21:31 +02:00
|
|
|
|
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-11-05 14:19:48 +01:00
|
|
|
|
value_to_spawn_template(struct context *ctx,
|
|
|
|
|
|
struct config_spawn_template *template)
|
2021-01-31 14:27:13 +01:00
|
|
|
|
{
|
2021-06-18 16:18:41 +02:00
|
|
|
|
spawn_template_free(template);
|
2021-01-31 14:27:13 +01:00
|
|
|
|
|
|
|
|
|
|
char **argv = NULL;
|
|
|
|
|
|
|
2021-11-05 14:19:48 +01:00
|
|
|
|
if (!tokenize_cmdline(ctx->value, &argv)) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("syntax error in command line");
|
2021-01-31 14:27:13 +01:00
|
|
|
|
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-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
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_main(struct context *ctx)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
const char *value = ctx->value;
|
|
|
|
|
|
bool errors_are_fatal = ctx->errors_are_fatal;
|
|
|
|
|
|
|
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] == '/') {
|
2022-05-24 18:18:15 +02:00
|
|
|
|
const char *home_dir = getenv("HOME");
|
2021-07-30 14:44:16 +02:00
|
|
|
|
|
2021-07-30 14:47:45 +02:00
|
|
|
|
if (home_dir == NULL) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERRNO("failed to expand '~'");
|
2021-07-30 14:47:45 +02:00
|
|
|
|
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] != '/') {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not an absolute 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) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERRNO("failed to open");
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
else if (strcmp(key, "term") == 0)
|
|
|
|
|
|
return value_to_str(ctx, &conf->term);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
else if (strcmp(key, "shell") == 0)
|
|
|
|
|
|
return value_to_str(ctx, &conf->shell);
|
2019-07-17 09:40:58 +02:00
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
else if (strcmp(key, "login-shell") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->login_shell);
|
2020-02-20 18:35:10 +01:00
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
else if (strcmp(key, "title") == 0)
|
|
|
|
|
|
return value_to_str(ctx, &conf->title);
|
2020-04-01 19:59:47 +02:00
|
|
|
|
|
2021-07-04 17:59:40 +02:00
|
|
|
|
else if (strcmp(key, "locked-title") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->locked_title);
|
2021-07-04 17:59:40 +02:00
|
|
|
|
|
2021-11-06 11:32:11 +01:00
|
|
|
|
else if (strcmp(key, "app-id") == 0)
|
|
|
|
|
|
return value_to_str(ctx, &conf->app_id);
|
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) {
|
2021-11-06 12:32:20 +01:00
|
|
|
|
if (!value_to_dimensions(ctx, &conf->size.width, &conf->size.height))
|
2020-09-08 19:17:29 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->size.type = CONF_SIZE_PX;
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
2020-09-08 19:17:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "initial-window-size-chars") == 0) {
|
2021-11-06 12:32:20 +01:00
|
|
|
|
if (!value_to_dimensions(ctx, &conf->size.width, &conf->size.height))
|
2019-08-23 17:26:41 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
2020-09-08 19:17:29 +02:00
|
|
|
|
conf->size.type = CONF_SIZE_CELLS;
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
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) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
2022-04-06 20:04:19 +02:00
|
|
|
|
"invalid padding (must be in the form PAD_XxPAD_Y [center])");
|
2020-02-15 19:00:56 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->pad_x = x;
|
|
|
|
|
|
conf->pad_y = y;
|
2021-01-06 11:17:29 +01:00
|
|
|
|
conf->center = center;
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
2020-02-15 19:00:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "resize-delay-ms") == 0)
|
|
|
|
|
|
return value_to_uint16(ctx, 10, &conf->resize_delay_ms);
|
2021-01-21 15:14:43 +01:00
|
|
|
|
|
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 {
|
2021-11-06 11:25:55 +01:00
|
|
|
|
if (!value_to_bool(ctx, &conf->bold_in_bright.enabled))
|
|
|
|
|
|
return false;
|
2021-04-17 21:57:08 +02:00
|
|
|
|
conf->bold_in_bright.palette_based = false;
|
|
|
|
|
|
}
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
2021-04-17 21:57:08 +02:00
|
|
|
|
}
|
2020-11-14 11:21:51 +01:00
|
|
|
|
|
2020-03-28 12:04:00 +01:00
|
|
|
|
else if (strcmp(key, "initial-window-mode") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->startup_mode) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"windowed", "maximized", "fullscreen", NULL},
|
|
|
|
|
|
(int *)&conf->startup_mode);
|
2020-03-26 19:39:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
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-11-05 14:17:42 +01:00
|
|
|
|
struct config_font_list new_list = value_to_fonts(ctx);
|
2021-10-31 20:58:17 +01:00
|
|
|
|
if (new_list.arr == NULL)
|
|
|
|
|
|
return false;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-10-31 20:58:17 +01:00
|
|
|
|
config_font_list_destroy(&conf->fonts[idx]);
|
|
|
|
|
|
conf->fonts[idx] = new_list;
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
2019-07-18 14:29:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-15 11:10:32 +01:00
|
|
|
|
else if (strcmp(key, "font-size-adjustment") == 0) {
|
|
|
|
|
|
const size_t len = strlen(ctx->value);
|
|
|
|
|
|
if (len >= 1 && ctx->value[len - 1] == '%') {
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
float percent = strtof(ctx->value, &end);
|
2022-12-17 10:25:48 +01:00
|
|
|
|
if (!(len > 1 && errno == 0 && end == ctx->value + len - 1)) {
|
2022-12-15 11:10:32 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
|
|
|
|
|
"invalid percent value (must be in the form 10.5%%)");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->font_size_adjustment.percent = percent / 100.;
|
|
|
|
|
|
conf->font_size_adjustment.pt_or_px.pt = 0;
|
|
|
|
|
|
conf->font_size_adjustment.pt_or_px.px = 0;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
bool ret = value_to_pt_or_px(ctx, &conf->font_size_adjustment.pt_or_px);
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
conf->font_size_adjustment.percent = 0.;
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "line-height") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->line_height);
|
2021-01-07 11:16:02 +01:00
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "letter-spacing") == 0)
|
2021-11-15 19:22:06 +01:00
|
|
|
|
return value_to_pt_or_px(ctx, &conf->letter_spacing);
|
2021-01-07 11:16:02 +01:00
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "horizontal-letter-offset") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->horizontal_letter_offset);
|
2021-01-07 11:16:02 +01:00
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "vertical-letter-offset") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->vertical_letter_offset);
|
2021-01-07 11:16:02 +01:00
|
|
|
|
|
2021-06-17 17:52:38 +02:00
|
|
|
|
else if (strcmp(key, "underline-offset") == 0) {
|
2021-11-05 14:13:30 +01:00
|
|
|
|
if (!value_to_pt_or_px(ctx, &conf->underline_offset))
|
2021-06-17 17:52:38 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
conf->use_custom_underline_offset = true;
|
2021-11-06 12:43:02 +01:00
|
|
|
|
return true;
|
2021-06-17 17:52:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-19 02:54:49 +02:00
|
|
|
|
else if (strcmp(key, "underline-thickness") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->underline_thickness);
|
|
|
|
|
|
|
2023-06-26 17:55:04 +02:00
|
|
|
|
else if (strcmp(key, "dpi-aware") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->dpi_aware);
|
2020-11-17 17:59:31 +01:00
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "workers") == 0)
|
|
|
|
|
|
return value_to_uint16(ctx, 10, &conf->render_worker_count);
|
2019-07-29 20:13:26 +02:00
|
|
|
|
|
2021-11-06 20:32:35 +01:00
|
|
|
|
else if (strcmp(key, "word-delimiters") == 0)
|
|
|
|
|
|
return value_to_wchars(ctx, &conf->word_delimiters);
|
2020-10-09 19:44:23 +02:00
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "notify") == 0)
|
|
|
|
|
|
return value_to_spawn_template(ctx, &conf->notify);
|
2020-12-08 19:19:55 +01:00
|
|
|
|
|
2021-11-06 12:43:02 +01:00
|
|
|
|
else if (strcmp(key, "notify-focus-inhibit") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->notify_focus_inhibit);
|
2021-10-05 23:07:01 +02:00
|
|
|
|
|
2021-01-16 11:26:03 +01:00
|
|
|
|
else if (strcmp(key, "selection-target") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->selection_target) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-01-16 15:39:44 +01:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"none", "primary", "clipboard", "both", NULL},
|
|
|
|
|
|
(int *)&conf->selection_target);
|
2021-01-16 11:26:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-09 23:19:20 +02:00
|
|
|
|
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
2021-04-09 23:19:20 +02:00
|
|
|
|
|
2023-07-31 16:28:07 +02:00
|
|
|
|
else if (strcmp(key, "utmp-helper") == 0) {
|
2023-05-17 20:51:40 +02:00
|
|
|
|
if (!value_to_str(ctx, &conf->utmp_helper_path))
|
2022-09-23 20:24:04 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
2023-05-17 20:51:40 +02:00
|
|
|
|
if (strcmp(conf->utmp_helper_path, "none") == 0) {
|
|
|
|
|
|
free(conf->utmp_helper_path);
|
|
|
|
|
|
conf->utmp_helper_path = NULL;
|
2022-09-23 20:24:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2020-07-29 17:41:24 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-29 04:12:55 -05:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_bell(struct context *ctx)
|
2021-04-29 04:12:55 -05:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2021-04-29 04:12:55 -05:00
|
|
|
|
if (strcmp(key, "urgent") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->bell.urgent);
|
2021-04-29 04:12:55 -05:00
|
|
|
|
else if (strcmp(key, "notify") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->bell.notify);
|
2021-12-25 23:51:02 +01:00
|
|
|
|
else if (strcmp(key, "command") == 0)
|
|
|
|
|
|
return value_to_spawn_template(ctx, &conf->bell.command);
|
|
|
|
|
|
else if (strcmp(key, "command-focused") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->bell.command_focused);
|
2021-04-29 04:12:55 -05:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2021-04-29 04:12:55 -05:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_scrollback(struct context *ctx)
|
2020-07-29 17:41:24 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
const char *value = ctx->value;
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (strcmp(key, "lines") == 0)
|
2021-12-25 23:48:05 +01:00
|
|
|
|
return value_to_uint32(ctx, 10, &conf->scrollback.lines);
|
2019-08-01 19:28:14 +02:00
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
|
else if (strcmp(key, "indicator-position") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(
|
|
|
|
|
|
sizeof(conf->scrollback.indicator.position) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"none", "fixed", "relative", NULL},
|
|
|
|
|
|
(int *)&conf->scrollback.indicator.position);
|
2020-07-25 14:31:45 +02:00
|
|
|
|
}
|
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;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2020-07-26 11:39:02 +02:00
|
|
|
|
} else if (strcmp(value, "line") == 0) {
|
|
|
|
|
|
conf->scrollback.indicator.format
|
|
|
|
|
|
= SCROLLBACK_INDICATOR_FORMAT_LINENO;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2021-11-13 12:40:59 +01:00
|
|
|
|
} else
|
|
|
|
|
|
return value_to_wchars(ctx, &conf->scrollback.indicator.text);
|
2020-07-24 18:34:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "multiplier") == 0)
|
2023-07-26 16:12:36 +02:00
|
|
|
|
return value_to_float(ctx, &conf->scrollback.multiplier);
|
2020-08-03 19:43:06 +02:00
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_url(struct context *ctx)
|
2021-05-20 17:56:56 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
const char *value = ctx->value;
|
|
|
|
|
|
|
2021-12-25 23:51:02 +01:00
|
|
|
|
if (strcmp(key, "launch") == 0)
|
|
|
|
|
|
return value_to_spawn_template(ctx, &conf->url.launch);
|
2021-05-20 17:56:56 +02:00
|
|
|
|
|
2021-11-06 20:32:35 +01:00
|
|
|
|
else if (strcmp(key, "label-letters") == 0)
|
|
|
|
|
|
return value_to_wchars(ctx, &conf->url.label_letters);
|
2021-05-20 17:56:56 +02:00
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "osc8-underline") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->url.osc8_underline) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"url-mode", "always", NULL},
|
|
|
|
|
|
(int *)&conf->url.osc8_underline);
|
2021-05-20 17:56:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
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 */
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while (isspace(prot[0]))
|
2021-05-20 20:45:01 +02:00
|
|
|
|
prot++;
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace */
|
|
|
|
|
|
size_t len = strlen(prot);
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while (isspace(prot[len - 1]))
|
|
|
|
|
|
len--;
|
|
|
|
|
|
prot[len] = '\0';
|
2021-05-20 20:45:01 +02:00
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
size_t chars = mbsntoc32(NULL, prot, len, 0);
|
2021-05-20 17:58:06 +02:00
|
|
|
|
if (chars == (size_t)-1) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
ctx->value = prot;
|
|
|
|
|
|
LOG_CONTEXTUAL_ERRNO("invalid protocol");
|
2021-05-20 17:58:06 +02:00
|
|
|
|
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;
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(char32_t));
|
|
|
|
|
|
mbsntoc32(conf->url.protocols[idx], prot, len, chars + 1);
|
|
|
|
|
|
c32cpy(&conf->url.protocols[idx][chars], U"://");
|
2021-05-20 17:58:06 +02:00
|
|
|
|
|
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-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2021-05-20 17:58:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
else if (strcmp(key, "uri-characters") == 0) {
|
2021-11-06 20:32:35 +01:00
|
|
|
|
if (!value_to_wchars(ctx, &conf->url.uri_characters))
|
2021-08-09 18:25:36 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
qsort(
|
2021-11-06 20:32:35 +01:00
|
|
|
|
conf->url.uri_characters,
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
c32len(conf->url.uri_characters),
|
2021-11-06 20:32:35 +01:00
|
|
|
|
sizeof(conf->url.uri_characters[0]),
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
&c32cmp_single);
|
2021-11-06 20:32:35 +01:00
|
|
|
|
return true;
|
2021-08-09 18:25:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-20 17:56:56 +02:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2021-05-20 17:56:56 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-21 11:31:16 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_colors(struct context *ctx)
|
2019-07-21 11:31:16 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
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;
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!str_to_ulong(key, 0, &index) ||
|
|
|
|
|
|
index >= ALEN(conf->colors.table))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
|
|
|
|
|
"invalid color palette index: %s (not in range 0-%zu)",
|
|
|
|
|
|
key, ALEN(conf->colors.table));
|
2021-05-08 02:18:45 -05:00
|
|
|
|
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-11-03 14:25:38 +01:00
|
|
|
|
else if (key_len == 4 && str_has_prefix(key, "dim") && last_digit < 8) {
|
|
|
|
|
|
if (!value_to_color(ctx, &conf->colors.dim[last_digit], false))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.dim |= 1 << last_digit;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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) {
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_two_colors(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
&conf->colors.jump_label.fg,
|
|
|
|
|
|
&conf->colors.jump_label.bg,
|
|
|
|
|
|
false))
|
2021-02-06 11:10:40 +01:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.jump_label = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-27 19:05:40 +00:00
|
|
|
|
else if (strcmp(key, "scrollback-indicator") == 0) {
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_two_colors(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
&conf->colors.scrollback_indicator.fg,
|
|
|
|
|
|
&conf->colors.scrollback_indicator.bg,
|
|
|
|
|
|
false))
|
2021-09-27 19:05:40 +00:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.scrollback_indicator = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-27 19:14:27 +02:00
|
|
|
|
else if (strcmp(key, "search-box-no-match") == 0) {
|
|
|
|
|
|
if (!value_to_two_colors(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
&conf->colors.search_box.no_match.fg,
|
|
|
|
|
|
&conf->colors.search_box.no_match.bg,
|
|
|
|
|
|
false))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.search_box_no_match = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "search-box-match") == 0) {
|
|
|
|
|
|
if (!value_to_two_colors(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
&conf->colors.search_box.match.fg,
|
|
|
|
|
|
&conf->colors.search_box.match.bg,
|
|
|
|
|
|
false))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.search_box_match = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-06 11:10:40 +01:00
|
|
|
|
else if (strcmp(key, "urls") == 0) {
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->colors.url, false))
|
2021-02-06 11:10:40 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->colors.use_custom.url = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-15 18:15:43 +02:00
|
|
|
|
else if (strcmp(key, "alpha") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
float alpha;
|
2023-07-26 16:12:36 +02:00
|
|
|
|
if (!value_to_float(ctx, &alpha))
|
2021-11-05 17:20:17 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (alpha < 0. || alpha > 1.) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("not in range 0.0-1.0");
|
2019-08-15 18:15:43 +02:00
|
|
|
|
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 {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not valid option");
|
2019-07-21 11:31:16 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
|
uint32_t color_value;
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_color(ctx, &color_value, false))
|
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
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_cursor(struct context *ctx)
|
2019-07-22 20:15:14 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2019-07-22 20:15:14 +02:00
|
|
|
|
if (strcmp(key, "style") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->cursor.style) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2019-07-22 20:15:14 +02:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"block", "underline", "beam", NULL},
|
|
|
|
|
|
(int *)&conf->cursor.style);
|
2019-07-22 20:15:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-30 17:45:34 +02:00
|
|
|
|
else if (strcmp(key, "blink") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->cursor.blink);
|
2020-06-30 17:45:34 +02:00
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
|
else if (strcmp(key, "color") == 0) {
|
2021-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_two_colors(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
&conf->cursor.color.text,
|
|
|
|
|
|
&conf->cursor.color.cursor,
|
|
|
|
|
|
false))
|
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;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2019-07-23 18:54:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-25 23:51:02 +01:00
|
|
|
|
else if (strcmp(key, "beam-thickness") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->cursor.beam_thickness);
|
2021-04-30 20:31:47 +02:00
|
|
|
|
|
2021-12-25 23:51:02 +01:00
|
|
|
|
else if (strcmp(key, "underline-thickness") == 0)
|
|
|
|
|
|
return value_to_pt_or_px(ctx, &conf->cursor.underline_thickness);
|
2021-05-18 18:52:10 +02:00
|
|
|
|
|
2019-07-22 20:15:14 +02:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2019-07-22 20:15:14 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_mouse(struct context *ctx)
|
2020-08-04 07:33:15 +02:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
|
if (strcmp(key, "hide-when-typing") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->mouse.hide_when_typing);
|
2020-08-04 07:33:15 +02:00
|
|
|
|
|
2020-09-15 19:09:00 +02:00
|
|
|
|
else if (strcmp(key, "alternate-scroll-mode") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->mouse.alternate_scroll_mode);
|
2020-09-15 19:09:00 +02:00
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2020-08-04 07:33:15 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_csd(struct context *ctx)
|
2020-03-02 18:42:49 +01:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
|
if (strcmp(key, "preferred") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->csd.preferred) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"none", "server", "client", NULL},
|
|
|
|
|
|
(int *)&conf->csd.preferred);
|
2020-03-02 18:42:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-22 23:21:31 +02:00
|
|
|
|
else if (strcmp(key, "font") == 0) {
|
2021-11-05 14:17:42 +01:00
|
|
|
|
struct config_font_list new_list = value_to_fonts(ctx);
|
2021-07-22 23:21:31 +02:00
|
|
|
|
if (new_list.arr == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
config_font_list_destroy(&conf->csd.font);
|
|
|
|
|
|
conf->csd.font = new_list;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2021-07-22 23:21:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
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-11-05 14:08:25 +01:00
|
|
|
|
if (!value_to_color(ctx, &color, true))
|
2020-03-02 18:42:49 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.title_set = true;
|
|
|
|
|
|
conf->csd.color.title = color;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2020-03-02 18:42:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "size") == 0)
|
|
|
|
|
|
return value_to_uint16(ctx, 10, &conf->csd.title_height);
|
2020-03-02 18:42:49 +01:00
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "button-width") == 0)
|
|
|
|
|
|
return value_to_uint16(ctx, 10, &conf->csd.button_width);
|
2020-03-02 20:29:28 +01:00
|
|
|
|
|
2021-06-20 10:44:50 +02:00
|
|
|
|
else if (strcmp(key, "button-color") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->csd.color.buttons, true))
|
2021-06-20 10:44:50 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.buttons_set = true;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2021-06-20 10:44:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
|
else if (strcmp(key, "button-minimize-color") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->csd.color.minimize, true))
|
2020-03-02 20:29:28 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.minimize_set = true;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2020-03-02 20:29:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "button-maximize-color") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->csd.color.maximize, true))
|
2020-03-02 20:29:28 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.maximize_set = true;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2020-03-02 20:29:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "button-close-color") == 0) {
|
2022-02-08 20:12:05 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->csd.color.quit, true))
|
2020-03-02 20:29:28 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.close_set = true;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2020-03-02 20:29:28 +01:00
|
|
|
|
}
|
2020-03-08 14:08:48 +01:00
|
|
|
|
|
2021-10-27 18:27:08 +02:00
|
|
|
|
else if (strcmp(key, "border-color") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
if (!value_to_color(ctx, &conf->csd.color.border, true))
|
2021-10-27 18:27:08 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
conf->csd.color.border_set = true;
|
2021-12-25 23:51:02 +01:00
|
|
|
|
return true;
|
2021-10-27 18:27:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
else if (strcmp(key, "border-width") == 0)
|
|
|
|
|
|
return value_to_uint16(ctx, 10, &conf->csd.border_width_visible);
|
2021-10-27 18:27:08 +02:00
|
|
|
|
|
2022-04-15 20:12:34 +02:00
|
|
|
|
else if (strcmp(key, "hide-when-maximized") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->csd.hide_when_maximized);
|
|
|
|
|
|
|
2023-07-14 12:03:35 +02:00
|
|
|
|
else if (strcmp(key, "double-click-to-maximize") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->csd.double_click_to_maximize);
|
|
|
|
|
|
|
2020-03-08 14:08:48 +01:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid action: %s", key);
|
2020-03-08 14:08:48 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2020-03-02 18:42:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-04 21:53:48 +01:00
|
|
|
|
static void
|
2022-02-08 19:35:41 +01:00
|
|
|
|
free_binding_aux(struct binding_aux *aux)
|
2021-12-04 21:53:48 +01:00
|
|
|
|
{
|
2023-01-15 10:24:01 +01:00
|
|
|
|
if (!aux->master_copy)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
switch (aux->type) {
|
|
|
|
|
|
case BINDING_AUX_NONE: break;
|
2022-02-06 19:36:44 +01:00
|
|
|
|
case BINDING_AUX_PIPE: free_argv(&aux->pipe); break;
|
|
|
|
|
|
case BINDING_AUX_TEXT: free(aux->text.data); break;
|
2022-02-08 19:35:41 +01:00
|
|
|
|
}
|
2021-12-04 21:53:48 +01:00
|
|
|
|
}
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
static void
|
|
|
|
|
|
free_key_binding(struct config_key_binding *binding)
|
|
|
|
|
|
{
|
2022-02-08 19:35:41 +01:00
|
|
|
|
free_binding_aux(&binding->aux);
|
2021-12-05 15:19:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
static void NOINLINE
|
2021-12-05 15:19:22 +01:00
|
|
|
|
free_key_binding_list(struct config_key_binding_list *bindings)
|
2021-12-04 21:53:48 +01:00
|
|
|
|
{
|
2021-12-05 15:19:22 +01:00
|
|
|
|
struct config_key_binding *binding = &bindings->arr[0];
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < bindings->count; i++, binding++)
|
|
|
|
|
|
free_key_binding(binding);
|
2021-12-04 21:53:48 +01:00
|
|
|
|
free(bindings->arr);
|
2021-12-05 15:19:22 +01:00
|
|
|
|
|
|
|
|
|
|
bindings->arr = NULL;
|
|
|
|
|
|
bindings->count = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
static bool NOINLINE
|
2021-11-05 14:48:06 +01:00
|
|
|
|
parse_modifiers(struct context *ctx, const char *text, size_t len,
|
|
|
|
|
|
struct config_key_modifiers *modifiers)
|
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};
|
2021-11-29 21:45:00 +00:00
|
|
|
|
|
|
|
|
|
|
/* Handle "none" separately because e.g. none+shift is nonsense */
|
|
|
|
|
|
if (strncmp(text, "none", len) == 0)
|
|
|
|
|
|
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
|
|
|
|
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)
|
2021-12-05 16:30:01 +01:00
|
|
|
|
modifiers->super = 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
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid modifier name: %s", 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 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
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
static int NOINLINE
|
|
|
|
|
|
argv_compare(const struct argv *argv1, const struct argv *argv2)
|
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-12-05 15:19:22 +01:00
|
|
|
|
if (argv1->args == NULL && argv2->args == NULL)
|
|
|
|
|
|
return 0;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
if (argv1->args == NULL)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
if (argv2->args == NULL)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; ; i++) {
|
|
|
|
|
|
if (argv1->args[i] == NULL && argv2->args[i] == NULL)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
if (argv1->args[i] == NULL)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
if (argv2->args[i] == NULL)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
int ret = strcmp(argv1->args[i], argv2->args[i]);
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BUG("unexpected loop break");
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
binding_aux_equal(const struct binding_aux *a,
|
|
|
|
|
|
const struct binding_aux *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (a->type != b->type)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
switch (a->type) {
|
2022-02-06 19:36:44 +01:00
|
|
|
|
case BINDING_AUX_NONE:
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
case BINDING_AUX_PIPE:
|
|
|
|
|
|
return argv_compare(&a->pipe, &b->pipe) == 0;
|
|
|
|
|
|
|
|
|
|
|
|
case BINDING_AUX_TEXT:
|
|
|
|
|
|
return a->text.len == b->text.len &&
|
|
|
|
|
|
memcmp(a->text.data, b->text.data, a->text.len) == 0;
|
2022-02-08 19:35:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BUG("invalid AUX type: %d", a->type);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
static void NOINLINE
|
2021-12-06 21:04:38 +01:00
|
|
|
|
remove_from_key_bindings_list(struct config_key_binding_list *bindings,
|
2022-02-08 19:35:41 +01:00
|
|
|
|
int action, const struct binding_aux *aux)
|
2021-12-05 15:19:22 +01:00
|
|
|
|
{
|
|
|
|
|
|
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)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding_aux_equal(&binding->aux, aux)) {
|
2021-12-05 15:19:22 +01:00
|
|
|
|
if (remove_count++ == 0)
|
|
|
|
|
|
remove_first_idx = i;
|
|
|
|
|
|
|
|
|
|
|
|
xassert(remove_first_idx + remove_count - 1 == i);
|
|
|
|
|
|
free_key_binding(binding);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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-12-06 21:04:38 +01:00
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *name;
|
|
|
|
|
|
int code;
|
|
|
|
|
|
} button_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},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
mouse_button_name_to_code(const char *name)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(button_map); i++) {
|
|
|
|
|
|
if (strcmp(button_map[i].name, name) == 0)
|
|
|
|
|
|
return button_map[i].code;
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char*
|
|
|
|
|
|
mouse_button_code_to_name(int code)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(button_map); i++) {
|
|
|
|
|
|
if (code == button_map[i].code)
|
|
|
|
|
|
return button_map[i].name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
static bool NOINLINE
|
2022-02-08 19:35:41 +01:00
|
|
|
|
value_to_key_combos(struct context *ctx, int action,
|
|
|
|
|
|
struct binding_aux *aux,
|
2021-12-06 21:04:38 +01:00
|
|
|
|
struct config_key_binding_list *bindings,
|
2022-02-08 19:02:28 +01:00
|
|
|
|
enum key_binding_type type)
|
2021-12-05 15:19:22 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (strcasecmp(ctx->value, "none") == 0) {
|
2022-02-08 19:35:41 +01:00
|
|
|
|
remove_from_key_bindings_list(bindings, action, aux);
|
2021-12-05 15:19:22 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Count number of combinations */
|
|
|
|
|
|
size_t combo_count = 1;
|
|
|
|
|
|
for (const char *p = strchr(ctx->value, ' ');
|
|
|
|
|
|
p != NULL;
|
|
|
|
|
|
p = strchr(p + 1, ' '))
|
|
|
|
|
|
{
|
|
|
|
|
|
combo_count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct config_key_binding new_combos[combo_count];
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
char *copy = xstrdup(ctx->value);
|
2021-12-05 15:19:22 +01:00
|
|
|
|
size_t idx = 0;
|
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;
|
2021-12-05 15:19:22 +01:00
|
|
|
|
combo = strtok_r(NULL, " ", &tok_ctx),
|
|
|
|
|
|
idx++)
|
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-12-05 15:19:22 +01:00
|
|
|
|
struct config_key_binding *new_combo = &new_combos[idx];
|
|
|
|
|
|
new_combo->action = action;
|
2022-02-08 19:35:41 +01:00
|
|
|
|
new_combo->aux = *aux;
|
|
|
|
|
|
new_combo->aux.master_copy = idx == 0;
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
new_combo->aux.type = BINDING_AUX_PIPE;
|
|
|
|
|
|
new_combo->aux.master_copy = idx == 0;
|
|
|
|
|
|
new_combo->aux.pipe = *argv;
|
|
|
|
|
|
#endif
|
2021-12-05 15:19:22 +01:00
|
|
|
|
new_combo->path = ctx->path;
|
|
|
|
|
|
new_combo->lineno = ctx->lineno;
|
|
|
|
|
|
|
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;
|
2021-12-05 15:51:52 +01:00
|
|
|
|
new_combo->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
|
|
|
|
} else {
|
2021-12-06 21:04:38 +01:00
|
|
|
|
*key = '\0';
|
2021-12-05 15:19:22 +01:00
|
|
|
|
if (!parse_modifiers(ctx, combo, key - combo, &new_combo->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
|
|
|
|
goto err;
|
|
|
|
|
|
key++; /* Skip past the '+' */
|
|
|
|
|
|
}
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
switch (type) {
|
|
|
|
|
|
case KEY_BINDING:
|
|
|
|
|
|
/* Translate key name to symbol */
|
|
|
|
|
|
new_combo->k.sym = xkb_keysym_from_name(key, 0);
|
|
|
|
|
|
if (new_combo->k.sym == XKB_KEY_NoSymbol) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid XKB key name: %s", key);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case MOUSE_BINDING: {
|
|
|
|
|
|
new_combo->m.count = 1;
|
|
|
|
|
|
|
|
|
|
|
|
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_CONTEXTUAL_ERRNO("invalid click count: %s", _count);
|
|
|
|
|
|
else
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid click count: %s", _count);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
new_combo->m.count = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
new_combo->m.button = mouse_button_name_to_code(key);
|
|
|
|
|
|
if (new_combo->m.button < 0) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid mouse button name: %s", key);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
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-12-06 21:04:38 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-05 15:19:22 +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
|
|
|
|
|
2021-12-18 20:39:08 +01:00
|
|
|
|
if (idx == 0) {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR(
|
|
|
|
|
|
"empty binding not allowed (set to 'none' to unmap)");
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
remove_from_key_bindings_list(bindings, action, aux);
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-12-05 15:19:22 +01:00
|
|
|
|
bindings->arr = xrealloc(
|
|
|
|
|
|
bindings->arr,
|
|
|
|
|
|
(bindings->count + combo_count) * sizeof(bindings->arr[0]));
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(&bindings->arr[bindings->count],
|
|
|
|
|
|
new_combos,
|
|
|
|
|
|
combo_count * sizeof(bindings->arr[0]));
|
|
|
|
|
|
bindings->count += combo_count;
|
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:
|
|
|
|
|
|
free(copy);
|
|
|
|
|
|
return false;
|
2020-03-12 17:16:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-04 21:53:48 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
modifiers_equal(const struct config_key_modifiers *mods1,
|
|
|
|
|
|
const struct config_key_modifiers *mods2)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool shift = mods1->shift == mods2->shift;
|
|
|
|
|
|
bool alt = mods1->alt == mods2->alt;
|
|
|
|
|
|
bool ctrl = mods1->ctrl == mods2->ctrl;
|
2021-12-05 16:30:01 +01:00
|
|
|
|
bool super = mods1->super == mods2->super;
|
|
|
|
|
|
return shift && alt && ctrl && super;
|
2021-12-04 21:53:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
modifiers_disjoint(const struct config_key_modifiers *mods1,
|
|
|
|
|
|
const struct config_key_modifiers *mods2)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool shift = mods1->shift && mods2->shift;
|
|
|
|
|
|
bool alt = mods1->alt && mods2->alt;
|
|
|
|
|
|
bool ctrl = mods1->ctrl && mods2->ctrl;
|
2021-12-05 16:30:01 +01:00
|
|
|
|
bool super = mods1->super && mods2->super;
|
|
|
|
|
|
return !(shift || alt || ctrl || super);
|
2021-12-04 21:53:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:59:08 +01:00
|
|
|
|
static char * NOINLINE
|
2021-12-04 21:53:48 +01:00
|
|
|
|
modifiers_to_str(const struct config_key_modifiers *mods)
|
|
|
|
|
|
{
|
2021-12-05 16:30:01 +01:00
|
|
|
|
char *ret = xasprintf(
|
|
|
|
|
|
"%s%s%s%s",
|
|
|
|
|
|
mods->ctrl ? XKB_MOD_NAME_CTRL "+" : "",
|
|
|
|
|
|
mods->alt ? XKB_MOD_NAME_ALT "+": "",
|
|
|
|
|
|
mods->super ? XKB_MOD_NAME_LOGO "+": "",
|
|
|
|
|
|
mods->shift ? XKB_MOD_NAME_SHIFT "+": "");
|
2021-12-04 21:53:48 +01:00
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-06 19:27:46 +01:00
|
|
|
|
/*
|
2022-04-06 20:04:19 +02:00
|
|
|
|
* Parses a key binding value in the form
|
2020-11-06 19:27:46 +01:00
|
|
|
|
* "[cmd-to-exec arg1 arg2] Mods+Key"
|
|
|
|
|
|
*
|
|
|
|
|
|
* and extracts 'cmd-to-exec' and its arguments.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Input:
|
2022-04-06 20:04:19 +02:00
|
|
|
|
* - value: raw string, in the form mentioned above
|
2020-11-06 19:27:46 +01:00
|
|
|
|
* - 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:
|
2021-11-20 20:49:01 -08:00
|
|
|
|
* - ssize_t, number of bytes that were stripped from 'value' to remove the '[]'
|
2020-11-06 19:27:46 +01:00
|
|
|
|
* 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.
|
2021-11-20 20:49:01 -08:00
|
|
|
|
* - argv: allocated array containing {"cmd", "arg1", "arg2", NULL}. Caller frees.
|
2020-11-06 19:27:46 +01:00
|
|
|
|
*/
|
2021-12-06 21:04:38 +01:00
|
|
|
|
static ssize_t NOINLINE
|
2021-11-21 17:51:46 +01:00
|
|
|
|
pipe_argv_from_value(struct context *ctx, struct argv *argv)
|
2020-11-06 19:27:46 +01:00
|
|
|
|
{
|
2021-11-21 17:51:46 +01:00
|
|
|
|
argv->args = NULL;
|
2020-11-06 19:27:46 +01:00
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
if (ctx->value[0] != '[')
|
2020-11-06 19:27:46 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
const char *pipe_cmd_end = strrchr(ctx->value, ']');
|
2020-11-06 19:27:46 +01:00
|
|
|
|
if (pipe_cmd_end == NULL) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("unclosed '['");
|
2020-11-06 19:27:46 +01:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
size_t pipe_len = pipe_cmd_end - ctx->value - 1;
|
|
|
|
|
|
char *cmd = xstrndup(&ctx->value[1], pipe_len);
|
2020-11-06 19:27:46 +01:00
|
|
|
|
|
2021-11-21 17:51:46 +01:00
|
|
|
|
if (!tokenize_cmdline(cmd, &argv->args)) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("syntax error in command line");
|
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
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ssize_t remove_len = pipe_cmd_end + 1 - ctx->value;
|
|
|
|
|
|
ctx->value = pipe_cmd_end + 1;
|
|
|
|
|
|
while (isspace(*ctx->value)) {
|
|
|
|
|
|
ctx->value++;
|
2020-11-06 19:27:46 +01:00
|
|
|
|
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-02-07 14:46:29 +01:00
|
|
|
|
static bool NOINLINE
|
2021-11-05 14:48:06 +01:00
|
|
|
|
parse_key_binding_section(struct context *ctx,
|
|
|
|
|
|
int action_count,
|
|
|
|
|
|
const char *const action_map[static action_count],
|
|
|
|
|
|
struct config_key_binding_list *bindings)
|
2020-03-09 20:03:04 +01:00
|
|
|
|
{
|
2022-02-08 19:35:41 +01:00
|
|
|
|
struct binding_aux aux;
|
2020-07-15 17:28:09 +02:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &aux.pipe);
|
2020-11-06 19:27:46 +01:00
|
|
|
|
if (pipe_remove_len < 0)
|
|
|
|
|
|
return false;
|
2020-07-15 17:28:09 +02:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
aux.type = pipe_remove_len == 0 ? BINDING_AUX_NONE : BINDING_AUX_PIPE;
|
|
|
|
|
|
aux.master_copy = true;
|
|
|
|
|
|
|
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-11-05 14:48:06 +01:00
|
|
|
|
if (strcmp(ctx->key, action_map[action]) != 0)
|
2020-03-09 20:03:04 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (!value_to_key_combos(ctx, action, &aux, bindings, KEY_BINDING)) {
|
|
|
|
|
|
free_binding_aux(&aux);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid action: %s", ctx->key);
|
2022-02-08 19:35:41 +01:00
|
|
|
|
free_binding_aux(&aux);
|
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};
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf,
|
|
|
|
|
|
.section = "",
|
|
|
|
|
|
.key = "foo",
|
|
|
|
|
|
.value = "Escape",
|
|
|
|
|
|
.path = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-06-29 18:08:15 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* ADD foo=Escape
|
|
|
|
|
|
*
|
|
|
|
|
|
* This verifies we can bind a single key combo to an action.
|
|
|
|
|
|
*/
|
2021-11-05 14:48:06 +01:00
|
|
|
|
xassert(parse_key_binding_section(&ctx, ALEN(map), map, &bindings));
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.count == 1);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
xassert(bindings.arr[0].k.sym == XKB_KEY_Escape);
|
2021-06-29 18:08:15 +02:00
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ADD bar=Control+g Control+Shift+x
|
|
|
|
|
|
*
|
|
|
|
|
|
* This verifies we can bind multiple key combos to an action.
|
|
|
|
|
|
*/
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx.key = "bar";
|
|
|
|
|
|
ctx.value = "Control+g Control+Shift+x";
|
|
|
|
|
|
xassert(parse_key_binding_section(&ctx, ALEN(map), map, &bindings));
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.count == 3);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[1].action == TEST_ACTION_BAR);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
xassert(bindings.arr[1].k.sym == XKB_KEY_g);
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.arr[1].modifiers.ctrl);
|
|
|
|
|
|
xassert(bindings.arr[2].action == TEST_ACTION_BAR);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
xassert(bindings.arr[2].k.sym == XKB_KEY_x);
|
2021-06-29 18:08:15 +02:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx.key = "foo";
|
|
|
|
|
|
ctx.value = "Mod1+v Shift+q";
|
|
|
|
|
|
xassert(parse_key_binding_section(&ctx, ALEN(map), map, &bindings));
|
2021-06-29 18:08:15 +02:00
|
|
|
|
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);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
xassert(bindings.arr[2].k.sym == XKB_KEY_v);
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.arr[2].modifiers.alt);
|
|
|
|
|
|
xassert(bindings.arr[3].action == TEST_ACTION_FOO);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
xassert(bindings.arr[3].k.sym == XKB_KEY_q);
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.arr[3].modifiers.shift);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* REMOVE bar
|
|
|
|
|
|
*/
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx.key = "bar";
|
|
|
|
|
|
ctx.value = "none";
|
|
|
|
|
|
xassert(parse_key_binding_section(&ctx, ALEN(map), map, &bindings));
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.count == 2);
|
|
|
|
|
|
xassert(bindings.arr[0].action == TEST_ACTION_FOO);
|
|
|
|
|
|
xassert(bindings.arr[1].action == TEST_ACTION_FOO);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* REMOVE foo
|
|
|
|
|
|
*/
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx.key = "foo";
|
|
|
|
|
|
ctx.value = "none";
|
|
|
|
|
|
xassert(parse_key_binding_section(&ctx, ALEN(map), map, &bindings));
|
2021-06-29 18:08:15 +02:00
|
|
|
|
xassert(bindings.count == 0);
|
|
|
|
|
|
|
|
|
|
|
|
free(bindings.arr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_key_bindings(struct context *ctx)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
return parse_key_binding_section(
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx,
|
|
|
|
|
|
BIND_ACTION_KEY_COUNT, binding_action_map,
|
|
|
|
|
|
&ctx->conf->bindings.key);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-29 17:27:01 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_search_bindings(struct context *ctx)
|
2020-07-29 17:27:01 +02:00
|
|
|
|
{
|
2021-02-07 14:46:29 +01:00
|
|
|
|
return parse_key_binding_section(
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx,
|
|
|
|
|
|
BIND_ACTION_SEARCH_COUNT, search_binding_action_map,
|
|
|
|
|
|
&ctx->conf->bindings.search);
|
2020-07-29 17:27:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-30 12:43:59 +01:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_url_bindings(struct context *ctx)
|
2021-01-30 12:43:59 +01:00
|
|
|
|
{
|
2021-02-07 14:46:29 +01:00
|
|
|
|
return parse_key_binding_section(
|
2021-11-05 14:48:06 +01:00
|
|
|
|
ctx,
|
|
|
|
|
|
BIND_ACTION_URL_COUNT, url_binding_action_map,
|
|
|
|
|
|
&ctx->conf->bindings.url);
|
2021-01-30 12:43:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
static bool NOINLINE
|
|
|
|
|
|
resolve_key_binding_collisions(struct config *conf, const char *section_name,
|
|
|
|
|
|
const char *const action_map[],
|
|
|
|
|
|
struct config_key_binding_list *bindings,
|
2022-02-08 19:02:28 +01:00
|
|
|
|
enum key_binding_type type)
|
2021-12-04 01:33:25 +00:00
|
|
|
|
{
|
2021-12-05 16:30:01 +01:00
|
|
|
|
bool ret = true;
|
2021-12-04 01:33:25 +00:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
for (size_t i = 1; i < bindings->count; i++) {
|
|
|
|
|
|
enum {COLLISION_NONE,
|
|
|
|
|
|
COLLISION_OVERRIDE,
|
|
|
|
|
|
COLLISION_BINDING} collision_type = COLLISION_NONE;
|
2021-12-06 21:04:38 +01:00
|
|
|
|
const struct config_key_binding *collision_binding = NULL;
|
2021-12-04 01:33:25 +00:00
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
struct config_key_binding *binding1 = &bindings->arr[i];
|
2021-12-05 16:30:01 +01:00
|
|
|
|
xassert(binding1->action != BIND_ACTION_NONE);
|
2021-12-04 01:33:25 +00:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
const struct config_key_modifiers *mods1 = &binding1->modifiers;
|
2021-09-27 19:09:07 +00:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
/* Does our modifiers collide with the selection override mods? */
|
2021-12-06 21:04:38 +01:00
|
|
|
|
if (type == MOUSE_BINDING &&
|
|
|
|
|
|
!modifiers_disjoint(
|
|
|
|
|
|
mods1, &conf->mouse.selection_override_modifiers))
|
|
|
|
|
|
{
|
2021-12-05 16:30:01 +01:00
|
|
|
|
collision_type = COLLISION_OVERRIDE;
|
2021-12-06 21:04:38 +01:00
|
|
|
|
}
|
2021-12-05 16:30:01 +01:00
|
|
|
|
|
|
|
|
|
|
/* Does our binding collide with another binding? */
|
|
|
|
|
|
for (ssize_t j = i - 1;
|
|
|
|
|
|
collision_type == COLLISION_NONE && j >= 0;
|
|
|
|
|
|
j--)
|
|
|
|
|
|
{
|
2021-12-06 21:04:38 +01:00
|
|
|
|
const struct config_key_binding *binding2 = &bindings->arr[j];
|
2021-12-05 16:30:01 +01:00
|
|
|
|
xassert(binding2->action != BIND_ACTION_NONE);
|
2021-12-04 01:33:25 +00:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding2->action == binding1->action &&
|
|
|
|
|
|
binding_aux_equal(&binding1->aux, &binding2->aux))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
2021-12-04 01:33:25 +00:00
|
|
|
|
}
|
2021-09-27 19:09:07 +00:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
const struct config_key_modifiers *mods2 = &binding2->modifiers;
|
2021-09-27 19:09:07 +00:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
bool mods_equal = modifiers_equal(mods1, mods2);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
bool sym_equal;
|
2021-11-05 14:48:06 +01:00
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
switch (type) {
|
|
|
|
|
|
case KEY_BINDING:
|
|
|
|
|
|
sym_equal = binding1->k.sym == binding2->k.sym;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case MOUSE_BINDING:
|
|
|
|
|
|
sym_equal = (binding1->m.button == binding2->m.button &&
|
|
|
|
|
|
binding1->m.count == binding2->m.count);
|
|
|
|
|
|
break;
|
2022-09-17 06:34:25 +01:00
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
BUG("unhandled key binding type");
|
2021-12-06 21:04:38 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!mods_equal || !sym_equal)
|
2021-12-05 16:30:01 +01:00
|
|
|
|
continue;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
collision_binding = binding2;
|
|
|
|
|
|
collision_type = COLLISION_BINDING;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-06-17 18:15:29 +02:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
if (collision_type != COLLISION_NONE) {
|
|
|
|
|
|
char *modifier_names = modifiers_to_str(mods1);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
char sym_name[64];
|
|
|
|
|
|
|
|
|
|
|
|
switch (type){
|
|
|
|
|
|
case KEY_BINDING:
|
|
|
|
|
|
xkb_keysym_get_name(binding1->k.sym, sym_name, sizeof(sym_name));
|
|
|
|
|
|
break;
|
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-12-06 21:04:38 +01:00
|
|
|
|
case MOUSE_BINDING: {
|
|
|
|
|
|
const char *button_name =
|
|
|
|
|
|
mouse_button_code_to_name(binding1->m.button);
|
|
|
|
|
|
|
|
|
|
|
|
if (binding1->m.count > 1) {
|
|
|
|
|
|
snprintf(sym_name, sizeof(sym_name), "%s-%d",
|
|
|
|
|
|
button_name, binding1->m.count);
|
|
|
|
|
|
} else
|
|
|
|
|
|
strcpy(sym_name, button_name);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-11-06 19:26:55 +01:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
switch (collision_type) {
|
|
|
|
|
|
case COLLISION_NONE:
|
|
|
|
|
|
break;
|
2021-12-04 21:53:48 +01:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
case COLLISION_BINDING: {
|
2022-02-08 19:35:41 +01:00
|
|
|
|
bool has_pipe = collision_binding->aux.type == BINDING_AUX_PIPE;
|
2021-12-05 16:30:01 +01:00
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-12-06 21:04:38 +01:00
|
|
|
|
"%s:%d: [%s].%s: %s%s already mapped to '%s%s%s%s'",
|
2021-12-05 16:30:01 +01:00
|
|
|
|
binding1->path, binding1->lineno, section_name,
|
|
|
|
|
|
action_map[binding1->action],
|
2021-12-06 21:04:38 +01:00
|
|
|
|
modifier_names, sym_name,
|
2021-12-05 16:30:01 +01:00
|
|
|
|
action_map[collision_binding->action],
|
|
|
|
|
|
has_pipe ? " [" : "",
|
2022-02-08 19:35:41 +01:00
|
|
|
|
has_pipe ? collision_binding->aux.pipe.args[0] : "",
|
2021-12-05 16:30:01 +01:00
|
|
|
|
has_pipe ? "]" : "");
|
2021-12-11 20:00:32 +01:00
|
|
|
|
ret = false;
|
2021-12-05 16:30:01 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-12-04 21:53:48 +01:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
case COLLISION_OVERRIDE: {
|
|
|
|
|
|
char *override_names = modifiers_to_str(
|
|
|
|
|
|
&conf->mouse.selection_override_modifiers);
|
|
|
|
|
|
|
|
|
|
|
|
if (override_names[0] != '\0')
|
|
|
|
|
|
override_names[strlen(override_names) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
LOG_AND_NOTIFY_ERR(
|
2021-12-06 21:04:38 +01:00
|
|
|
|
"%s:%d: [%s].%s: %s%s: "
|
2021-12-05 16:30:01 +01:00
|
|
|
|
"modifiers conflict with 'selection-override-modifiers=%s'",
|
2021-12-06 21:20:03 +01:00
|
|
|
|
binding1->path != NULL ? binding1->path : "(default)",
|
|
|
|
|
|
binding1->lineno, section_name,
|
2021-12-05 16:30:01 +01:00
|
|
|
|
action_map[binding1->action],
|
2021-12-06 21:04:38 +01:00
|
|
|
|
modifier_names, sym_name, override_names);
|
2021-12-11 20:00:32 +01:00
|
|
|
|
ret = false;
|
2021-12-05 16:30:01 +01:00
|
|
|
|
free(override_names);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-12-06 21:04:38 +01:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
free(modifier_names);
|
2021-12-04 21:53:48 +01:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding1->aux.master_copy && i + 1 < bindings->count) {
|
2021-12-06 21:04:38 +01:00
|
|
|
|
struct config_key_binding *next = &bindings->arr[i + 1];
|
2021-12-04 21:53:48 +01:00
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
if (next->action == binding1->action &&
|
2022-02-08 19:35:41 +01:00
|
|
|
|
binding_aux_equal(&binding1->aux, &next->aux))
|
2021-12-05 16:30:01 +01:00
|
|
|
|
{
|
|
|
|
|
|
/* Transfer ownership to next binding */
|
2022-02-08 19:35:41 +01:00
|
|
|
|
next->aux.master_copy = true;
|
|
|
|
|
|
binding1->aux.master_copy = false;
|
2021-12-05 16:30:01 +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
|
|
|
|
}
|
2021-12-05 16:30:01 +01:00
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
free_key_binding(binding1);
|
2021-12-05 16:30:01 +01:00
|
|
|
|
|
|
|
|
|
|
/* Remove the most recent binding */
|
|
|
|
|
|
size_t move_count = bindings->count - (i + 1);
|
|
|
|
|
|
memmove(&bindings->arr[i], &bindings->arr[i + 1],
|
|
|
|
|
|
move_count * sizeof(bindings->arr[0]));
|
|
|
|
|
|
bindings->count--;
|
|
|
|
|
|
|
|
|
|
|
|
i--;
|
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-12-05 16:30:01 +01:00
|
|
|
|
return ret;
|
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 10:20:05 +01:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_mouse_bindings(struct context *ctx)
|
2020-03-12 10:20:05 +01:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
const char *value = ctx->value;
|
|
|
|
|
|
|
2021-12-05 16:30:01 +01:00
|
|
|
|
if (strcmp(key, "selection-override-modifiers") == 0) {
|
|
|
|
|
|
if (!parse_modifiers(
|
|
|
|
|
|
ctx, ctx->value, strlen(value),
|
|
|
|
|
|
&conf->mouse.selection_override_modifiers))
|
|
|
|
|
|
{
|
2021-11-29 22:06:35 +00:00
|
|
|
|
LOG_CONTEXTUAL_ERR("%s: invalid modifiers '%s'", key, ctx->value);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
struct binding_aux aux;
|
2020-11-06 19:29:23 +01:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &aux.pipe);
|
2020-11-06 19:29:23 +01:00
|
|
|
|
if (pipe_remove_len < 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
aux.type = pipe_remove_len == 0 ? BINDING_AUX_NONE : BINDING_AUX_PIPE;
|
|
|
|
|
|
aux.master_copy = true;
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2021-12-06 21:04:38 +01:00
|
|
|
|
if (!value_to_key_combos(
|
2022-02-08 19:35:41 +01:00
|
|
|
|
ctx, action, &aux, &conf->bindings.mouse, MOUSE_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
|
|
|
|
{
|
2022-02-08 19:35:41 +01:00
|
|
|
|
free_binding_aux(&aux);
|
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
|
|
|
|
|
|
|
|
|
|
return true;
|
2020-03-12 10:20:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2022-02-08 19:35:41 +01:00
|
|
|
|
free_binding_aux(&aux);
|
2020-03-12 10:20:05 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-06 19:36:44 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_text_bindings(struct context *ctx)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
|
|
|
|
|
const size_t key_len = strlen(key);
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t *data = xmalloc(key_len + 1);
|
|
|
|
|
|
size_t data_len = 0;
|
|
|
|
|
|
bool esc = false;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < key_len; i++) {
|
|
|
|
|
|
if (key[i] == '\\') {
|
|
|
|
|
|
if (i + 1 >= key_len) {
|
|
|
|
|
|
ctx->value = "";
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("trailing backslash");
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
esc = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (esc) {
|
|
|
|
|
|
if (key[i] != 'x') {
|
|
|
|
|
|
ctx->value = "";
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid escaped character: %c", key[i]);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (i + 2 >= key_len) {
|
|
|
|
|
|
ctx->value = "";
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("\\x sequence too short");
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const uint8_t nib1 = hex2nibble(key[i + 1]);
|
|
|
|
|
|
const uint8_t nib2 = hex2nibble(key[i + 2]);
|
|
|
|
|
|
|
|
|
|
|
|
if (nib1 >= HEX_DIGIT_INVALID || nib2 >= HEX_DIGIT_INVALID) {
|
|
|
|
|
|
ctx->value = "";
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid \\x sequence: \\x%c%c",
|
|
|
|
|
|
key[i + 1], key[i + 2]);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
data[data_len++] = nib1 << 4 | nib2;
|
|
|
|
|
|
esc = false;
|
|
|
|
|
|
i += 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
data[data_len++] = key[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct binding_aux aux = {
|
|
|
|
|
|
.type = BINDING_AUX_TEXT,
|
|
|
|
|
|
.text = {
|
|
|
|
|
|
.data = data,
|
|
|
|
|
|
.len = data_len,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (!value_to_key_combos(ctx, BIND_ACTION_TEXT_BINDING, &aux,
|
|
|
|
|
|
&conf->bindings.key, KEY_BINDING))
|
|
|
|
|
|
{
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
|
free(data);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-28 19:27:29 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_environment(struct context *ctx)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2022-12-01 19:43:38 +01:00
|
|
|
|
/* Check for pre-existing env variable */
|
2022-05-28 19:27:29 +02:00
|
|
|
|
tll_foreach(conf->env_vars, it) {
|
2022-12-01 19:43:38 +01:00
|
|
|
|
if (strcmp(it->item.name, key) == 0)
|
|
|
|
|
|
return value_to_str(ctx, &it->item.value);
|
2022-05-28 19:27:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-01 19:43:38 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* No pre-existing variable - allocate a new one
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
char *value = NULL;
|
|
|
|
|
|
if (!value_to_str(ctx, &value))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
tll_push_back(conf->env_vars, ((struct env_var){xstrdup(key), value}));
|
2022-05-28 19:27:29 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-17 16:46:54 +01:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_section_tweak(struct context *ctx)
|
2020-03-17 16:46:54 +01:00
|
|
|
|
{
|
2021-11-05 13:50:14 +01:00
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
2020-09-13 17:59:56 +02:00
|
|
|
|
if (strcmp(key, "scaling-filter") == 0) {
|
2021-11-05 18:11:53 +01:00
|
|
|
|
static const char *filters[] = {
|
2021-05-08 16:50:49 +01:00
|
|
|
|
[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",
|
2021-11-05 18:11:53 +01:00
|
|
|
|
NULL,
|
2020-09-13 17:59:56 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->tweak.fcft_filter) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
|
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return value_to_enum(ctx, filters, (int *)&conf->tweak.fcft_filter);
|
2020-09-13 17:59:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
else if (strcmp(key, "overflowing-glyphs") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->tweak.overflowing_glyphs);
|
2021-05-31 17:10:05 +02:00
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
else if (strcmp(key, "damage-whole-window") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->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) {
|
2021-11-06 11:25:55 +01:00
|
|
|
|
if (!value_to_bool(ctx, &conf->tweak.grapheme_shaping))
|
|
|
|
|
|
return false;
|
2020-08-20 19:25:35 +02:00
|
|
|
|
|
|
|
|
|
|
#if !defined(FOOT_GRAPHEME_CLUSTERING)
|
|
|
|
|
|
if (conf->tweak.grapheme_shaping) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_WARN(
|
|
|
|
|
|
"foot was not compiled with support for grapheme shaping");
|
2020-08-20 19:25:35 +02:00
|
|
|
|
conf->tweak.grapheme_shaping = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
if (conf->tweak.grapheme_shaping && !conf->can_shape_grapheme) {
|
|
|
|
|
|
LOG_WARN(
|
2021-11-05 17:13:39 +01:00
|
|
|
|
"fcft was not compiled with support for grapheme shaping");
|
2020-08-20 19:25:35 +02:00
|
|
|
|
|
|
|
|
|
|
/* Keep it enabled though - this will cause us to do
|
|
|
|
|
|
* grapheme-clustering at least */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return true;
|
2020-08-20 19:25:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-30 18:00:33 +02:00
|
|
|
|
else if (strcmp(key, "grapheme-width-method") == 0) {
|
2021-11-06 13:39:59 +01:00
|
|
|
|
_Static_assert(sizeof(conf->tweak.grapheme_width_method) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
2021-11-22 23:02:25 +01:00
|
|
|
|
(const char *[]){"wcswidth", "double-width", "max", NULL},
|
2021-11-06 13:44:50 +01:00
|
|
|
|
(int *)&conf->tweak.grapheme_width_method);
|
2021-06-30 18:00:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-03 17:37:44 +02:00
|
|
|
|
else if (strcmp(key, "render-timer") == 0) {
|
2022-01-13 12:08:20 +01:00
|
|
|
|
_Static_assert(sizeof(conf->tweak.render_timer) == sizeof(int),
|
|
|
|
|
|
"enum is not 32-bit");
|
2021-11-05 18:11:53 +01:00
|
|
|
|
|
2022-01-13 12:08:20 +01:00
|
|
|
|
return value_to_enum(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
(const char *[]){"none", "osd", "log", "both", NULL},
|
|
|
|
|
|
(int *)&conf->tweak.render_timer);
|
2020-08-13 18:35:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "delayed-render-lower") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
uint32_t ns;
|
|
|
|
|
|
if (!value_to_uint32(ctx, 10, &ns))
|
2020-03-17 16:46:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (ns > 16666666) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("timeout must not exceed 16ms");
|
2020-03-17 16:46:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->tweak.delayed_render_lower_ns = ns;
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return true;
|
2020-03-17 16:46:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "delayed-render-upper") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
uint32_t ns;
|
|
|
|
|
|
if (!value_to_uint32(ctx, 10, &ns))
|
2020-03-17 16:46:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (ns > 16666666) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("timeout must not exceed 16ms");
|
2020-03-17 16:46:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
conf->tweak.delayed_render_upper_ns = ns;
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return true;
|
2020-03-17 16:46:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-25 20:48:02 +01:00
|
|
|
|
else if (strcmp(key, "max-shm-pool-size-mb") == 0) {
|
2021-11-06 12:01:57 +01:00
|
|
|
|
uint32_t mb;
|
|
|
|
|
|
if (!value_to_uint32(ctx, 10, &mb))
|
2020-03-25 20:48:02 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
2021-11-06 12:01:57 +01:00
|
|
|
|
conf->tweak.max_shm_pool_size = min((int32_t)mb * 1024 * 1024, INT32_MAX);
|
2021-11-06 13:44:50 +01:00
|
|
|
|
return true;
|
2021-01-08 10:27:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
else if (strcmp(key, "box-drawing-base-thickness") == 0)
|
2023-07-26 16:12:36 +02:00
|
|
|
|
return value_to_float(ctx, &conf->tweak.box_drawing_base_thickness);
|
2021-05-05 22:24:25 +02:00
|
|
|
|
|
2021-11-06 13:44:50 +01:00
|
|
|
|
else if (strcmp(key, "box-drawing-solid-shades") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->tweak.box_drawing_solid_shades);
|
2021-05-05 22:24:25 +02:00
|
|
|
|
|
2021-08-31 19:42:22 +02:00
|
|
|
|
else if (strcmp(key, "font-monospace-warn") == 0)
|
2021-11-06 11:25:55 +01:00
|
|
|
|
return value_to_bool(ctx, &conf->tweak.font_monospace_warn);
|
2021-08-31 19:42:22 +02:00
|
|
|
|
|
2022-02-27 16:29:35 +01:00
|
|
|
|
else if (strcmp(key, "sixel") == 0)
|
|
|
|
|
|
return value_to_bool(ctx, &conf->tweak.sixel);
|
|
|
|
|
|
|
2023-07-26 16:12:36 +02:00
|
|
|
|
else if (strcmp(key, "bold-text-in-bright-amount") == 0)
|
|
|
|
|
|
return value_to_float(ctx, &conf->bold_in_bright.amount);
|
|
|
|
|
|
|
2020-03-17 16:46:54 +01:00
|
|
|
|
else {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
2020-03-17 16:46:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-05 00:19:21 +08:00
|
|
|
|
static bool
|
|
|
|
|
|
parse_section_touch(struct context *ctx) {
|
|
|
|
|
|
struct config *conf = ctx->conf;
|
|
|
|
|
|
const char *key = ctx->key;
|
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(key, "long-press-delay") == 0)
|
|
|
|
|
|
return value_to_uint32(ctx, 10, &conf->touch.long_press_delay);
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
|
static bool
|
2021-11-05 13:50:14 +01:00
|
|
|
|
parse_key_value(char *kv, const char **section, const char **key, const char **value)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2022-01-29 23:10:26 +05:30
|
|
|
|
bool section_is_needed = section != NULL;
|
|
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
/* Strip leading whitespace */
|
|
|
|
|
|
while (isspace(kv[0]))
|
2021-06-11 04:40:08 -05:00
|
|
|
|
++kv;
|
|
|
|
|
|
|
2022-01-29 23:10:26 +05:30
|
|
|
|
if (section_is_needed)
|
|
|
|
|
|
*section = "main";
|
|
|
|
|
|
|
|
|
|
|
|
if (kv[0] == '=')
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
*key = kv;
|
|
|
|
|
|
*value = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
size_t kvlen = strlen(kv);
|
2022-02-04 18:30:29 +05:30
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace */
|
|
|
|
|
|
while (isspace(kv[kvlen - 1]))
|
|
|
|
|
|
kvlen--;
|
|
|
|
|
|
kv[kvlen] = '\0';
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
for (size_t i = 0; i < kvlen; ++i) {
|
2022-01-29 23:10:26 +05:30
|
|
|
|
if (kv[i] == '.' && section_is_needed) {
|
|
|
|
|
|
section_is_needed = false;
|
|
|
|
|
|
*section = kv;
|
|
|
|
|
|
kv[i] = '\0';
|
|
|
|
|
|
if (i == kvlen - 1 || kv[i + 1] == '=') {
|
|
|
|
|
|
*key = NULL;
|
|
|
|
|
|
return false;
|
2021-06-11 04:40:08 -05:00
|
|
|
|
}
|
2022-01-29 23:10:26 +05:30
|
|
|
|
*key = &kv[i + 1];
|
2021-06-11 04:40:08 -05:00
|
|
|
|
} else if (kv[i] == '=') {
|
|
|
|
|
|
kv[i] = '\0';
|
2022-02-04 18:30:29 +05:30
|
|
|
|
if (i != kvlen - 1)
|
|
|
|
|
|
*value = &kv[i + 1];
|
2021-06-11 04:40:08 -05:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-01-29 23:10:26 +05:30
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
if (*value == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace from key (leading stripped earlier) */
|
|
|
|
|
|
{
|
2022-02-04 18:30:29 +05:30
|
|
|
|
xassert(!isspace(*key[0]));
|
2021-06-11 04:40:08 -05:00
|
|
|
|
|
2021-11-05 13:50:14 +01:00
|
|
|
|
char *end = (char *)*key + strlen(*key) - 1;
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while (isspace(end[0]))
|
2021-06-11 04:40:08 -05:00
|
|
|
|
end--;
|
2022-02-04 18:30:29 +05:30
|
|
|
|
end[1] = '\0';
|
2021-06-11 04:40:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
/* Strip leading whitespace from value (trailing stripped earlier) */
|
|
|
|
|
|
while (isspace(*value[0]))
|
|
|
|
|
|
++*value;
|
|
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
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,
|
2022-02-06 19:36:44 +01:00
|
|
|
|
SECTION_TEXT_BINDINGS,
|
2022-05-28 19:27:29 +02:00
|
|
|
|
SECTION_ENVIRONMENT,
|
2021-06-11 04:40:08 -05:00
|
|
|
|
SECTION_TWEAK,
|
2023-07-05 00:19:21 +08:00
|
|
|
|
SECTION_TOUCH,
|
2021-06-11 04:40:08 -05:00
|
|
|
|
SECTION_COUNT,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Function pointer, called for each key/value line */
|
2021-11-05 13:50:14 +01:00
|
|
|
|
typedef bool (*parser_fun_t)(struct context *ctx);
|
2021-06-11 04:40:08 -05:00
|
|
|
|
|
|
|
|
|
|
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"},
|
2022-02-06 19:36:44 +01:00
|
|
|
|
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
|
2022-05-28 19:27:29 +02:00
|
|
|
|
[SECTION_ENVIRONMENT] = {&parse_section_environment, "environment"},
|
2021-06-11 04:40:08 -05:00
|
|
|
|
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
2023-07-05 00:19:21 +08:00
|
|
|
|
[SECTION_TOUCH] = {&parse_section_touch, "touch"},
|
2021-06-11 04:40:08 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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-21 11:46:46 +02:00
|
|
|
|
char *_line = NULL;
|
2020-03-02 18:48:29 +01:00
|
|
|
|
size_t count = 0;
|
2022-02-04 18:30:29 +05:30
|
|
|
|
bool ret = true;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
|
#define error_or_continue() \
|
|
|
|
|
|
{ \
|
2022-02-04 18:30:29 +05:30
|
|
|
|
if (errors_are_fatal) { \
|
|
|
|
|
|
ret = false; \
|
|
|
|
|
|
goto done; \
|
|
|
|
|
|
} else \
|
2020-07-31 17:07:14 +02:00
|
|
|
|
continue; \
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 10:55:57 +01:00
|
|
|
|
char *section_name = xstrdup("main");
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
struct context context = {
|
2021-11-05 13:50:14 +01:00
|
|
|
|
.conf = conf,
|
2021-11-21 10:55:57 +01:00
|
|
|
|
.section = section_name,
|
2021-11-05 13:50:14 +01:00
|
|
|
|
.path = path,
|
|
|
|
|
|
.lineno = 0,
|
|
|
|
|
|
.errors_are_fatal = errors_are_fatal,
|
|
|
|
|
|
};
|
2021-11-05 14:48:06 +01:00
|
|
|
|
struct context *ctx = &context; /* For LOG_AND_*() */
|
2021-11-05 13:50:14 +01:00
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
errno = 0;
|
|
|
|
|
|
ssize_t len;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while ((len = getline(&_line, &count, f)) != -1) {
|
|
|
|
|
|
context.key = NULL;
|
|
|
|
|
|
context.value = NULL;
|
|
|
|
|
|
context.lineno++;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
char *line = _line;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
|
/* Strip leading whitespace */
|
2022-02-04 18:30:29 +05:30
|
|
|
|
while (isspace(line[0])) {
|
|
|
|
|
|
line++;
|
|
|
|
|
|
len--;
|
2019-07-21 11:46:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
/* Strip the trailing newline - may be absent on the last line */
|
|
|
|
|
|
if (line[len - 1] == '\n')
|
|
|
|
|
|
line[--len] = '\0';
|
|
|
|
|
|
|
2021-01-03 14:08:25 -05:00
|
|
|
|
/* Split up into key/value pair + trailing comment separated by blank */
|
|
|
|
|
|
char *key_value = line;
|
2022-02-04 18:30:29 +05:30
|
|
|
|
char *kv_trailing = &line[len - 1];
|
|
|
|
|
|
char *comment = &line[1];
|
|
|
|
|
|
while (comment[1] != '\0') {
|
|
|
|
|
|
if (isblank(comment[0]) && comment[1] == '#') {
|
|
|
|
|
|
comment[1] = '\0'; /* Terminate key/value pair */
|
|
|
|
|
|
kv_trailing = comment++;
|
2021-01-03 14:08:25 -05:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2022-02-04 18:30:29 +05:30
|
|
|
|
comment++;
|
2021-01-03 14:08:25 -05:00
|
|
|
|
}
|
2022-02-04 18:30:29 +05:30
|
|
|
|
comment++;
|
|
|
|
|
|
|
|
|
|
|
|
/* Strip trailing whitespace */
|
|
|
|
|
|
while (isspace(kv_trailing[0]))
|
|
|
|
|
|
kv_trailing--;
|
|
|
|
|
|
kv_trailing[1] = '\0';
|
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] == '[') {
|
2022-01-29 23:10:26 +05:30
|
|
|
|
key_value++;
|
|
|
|
|
|
|
|
|
|
|
|
if (key_value[0] == ']') {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("empty section name");
|
2022-02-04 18:30:29 +05:30
|
|
|
|
section = SECTION_COUNT;
|
2022-01-29 23:10:26 +05:30
|
|
|
|
error_or_continue();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
|
char *end = strchr(key_value, ']');
|
2022-01-29 23:10:26 +05:30
|
|
|
|
|
2019-07-21 11:31:16 +02:00
|
|
|
|
if (end == NULL) {
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = key_value;
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("syntax error: no closing ']'");
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = section_name;
|
|
|
|
|
|
section = SECTION_COUNT;
|
2020-07-31 17:07:14 +02:00
|
|
|
|
error_or_continue();
|
2019-07-21 11:31:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-29 23:10:26 +05:30
|
|
|
|
end[0] = '\0';
|
2019-07-21 11:31:16 +02:00
|
|
|
|
|
2022-01-29 23:10:26 +05:30
|
|
|
|
if (end[1] != '\0') {
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = key_value;
|
2022-01-29 23:10:26 +05:30
|
|
|
|
LOG_CONTEXTUAL_ERR("section declaration contains trailing "
|
|
|
|
|
|
"characters");
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = section_name;
|
|
|
|
|
|
section = SECTION_COUNT;
|
2022-01-29 23:10:26 +05:30
|
|
|
|
error_or_continue();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
section = str_to_section(key_value);
|
2020-03-09 20:03:04 +01:00
|
|
|
|
if (section == SECTION_COUNT) {
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = key_value;
|
2022-01-29 23:10:26 +05:30
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid section name: %s", key_value);
|
2022-02-04 18:30:29 +05:30
|
|
|
|
context.section = section_name;
|
2020-07-31 17:07:14 +02:00
|
|
|
|
error_or_continue();
|
2019-07-21 11:31:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 10:55:57 +01:00
|
|
|
|
free(section_name);
|
2022-01-29 23:10:26 +05:30
|
|
|
|
section_name = xstrdup(key_value);
|
2021-11-21 10:55:57 +01:00
|
|
|
|
context.section = section_name;
|
2021-11-05 13:50:14 +01: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-11-05 14:48:06 +01:00
|
|
|
|
if (!parse_key_value(key_value, NULL, &context.key, &context.value)) {
|
2022-01-29 23:10:26 +05:30
|
|
|
|
LOG_CONTEXTUAL_ERR("syntax error: key/value pair has no %s",
|
|
|
|
|
|
context.key == NULL ? "key" : "value");
|
2022-02-04 18:30:29 +05:30
|
|
|
|
error_or_continue();
|
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'",
|
2022-02-04 18:30:29 +05:30
|
|
|
|
section_info[section].name, context.key, context.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-11-05 14:48:06 +01:00
|
|
|
|
if (!section_parser(ctx))
|
2020-07-31 17:07:14 +02:00
|
|
|
|
error_or_continue();
|
2022-07-29 21:27:27 +02:00
|
|
|
|
|
|
|
|
|
|
/* For next iteration of getline() */
|
|
|
|
|
|
errno = 0;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
if (errno != 0) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO("failed to read from configuration");
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
ret = false;
|
|
|
|
|
|
}
|
2019-07-21 11:46:46 +02:00
|
|
|
|
|
2022-02-04 18:30:29 +05:30
|
|
|
|
done:
|
2021-11-21 10:55:57 +01:00
|
|
|
|
free(section_name);
|
2019-07-21 11:46:46 +02:00
|
|
|
|
free(_line);
|
2022-02-04 18:30:29 +05:30
|
|
|
|
return ret;
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-26 17:54:03 +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}
|
|
|
|
|
|
#define m_ctrl_shift_alt {.ctrl = true, .shift = true, .alt = 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-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_SCROLLBACK_UP_PAGE, m_shift, {{XKB_KEY_Prior}}},
|
|
|
|
|
|
{BIND_ACTION_SCROLLBACK_DOWN_PAGE, m_shift, {{XKB_KEY_Next}}},
|
|
|
|
|
|
{BIND_ACTION_CLIPBOARD_COPY, m_ctrl_shift, {{XKB_KEY_c}}},
|
2022-01-17 13:57:24 -05:00
|
|
|
|
{BIND_ACTION_CLIPBOARD_COPY, m_none, {{XKB_KEY_XF86Copy}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_CLIPBOARD_PASTE, m_ctrl_shift, {{XKB_KEY_v}}},
|
2022-01-17 13:57:24 -05:00
|
|
|
|
{BIND_ACTION_CLIPBOARD_PASTE, m_none, {{XKB_KEY_XF86Paste}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{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 control-shift-u to unicode-input, move urls to o
Having a keybinding to invoke arbitrary unicode characters is very
useful. It's often used as a method of last resort to communicate with
people outside of your main language. For example, if you want to type
the last letter of my real name, you can invoke the latin-1 character
0xe9 or unicode 0x00e9.
You can also use this to type special characters, for example, unicode
U+1F4A9 is of course, the infamous PILE OF POO, which is sure to
produce million laughs everywhere you go.
In foot, there's no keybinding by default to invoke the very useful
unicode-input command. There is no "standard" (as in "ISO") keybinding
this either. But there *is* a de-facto standard currently deployed
by *both* GTK and Qt (a rare feat) *and* Chrome OS (an even rarer
feat) and it's control-shift-u.
Alternatives include Control-x 8 (emacs), Control V u (vim),
Alt (Windows, LibreOffice), or Option (Mac). I doubt we want to adopt
any of those.
So let's use control-shift-u for this. Unfortunately, it's currently
assigned to show-urls-launch, which is unfortunate, but
insurmountable. We can reassign this keybinding elsewhere. I have
picked control-shift-o in my configuration, because "o" is a good
mnemonic for "open URLs". Others have suggested "m" instead.
Closes: #1183
2022-12-15 11:49:51 -05:00
|
|
|
|
{BIND_ACTION_SHOW_URLS_LAUNCH, m_ctrl_shift, {{XKB_KEY_o}}},
|
|
|
|
|
|
{BIND_ACTION_UNICODE_INPUT, m_ctrl_shift, {{XKB_KEY_u}}},
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
|
{BIND_ACTION_PROMPT_PREV, m_ctrl_shift, {{XKB_KEY_z}}},
|
|
|
|
|
|
{BIND_ACTION_PROMPT_NEXT, m_ctrl_shift, {{XKB_KEY_x}}},
|
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[] = {
|
2023-09-26 17:54:03 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE, m_shift, {{XKB_KEY_Prior}}},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE, m_shift, {{XKB_KEY_Next}}},
|
2021-12-06 21:04:38 +01: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}}},
|
2023-09-26 17:54:03 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_CHAR, m_shift, {{XKB_KEY_Right}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_WORD, m_ctrl, {{XKB_KEY_w}}},
|
2023-10-08 10:10:03 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_WORD, m_ctrl_shift, {{XKB_KEY_Right}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_WORD_WS, m_ctrl_shift, {{XKB_KEY_w}}},
|
2023-09-26 17:54:03 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_LINE_DOWN, m_shift, {{XKB_KEY_Down}}},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_BACKWARD_CHAR, m_shift, {{XKB_KEY_Left}}},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD, m_ctrl_shift, {{XKB_KEY_Left}}},
|
|
|
|
|
|
{BIND_ACTION_SEARCH_EXTEND_LINE_UP, m_shift, {{XKB_KEY_Up}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, {{XKB_KEY_v}}},
|
2022-04-26 18:34:18 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl_shift, {{XKB_KEY_v}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, {{XKB_KEY_y}}},
|
2022-04-26 18:34:18 +02:00
|
|
|
|
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_none, {{XKB_KEY_XF86Paste}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{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-12-06 21:04:38 +01: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-12-06 21:04:38 +01:00
|
|
|
|
static const struct config_key_binding bindings[] = {
|
2023-09-18 16:36:39 +02:00
|
|
|
|
{BIND_ACTION_SCROLLBACK_UP_MOUSE, m_none, {.m = {BTN_BACK, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SCROLLBACK_DOWN_MOUSE, m_none, {.m = {BTN_FORWARD, 1}}},
|
2021-12-06 21:04:38 +01:00
|
|
|
|
{BIND_ACTION_PRIMARY_PASTE, m_none, {.m = {BTN_MIDDLE, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_BEGIN, m_none, {.m = {BTN_LEFT, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_BEGIN_BLOCK, m_ctrl, {.m = {BTN_LEFT, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_EXTEND, m_none, {.m = {BTN_RIGHT, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_EXTEND_CHAR_WISE, m_ctrl, {.m = {BTN_RIGHT, 1}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_WORD, m_none, {.m = {BTN_LEFT, 2}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_WORD_WS, m_ctrl, {.m = {BTN_LEFT, 2}}},
|
selection: add support for selecting the contents of a quote
This patch changes the default of triple clicking, from selecting the
current logical row, to first trying to select the contents of the
quote under the cursor, and if failing to find a quote, selecting the
current row (like before).
This is implemented by adding a new key binding, 'select-quote'.
It will search for surrounding quote characters, and if one is found
on each side of the cursor, the quote is selected. If not, the entire
row is selected instead.
Subsequent selection operations will behave as if the selection is
either a word selection (a quote was found), or a row selection (no
quote found).
Escaped quote characters are not supported: "foo \" bar" will match
'foo \', and not 'foo " bar'.
Mismatched quotes are not custom handled. They will simply not match.
Nested quotes ("123 'abc def' 456") are supported.
Closes #1364
2023-09-19 16:23:34 +02:00
|
|
|
|
{BIND_ACTION_SELECT_QUOTE, m_none, {.m = {BTN_LEFT, 3}}},
|
|
|
|
|
|
{BIND_ACTION_SELECT_ROW, m_none, {.m = {BTN_LEFT, 4}}},
|
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,
|
2023-05-02 01:53:01 +10:00
|
|
|
|
config_override_t *overrides, bool errors_are_fatal,
|
|
|
|
|
|
bool as_server)
|
2019-07-16 11:52:22 +02:00
|
|
|
|
{
|
2023-09-22 08:23:38 +00:00
|
|
|
|
bool ret = true;
|
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"),
|
2023-05-02 01:53:01 +10:00
|
|
|
|
.app_id = (as_server ? xstrdup("footclient") : xstrdup("foot")),
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
.word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
|
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
|
|
|
|
},
|
2022-09-03 12:16:41 +02:00
|
|
|
|
.pad_x = 0,
|
|
|
|
|
|
.pad_y = 0,
|
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,
|
2023-07-26 16:12:36 +02:00
|
|
|
|
.amount = 1.3,
|
2021-04-17 21:57:08 +02:00
|
|
|
|
},
|
2020-03-26 19:39:12 +01:00
|
|
|
|
.startup_mode = STARTUP_WINDOWED,
|
2021-06-17 18:15:29 +02:00
|
|
|
|
.fonts = {{0}},
|
2022-12-15 11:10:32 +01:00
|
|
|
|
.font_size_adjustment = {.percent = 0., .pt_or_px = {.pt = 0.5, .px = 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,
|
2022-08-19 02:54:49 +02:00
|
|
|
|
.underline_thickness = {.pt = 0., .px = -1},
|
2023-06-26 17:55:04 +02:00
|
|
|
|
.dpi_aware = false,
|
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 = {
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
.label_letters = xc32dup(U"sadfjklewcmpgh"),
|
|
|
|
|
|
.uri_characters = xc32dup(U"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,
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
.text = xc32dup(U""),
|
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,
|
2021-09-27 19:05:40 +00:00
|
|
|
|
.scrollback_indicator = false,
|
2021-02-06 11:10:40 +01:00
|
|
|
|
.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,
|
2021-09-27 19:09:07 +00:00
|
|
|
|
.selection_override_modifiers = {
|
|
|
|
|
|
.shift = true,
|
|
|
|
|
|
.alt = false,
|
|
|
|
|
|
.ctrl = false,
|
2021-12-05 16:30:01 +01:00
|
|
|
|
.super = false,
|
2021-09-27 19:09:07 +00:00
|
|
|
|
},
|
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},
|
2022-04-15 20:12:34 +02:00
|
|
|
|
.hide_when_maximized = false,
|
2023-07-14 12:03:35 +02:00
|
|
|
|
.double_click_to_maximize = true,
|
2020-03-02 18:42:49 +01:00
|
|
|
|
.title_height = 26,
|
|
|
|
|
|
.border_width = 5,
|
2021-12-22 20:21:46 +01:00
|
|
|
|
.border_width_visible = 0,
|
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
|
|
|
|
},
|
2021-10-05 23:07:01 +02:00
|
|
|
|
.notify_focus_inhibit = true,
|
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,
|
2021-10-13 17:42:40 +02:00
|
|
|
|
#if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING
|
|
|
|
|
|
.grapheme_shaping = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
|
|
|
|
|
|
#endif
|
2022-12-16 16:56:43 +01: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,
|
2022-01-13 12:08:20 +01:00
|
|
|
|
.render_timer = RENDER_TIMER_NONE,
|
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,
|
2022-02-27 16:29:35 +01:00
|
|
|
|
.sixel = true,
|
2020-03-17 16:46:54 +01:00
|
|
|
|
},
|
2020-07-29 19:42:12 +02:00
|
|
|
|
|
2023-07-05 00:19:21 +08:00
|
|
|
|
.touch = {
|
|
|
|
|
|
.long_press_delay = 400,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2022-05-28 19:27:29 +02:00
|
|
|
|
.env_vars = tll_init(),
|
2023-05-17 20:51:40 +02:00
|
|
|
|
#if defined(UTMP_DEFAULT_HELPER_PATH)
|
|
|
|
|
|
.utmp_helper_path = ((strlen(UTMP_DEFAULT_HELPER_PATH) > 0 &&
|
|
|
|
|
|
access(UTMP_DEFAULT_HELPER_PATH, X_OK) == 0)
|
|
|
|
|
|
? xstrdup(UTMP_DEFAULT_HELPER_PATH)
|
|
|
|
|
|
: NULL),
|
|
|
|
|
|
#endif
|
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
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
static const char32_t *url_protocols[] = {
|
|
|
|
|
|
U"http://",
|
|
|
|
|
|
U"https://",
|
|
|
|
|
|
U"ftp://",
|
|
|
|
|
|
U"ftps://",
|
|
|
|
|
|
U"file://",
|
|
|
|
|
|
U"gemini://",
|
|
|
|
|
|
U"gopher://",
|
|
|
|
|
|
U"irc://",
|
|
|
|
|
|
U"ircs://",
|
2021-05-20 17:58:06 +02:00
|
|
|
|
};
|
|
|
|
|
|
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++) {
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
size_t len = c32len(url_protocols[i]);
|
2021-05-20 17:58:06 +02:00
|
|
|
|
if (len > conf->url.max_prot_len)
|
|
|
|
|
|
conf->url.max_prot_len = len;
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
conf->url.protocols[i] = xc32dup(url_protocols[i]);
|
2021-05-20 17:58:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 18:25:36 +02:00
|
|
|
|
qsort(
|
|
|
|
|
|
conf->url.uri_characters,
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
c32len(conf->url.uri_characters),
|
2021-08-09 18:25:36 +02:00
|
|
|
|
sizeof(conf->url.uri_characters[0]),
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
&c32cmp_single);
|
2021-08-09 18:25:36 +02:00
|
|
|
|
|
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;
|
2023-09-22 08:23:38 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
conf_file.path = xstrdup(conf_path);
|
|
|
|
|
|
conf_file.fd = fd;
|
2019-12-17 19:08:43 +01:00
|
|
|
|
}
|
2020-08-27 19:48:13 +02:00
|
|
|
|
} 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;
|
|
|
|
|
|
}
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-22 08:23:38 +00:00
|
|
|
|
if (conf_file.path && conf_file.fd >= 0) {
|
|
|
|
|
|
LOG_INFO("loading configuration from %s", conf_file.path);
|
2019-08-12 19:31:21 +02:00
|
|
|
|
|
2023-09-22 08:23:38 +00:00
|
|
|
|
FILE *f = fdopen(conf_file.fd, "r");
|
|
|
|
|
|
if (f == NULL) {
|
|
|
|
|
|
LOG_AND_NOTIFY_ERRNO("%s: failed to open", conf_file.path);
|
|
|
|
|
|
ret = !errors_are_fatal;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_config_file(f, conf, conf_file.path, errors_are_fatal))
|
|
|
|
|
|
ret = !errors_are_fatal;
|
|
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
}
|
2019-07-16 11:52:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-22 08:23:38 +00:00
|
|
|
|
if (!config_override_apply(conf, overrides, errors_are_fatal))
|
2021-12-05 15:19:22 +01:00
|
|
|
|
ret = !errors_are_fatal;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
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;
|
|
|
|
|
|
|
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
|
2021-12-05 15:19:22 +01:00
|
|
|
|
config_override_apply(struct config *conf, config_override_t *overrides,
|
|
|
|
|
|
bool errors_are_fatal)
|
2021-06-11 04:40:08 -05:00
|
|
|
|
{
|
2021-11-05 14:48:06 +01:00
|
|
|
|
struct context context = {
|
2021-11-05 13:50:14 +01:00
|
|
|
|
.conf = conf,
|
|
|
|
|
|
.path = "override",
|
|
|
|
|
|
.lineno = 0,
|
|
|
|
|
|
.errors_are_fatal = errors_are_fatal,
|
|
|
|
|
|
};
|
2021-11-05 14:48:06 +01:00
|
|
|
|
struct context *ctx = &context;
|
2021-11-05 13:50:14 +01:00
|
|
|
|
|
2021-06-11 04:40:08 -05:00
|
|
|
|
tll_foreach(*overrides, it) {
|
2021-11-05 14:48:06 +01:00
|
|
|
|
context.lineno++;
|
2021-11-05 13:50:14 +01:00
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
if (!parse_key_value(
|
|
|
|
|
|
it->item, &context.section, &context.key, &context.value))
|
|
|
|
|
|
{
|
2022-01-29 23:10:26 +05:30
|
|
|
|
LOG_CONTEXTUAL_ERR("syntax error: key/value pair has no %s",
|
|
|
|
|
|
context.key == NULL ? "key" : "value");
|
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (context.section[0] == '\0') {
|
|
|
|
|
|
LOG_CONTEXTUAL_ERR("empty section name");
|
2021-06-11 04:40:08 -05:00
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
enum section section = str_to_section(context.section);
|
2021-06-11 04:40:08 -05:00
|
|
|
|
if (section == SECTION_COUNT) {
|
2021-11-05 17:13:39 +01:00
|
|
|
|
LOG_CONTEXTUAL_ERR("invalid section name: %s", context.section);
|
2021-06-11 04:40:08 -05:00
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
parser_fun_t section_parser = section_info[section].fun;
|
|
|
|
|
|
xassert(section_parser != NULL);
|
|
|
|
|
|
|
2021-11-05 14:48:06 +01:00
|
|
|
|
if (!section_parser(ctx)) {
|
2021-06-11 04:40:08 -05:00
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-12-05 15:19:22 +01:00
|
|
|
|
|
2021-12-22 20:21:46 +01:00
|
|
|
|
conf->csd.border_width = max(
|
|
|
|
|
|
min_csd_border_width, conf->csd.border_width_visible);
|
|
|
|
|
|
|
2022-02-08 21:21:17 +01:00
|
|
|
|
return
|
|
|
|
|
|
resolve_key_binding_collisions(
|
|
|
|
|
|
conf, section_info[SECTION_KEY_BINDINGS].name,
|
|
|
|
|
|
binding_action_map, &conf->bindings.key, KEY_BINDING) &&
|
|
|
|
|
|
resolve_key_binding_collisions(
|
|
|
|
|
|
conf, section_info[SECTION_SEARCH_BINDINGS].name,
|
|
|
|
|
|
search_binding_action_map, &conf->bindings.search, KEY_BINDING) &&
|
|
|
|
|
|
resolve_key_binding_collisions(
|
|
|
|
|
|
conf, section_info[SECTION_URL_BINDINGS].name,
|
|
|
|
|
|
url_binding_action_map, &conf->bindings.url, KEY_BINDING) &&
|
|
|
|
|
|
resolve_key_binding_collisions(
|
|
|
|
|
|
conf, section_info[SECTION_MOUSE_BINDINGS].name,
|
|
|
|
|
|
binding_action_map, &conf->bindings.mouse, MOUSE_BINDING);
|
2021-06-11 04:40:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
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;
|
2022-02-06 19:36:44 +01:00
|
|
|
|
uint8_t *last_master_text_data = NULL;
|
|
|
|
|
|
size_t last_master_text_len = 0;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
switch (old->aux.type) {
|
|
|
|
|
|
case BINDING_AUX_NONE:
|
|
|
|
|
|
last_master_argv = NULL;
|
2022-02-06 19:36:44 +01:00
|
|
|
|
last_master_text_data = NULL;
|
|
|
|
|
|
last_master_text_len = 0;
|
2022-02-08 19:35:41 +01:00
|
|
|
|
break;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
case BINDING_AUX_PIPE:
|
|
|
|
|
|
if (old->aux.master_copy) {
|
|
|
|
|
|
clone_argv(&new->aux.pipe, &old->aux.pipe);
|
|
|
|
|
|
last_master_argv = &new->aux.pipe;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xassert(last_master_argv != NULL);
|
|
|
|
|
|
new->aux.pipe = *last_master_argv;
|
|
|
|
|
|
}
|
2022-02-06 19:36:44 +01:00
|
|
|
|
last_master_text_data = NULL;
|
|
|
|
|
|
last_master_text_len = 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case BINDING_AUX_TEXT:
|
|
|
|
|
|
if (old->aux.master_copy) {
|
|
|
|
|
|
const size_t len = old->aux.text.len;
|
|
|
|
|
|
new->aux.text.len = len;
|
|
|
|
|
|
new->aux.text.data = xmalloc(len);
|
|
|
|
|
|
memcpy(new->aux.text.data, old->aux.text.data, len);
|
|
|
|
|
|
|
|
|
|
|
|
last_master_text_len = len;
|
|
|
|
|
|
last_master_text_data = new->aux.text.data;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xassert(last_master_text_data != NULL);
|
|
|
|
|
|
new->aux.text.len = last_master_text_len;
|
|
|
|
|
|
new->aux.text.data = last_master_text_data;
|
|
|
|
|
|
}
|
|
|
|
|
|
last_master_argv = NULL;
|
2022-02-08 19:35:41 +01:00
|
|
|
|
break;
|
2021-06-18 16:18:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
conf->word_delimiters = xc32dup(old->word_delimiters);
|
|
|
|
|
|
conf->scrollback.indicator.text = xc32dup(old->scrollback.indicator.text);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
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
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
conf->url.label_letters = xc32dup(old->url.label_letters);
|
|
|
|
|
|
conf->url.uri_characters = xc32dup(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++)
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
conf->url.protocols[i] = xc32dup(old->url.protocols[i]);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
|
|
|
|
|
|
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);
|
2021-12-06 21:04:38 +01:00
|
|
|
|
key_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
|
2022-05-28 19:27:29 +02:00
|
|
|
|
tll_foreach(old->env_vars, it) {
|
|
|
|
|
|
struct env_var copy = {
|
|
|
|
|
|
.name = xstrdup(it->item.name),
|
|
|
|
|
|
.value = xstrdup(it->item.value),
|
|
|
|
|
|
};
|
|
|
|
|
|
tll_push_back(conf->env_vars, copy);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-17 20:51:40 +02:00
|
|
|
|
conf->utmp_helper_path =
|
|
|
|
|
|
old->utmp_helper_path != NULL ? xstrdup(old->utmp_helper_path) : NULL;
|
2022-09-23 20:24:04 +02:00
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
|
conf->notifications.length = 0;
|
|
|
|
|
|
conf->notifications.head = conf->notifications.tail = 0;
|
|
|
|
|
|
tll_foreach(old->notifications, it) {
|
2021-11-14 15:08:47 +00:00
|
|
|
|
char *text = xstrdup(it->item.text);
|
|
|
|
|
|
user_notification_add(&conf->notifications, it->item.kind, text);
|
2021-06-18 16:18:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
2023-06-26 20:15:36 +02:00
|
|
|
|
fcft_init(FCFT_LOG_COLORIZE_NEVER, false, FCFT_LOG_CLASS_NONE);
|
|
|
|
|
|
|
2023-05-02 01:53:01 +10:00
|
|
|
|
bool ret = config_load(&original, "/dev/null", ¬s, &overrides, false, false);
|
2021-06-30 21:06:21 +02:00
|
|
|
|
xassert(ret);
|
|
|
|
|
|
|
|
|
|
|
|
struct config *clone = config_clone(&original);
|
|
|
|
|
|
xassert(clone != NULL);
|
|
|
|
|
|
xassert(clone != &original);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&original);
|
|
|
|
|
|
config_free(clone);
|
2021-06-30 21:06:21 +02:00
|
|
|
|
free(clone);
|
|
|
|
|
|
|
2023-06-26 20:15:36 +02:00
|
|
|
|
fcft_fini();
|
|
|
|
|
|
|
2021-06-30 21:06:21 +02:00
|
|
|
|
tll_free(overrides);
|
|
|
|
|
|
tll_free(nots);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
|
void
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(struct config *conf)
|
|
|
|
|
|
{
|
|
|
|
|
|
free(conf->term);
|
|
|
|
|
|
free(conf->shell);
|
|
|
|
|
|
free(conf->title);
|
|
|
|
|
|
free(conf->app_id);
|
|
|
|
|
|
free(conf->word_delimiters);
|
|
|
|
|
|
spawn_template_free(&conf->bell.command);
|
|
|
|
|
|
free(conf->scrollback.indicator.text);
|
|
|
|
|
|
spawn_template_free(&conf->notify);
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(conf->fonts); i++)
|
|
|
|
|
|
config_font_list_destroy(&conf->fonts[i]);
|
|
|
|
|
|
free(conf->server_socket_path);
|
|
|
|
|
|
|
|
|
|
|
|
config_font_list_destroy(&conf->csd.font);
|
|
|
|
|
|
|
|
|
|
|
|
free(conf->url.label_letters);
|
|
|
|
|
|
spawn_template_free(&conf->url.launch);
|
|
|
|
|
|
for (size_t i = 0; i < conf->url.prot_count; i++)
|
|
|
|
|
|
free(conf->url.protocols[i]);
|
|
|
|
|
|
free(conf->url.protocols);
|
|
|
|
|
|
free(conf->url.uri_characters);
|
|
|
|
|
|
|
|
|
|
|
|
free_key_binding_list(&conf->bindings.key);
|
|
|
|
|
|
free_key_binding_list(&conf->bindings.search);
|
|
|
|
|
|
free_key_binding_list(&conf->bindings.url);
|
|
|
|
|
|
free_key_binding_list(&conf->bindings.mouse);
|
|
|
|
|
|
|
2022-05-28 19:27:29 +02:00
|
|
|
|
tll_foreach(conf->env_vars, it) {
|
|
|
|
|
|
free(it->item.name);
|
|
|
|
|
|
free(it->item.value);
|
|
|
|
|
|
tll_remove(conf->env_vars, it);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-17 20:51:40 +02:00
|
|
|
|
free(conf->utmp_helper_path);
|
2022-04-12 13:01:56 +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
|
|
|
|
|
2023-03-03 17:21:11 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* First look for user specified {pixel}size option
|
|
|
|
|
|
* e.g. “font-name:size=12”
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2020-07-07 10:44:55 +02:00
|
|
|
|
double pt_size = -1.0;
|
2023-03-03 17:21:11 +01:00
|
|
|
|
FcResult have_pt_size = FcPatternGetDouble(pat, FC_SIZE, 0, &pt_size);
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
|
|
|
|
|
int px_size = -1;
|
2023-03-03 17:21:11 +01:00
|
|
|
|
FcResult have_px_size = FcPatternGetInteger(pat, FC_PIXEL_SIZE, 0, &px_size);
|
|
|
|
|
|
|
|
|
|
|
|
if (have_pt_size != FcResultMatch && have_px_size != FcResultMatch) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Apply fontconfig config. Can’t do that until we’ve first
|
|
|
|
|
|
* checked for a user provided size, since we may end up with
|
|
|
|
|
|
* both “size” and “pixelsize” being set, and we don’t know
|
|
|
|
|
|
* which one takes priority.
|
|
|
|
|
|
*/
|
|
|
|
|
|
FcPattern *pat_copy = FcPatternDuplicate(pat);
|
|
|
|
|
|
if (pat_copy == NULL ||
|
|
|
|
|
|
!FcConfigSubstitute(NULL, pat_copy, FcMatchPattern))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_WARN("%s: failed to do config substitution", pattern);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
have_pt_size = FcPatternGetDouble(pat_copy, FC_SIZE, 0, &pt_size);
|
|
|
|
|
|
have_px_size = FcPatternGetInteger(pat_copy, FC_PIXEL_SIZE, 0, &px_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FcPatternDestroy(pat_copy);
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
2023-03-03 17:21:11 +01:00
|
|
|
|
if (have_pt_size != FcResultMatch && have_px_size != FcResultMatch)
|
|
|
|
|
|
pt_size = 8.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FcPatternRemove(pat, FC_SIZE, 0);
|
|
|
|
|
|
FcPatternRemove(pat, FC_PIXEL_SIZE, 0);
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
|
|
|
|
|
char *stripped_pattern = (char *)FcNameUnparse(pat);
|
|
|
|
|
|
FcPatternDestroy(pat);
|
|
|
|
|
|
|
2023-03-03 17:21:11 +01:00
|
|
|
|
LOG_DBG("%s: pt-size=%.2f, px-size=%d", stripped_pattern, pt_size, px_size);
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
static const char32_t chars[] = {U'a', U'i', U'l', U'M', U'W'};
|
2021-08-31 19:42:22 +02:00
|
|
|
|
|
|
|
|
|
|
bool is_monospaced = true;
|
|
|
|
|
|
int last_width = -1;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) {
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
const struct fcft_glyph *g = fcft_rasterize_char_utf32(
|
2021-08-31 19:42:22 +02:00
|
|
|
|
f, chars[i], FCFT_SUBPIXEL_NONE);
|
|
|
|
|
|
|
|
|
|
|
|
if (g == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (last_width >= 0 && g->advance.x != last_width) {
|
2022-02-06 12:06:23 +01:00
|
|
|
|
const char *font_name = f->name != NULL
|
|
|
|
|
|
? f->name
|
|
|
|
|
|
: pattern;
|
|
|
|
|
|
|
2021-08-31 19:42:22 +02:00
|
|
|
|
LOG_WARN("%s: font does not appear to be monospace; "
|
|
|
|
|
|
"check your config, or disable this warning by "
|
|
|
|
|
|
"setting [tweak].font-monospace-warn=no",
|
2022-02-06 12:06:23 +01:00
|
|
|
|
font_name);
|
2021-08-31 19:42:22 +02:00
|
|
|
|
|
2021-11-14 15:08:47 +00:00
|
|
|
|
static const char fmt[] =
|
2021-08-31 19:42:22 +02:00
|
|
|
|
"%s: font does not appear to be monospace; "
|
|
|
|
|
|
"check your config, or disable this warning by "
|
2021-11-14 15:08:47 +00:00
|
|
|
|
"setting \033[1m[tweak].font-monospace-warn=no\033[22m";
|
|
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
|
user_notification_add_fmt(
|
2022-02-06 12:06:23 +01:00
|
|
|
|
notifications, USER_NOTIFICATION_WARNING, fmt, font_name);
|
2021-08-31 19:42:22 +02:00
|
|
|
|
|
|
|
|
|
|
is_monospaced = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
last_width = g->advance.x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fcft_destroy(f);
|
|
|
|
|
|
return is_monospaced;
|
|
|
|
|
|
}
|
key-binding: new API, for handling sets of key bindings
Up until now, our Wayland seats have been tracking key bindings. This
makes sense, since the seat’s keymap determines how the key bindings
are resolved.
However, tying bindings to the seat/keymap alone isn’t enough, since
we also depend on the current configuration (i.e. user settings) when
resolving a key binding.
This means configurations that doesn’t match the wayland object’s
configuration, currently don’t resolve key bindings correctly. This
applies to footclients where the user has overridden key bindings on
the command line (e.g. --override key-bindings.foo=bar).
Thus, to correctly resolve key bindings, each set of key bindings must
be tied *both* to a seat/keymap, *and* a configuration.
This patch introduces a key-binding manager, with an API to
add/remove/lookup, and load/unload keymaps from sets of key bindings.
In the API, sets are tied to a seat and terminal instance, since this
makes the most sense (we need to instantiate, or incref a set whenever
a new terminal instance is created). Internally, the set is tied to a
seat and the terminal’s configuration.
Sets are *added* when a new seat is added, and when a new terminal
instance is created. Since there can only be one instance of each
seat, sets are always removed when a seat is removed.
Terminals on the other hand can re-use the same configuration (and
typically do). Thus, sets ref-count the configuration. In other words,
when instantiating a new terminal, we may not have to instantiate a
new set of key bindings, but can often be incref:ed instead.
Whenever the keymap changes on a seat, all key bindings sets
associated with that seat reloads (re-resolves) their key bindings.
Closes #931
2022-04-17 15:39:51 +02:00
|
|
|
|
|
|
|
|
|
|
xkb_mod_mask_t
|
|
|
|
|
|
conf_modifiers_to_mask(const struct seat *seat,
|
|
|
|
|
|
const struct config_key_modifiers *modifiers)
|
|
|
|
|
|
{
|
|
|
|
|
|
xkb_mod_mask_t mods = 0;
|
|
|
|
|
|
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->shift << seat->kbd.mod_shift;
|
|
|
|
|
|
if (seat->kbd.mod_ctrl != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->ctrl << seat->kbd.mod_ctrl;
|
|
|
|
|
|
if (seat->kbd.mod_alt != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->alt << seat->kbd.mod_alt;
|
|
|
|
|
|
if (seat->kbd.mod_super != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->super << seat->kbd.mod_super;
|
|
|
|
|
|
return mods;
|
|
|
|
|
|
}
|