diff --git a/README.md b/README.md index 91b1bff..6e361ef 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -[![Kofi](https://badgen.net/badge/icon/kofi?icon=kofi&label)](https://ko-fi.com/rvaiya) - -# Impetus - -[![Packaging status](https://repology.org/badge/tiny-repos/keyd.svg)](https://repology.org/project/keyd/versions) +# keyd++ Linux lacks a good key remapping solution. In order to achieve satisfactory results a medley of tools need to be employed (e.g xcape, xmodmap) with the end @@ -10,20 +6,12 @@ result often being tethered to a specified environment (X11). keyd attempts to solve this problem by providing a flexible system wide daemon which remaps keys using kernel level input primitives (evdev, uinput). -# Note on v2 - -The config format has undergone several iterations since the first -release. For those migrating their configs from v1 it is best -to reread the man page. - -See also: [changelog](docs/CHANGELOG.md). - # Goals - - Speed (a hand tuned input loop written in C that takes <<1ms) + - Speed (event loop that takes <<1ms for input event) - Simplicity (a [config format](#sample-config) that is intuitive) - - Consistency (modifiers that [play nicely with layers](https://github.com/rvaiya/keyd/blob/6dc2d5c4ea76802fd192b143bdd53b1787fd6deb/docs/keyd.scdoc#L128) by default) - - Modularity (a UNIXy core extensible through the use of an [IPC](https://github.com/rvaiya/keyd/blob/90973686723522c2e44d8e90bb3508a6da625a20/docs/keyd.scdoc#L391) mechanism) + - Consistency (modifiers that [play nicely with layers](docs/keyd.scdoc#L128) by default) + - Modularity (a UNIXy core extensible through the use of an [IPC](docs/keyd.scdoc#L391) mechanism) # Features @@ -33,7 +21,7 @@ as well as some which are unique to keyd. Some of the more interesting ones include: -- Layers (with support for [hybrid modifiers](https://github.com/rvaiya/keyd/blob/6dc2d5c4ea76802fd192b143bdd53b1787fd6deb/docs/keyd.scdoc#L128)). +- Layers (with support for [hybrid modifiers](docs/keyd.scdoc#L128)). - Key overloading (different behaviour on tap/hold). - Keyboard specific configuration. - Instantaneous remapping (no more flashing :)). @@ -42,6 +30,23 @@ Some of the more interesting ones include: - First class support for modifier overloading. - Unicode support. +keyd++ has specific features at the moment: + +- Virtually unlimited sizes/counts (keyd has had many hardcoded limitations). +- Layer indicator with keyboard led of choice (keyd is somewhat broken). +- **Macro** can now do `type(Hello world)` without worrying about spaces. +- **Macro** can now execute `cmd(gnome-terminal)` and it should **just work**. +- Allow using `ctrl` as an alias for `control` (my personal whim). +- Wildcard for mice `m:` that excludes problematic abs ptr devices(`a:`). +- More flexible text parsing (in progress, eg. 'a+b' now equals 'b+a'). +- Memory optimizations (sometimes only 1/5 of what keyd has had). +- Performance optimizations, eg. events from ungrabbed device are ignored. +- Some security improvement: privileged commands shall be in /etc/keyd/ conf. +- Bindings coming from ex. `keyd-application-mapper` inherit uid+gid+environ. +- `keyd reload` from user loads user binds from `~/.config/keyd/bindings.conf`. +- New commands for config control: push, pop, pop_all. Can unload user binds. +- Convenience and safety coming from C++20. Sorry for longer compilation. + ### keyd is for people who: - Would like to experiment with custom [layers](https://docs.qmk.fm/feature_layers) (i.e custom shift keys) @@ -59,7 +64,7 @@ Some of the more interesting ones include: # Dependencies - - Your favourite C compiler + - C++20 compiler starting from clang++-14 or g++-11 - Linux kernel headers (already present on most systems) ## Optional @@ -70,15 +75,22 @@ Some of the more interesting ones include: # Installation -*Note:* master serves as the development branch, things may occasionally break -between releases. Releases are [tagged](https://github.com/rvaiya/keyd/tags), and should be considered stable. +*Note:* master serves as the development branch, things may occasionally break. ## From Source - git clone https://github.com/rvaiya/keyd - cd keyd - make && sudo make install - sudo systemctl enable --now keyd +```bash +# Install dependencies (if necessary) +sudo apt install build-essentials git +# Clone with git clone (.) or download sources manually to keyd directory +cd keyd +# Specify your favourite compiler (optional) +export CXX=clang++-18 +# First time install +make && sudo make install && sudo systemctl enable --now keyd +# Second time install (update, contains **example** flags for statically linking libstdc++) +CXX=clang++-18 CXXFLAGS='-static-libgcc -static-libstdc++' make && sudo make install && sudo systemctl daemon-reload && sudo systemctl restart keyd +``` # Quickstart @@ -119,6 +131,24 @@ Some mice (e.g the Logitech MX Master) are capable of emitting keys and are consequently matched by the wildcard id. It may be necessary to explicitly blacklist these. +## User Specific Remapping (experimental) + +- Add yourself to the keyd group: + + `usermod -aG keyd ` + +- Create `~/.config/keyd/bindings.conf`: + +E.G + meta.t = macro(cmd(gnome-terminal)) + +- Execute `keyd reload` without sudo (possibly at startup) + +- Environment variables whel launching `keyd reload` will be used. + +- `/root/keyd/bindings.conf` may be read at `sudo keyd reload`. + + ## Application Specific Remapping (experimental) - Add yourself to the keyd group: @@ -161,46 +191,6 @@ Third party packages for the some distributions also exist. If you wish to add yours to the list please file a PR. These are kindly maintained by community members, no personal responsibility is taken for them. -### Alpine Linux - -[keyd](https://pkgs.alpinelinux.org/packages?name=keyd) package maintained by [@jirutka](https://github.com/jirutka). - -### Arch - -[Arch Linux](https://archlinux.org/packages/extra/x86_64/keyd/) package maintained by Arch packagers. - -### Debian - -Experimental `keyd` and `keyd-application-mapper` packages can be found in the -CI build artifacts of the [work-in-progress Debian package -repository](https://salsa.debian.org/rhansen/keyd): - - * [amd64 (64-bit)](https://salsa.debian.org/rhansen/keyd/-/jobs/artifacts/debian/latest/browse/debian/output?job=build) - * [i386 (32-bit)](https://salsa.debian.org/rhansen/keyd/-/jobs/artifacts/debian/latest/browse/debian/output?job=build%20i386) - -Any Debian Developer who is willing to review the debianization effort and -sponsor its upload is encouraged to contact -[@rhansen](https://github.com/rhansen) (also see the [Debian ITP -bug](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1060023)). - -### Fedora - -[COPR](https://copr.fedorainfracloud.org/coprs/alternateved/keyd/) package maintained by [@alternateved](https://github.com/alternateved). - -### openSUSE -[opensuse](https://software.opensuse.org//download.html?project=hardware&package=keyd) package maintained by [@bubbleguuum](https://github.com/bubbleguuum). - -Easy install with `sudo zypper in keyd`. - -### Ubuntu - -Experimental `keyd` and `keyd-application-mapper` packages can be found in the -[`ppa:keyd-team/ppa` -archive](https://launchpad.net/~keyd-team/+archive/ubuntu/ppa). - -If you wish to help maintain this PPA, please contact -[@rhansen](https://github.com/rhansen). - # Sample Config [ids] @@ -218,7 +208,7 @@ If you wish to help maintain this PPA, please contact f = / ... -# Recommended config +# Example config Many users will probably not be interested in taking full advantage of keyd. For those who seek simple quality of life improvements I can recommend the diff --git a/data/keyd-application-mapper.1.gz b/data/keyd-application-mapper.1.gz index bc2c9be..38f36ca 100644 Binary files a/data/keyd-application-mapper.1.gz and b/data/keyd-application-mapper.1.gz differ diff --git a/data/keyd.1.gz b/data/keyd.1.gz index 6fb6c56..df1750d 100644 Binary files a/data/keyd.1.gz and b/data/keyd.1.gz differ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8b5180d..51d4389 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -157,86 +157,3 @@ See [DESIGN.md](DESIGN.md) for a more thorough description of changes. # v2.0.0-beta Major version update. - -This breaks 1.x configs. The format may ~~change slightly~~ (see [2.3.0-rc](#v230-rc)) before leaving beta, -but once it does should remain backwards compatible for the foreseeable future. - -A non exhaustive list of changes can be found below. It is best to forget -everything you know and read man page anew. - - - Eliminate layer inheritance in favour of simple types. - (layouts are now defined with `:layout` instead of `:main`) - - Macros are now repeatable. - - Overload now accepts a hold threshold timeout. - - Config files are now vendor/product id oriented. - - SIGUSR1 now triggers a config reload. - - Modifiers are layers by default and can be extended directly. - - Config files now end in `.conf`. - - `layert()` is now `toggle()`. - - All layers are 'modifier layers' (terminological change) - - Eliminate the dedicated modifer layout. - - Modifiers no longer apply to key sequences defined within a layer. - (Layer entries are now always executed verbatim.) - - The old behaviour was unintuitive and can be emulated using nested - layers if necessary. - -For most old configs transitioning should be a simple matter of changing -the file extension from `.cfg` to `.conf`, replacing `layert` with -`toggle`, changing `:main` to `:layout` and adding - -``` -[ids] - -* - -[main] -``` - -to the top of the file. - -More involved configs may need additional changes, but should be possible -to replicate using the new rules. If not, please file an issue on -[github](https://github.com/rvaiya/keyd/issues). - -# v1.1.1 - -- Make layert behaviour more intuitive when combined with oneshot() and layer(). - -# v1.1.0 - -- Add layert() to facilitate semipermanent-activation of occluding layers. -- Resolve layer conflicts by picking the one which was most recently activated. - -# v1.0.0 - -Major version update: - -- Introduce support for modifier layers. -- Simplify the config format. -- Improve consistency/expected key behaviour. -- Symbols can now be assigned directly place of their names (e.g `&` instead of `S-7`). -- Macro support. - -*This breaks existing configs*. Moving forward the config format is expected to -remain backwards compatible. - -Main Config Changes: - -- Modifiers are now just treated as layers -- The default layer is now called main -- The modifier layout is distinct from the key layout - -Config migration map: - -``` -mods_on_hold(C, esc) = overload(C, esc) -layer_on_hold(layer, esc) = overload(layer, esc) -layer_toggle(layer) = layout(layer) -layer(layer) = layer(layer) -oneshot(mods) = oneshot(mods) -oneshot_layer(layer) = oneshot(layer) -[dvorak:default] = [dvorak:main] -``` - -See the [manpage](man.md) for details. diff --git a/docs/keyd.scdoc b/docs/keyd.scdoc index 8ed95ed..7377f3c 100644 --- a/docs/keyd.scdoc +++ b/docs/keyd.scdoc @@ -16,11 +16,11 @@ keyd(1) *listen* Print layer state changes of the running keyd daemon to stdout. Useful for scripting. -*bind reset| [...]* +*bind reset|push|pop|pop_all [...]* Apply the supplied bindings. See _Bindings_ for details. *reload* - Reload config files. + Reload config files. Optionally loads bindings from $HOME/.config/keyd/bindings.conf *list-keys* List valid key names. @@ -797,8 +797,9 @@ The _bind_ command accepts one or more _bindings_, each of which must have the f Where __ is the name of an (existing) layer in which the key is to be bound. As a special case, the string "reset" may be used in place of a binding, in -which case the current keymap will revert to its original state (all +which case the current keymap will revert to its last pushed state (all dynamically applied bindings will be dropped). +Commands "push" and "pop|pop_all" maintain additional layers of states. Examples: @@ -970,10 +971,5 @@ Disables the esc and end keys. # AUTHOR -Written by Raheman Vaiya (2017-). - -# BUGS - -Please file any bugs or feature requests at the following url: - - +Written by Raheman Vaiya (2017-) in C. +Port to C++ in progress by Nekotekina. diff --git a/src/config.cpp b/src/config.cpp index e57f035..1f1bd6a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -326,9 +326,6 @@ static int set_layer_entry(const struct config *config, static std::string layer_sorted_name(std::string_view name) { -#ifdef __cpp_lib_constexpr_vector - constinit -#endif static std::vector arr; arr.assign(split_char<'+'>(name), split_str<'+'>()); for (auto& name : arr) { @@ -381,7 +378,7 @@ static int new_layer(std::string_view s, std::string_view name, struct config *c for (auto layername : split_char<'+'>(name)) { int idx = config_access_layer(config, layername, true); if (idx < 0) { - err("%s is not a valid layer", std::string(layername).c_str()); + err("%.*s is not a valid layer", (int)layername.size(), layername.data()); return -1; } @@ -401,7 +398,7 @@ static int new_layer(std::string_view s, std::string_view name, struct config *c layer->mods = mods; } else { if (!type.empty()) - warn("\"%s\" is not a valid layer type, ignoring\n", std::string(type).c_str()); + warn("\"%.*s\" is not a valid layer type, ignoring\n", (int)type.size(), type.data()); layer->type = LT_NORMAL; layer->mods = 0; @@ -538,7 +535,7 @@ static int parse_macro_expression(std::string_view s, macro& macro, struct confi s.remove_suffix(1); s.remove_prefix(6); } else if (parse_key_sequence(s, &code, &mods) && utf8_strlen(s) != 1) { - err("Invalid macro: %s\n", std::string(s).c_str()); + err("Invalid macro: %.*s\n", (int)s.size(), s.data()); return -1; } @@ -905,7 +902,7 @@ static int config_parse_string(struct config *config, char *content) entry += ent->key; entry += " = "; entry += ent->val; - if (config_add_entry(config, entry.c_str()) < 0) + if (config_add_entry(config, entry) < 0) keyd_log("\tr{ERROR:} line m{%zd}: %s\n", ent->lnum, errstr); } } @@ -1011,14 +1008,15 @@ int config_get_layer_index(const struct config *config, std::string_view name) * Adds a binding of the form [.] = * to the given config. Returns layer index that was modified. */ -int config_add_entry(struct config *config, const char *exp) +int config_add_entry(struct config* config, std::string_view exp) { char *keyname, *descstr, *dot, *paren, *s; const char *layername = "main"; struct descriptor d; struct layer *layer; - std::string buf = exp; + static std::string buf; + buf.assign(exp); s = buf.data(); dot = strchr(s, '.'); @@ -1048,6 +1046,25 @@ int config_add_entry(struct config *config, const char *exp) return idx; } +const char* env_pack::getenv(std::string_view name) +{ + if (!env) + return nullptr; + for (size_t i = 0;; i++){ + const char* ptr = env[i]; + if (!ptr) + return nullptr; + // TODO: is this safe? + if (!strncmp(ptr, name.data(), name.size())) { + ptr += name.size(); + if (*ptr == '=') + return ptr + 1; + if (*ptr == 0) + return nullptr; + } + } +} + config_backup::config_backup(const struct config& cfg) : descriptor_count(cfg.descriptors.size()) , macro_count(cfg.macros.size()) diff --git a/src/config.h b/src/config.h index 830ba4b..e4f661c 100644 --- a/src/config.h +++ b/src/config.h @@ -106,7 +106,7 @@ enum class layer_type_e : signed char { using enum layer_type_e; struct layer { - mutable std::string name_buf; + std::string name_buf; enum layer_type_e type; bool modified = false; // Modified by kbd_eval @@ -132,6 +132,8 @@ struct env_pack { std::unique_ptr env; uid_t uid; gid_t gid; + + const char* getenv(std::string_view); }; struct ucmd { @@ -202,7 +204,7 @@ struct config_backup { }; int config_parse(struct config *config, const char *path); -int config_add_entry(struct config *config, const char *exp); +int config_add_entry(struct config *config, std::string_view); int config_check_match(struct config *config, const char *id, uint8_t flags); diff --git a/src/daemon.cpp b/src/daemon.cpp index 95959d7..1ebd00b 100644 --- a/src/daemon.cpp +++ b/src/daemon.cpp @@ -1,4 +1,5 @@ #include "keyd.h" +#include "log.h" #include #include #include @@ -164,7 +165,7 @@ static void restore_leds() } } -static void on_layer_change(const struct keyboard *kbd, const struct layer *layer, uint8_t state) +static void on_layer_change(const struct keyboard *kbd, struct layer *layer, uint8_t state) { if (kbd->config.layer_indicator <= LED_MAX) { activate_leds(kbd); @@ -212,7 +213,7 @@ static void load_configs() .on_layer_change = on_layer_change, }; kbd = new_keyboard(std::move(kbd)); - kbd->original_config.reserve(1); + kbd->original_config.reserve(2); kbd->original_config.emplace_back(kbd->config); ent->kbd = std::move(kbd); @@ -283,7 +284,7 @@ static void manage_device(struct device *dev) } } -static void reload() +static void reload(std::shared_ptr env) { restore_leds(); configs.reset(); @@ -293,6 +294,39 @@ static void reload() manage_device(&device_table[i]); clear_vkbd(); + + if (env) { + // Load user bindings (may be not loaded when executed as root) + static std::string buf; + if (env->uid == 0) + buf.assign("/root/.config/"); + else if (auto v = env->getenv("XDG_CONFIG_HOME")) + buf.assign(v) += "/"; + else if (auto v = env->getenv("HOME")) + buf.assign(v) += "/.config/"; + else + buf.clear(); + buf += "keyd/bindings.conf"; + std::ifstream file(buf, std::ios::binary); + if (!file.is_open()) { + keyd_log("Unable to open %s\n", buf.c_str()); + return; + } + buf.assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); + + for (auto ent = configs.get(); ent; ent = ent->next.get()) { + ent->kbd->config.cfg_use_uid = env->uid; + ent->kbd->config.cfg_use_gid = env->gid; + ent->kbd->config.env = env; + for (auto str : split_char<'\n'>(buf)) { + if (str.empty()) + continue; + if (!kbd_eval(ent->kbd.get(), str)) + keyd_log("Invalid binding: %.*s\n", (int)str.size(), str.data()); + } + ent->kbd->original_config.emplace_back(ent->kbd->config); + } + } } static void send_success(int con) @@ -386,7 +420,7 @@ static int input(char *buf, [[maybe_unused]] size_t sz, uint32_t timeout) return 0; } -static bool handle_message(int con, struct config* config) +static bool handle_message(int con, struct config* config, std::shared_ptr env) { struct ipc_message msg; if (!xread(con, &msg, sizeof(msg))) { @@ -429,7 +463,7 @@ static bool handle_message(int con, struct config* config) send_success(con); break; case IPC_RELOAD: - reload(); + reload(std::move(env)); send_success(con); break; case IPC_LAYER_LISTEN: @@ -449,8 +483,7 @@ static bool handle_message(int con, struct config* config) ent->kbd->config.cfg_use_uid = config->cfg_use_uid; ent->kbd->config.cfg_use_gid = config->cfg_use_gid; ent->kbd->config.env = config->env; - if (!kbd_eval(ent->kbd.get(), msg.data)) - success = 1; + success |= kbd_eval(ent->kbd.get(), msg.data); } if (success) @@ -478,6 +511,9 @@ static void handle_client(int con) ::config ephemeral_config; ephemeral_config.cfg_use_gid = cred.gid; ephemeral_config.cfg_use_uid = cred.uid; + static std::shared_ptr prev = nullptr; + if (!prev || prev->uid != cred.uid || prev->gid != cred.gid) + prev.reset(); if (getuid() < 1000) { // Copy initial environment variables from caller process @@ -485,8 +521,7 @@ static void handle_client(int con) if (envf.is_open()) { std::vector buf; buf.assign(std::istreambuf_iterator(envf), std::istreambuf_iterator()); - static std::shared_ptr prev = nullptr; - if (prev && prev->buf == buf && prev->uid == cred.uid && prev->gid == cred.gid) { + if (prev && prev->buf == buf) { // Share previous environment variables ephemeral_config.env = prev; } else { @@ -507,7 +542,7 @@ static void handle_client(int con) } size_t msg_count = 0; - while (handle_message(con, &ephemeral_config)) { + while (handle_message(con, &ephemeral_config, prev ? prev : std::make_shared())) { msg_count++; } dbg2("%zu messages processed", msg_count); @@ -675,7 +710,7 @@ int run_daemon(int, char *[]) evloop_add_fd(ipcfd); - reload(); + reload(std::make_shared()); atexit(cleanup); diff --git a/src/keyboard.cpp b/src/keyboard.cpp index e4c0626..5237934 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -245,9 +245,7 @@ static void lookup_descriptor(struct keyboard *kbd, uint8_t code, static void deactivate_layer(struct keyboard *kbd, int idx) { ::layer& layer = kbd->config.layers[idx]; - layer.name_buf.back() = 0; - dbg("Deactivating layer %s", layer.name_buf.c_str() + 1); - layer.name_buf.back() = '\n'; + dbg("Deactivating layer %.*s", (int)layer.name_buf.size() - 2, layer.name_buf.c_str() + 1); assert(kbd->layer_state[idx].active > 0); kbd->layer_state[idx].active--; @@ -263,9 +261,7 @@ static void deactivate_layer(struct keyboard *kbd, int idx) static void activate_layer(struct keyboard *kbd, uint8_t code, int idx) { ::layer& layer = kbd->config.layers[idx]; - layer.name_buf.back() = 0; - dbg("Activating layer %s", layer.name_buf.c_str() + 1); - layer.name_buf.back() = '\n'; + dbg("Activating layer %.*s", (int)layer.name_buf.size() - 2, layer.name_buf.c_str() + 1); struct cache_entry *ce; kbd->layer_state[idx].activation_time = get_time(); @@ -402,7 +398,7 @@ void execute_command(ucmd& cmd) dup2(fd, 1); dup2(fd, 2); - if (auto env = cmd.env.get()) + if (auto env = cmd.env.get(); env && env->env) execle("/bin/sh", "/bin/sh", "-c", cmd.cmd.c_str(), nullptr, env->env.get()); else execl("/bin/sh", "/bin/sh", "-c", cmd.cmd.c_str(), nullptr); @@ -1214,20 +1210,35 @@ long kbd_process_events(struct keyboard *kbd, const struct key_event *events, si return timeout; } -int kbd_eval(struct keyboard *kbd, const char *exp) +bool kbd_eval(struct keyboard* kbd, std::string_view exp) { - if (!strcmp(exp, "reset")) { + if (exp.empty()) + return true; + if (exp == "reset") { kbd->original_config.back().restore(kbd->config); kbd->layer_state.resize(kbd->config.layers.size()); - return 0; + return true; + } else if (exp == "push") { + kbd->original_config.emplace_back(kbd->config); + return true; + } else if (exp == "pop") { + // Don't allow removing first "backup" + if (kbd->original_config.size() <= 1) + return false; + kbd->original_config.pop_back(); + return true; + } else if (exp == "pop_all") { + while (kbd->original_config.size() > 1) + kbd->original_config.pop_back(); + return true; } else { if (int idx = config_add_entry(&kbd->config, exp); idx >= 0) { kbd->layer_state.resize(kbd->config.layers.size()); kbd->config.layers[idx].modified = true; kbd->config.layers[idx].keymap.sort(); - return 0; + return true; } } - return -1; + return false; } diff --git a/src/keyboard.h b/src/keyboard.h index d9828ae..ef86153 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -33,7 +33,7 @@ struct key_event { struct output { void (*send_key) (uint8_t code, uint8_t state); - void (*on_layer_change) (const struct keyboard *kbd, const struct layer *layer, uint8_t active); + void (*on_layer_change) (const struct keyboard *kbd, struct layer *layer, uint8_t active); }; enum class chord_state_e : signed char { @@ -148,7 +148,7 @@ struct keyboard { std::unique_ptr new_keyboard(std::unique_ptr); long kbd_process_events(struct keyboard *kbd, const struct key_event *events, size_t n); -int kbd_eval(struct keyboard *kbd, const char *exp); +bool kbd_eval(struct keyboard *kbd, std::string_view); void kbd_reset(struct keyboard *kbd); #endif diff --git a/src/macro.cpp b/src/macro.cpp index 3e24743..e2cf053 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -89,7 +89,7 @@ int macro_parse(std::string_view s, macro& macro, struct config* config) tok.remove_prefix(chrsz); } if (!tok.empty()) { - err("invalid macro text found: %s", std::string(tok).c_str()); + err("invalid macro text found: %.*s", (int)tok.size(), tok.data()); return -1; } } @@ -112,7 +112,7 @@ int macro_parse(std::string_view s, macro& macro, struct config* config) else if (!parse_key_sequence(key, &code, &mods)) ADD_ENTRY(MACRO_HOLD, code); else { - err("%s is not a valid key", std::string(key).c_str()); + err("%.*s is not a valid key", (int)key.size(), key.data()); return -1; } } @@ -153,7 +153,7 @@ int macro_parse(std::string_view s, macro& macro, struct config* config) } } - err("%s is not a valid key sequence", std::string(tok).c_str()); + err("%.*s is not a valid key sequence", (int)tok.size(), tok.data()); return -1; } diff --git a/t/test-io.cpp b/t/test-io.cpp index d4ecbc0..b712fc4 100644 --- a/t/test-io.cpp +++ b/t/test-io.cpp @@ -234,7 +234,7 @@ uint64_t run_test(struct keyboard *kbd, const char *path) return time; } -static void on_layer_change(const struct keyboard *kbd, const struct layer *layer, uint8_t active) +static void on_layer_change(const struct keyboard *kbd, struct layer *layer, uint8_t active) { }