2021-11-27 17:23:33 +01:00
|
|
|
|
#if !defined(_DEBUG)
|
|
|
|
|
|
#define _DEBUG
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#undef NDEBUG
|
|
|
|
|
|
|
|
|
|
|
|
#include "../log.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "../config.c"
|
|
|
|
|
|
|
|
|
|
|
|
#define ALEN(v) (sizeof(v) / sizeof((v)[0]))
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Stubs
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
user_notification_add_fmt(user_notifications_t *notifications,
|
|
|
|
|
|
enum user_notification_kind kind,
|
|
|
|
|
|
const char *fmt, ...)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_invalid_key(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
ctx->value = "value for invalid key";
|
|
|
|
|
|
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s: did not fail to parse as expected"
|
|
|
|
|
|
"(key should be invalid)", ctx->section, ctx->key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
test_string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, char *const *ptr)
|
2021-11-27 20:40:45 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
const char *value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"a string", "a string"},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2024-01-24 23:17:28 +00:00
|
|
|
|
if (!streq(*ptr, input[i].value)) {
|
2021-11-27 20:40:45 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%s) not the expected one (%s)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
*ptr, input[i].value);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
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
|
|
|
|
test_c32string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key, char32_t *const *ptr)
|
2021-11-27 20:40:45 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
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 *value;
|
2021-11-27 20:40:45 +01:00
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
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
|
|
|
|
{"a string", U"a string"},
|
2021-11-27 20:40:45 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
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
|
|
|
|
if (c32cmp(*ptr, input[i].value) != 0) {
|
2021-11-27 20:40:45 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%ls) not the expected one (%ls)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
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 wchar_t *)*ptr,
|
|
|
|
|
|
(const wchar_t *)input[i].value);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-27 17:23:33 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_boolean(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, const bool *ptr)
|
2021-11-27 17:23:33 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
bool value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"1", true}, {"0", false},
|
|
|
|
|
|
{"on", true}, {"off", false},
|
|
|
|
|
|
{"true", true}, {"false", false},
|
|
|
|
|
|
{"unittest-invalid-boolean-value", false, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2021-12-29 19:10:37 +01:00
|
|
|
|
if (*ptr != input[i].value) {
|
2021-11-27 17:23:33 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%s) not the expected one (%s)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
*ptr ? "true" : "false",
|
2021-11-27 17:23:33 +01:00
|
|
|
|
input[i].value ? "true" : "false");
|
2021-11-27 20:40:45 +01:00
|
|
|
|
}
|
2021-11-27 17:23:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2021-11-27 20:40:45 +01:00
|
|
|
|
test_uint16(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, const uint16_t *ptr)
|
2021-11-27 17:23:33 +01:00
|
|
|
|
{
|
2021-11-27 20:40:45 +01:00
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
uint16_t value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"0", 0}, {"65535", 65535}, {"65536", 0, true},
|
|
|
|
|
|
{"abc", 0, true}, {"true", 0, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
2021-12-26 12:36:43 +01:00
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2021-12-29 19:10:37 +01:00
|
|
|
|
if (*ptr != input[i].value) {
|
2021-12-26 12:36:43 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%hu) not the expected one (%hu)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
*ptr, input[i].value);
|
2021-12-26 12:36:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
test_uint32(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, const uint32_t *ptr)
|
2021-12-26 12:36:43 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
uint32_t value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"0", 0}, {"65536", 65536}, {"4294967295", 4294967295},
|
|
|
|
|
|
{"4294967296", 0, true}, {"abc", 0, true}, {"true", 0, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
2021-11-27 20:40:45 +01:00
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2021-12-29 19:10:37 +01:00
|
|
|
|
if (*ptr != input[i].value) {
|
2021-12-26 13:03:45 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%u) not the expected one (%u)",
|
2021-11-27 20:40:45 +01:00
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
*ptr, input[i].value);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-26 12:36:55 +01:00
|
|
|
|
static void
|
2023-07-26 16:12:36 +02:00
|
|
|
|
test_float(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, const float *ptr)
|
2021-12-26 12:36:55 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
float value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"0", 0}, {"0.1", 0.1}, {"1e10", 1e10}, {"-10.7", -10.7},
|
|
|
|
|
|
{"abc", 0, true}, {"true", 0, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2021-12-29 19:10:37 +01:00
|
|
|
|
if (*ptr != input[i].value) {
|
2021-12-26 12:36:55 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value (%f) not the expected one (%f)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
*ptr, input[i].value);
|
2021-12-26 12:36:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_pt_or_px(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
2021-12-29 19:10:37 +01:00
|
|
|
|
const char *key, const struct pt_or_px *ptr)
|
2021-11-27 20:40:45 +01:00
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
2021-11-27 17:23:33 +01:00
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
static const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
struct pt_or_px value;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"12", {.pt = 12}}, {"12px", {.px = 12}},
|
|
|
|
|
|
{"unittest-invalid-pt-or-px-value", {0}, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2021-12-29 19:10:37 +01:00
|
|
|
|
if (memcmp(ptr, &input[i].value, sizeof(*ptr)) != 0) {
|
2021-11-27 20:40:45 +01:00
|
|
|
|
BUG("[%s].%s=%s: "
|
|
|
|
|
|
"set value (pt=%f, px=%d) not the expected one (pt=%f, px=%d)",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2021-12-29 19:10:37 +01:00
|
|
|
|
ptr->pt, ptr->px,
|
2021-11-27 20:40:45 +01:00
|
|
|
|
input[i].value.pt, input[i].value.px);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-25 23:27:10 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_spawn_template(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key, const struct config_spawn_template *ptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
static const char *const args[] = {
|
|
|
|
|
|
"command", "arg1", "arg2", "arg3 has spaces"};
|
|
|
|
|
|
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
ctx->value = "command arg1 arg2 \"arg3 has spaces\"";
|
|
|
|
|
|
|
|
|
|
|
|
if (!parse_fun(ctx))
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse", ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
|
|
|
|
|
|
if (ptr->argv.args == NULL)
|
|
|
|
|
|
BUG("[%s].%s=%s: argv is NULL", ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(args); i++) {
|
2024-01-24 23:17:28 +00:00
|
|
|
|
if (ptr->argv.args[i] == NULL || !streq(ptr->argv.args[i], args[i])) {
|
2021-12-25 23:27:10 +01:00
|
|
|
|
BUG("[%s].%s=%s: set value not the expected one: "
|
|
|
|
|
|
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, i,
|
|
|
|
|
|
args[i], ptr->argv.args[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ptr->argv.args[ALEN(args)] != NULL) {
|
|
|
|
|
|
BUG("[%s].%s=%s: set value not the expected one: "
|
|
|
|
|
|
"expected NULL terminator at arg #%zu, got=\"%s\"",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
ALEN(args), ptr->argv.args[ALEN(args)]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Trigger parse failure */
|
|
|
|
|
|
ctx->value = "command with \"unterminated quote";
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:03:36 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_enum(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key, size_t count, const char *enum_strings[static count],
|
|
|
|
|
|
int enum_values[static count], int *ptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
|
|
ctx->value = enum_strings[i];
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*ptr != enum_values[i]) {
|
|
|
|
|
|
BUG("[%s].%s=%s: set value not the expected one: expected %d, got %d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, enum_values[i], *ptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ctx->value = "invalid-enum-value";
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
2022-02-07 12:32:23 +00:00
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
2021-12-29 19:03:36 +01:00
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:43:01 +01:00
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
test_color(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key, bool alpha_allowed, uint32_t *ptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
uint32_t color;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"000000", 0},
|
2021-12-29 19:54:21 +01:00
|
|
|
|
{"999999", 0x999999},
|
2021-12-29 19:43:01 +01:00
|
|
|
|
{"ffffff", 0xffffff},
|
|
|
|
|
|
{"ffffffff", 0xffffffff, !alpha_allowed},
|
2021-12-29 19:54:21 +01:00
|
|
|
|
{"aabbccdd", 0xaabbccdd, !alpha_allowed},
|
2023-10-26 16:22:41 +02:00
|
|
|
|
{"00", 0, true},
|
|
|
|
|
|
{"0000", 0, true},
|
|
|
|
|
|
{"00000", 0, true},
|
|
|
|
|
|
{"000000000", 0, true},
|
2021-12-29 19:43:01 +01:00
|
|
|
|
{"unittest-invalid-color", 0, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2025-04-20 07:29:54 +02:00
|
|
|
|
|
|
|
|
|
|
uint32_t color = input[i].color;
|
|
|
|
|
|
if (alpha_allowed && strlen(input[i].option_string) == 6)
|
|
|
|
|
|
color |= 0xff000000;
|
|
|
|
|
|
|
|
|
|
|
|
if (*ptr != color) {
|
|
|
|
|
|
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
color, *ptr);
|
|
|
|
|
|
}
|
2021-12-29 19:43:01 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-27 19:13:39 +02:00
|
|
|
|
static void
|
|
|
|
|
|
test_two_colors(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
const char *key, bool alpha_allowed,
|
|
|
|
|
|
uint32_t *ptr1, uint32_t *ptr2)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
|
|
|
|
|
|
const struct {
|
|
|
|
|
|
const char *option_string;
|
|
|
|
|
|
uint32_t color1;
|
|
|
|
|
|
uint32_t color2;
|
|
|
|
|
|
bool invalid;
|
|
|
|
|
|
} input[] = {
|
|
|
|
|
|
{"000000 000000", 0, 0},
|
|
|
|
|
|
|
|
|
|
|
|
/* No alpha */
|
|
|
|
|
|
{"999999 888888", 0x999999, 0x888888},
|
|
|
|
|
|
{"ffffff aaaaaa", 0xffffff, 0xaaaaaa},
|
|
|
|
|
|
|
|
|
|
|
|
/* Both colors have alpha component */
|
|
|
|
|
|
{"ffffffff 00000000", 0xffffffff, 0x00000000, !alpha_allowed},
|
|
|
|
|
|
{"aabbccdd, ee112233", 0xaabbccdd, 0xee112233, !alpha_allowed},
|
|
|
|
|
|
|
|
|
|
|
|
/* Only one color has alpha component */
|
|
|
|
|
|
{"ffffffff 112233", 0xffffffff, 0x112233, !alpha_allowed},
|
|
|
|
|
|
{"ffffff ff112233", 0x00ffffff, 0xff112233, !alpha_allowed},
|
|
|
|
|
|
|
|
|
|
|
|
{"unittest-invalid-color", 0, 0, true},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(input); i++) {
|
|
|
|
|
|
ctx->value = input[i].option_string;
|
|
|
|
|
|
if (input[i].invalid) {
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s: failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
2025-04-20 07:29:54 +02:00
|
|
|
|
|
|
|
|
|
|
if (*ptr1 != input[i].color1) {
|
|
|
|
|
|
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
input[i].color1, *ptr1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*ptr2 != input[i].color2) {
|
|
|
|
|
|
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
input[i].color2, *ptr2);
|
|
|
|
|
|
}
|
2022-07-27 19:13:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_main(void)
|
|
|
|
|
|
{
|
2021-11-27 17:23:33 +01:00
|
|
|
|
struct config conf = {0};
|
2021-11-27 20:40:45 +01:00
|
|
|
|
struct context ctx = {.conf = &conf, .section = "main", .path = "unittest"};
|
2021-11-27 17:23:33 +01:00
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
test_invalid_key(&ctx, &parse_section_main, "invalid-key");
|
2021-11-27 17:23:33 +01:00
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
|
|
|
|
|
|
test_string(&ctx, &parse_section_main, "term", &conf.term);
|
|
|
|
|
|
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
|
2023-05-17 21:05:43 +02:00
|
|
|
|
test_string(&ctx, &parse_section_main, "utmp-helper", &conf.utmp_helper_path);
|
2021-11-27 20:40:45 +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
|
|
|
|
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
|
2021-11-27 17:23:33 +01:00
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_main, "login-shell", &conf.login_shell);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs);
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
|
2023-06-26 17:55:04 +02:00
|
|
|
|
test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware);
|
2025-05-01 08:09:08 +02:00
|
|
|
|
test_boolean(&ctx, &parse_section_main, "gamma-correct-blending", &conf.gamma_correct);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
|
2022-12-15 11:10:32 +01:00
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test ‘N%’ values too */
|
2021-11-27 20:40:45 +01:00
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
|
|
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing);
|
|
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset);
|
|
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "vertical-letter-offset", &conf.vertical_letter_offset);
|
2022-08-19 02:54:49 +02:00
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "underline-thickness", &conf.underline_thickness);
|
2024-08-25 11:28:21 +03:00
|
|
|
|
test_pt_or_px(&ctx, &parse_section_main, "strikeout-thickness", &conf.strikeout_thickness);
|
2021-11-27 20:40:45 +01:00
|
|
|
|
|
|
|
|
|
|
test_uint16(&ctx, &parse_section_main, "resize-delay-ms", &conf.resize_delay_ms);
|
|
|
|
|
|
test_uint16(&ctx, &parse_section_main, "workers", &conf.render_worker_count);
|
|
|
|
|
|
|
2021-12-29 19:03:59 +01:00
|
|
|
|
test_enum(&ctx, &parse_section_main, "selection-target",
|
|
|
|
|
|
4,
|
|
|
|
|
|
(const char *[]){"none", "primary", "clipboard", "both"},
|
|
|
|
|
|
(int []){SELECTION_TARGET_NONE,
|
|
|
|
|
|
SELECTION_TARGET_PRIMARY,
|
|
|
|
|
|
SELECTION_TARGET_CLIPBOARD,
|
|
|
|
|
|
SELECTION_TARGET_BOTH},
|
|
|
|
|
|
(int *)&conf.selection_target);
|
|
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_main, "initial-window-mode",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"windowed", "maximized", "fullscreen"},
|
|
|
|
|
|
(int []){STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN},
|
|
|
|
|
|
(int *)&conf.startup_mode);
|
|
|
|
|
|
|
2021-11-27 20:40:45 +01:00
|
|
|
|
/* TODO: font (custom) */
|
|
|
|
|
|
/* TODO: include (custom) */
|
|
|
|
|
|
/* TODO: bold-text-in-bright (enum/boolean) */
|
|
|
|
|
|
/* TODO: pad (geometry + optional string)*/
|
|
|
|
|
|
/* TODO: initial-window-size-pixels (geometry) */
|
|
|
|
|
|
/* TODO: initial-window-size-chars (geometry) */
|
2021-11-27 17:23:33 +01:00
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-11-27 17:23:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-21 06:52:00 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_security(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {.conf = &conf, .section = "security", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_security, "invalid-key");
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_security, "osc52", 4,
|
|
|
|
|
|
(const char*[]){"disabled", "copy-enabled", "paste-enabled", "enabled"},
|
|
|
|
|
|
(int []){OSC52_DISABLED, OSC52_COPY_ENABLED, OSC52_PASTE_ENABLED, OSC52_ENABLED},
|
|
|
|
|
|
(int *)&conf.security.osc52);
|
|
|
|
|
|
|
|
|
|
|
|
config_free(&conf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-25 23:03:11 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_bell(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {.conf = &conf, .section = "bell", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_bell, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_bell, "urgent", &conf.bell.urgent);
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_bell, "notify", &conf.bell.notify);
|
2025-01-17 10:10:10 +01:00
|
|
|
|
test_boolean(&ctx, &parse_section_bell, "system", &conf.bell.system_bell);
|
2021-12-25 23:03:11 +01:00
|
|
|
|
test_boolean(&ctx, &parse_section_bell, "command-focused",
|
|
|
|
|
|
&conf.bell.command_focused);
|
2021-12-25 23:27:36 +01:00
|
|
|
|
test_spawn_template(&ctx, &parse_section_bell, "command",
|
|
|
|
|
|
&conf.bell.command);
|
2021-12-25 23:03:11 +01:00
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-25 23:03:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_desktop_notifications(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {.conf = &conf, .section = "desktop-notifications", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_desktop_notifications, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_desktop_notifications, "inhibit-when-focused", &conf.desktop_notifications.inhibit_when_focused);
|
|
|
|
|
|
test_spawn_template(&ctx, &parse_section_desktop_notifications, "command", &conf.desktop_notifications.command);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
|
test_spawn_template(&ctx, &parse_section_desktop_notifications, "command-action-argument", &conf.desktop_notifications.command_action_arg);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
test_spawn_template(&ctx, &parse_section_desktop_notifications, "close", &conf.desktop_notifications.close);
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
|
|
|
|
|
|
|
config_free(&conf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-26 12:37:12 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_scrollback(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "scrollback", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_scrollback, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_scrollback, "lines",
|
|
|
|
|
|
&conf.scrollback.lines);
|
2023-07-26 16:12:36 +02:00
|
|
|
|
test_float(&ctx, parse_section_scrollback, "multiplier", &conf.scrollback.multiplier);
|
2021-12-26 12:37:12 +01:00
|
|
|
|
|
2021-12-29 19:04:19 +01:00
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_scrollback, "indicator-position",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"none", "fixed", "relative"},
|
|
|
|
|
|
(int []){SCROLLBACK_INDICATOR_POSITION_NONE,
|
|
|
|
|
|
SCROLLBACK_INDICATOR_POSITION_FIXED,
|
|
|
|
|
|
SCROLLBACK_INDICATOR_POSITION_RELATIVE},
|
|
|
|
|
|
(int *)&conf.scrollback.indicator.position);
|
|
|
|
|
|
|
2021-12-26 12:37:12 +01:00
|
|
|
|
/* TODO: indicator-format (enum, sort-of) */
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-26 12:37:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:10:49 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_url(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "url", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_url, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_spawn_template(&ctx, &parse_section_url, "launch", &conf.url.launch);
|
|
|
|
|
|
test_enum(&ctx, &parse_section_url, "osc8-underline",
|
|
|
|
|
|
2,
|
|
|
|
|
|
(const char *[]){"url-mode", "always"},
|
|
|
|
|
|
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
|
|
|
|
|
|
(int *)&conf.url.osc8_underline);
|
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
|
|
|
|
test_c32string(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters);
|
2021-12-29 19:10:49 +01:00
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-29 19:10:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:15:15 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_cursor(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "cursor", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_cursor, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_cursor, "style",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"block", "beam", "underline"},
|
|
|
|
|
|
(int []){CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE},
|
|
|
|
|
|
(int *)&conf.cursor.style);
|
2024-04-09 16:28:54 +02:00
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_cursor, "unfocused-style",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"unchanged", "hollow", "none"},
|
|
|
|
|
|
(int []){CURSOR_UNFOCUSED_UNCHANGED, CURSOR_UNFOCUSED_HOLLOW, CURSOR_UNFOCUSED_NONE},
|
|
|
|
|
|
(int *)&conf.cursor.unfocused_style);
|
2024-05-20 09:03:29 +02:00
|
|
|
|
test_boolean(&ctx, &parse_section_cursor, "blink", &conf.cursor.blink.enabled);
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_cursor, "blink-rate", &conf.cursor.blink.rate_ms);
|
2021-12-29 19:15:15 +01:00
|
|
|
|
test_pt_or_px(&ctx, &parse_section_cursor, "beam-thickness",
|
|
|
|
|
|
&conf.cursor.beam_thickness);
|
|
|
|
|
|
test_pt_or_px(&ctx, &parse_section_cursor, "underline-thickness",
|
|
|
|
|
|
&conf.cursor.underline_thickness);
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: color (two RRGGBB values) */
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-29 19:15:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:18:25 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_mouse(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "mouse", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_mouse, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_mouse, "hide-when-typing",
|
|
|
|
|
|
&conf.mouse.hide_when_typing);
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_mouse, "alternate-scroll-mode",
|
|
|
|
|
|
&conf.mouse.alternate_scroll_mode);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-29 19:18:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-05 00:19:21 +08:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_touch(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "touch", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_touch, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_touch, "long-press-delay",
|
|
|
|
|
|
&conf.touch.long_press_delay);
|
|
|
|
|
|
|
|
|
|
|
|
config_free(&conf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:54:21 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_colors(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "colors", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_colors, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "foreground", false, &conf.colors.fg);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "background", false, &conf.colors.bg);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular0", false, &conf.colors.table[0]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular1", false, &conf.colors.table[1]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular2", false, &conf.colors.table[2]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular3", false, &conf.colors.table[3]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular4", false, &conf.colors.table[4]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular5", false, &conf.colors.table[5]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular6", false, &conf.colors.table[6]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "regular7", false, &conf.colors.table[7]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright0", false, &conf.colors.table[8]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright1", false, &conf.colors.table[9]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright2", false, &conf.colors.table[10]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright3", false, &conf.colors.table[11]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright4", false, &conf.colors.table[12]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright5", false, &conf.colors.table[13]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright6", false, &conf.colors.table[14]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "bright7", false, &conf.colors.table[15]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim0", false, &conf.colors.dim[0]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim1", false, &conf.colors.dim[1]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim2", false, &conf.colors.dim[2]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim3", false, &conf.colors.dim[3]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim4", false, &conf.colors.dim[4]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim5", false, &conf.colors.dim[5]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim6", false, &conf.colors.dim[6]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "dim7", false, &conf.colors.dim[7]);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "selection-foreground", false, &conf.colors.selection_fg);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "selection-background", false, &conf.colors.selection_bg);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, "urls", false, &conf.colors.url);
|
2022-07-27 19:13:39 +02:00
|
|
|
|
test_two_colors(&ctx, &parse_section_colors, "jump-labels", false,
|
|
|
|
|
|
&conf.colors.jump_label.fg,
|
|
|
|
|
|
&conf.colors.jump_label.bg);
|
|
|
|
|
|
test_two_colors(&ctx, &parse_section_colors, "scrollback-indicator", false,
|
|
|
|
|
|
&conf.colors.scrollback_indicator.fg,
|
|
|
|
|
|
&conf.colors.scrollback_indicator.bg);
|
2022-07-27 19:14:27 +02:00
|
|
|
|
test_two_colors(&ctx, &parse_section_colors, "search-box-no-match", false,
|
|
|
|
|
|
&conf.colors.search_box.no_match.fg,
|
|
|
|
|
|
&conf.colors.search_box.no_match.bg);
|
|
|
|
|
|
test_two_colors(&ctx, &parse_section_colors, "search-box-match", false,
|
|
|
|
|
|
&conf.colors.search_box.match.fg,
|
|
|
|
|
|
&conf.colors.search_box.match.bg);
|
2021-12-29 19:54:21 +01:00
|
|
|
|
|
2025-04-20 07:29:54 +02:00
|
|
|
|
test_two_colors(&ctx, &parse_section_colors, "cursor", false,
|
|
|
|
|
|
&conf.colors.cursor.text,
|
|
|
|
|
|
&conf.colors.cursor.cursor);
|
|
|
|
|
|
|
2025-04-14 17:00:07 +02:00
|
|
|
|
test_enum(&ctx, &parse_section_colors, "alpha-mode", 3,
|
|
|
|
|
|
(const char *[]){"default", "matching", "all"},
|
|
|
|
|
|
(int []){ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL},
|
|
|
|
|
|
(int *)&conf.colors.alpha_mode);
|
|
|
|
|
|
|
2021-12-29 19:54:21 +01:00
|
|
|
|
for (size_t i = 0; i < 255; i++) {
|
|
|
|
|
|
char key_name[4];
|
|
|
|
|
|
sprintf(key_name, "%zu", i);
|
|
|
|
|
|
test_color(&ctx, &parse_section_colors, key_name, false,
|
|
|
|
|
|
&conf.colors.table[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-30 21:49:08 +01:00
|
|
|
|
test_invalid_key(&ctx, &parse_section_colors, "256");
|
|
|
|
|
|
|
2021-12-29 19:54:21 +01:00
|
|
|
|
/* TODO: alpha (float in range 0-1, converted to uint16_t) */
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-29 19:54:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-29 19:33:18 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_csd(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "csd", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_csd, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_csd, "preferred",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"none", "client", "server"},
|
|
|
|
|
|
(int []){CONF_CSD_PREFER_NONE,
|
|
|
|
|
|
CONF_CSD_PREFER_CLIENT,
|
|
|
|
|
|
CONF_CSD_PREFER_SERVER},
|
|
|
|
|
|
(int *)&conf.csd.preferred);
|
|
|
|
|
|
test_uint16(&ctx, &parse_section_csd, "size", &conf.csd.title_height);
|
2021-12-29 19:43:11 +01:00
|
|
|
|
test_color(&ctx, &parse_section_csd, "color", true, &conf.csd.color.title);
|
2021-12-29 19:33:18 +01:00
|
|
|
|
test_uint16(&ctx, &parse_section_csd, "border-width",
|
|
|
|
|
|
&conf.csd.border_width_visible);
|
2021-12-29 19:43:11 +01:00
|
|
|
|
test_color(&ctx, &parse_section_csd, "border-color", true,
|
|
|
|
|
|
&conf.csd.color.border);
|
2021-12-29 19:33:18 +01:00
|
|
|
|
test_uint16(&ctx, &parse_section_csd, "button-width",
|
|
|
|
|
|
&conf.csd.button_width);
|
2021-12-29 19:43:11 +01:00
|
|
|
|
test_color(&ctx, &parse_section_csd, "button-color", true,
|
|
|
|
|
|
&conf.csd.color.buttons);
|
|
|
|
|
|
test_color(&ctx, &parse_section_csd, "button-minimize-color", true,
|
|
|
|
|
|
&conf.csd.color.minimize);
|
|
|
|
|
|
test_color(&ctx, &parse_section_csd, "button-maximize-color", true,
|
|
|
|
|
|
&conf.csd.color.maximize);
|
|
|
|
|
|
test_color(&ctx, &parse_section_csd, "button-close-color", true,
|
2022-02-08 20:12:05 +01:00
|
|
|
|
&conf.csd.color.quit);
|
2022-04-15 20:12:34 +02:00
|
|
|
|
test_boolean(&ctx, &parse_section_csd, "hide-when-maximized",
|
|
|
|
|
|
&conf.csd.hide_when_maximized);
|
2023-07-14 12:03:35 +02:00
|
|
|
|
test_boolean(&ctx, &parse_section_csd, "double-click-to-maximize",
|
|
|
|
|
|
&conf.csd.double_click_to_maximize);
|
2021-12-29 19:43:11 +01:00
|
|
|
|
|
|
|
|
|
|
/* TODO: verify the ‘set’ bit is actually set for colors */
|
2021-12-29 19:33:18 +01:00
|
|
|
|
/* TODO: font */
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-29 19:33:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
static bool
|
|
|
|
|
|
have_modifier(const config_modifier_list_t *mods, const char *mod)
|
|
|
|
|
|
{
|
|
|
|
|
|
tll_foreach(*mods, it) {
|
|
|
|
|
|
if (strcmp(it->item, mod) == 0)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 18:30:17 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|
|
|
|
|
int action, int max_action, const char *const *map,
|
2021-12-11 20:01:16 +01:00
|
|
|
|
struct config_key_binding_list *bindings,
|
2025-02-03 09:29:42 +01:00
|
|
|
|
enum key_binding_type type, bool need_argv, bool need_section_id)
|
2021-12-11 18:30:17 +01:00
|
|
|
|
{
|
|
|
|
|
|
xassert(map[action] != NULL);
|
|
|
|
|
|
xassert(bindings->count == 0);
|
|
|
|
|
|
|
|
|
|
|
|
const char *key = map[action];
|
|
|
|
|
|
|
|
|
|
|
|
/* “Randomize” which modifiers to enable */
|
|
|
|
|
|
const bool ctrl = action % 2;
|
|
|
|
|
|
const bool alt = action % 3;
|
|
|
|
|
|
const bool shift = action % 4;
|
|
|
|
|
|
const bool super = action % 5;
|
2025-02-03 09:29:42 +01:00
|
|
|
|
const bool argv = need_argv;
|
|
|
|
|
|
const bool section_id = need_section_id;
|
|
|
|
|
|
|
|
|
|
|
|
xassert(!(argv && section_id));
|
2021-12-25 23:37:04 +01:00
|
|
|
|
|
|
|
|
|
|
static const char *const args[] = {
|
|
|
|
|
|
"command", "arg1", "arg2", "arg3 has spaces"};
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
|
|
|
|
|
/* Generate the modifier part of the ‘value’ */
|
|
|
|
|
|
char modifier_string[32];
|
2021-12-11 20:01:16 +01:00
|
|
|
|
sprintf(modifier_string, "%s%s%s%s",
|
|
|
|
|
|
ctrl ? XKB_MOD_NAME_CTRL "+" : "",
|
|
|
|
|
|
alt ? XKB_MOD_NAME_ALT "+" : "",
|
|
|
|
|
|
shift ? XKB_MOD_NAME_SHIFT "+" : "",
|
|
|
|
|
|
super ? XKB_MOD_NAME_LOGO "+" : "");
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
/* Use a unique symbol for this action (key bindings) */
|
|
|
|
|
|
const xkb_keysym_t sym = XKB_KEY_a + action;
|
|
|
|
|
|
|
|
|
|
|
|
/* Mouse button (mouse bindings) */
|
|
|
|
|
|
const int button_idx = action % ALEN(button_map);
|
|
|
|
|
|
const int button = button_map[button_idx].code;
|
|
|
|
|
|
const int click_count = action % 3 + 1;
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
|
|
|
|
|
/* Finally, generate the ‘value’ (e.g. “Control+shift+x”) */
|
2021-12-18 20:42:37 +01:00
|
|
|
|
char value[128] = {0};
|
|
|
|
|
|
|
|
|
|
|
|
ctx->key = key;
|
|
|
|
|
|
ctx->value = value;
|
|
|
|
|
|
|
|
|
|
|
|
/* First, try setting the empty string */
|
|
|
|
|
|
if (parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=<empty>: did not fail to parse as expected",
|
|
|
|
|
|
ctx->section, ctx->key);
|
|
|
|
|
|
}
|
2021-12-11 20:01:16 +01:00
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case KEY_BINDING: {
|
|
|
|
|
|
char sym_name[16];
|
|
|
|
|
|
xkb_keysym_get_name(sym, sym_name, sizeof(sym_name));
|
|
|
|
|
|
|
2021-12-25 23:37:04 +01:00
|
|
|
|
snprintf(value, sizeof(value), "%s%s%s",
|
2025-02-03 09:29:42 +01:00
|
|
|
|
argv ? "[command arg1 arg2 \"arg3 has spaces\"] " : section_id ? "[foobar]" : "",
|
2021-12-25 23:37:04 +01:00
|
|
|
|
modifier_string, sym_name);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case MOUSE_BINDING: {
|
|
|
|
|
|
const char *const button_name = button_map[button_idx].name;
|
|
|
|
|
|
int chars = snprintf(
|
2021-12-25 23:37:04 +01:00
|
|
|
|
value, sizeof(value), "%s%s%s",
|
2025-02-03 09:29:42 +01:00
|
|
|
|
argv ? "[command arg1 arg2 \"arg3 has spaces\"] " : section_id ? "[foobar]" : "",
|
2021-12-25 23:37:04 +01:00
|
|
|
|
modifier_string, button_name);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
|
|
|
|
|
|
xassert(click_count > 0);
|
|
|
|
|
|
if (click_count > 1)
|
|
|
|
|
|
snprintf(&value[chars], sizeof(value) - chars, "-%d", click_count);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
|
|
|
|
|
if (!parse_fun(ctx)) {
|
|
|
|
|
|
BUG("[%s].%s=%s failed to parse",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const struct config_key_binding *binding =
|
|
|
|
|
|
&bindings->arr[bindings->count - 1];
|
|
|
|
|
|
|
2021-12-25 23:37:04 +01:00
|
|
|
|
if (argv) {
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding->aux.pipe.args == NULL) {
|
2021-12-25 23:37:04 +01:00
|
|
|
|
BUG("[%s].%s=%s: pipe argv is NULL",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(args); i++) {
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding->aux.pipe.args[i] == NULL ||
|
2024-01-24 23:17:28 +00:00
|
|
|
|
!streq(binding->aux.pipe.args[i], args[i]))
|
2021-12-25 23:37:04 +01:00
|
|
|
|
{
|
|
|
|
|
|
BUG("[%s].%s=%s: pipe argv not the expected one: "
|
|
|
|
|
|
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, i,
|
2022-02-08 19:35:41 +01:00
|
|
|
|
args[i], binding->aux.pipe.args[i]);
|
2021-12-25 23:37:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding->aux.pipe.args[ALEN(args)] != NULL) {
|
2021-12-25 23:37:04 +01:00
|
|
|
|
BUG("[%s].%s=%s: pipe argv not the expected one: "
|
|
|
|
|
|
"expected NULL terminator at arg #%zu, got=\"%s\"",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
2022-02-08 19:35:41 +01:00
|
|
|
|
ALEN(args), binding->aux.pipe.args[ALEN(args)]);
|
2021-12-25 23:37:04 +01:00
|
|
|
|
}
|
2025-02-03 09:29:42 +01:00
|
|
|
|
} else if (section_id) {
|
|
|
|
|
|
if (binding->aux.regex_name == NULL) {
|
|
|
|
|
|
BUG("[%s].%s=%s: regex name is NULL",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!streq(binding->aux.regex_name, "foobar")) {
|
|
|
|
|
|
BUG("[%s].%s=%s: regex name not the expected one: "
|
|
|
|
|
|
"expected=\"%s\", got=\"%s\"",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
"foobar", binding->aux.regex_name);
|
|
|
|
|
|
}
|
2021-12-25 23:37:04 +01:00
|
|
|
|
} else {
|
2022-02-08 19:35:41 +01:00
|
|
|
|
if (binding->aux.pipe.args != NULL) {
|
2021-12-25 23:37:04 +01:00
|
|
|
|
BUG("[%s].%s=%s: pipe argv not NULL",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
|
|
|
|
|
if (binding->action != action) {
|
|
|
|
|
|
BUG("[%s].%s=%s: action mismatch: %d != %d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, binding->action, action);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
bool have_ctrl = have_modifier(&binding->modifiers, XKB_MOD_NAME_CTRL);
|
|
|
|
|
|
bool have_alt = have_modifier(&binding->modifiers, XKB_MOD_NAME_ALT);
|
|
|
|
|
|
bool have_shift = have_modifier(&binding->modifiers, XKB_MOD_NAME_SHIFT);
|
|
|
|
|
|
bool have_super = have_modifier(&binding->modifiers, XKB_MOD_NAME_LOGO);
|
|
|
|
|
|
|
|
|
|
|
|
if (have_ctrl != ctrl || have_alt != alt ||
|
|
|
|
|
|
have_shift != shift || have_super != super)
|
2021-12-11 18:30:17 +01:00
|
|
|
|
{
|
|
|
|
|
|
BUG("[%s].%s=%s: modifier mismatch:\n"
|
|
|
|
|
|
" have: ctrl=%d, alt=%d, shift=%d, super=%d\n"
|
|
|
|
|
|
" expected: ctrl=%d, alt=%d, shift=%d, super=%d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
have_ctrl, have_alt, have_shift, have_super,
|
2021-12-11 18:30:17 +01:00
|
|
|
|
ctrl, alt, shift, super);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
switch (type) {
|
|
|
|
|
|
case KEY_BINDING:
|
|
|
|
|
|
if (binding->k.sym != sym) {
|
|
|
|
|
|
BUG("[%s].%s=%s: key symbol mismatch: %d != %d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, binding->k.sym, sym);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
case MOUSE_BINDING:;
|
|
|
|
|
|
if (binding->m.button != button) {
|
|
|
|
|
|
BUG("[%s].%s=%s: mouse button mismatch: %d != %d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value, binding->m.button, button);
|
|
|
|
|
|
}
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
if (binding->m.count != click_count) {
|
|
|
|
|
|
BUG("[%s].%s=%s: mouse button click count mismatch: %d != %d",
|
|
|
|
|
|
ctx->section, ctx->key, ctx->value,
|
|
|
|
|
|
binding->m.count, click_count);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
free_key_binding_list(bindings);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
enum collision_test_mode {
|
|
|
|
|
|
FAIL_DIFFERENT_ACTION,
|
|
|
|
|
|
FAIL_DIFFERENT_ARGV,
|
2021-12-11 20:44:01 +01:00
|
|
|
|
FAIL_MOUSE_OVERRIDE,
|
2023-03-29 00:31:49 +03:00
|
|
|
|
SUCCEED_SAME_ACTION_AND_ARGV,
|
2021-12-11 20:01:16 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2021-12-11 18:49:59 +01:00
|
|
|
|
static void
|
2021-12-11 20:01:16 +01:00
|
|
|
|
_test_binding_collisions(struct context *ctx,
|
|
|
|
|
|
int max_action, const char *const *map,
|
2022-02-08 19:02:28 +01:00
|
|
|
|
enum key_binding_type type,
|
2021-12-11 20:01:16 +01:00
|
|
|
|
enum collision_test_mode test_mode)
|
2021-12-11 18:49:59 +01:00
|
|
|
|
{
|
2021-12-11 20:01:16 +01:00
|
|
|
|
struct config_key_binding *bindings_array =
|
|
|
|
|
|
xcalloc(2, sizeof(bindings_array[0]));
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
struct config_key_binding_list bindings = {
|
|
|
|
|
|
.count = 2,
|
|
|
|
|
|
.arr = bindings_array,
|
|
|
|
|
|
};
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
/* First, verify we get a collision when trying to assign the same
|
|
|
|
|
|
* key combo to multiple actions */
|
|
|
|
|
|
bindings.arr[0] = (struct config_key_binding){
|
|
|
|
|
|
.action = (test_mode == FAIL_DIFFERENT_ACTION
|
|
|
|
|
|
? max_action - 1 : max_action),
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
.modifiers = tll_init(),
|
2021-12-11 20:01:16 +01:00
|
|
|
|
.path = "unittest",
|
|
|
|
|
|
};
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
tll_push_back(bindings.arr[0].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
bindings.arr[1] = (struct config_key_binding){
|
|
|
|
|
|
.action = max_action,
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
.modifiers = tll_init(),
|
2021-12-11 20:01:16 +01:00
|
|
|
|
.path = "unittest",
|
|
|
|
|
|
};
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
tll_push_back(bindings.arr[1].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
switch (type) {
|
|
|
|
|
|
case KEY_BINDING:
|
|
|
|
|
|
bindings.arr[0].k.sym = XKB_KEY_a;
|
|
|
|
|
|
bindings.arr[1].k.sym = XKB_KEY_a;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case MOUSE_BINDING:
|
|
|
|
|
|
bindings.arr[0].m.button = BTN_LEFT;
|
|
|
|
|
|
bindings.arr[0].m.count = 1;
|
|
|
|
|
|
bindings.arr[1].m.button = BTN_LEFT;
|
|
|
|
|
|
bindings.arr[1].m.count = 1;
|
|
|
|
|
|
break;
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
switch (test_mode) {
|
|
|
|
|
|
case FAIL_DIFFERENT_ACTION:
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2021-12-11 20:44:01 +01:00
|
|
|
|
case FAIL_MOUSE_OVERRIDE:
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
tll_free_and_free(ctx->conf->mouse.selection_override_modifiers, free);
|
|
|
|
|
|
tll_push_back(ctx->conf->mouse.selection_override_modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
2021-12-11 20:44:01 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
case FAIL_DIFFERENT_ARGV:
|
2023-03-29 00:31:49 +03:00
|
|
|
|
case SUCCEED_SAME_ACTION_AND_ARGV:
|
2022-02-08 19:35:41 +01:00
|
|
|
|
bindings.arr[0].aux.type = BINDING_AUX_PIPE;
|
|
|
|
|
|
bindings.arr[0].aux.master_copy = true;
|
|
|
|
|
|
bindings.arr[0].aux.pipe.args = xcalloc(
|
|
|
|
|
|
4, sizeof(bindings.arr[0].aux.pipe.args[0]));
|
|
|
|
|
|
bindings.arr[0].aux.pipe.args[0] = xstrdup("/usr/bin/foobar");
|
|
|
|
|
|
bindings.arr[0].aux.pipe.args[1] = xstrdup("hello");
|
|
|
|
|
|
bindings.arr[0].aux.pipe.args[2] = xstrdup("world");
|
|
|
|
|
|
|
|
|
|
|
|
bindings.arr[1].aux.type = BINDING_AUX_PIPE;
|
|
|
|
|
|
bindings.arr[1].aux.master_copy = true;
|
|
|
|
|
|
bindings.arr[1].aux.pipe.args = xcalloc(
|
|
|
|
|
|
4, sizeof(bindings.arr[1].aux.pipe.args[0]));
|
|
|
|
|
|
bindings.arr[1].aux.pipe.args[0] = xstrdup("/usr/bin/foobar");
|
|
|
|
|
|
bindings.arr[1].aux.pipe.args[1] = xstrdup("hello");
|
2021-12-11 20:01:16 +01:00
|
|
|
|
|
2023-03-29 00:31:49 +03:00
|
|
|
|
if (test_mode == SUCCEED_SAME_ACTION_AND_ARGV)
|
2022-02-08 19:35:41 +01:00
|
|
|
|
bindings.arr[1].aux.pipe.args[2] = xstrdup("world");
|
2021-12-11 20:01:16 +01:00
|
|
|
|
break;
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
bool expected_result =
|
2023-03-29 00:31:49 +03:00
|
|
|
|
test_mode == SUCCEED_SAME_ACTION_AND_ARGV ? true : false;
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
if (resolve_key_binding_collisions(
|
|
|
|
|
|
ctx->conf, ctx->section, map, &bindings, type) != expected_result)
|
2021-12-11 18:49:59 +01:00
|
|
|
|
{
|
2021-12-11 20:01:16 +01:00
|
|
|
|
BUG("[%s].%s vs. %s: %s",
|
|
|
|
|
|
ctx->section, map[max_action - 1], map[max_action],
|
|
|
|
|
|
(expected_result == true
|
|
|
|
|
|
? "invalid key combo collision detected"
|
|
|
|
|
|
: "key combo collision not detected"));
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
if (expected_result == false) {
|
|
|
|
|
|
if (bindings.count != 1)
|
|
|
|
|
|
BUG("[%s]: colliding binding not removed", ctx->section);
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
if (bindings.arr[0].action !=
|
|
|
|
|
|
(test_mode == FAIL_DIFFERENT_ACTION ? max_action - 1 : max_action))
|
|
|
|
|
|
{
|
|
|
|
|
|
BUG("[%s]: wrong binding removed", ctx->section);
|
|
|
|
|
|
}
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
free_key_binding_list(&bindings);
|
|
|
|
|
|
}
|
2021-12-11 18:49:59 +01:00
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_binding_collisions(struct context *ctx,
|
|
|
|
|
|
int max_action, const char *const *map,
|
2022-02-08 19:02:28 +01:00
|
|
|
|
enum key_binding_type type)
|
2021-12-11 20:01:16 +01:00
|
|
|
|
{
|
|
|
|
|
|
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ACTION);
|
|
|
|
|
|
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ARGV);
|
2023-03-29 00:31:49 +03:00
|
|
|
|
_test_binding_collisions(ctx, max_action, map, type, SUCCEED_SAME_ACTION_AND_ARGV);
|
2021-12-11 20:44:01 +01:00
|
|
|
|
|
|
|
|
|
|
if (type == MOUSE_BINDING) {
|
|
|
|
|
|
_test_binding_collisions(
|
|
|
|
|
|
ctx, max_action, map, type, FAIL_MOUSE_OVERRIDE);
|
|
|
|
|
|
}
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 18:30:17 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_key_bindings(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "key-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_key_bindings, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
for (int action = 0; action < BIND_ACTION_KEY_COUNT; action++) {
|
|
|
|
|
|
if (binding_action_map[action] == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
test_key_binding(
|
|
|
|
|
|
&ctx, &parse_section_key_bindings,
|
|
|
|
|
|
action, BIND_ACTION_KEY_COUNT - 1,
|
2025-02-03 09:29:42 +01:00
|
|
|
|
binding_action_map, &conf.bindings.key, KEY_BINDING,
|
|
|
|
|
|
action >= BIND_ACTION_PIPE_SCROLLBACK && action <= BIND_ACTION_PIPE_COMMAND_OUTPUT,
|
|
|
|
|
|
action >= BIND_ACTION_REGEX_LAUNCH && action <= BIND_ACTION_REGEX_COPY);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_key_bindings_collisions(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "key-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_binding_collisions(
|
|
|
|
|
|
&ctx, BIND_ACTION_KEY_COUNT - 1, binding_action_map, KEY_BINDING);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 18:30:17 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_search_bindings(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "search-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_search_bindings, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
for (int action = 0; action < BIND_ACTION_SEARCH_COUNT; action++) {
|
|
|
|
|
|
if (search_binding_action_map[action] == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
test_key_binding(
|
|
|
|
|
|
&ctx, &parse_section_search_bindings,
|
|
|
|
|
|
action, BIND_ACTION_SEARCH_COUNT - 1,
|
2025-02-03 09:29:42 +01:00
|
|
|
|
search_binding_action_map, &conf.bindings.search, KEY_BINDING,
|
|
|
|
|
|
false, false);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_search_bindings_collisions(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "search-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_binding_collisions(
|
|
|
|
|
|
&ctx,
|
|
|
|
|
|
BIND_ACTION_SEARCH_COUNT - 1, search_binding_action_map, KEY_BINDING);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 18:30:17 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_url_bindings(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "rul-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_url_bindings, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
for (int action = 0; action < BIND_ACTION_URL_COUNT; action++) {
|
|
|
|
|
|
if (url_binding_action_map[action] == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
test_key_binding(
|
|
|
|
|
|
&ctx, &parse_section_url_bindings,
|
|
|
|
|
|
action, BIND_ACTION_URL_COUNT - 1,
|
2025-02-03 09:29:42 +01:00
|
|
|
|
url_binding_action_map, &conf.bindings.url, KEY_BINDING,
|
|
|
|
|
|
false, false);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 18:30:17 +01:00
|
|
|
|
}
|
2021-12-11 20:01:16 +01:00
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
test_section_url_bindings_collisions(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "url-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_binding_collisions(
|
|
|
|
|
|
&ctx,
|
|
|
|
|
|
BIND_ACTION_URL_COUNT - 1, url_binding_action_map, KEY_BINDING);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
}
|
2021-12-11 18:30:17 +01:00
|
|
|
|
|
2021-12-11 18:49:59 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_mouse_bindings(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "mouse-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_mouse_bindings, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
for (int action = 0; action < BIND_ACTION_COUNT; action++) {
|
|
|
|
|
|
if (binding_action_map[action] == NULL)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
test_key_binding(
|
2021-12-11 18:49:59 +01:00
|
|
|
|
&ctx, &parse_section_mouse_bindings,
|
|
|
|
|
|
action, BIND_ACTION_COUNT - 1,
|
2025-02-03 09:29:42 +01:00
|
|
|
|
binding_action_map, &conf.bindings.mouse, MOUSE_BINDING,
|
|
|
|
|
|
false, false);
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 18:49:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-11 20:01:16 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_mouse_bindings_collisions(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "mouse-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_binding_collisions(
|
|
|
|
|
|
&ctx,
|
|
|
|
|
|
BIND_ACTION_COUNT - 1, binding_action_map, MOUSE_BINDING);
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2021-12-11 20:01:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-06 21:46:41 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_text_bindings(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "text-bindings", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "abcd";
|
|
|
|
|
|
ctx.value = XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT "+x";
|
|
|
|
|
|
xassert(parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\x07";
|
|
|
|
|
|
xassert(parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\x1g";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\x1";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\x";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.key = "\\y";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
|
|
|
|
|
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
#if 0
|
|
|
|
|
|
/* Invalid modifier and key names are detected later, when a
|
|
|
|
|
|
* layout is applied */
|
2022-02-06 21:46:41 +01:00
|
|
|
|
ctx.key = "abcd";
|
|
|
|
|
|
ctx.value = "InvalidMod+y";
|
|
|
|
|
|
xassert(!parse_section_text_bindings(&ctx));
|
input/config: support *all* modifier names
That is, allow custom modifiers (i.e. other than ctrl/shift/alt etc)
in key bindings.
This is done by no longer validating/translating modifier names to
booleans for a pre-configured set of modifiers (ctrl, shift, alt,
super).
Instead, we keep the modifier *names* in a list, in the key binding
struct.
When a keymap is loaded, and we "convert" the key binding, _then_ we
do modifier translation. For invalid modifier names, we print an
error, and then ignore it. I.e. we no longer fail to load a config due
to invalid modifier names.
We also need to update how we determine the set of significant
modifiers. Any modifier not in this list will be ignored when matching
key bindings.
Before this patch, we hardcoded this to shift/alt/ctrl/super. Now, to
handle custom modifiers as well, we simply treat *all* modifiers
defined by the current layout as significant.
Typically, the only unwanted modifiers are "locked" modifiers. We are
already filtering these out.
2023-05-06 11:39:38 +02:00
|
|
|
|
#endif
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2022-02-06 21:46:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-28 19:34:16 +02:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_environment(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "environment", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
/* A single variable */
|
|
|
|
|
|
ctx.key = "FOO";
|
|
|
|
|
|
ctx.value = "bar";
|
|
|
|
|
|
xassert(parse_section_environment(&ctx));
|
|
|
|
|
|
xassert(tll_length(conf.env_vars) == 1);
|
2024-01-24 23:17:28 +00:00
|
|
|
|
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
|
|
|
|
|
|
xassert(streq(tll_front(conf.env_vars).value, "bar"));
|
2022-05-28 19:34:16 +02:00
|
|
|
|
|
|
|
|
|
|
/* Add a second variable */
|
|
|
|
|
|
ctx.key = "BAR";
|
|
|
|
|
|
ctx.value = "123";
|
|
|
|
|
|
xassert(parse_section_environment(&ctx));
|
|
|
|
|
|
xassert(tll_length(conf.env_vars) == 2);
|
2024-01-24 23:17:28 +00:00
|
|
|
|
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
|
|
|
|
|
|
xassert(streq(tll_back(conf.env_vars).value, "123"));
|
2022-05-28 19:34:16 +02:00
|
|
|
|
|
|
|
|
|
|
/* Replace the *value* of the first variable */
|
|
|
|
|
|
ctx.key = "FOO";
|
|
|
|
|
|
ctx.value = "456";
|
|
|
|
|
|
xassert(parse_section_environment(&ctx));
|
|
|
|
|
|
xassert(tll_length(conf.env_vars) == 2);
|
2024-01-24 23:17:28 +00:00
|
|
|
|
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
|
|
|
|
|
|
xassert(streq(tll_front(conf.env_vars).value, "456"));
|
|
|
|
|
|
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
|
|
|
|
|
|
xassert(streq(tll_back(conf.env_vars).value, "123"));
|
2022-05-28 19:34:16 +02:00
|
|
|
|
|
|
|
|
|
|
config_free(&conf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-13 12:16:57 +01:00
|
|
|
|
static void
|
|
|
|
|
|
test_section_tweak(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct config conf = {0};
|
|
|
|
|
|
struct context ctx = {
|
|
|
|
|
|
.conf = &conf, .section = "tweak", .path = "unittest"};
|
|
|
|
|
|
|
|
|
|
|
|
test_invalid_key(&ctx, &parse_section_tweak, "invalid-key");
|
|
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_tweak, "scaling-filter",
|
|
|
|
|
|
5,
|
|
|
|
|
|
(const char *[]){"none", "nearest", "bilinear", "cubic", "lanczos3"},
|
|
|
|
|
|
(int []){FCFT_SCALING_FILTER_NONE,
|
|
|
|
|
|
FCFT_SCALING_FILTER_NEAREST,
|
|
|
|
|
|
FCFT_SCALING_FILTER_BILINEAR,
|
|
|
|
|
|
FCFT_SCALING_FILTER_CUBIC,
|
|
|
|
|
|
FCFT_SCALING_FILTER_LANCZOS3},
|
|
|
|
|
|
(int *)&conf.tweak.fcft_filter);
|
|
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_tweak, "overflowing-glyphs",
|
|
|
|
|
|
&conf.tweak.overflowing_glyphs);
|
|
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_tweak, "render-timer",
|
|
|
|
|
|
4,
|
|
|
|
|
|
(const char *[]){"none", "osd", "log", "both"},
|
|
|
|
|
|
(int []){RENDER_TIMER_NONE,
|
|
|
|
|
|
RENDER_TIMER_OSD,
|
|
|
|
|
|
RENDER_TIMER_LOG,
|
|
|
|
|
|
RENDER_TIMER_BOTH},
|
|
|
|
|
|
(int *)&conf.tweak.render_timer);
|
|
|
|
|
|
|
2023-07-26 16:12:36 +02:00
|
|
|
|
test_float(&ctx, &parse_section_tweak, "box-drawing-base-thickness",
|
2022-01-13 12:16:57 +01:00
|
|
|
|
&conf.tweak.box_drawing_base_thickness);
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_tweak, "box-drawing-solid-shades",
|
|
|
|
|
|
&conf.tweak.box_drawing_solid_shades);
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 /* Must be less than 16ms */
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_tweak, "delayed-render-lower",
|
|
|
|
|
|
&conf.tweak.delayed_render_lower_ns);
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_tweak, "delayed-render-upper",
|
|
|
|
|
|
&conf.tweak.delayed_render_upper_ns);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_tweak, "damage-whole-window",
|
|
|
|
|
|
&conf.tweak.damage_whole_window);
|
2022-01-13 12:26:20 +01:00
|
|
|
|
|
|
|
|
|
|
#if defined(FOOT_GRAPHEME_CLUSTERING)
|
2022-01-13 12:16:57 +01:00
|
|
|
|
test_boolean(&ctx, &parse_section_tweak, "grapheme-shaping",
|
|
|
|
|
|
&conf.tweak.grapheme_shaping);
|
2022-01-13 12:26:20 +01:00
|
|
|
|
#else
|
|
|
|
|
|
/* TODO: the setting still exists, but is always forced to ‘false’. */
|
|
|
|
|
|
#endif
|
2022-01-13 12:16:57 +01:00
|
|
|
|
|
|
|
|
|
|
test_enum(
|
|
|
|
|
|
&ctx, &parse_section_tweak, "grapheme-width-method",
|
|
|
|
|
|
3,
|
|
|
|
|
|
(const char *[]){"wcswidth", "double-width", "max"},
|
|
|
|
|
|
(int []){GRAPHEME_WIDTH_WCSWIDTH,
|
|
|
|
|
|
GRAPHEME_WIDTH_DOUBLE,
|
|
|
|
|
|
GRAPHEME_WIDTH_MAX},
|
|
|
|
|
|
(int *)&conf.tweak.grapheme_width_method);
|
|
|
|
|
|
|
|
|
|
|
|
test_boolean(&ctx, &parse_section_tweak, "font-monospace-warn",
|
|
|
|
|
|
&conf.tweak.font_monospace_warn);
|
|
|
|
|
|
|
2023-07-26 16:12:36 +02:00
|
|
|
|
test_float(&ctx, &parse_section_tweak, "bold-text-in-bright-amount",
|
|
|
|
|
|
&conf.bold_in_bright.amount);
|
|
|
|
|
|
|
2022-01-13 12:16:57 +01:00
|
|
|
|
#if 0 /* Must be equal to, or less than INT32_MAX */
|
|
|
|
|
|
test_uint32(&ctx, &parse_section_tweak, "max-shm-pool-size-mb",
|
|
|
|
|
|
&conf.tweak.max_shm_pool_size);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2022-04-12 13:01:56 +02:00
|
|
|
|
config_free(&conf);
|
2022-01-13 12:16:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-27 17:23:33 +01:00
|
|
|
|
int
|
|
|
|
|
|
main(int argc, const char *const *argv)
|
|
|
|
|
|
{
|
2023-03-31 10:30:58 +02:00
|
|
|
|
FcInit();
|
2021-11-27 17:23:33 +01:00
|
|
|
|
log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR);
|
|
|
|
|
|
test_section_main();
|
2024-12-21 06:52:00 +01:00
|
|
|
|
test_section_security();
|
2021-12-25 23:03:11 +01:00
|
|
|
|
test_section_bell();
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
|
test_section_desktop_notifications();
|
2021-12-26 12:37:12 +01:00
|
|
|
|
test_section_scrollback();
|
2021-12-29 19:10:49 +01:00
|
|
|
|
test_section_url();
|
2021-12-29 19:15:15 +01:00
|
|
|
|
test_section_cursor();
|
2021-12-29 19:18:25 +01:00
|
|
|
|
test_section_mouse();
|
2023-07-05 00:19:21 +08:00
|
|
|
|
test_section_touch();
|
2021-12-29 19:54:21 +01:00
|
|
|
|
test_section_colors();
|
2021-12-29 19:33:18 +01:00
|
|
|
|
test_section_csd();
|
2021-12-11 18:30:17 +01:00
|
|
|
|
test_section_key_bindings();
|
2021-12-11 20:01:16 +01:00
|
|
|
|
test_section_key_bindings_collisions();
|
2021-12-11 18:30:17 +01:00
|
|
|
|
test_section_search_bindings();
|
2021-12-11 20:01:16 +01:00
|
|
|
|
test_section_search_bindings_collisions();
|
2021-12-11 18:30:17 +01:00
|
|
|
|
test_section_url_bindings();
|
2021-12-11 20:01:16 +01:00
|
|
|
|
test_section_url_bindings_collisions();
|
2021-12-11 18:49:59 +01:00
|
|
|
|
test_section_mouse_bindings();
|
2021-12-11 20:01:16 +01:00
|
|
|
|
test_section_mouse_bindings_collisions();
|
2022-02-06 21:46:41 +01:00
|
|
|
|
test_section_text_bindings();
|
2022-05-28 19:34:16 +02:00
|
|
|
|
test_section_environment();
|
2022-01-13 12:16:57 +01:00
|
|
|
|
test_section_tweak();
|
2021-11-27 17:23:33 +01:00
|
|
|
|
log_deinit();
|
2023-03-31 10:30:58 +02:00
|
|
|
|
FcFini();
|
2021-11-27 17:23:33 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|