itaygarin / ktrl Goto Github PK
View Code? Open in Web Editor NEWA Supercharged Keyboard Programming Daemon ⌨️
License: GNU Lesser General Public License v3.0
A Supercharged Keyboard Programming Daemon ⌨️
License: GNU Lesser General Public License v3.0
I'm just poking around the edges of ktrl at the moment (nice work!) and I have a question/suggestion:
It seems to me that the dual function of a tap/hold key need not be dependent on the length of time the key is held down.
Example use case:
One or two of the other keyboard event mappers I've seen (notably interception) works by waiting to see if a second key is pressed before the first is released. Here's how it might work:
This way, the user need not think about whether they've reached a certain time threshold. Is this reasonable? Has it already been covered by ktrl
but I didn't notice?
I'm getting the error:
Failed to parse the config file: Error { code: Message("unknown variant `Sound`, expected one of `NoOp`, `Key`, `KeySticky`, `KeySeq`, `Meh`, `Hyper`, `ActivateProfile`, `DeactivateProfile`, `DeactivateAllProfiles`, `TurnOnLayer`, `TurnOffLayer`, `ToggleLayer`, `TurnOnLayerAlias`, `TurnOffLayerAlias`, `ToggleLayerAlias`, `MomentaryLayer`, `Multi`")
Not sure why Sound
is not a variant.
I installed ktrl
using cargo and am on version 0.1.7
系统:Debian 12
ktrl 版本: 最新
问题描述:把 dance 的周期设置为 250 毫秒时,dance 的内容会无限重复,造成系统按键出现问题。
问题复现步骤:
将 dance 的时间设定为:
tap_dance_wait_time: 250,
将按键设定为:
KEY_CAPSLOCK: TapDance(2, Key(KEY_LEFTCTRL), KeySeq([KEY_LEFTALT, KEY_LEFTSHIFT, KEY_LEFTMETA, KEY_L])),
做好上述设定后,正常启动 ktrl ,当快速连按两次 CAPSLOCK 键时,会有 KEY_LEFTALT, KEY_LEFTSHIFT, KEY_LEFTMETA, KEY_L
的组合按键操作一直被触发,导至系统无法正常使用。
注:在当前系统下设定了 KEY_LEFTALT, KEY_LEFTSHIFT, KEY_LEFTMETA, KEY_L
该组快捷键执行一个 shell 脚本,当快速连按两次 CAPSLOCK 键时,脚本会一直被触发,无限重复执行。
问题复现程度:必现
临时解决办法:将 dance 周期设定为300毫秒,tap_dance_wait_time: 300,
Can you change the defaults to follow the XDG Base Directory Specification?
Full spec is here but the main changes would just be to change the assumption that configs and assets live in /opt/ktrl
to the specified dirs.
It would only involve the following changes:
$XDG_CONFIG_HOME/ktrl/cfg.ron
if the variable is set and $HOME/.config/ktrl/cfg.ron
otherwise$XDG_DATA_HOME/ktrl/assets
if the variable is set and $HOME/.local/share/ktrl/assets
otherwise$XDG_CACHE_HOME/ktrl/log.txt
if the variable is set and $HOME/.cache/ktrl/log.txt
otherwisecurrently we need to configure karibener for mac and autohot keys for windows as well as ktrl. When we use a laptop we are stuck with the keyboard we have (otherwise QMK or similar makes sense).
Is there some interest in cross platform support?
Started as a bug-report in #29
Currently, ktrl crashes when you chain TapHolds
Hi,
Here is my config file:
(
tap_hold_wait_time: 300,
tap_dance_wait_time: 1000,
layer_aliases: {
"base": 0,
},
layer_profiles: {
},
layers: [
{
KEY_RIGHTALT: Tap(Key(KEY_COMPOSE)),
},
],
)
With the aim of duplicating the functionality of this setxkbmap
command but with Alt Gr instead of the menu key (as my laptop doesn't have a dedicated menu key):
setxkbmap -option compose:menu ;
However the compose output doesn't seem to work. If I change it to another key (like KEY_U for example) it works fine, but the compose key isn't working as desired (to allow one to enter accented characters, etc.).
Do you have any ideas? Or how to debug what key action gets produced?
Incidentally is there a way to enable even more verbose debugging/logging to see each relevant key press from the device, etc.? As for a while it was tricky to work out that I had the right device and keys were being read correctly.
This seems like a great tool, it'd be great to simplify and unify a lot of the disparate (and often poorly documented) tooling for this in Linux at the moment - see this Ubuntu question for example!.
For example, a mouse and a keyboard
Suggestd by tux_mark_5 on reddit
This should be a markdown file under a new doc/ directory.
It'll explain -
Suggested by u/tux_mark_5 over on reddit
Suggested by tending on reddit
It looks like there is a key queue being built up, but I think it should wait for either the hold time of the tap to be completed, or for key release on the TapHold key, whichever comes first.
With a minimal config; only one layer and these effects:
KEY_F: TapHold(Key(KEY_F), Key(KEY_LEFTSHIFT)),
KEY_J: TapHold(Key(KEY_J), Key(KEY_RIGHTSHIFT)),
j pressed "jg" emitted j released
v v v
---------------------------------------------------------> time
^ ^ ^
g pressed g released hold time reached
j pressed "S-g" emitted j released
v v v
---------------------------------------------------------> time
^ ^ ^
g pressed g released hold time reached
"S-g" = shift+g
OR
j pressed "jg" emitted
v v
---------------------------------------------------------> time
^ ^ ^ ^
g pressed g released j released hold time (not reached)
Sound effects are a bit brittle at this point. It'd be better to just output a warning instead of crashing the entire process with unwrap/expect
This is a great program! Thank you for creating and sharing it.
Here is the little bug I ran into with tap-hold for home row modifiers. With a minimal config; only one layer and these effects:
KEY_CAPSLOCK: Tap(Key(KEY_LEFTCTRL)),
KEY_F: TapHold(Key(KEY_F), Key(KEY_LEFTSHIFT)),
KEY_J: TapHold(Key(KEY_J), Key(KEY_RIGHTSHIFT)),
The bug: Holding j
and tapping f
e
e
results in fee
, which one would expect to be capitalized.
Not the bug: This above only happens when the first tapped key is another TapHold hey—holding j
and tapping e
f
f
o
r
t
results in EFFORT
as expected.
Also not the bug: The normal tap effect also works fine with TapHold, eg holding capslock
and tapping f
sends ctrl+f
as expected, holding capslock
and holding j
then tapping e
sends ctrl+shift+e
as expected.
Back to the bug: Holding capslock
and holding j
then tapping f
sends ctrl+f
with no shift.
As some users pointed out, it'd be nice if there was a way to disable sound effects.
Currently, these effects depend on libalsa
. We can make this dependency optional
With this script
KEY_C: TapModi(KEY_LEFTCTRL, Key(KEY_C), Key(KEY_I)),
The following sequence works fine:
LEFTCTRL down -> KEY_C down -> KEY_C up -> LEFTCTRL up
But if I lift LEFTCTRL
before KEY_C
, KEY_I
will stick and start emitting on repeat:
LEFTCTRL down -> KEY_C down -> LEFTCTRL up -> KEY_C up
Result: ccccccccccccccccccccc...
(the character under KEY_I
) is printed until I press some other button.
There also sometimes happens another bug where (probably) my Win key sticks and I become unable to type any text but I couldn't track down what exactly causes it.
Suggested by tending on reddit
As suggested by @brockelmore,
maintaining a state file with the current bound port will allow clients
to resolve the port without needing to ask the user for it.
One ktrl supports multi-devices, we'd want to map device => port.
I'm using ktrl installed from crates.io and trying to test it using example configuration based on steps from README, but it doesn't work. When i press any key from my keyboard, it doesn't appear in my monitor. It's like the keyboard just unplugged until i stopped the systemd service and then it worked again. This is my setup
OS: Arch Linux x86_64
Kernel: 5.10.19-1-lts
DE: GNOME 3.38.3 (Wayland)
WM: Mutter
It doesn't output any error in program log. But here some error related to ktrl from journalctl
:
kernel: input: ktrl as /devices/virtual/input/input41
systemd-logind[1554]: Watching system buttons on /dev/input/event18 (ktrl)
gnome-shell[2630]: Could not open device /dev/input/event18: GDBus.Error:System.Error.ENODEV: No such device
This was suggested by diogoxpinto over on reddit -
This is fantastic! I tried it for a short while and already added it to my startup script ^^
The only downside I noticed is that `TapHold` seems to take a while to "go back" to Tap mode. The holding itself works fine after tweaking it to my liking, but then I stop holding it, and tap it, and the "hold" effect is still on. I have to tap it a few times to get it working. (Presumably, whatever delay there is just runs out while I do this)
Great project, thanks for sharing it!
It would be great to be able to name layers so if #13 gets integrated, you could activate via name. Also just helps for complex configs
For example, being able to bind Hyper
+Mouse button
Suggested by tux_mark_5 on reddit
It would be great to be able to activate a particular layer via a script. I.e. I use bspwm, and when I change focus between windows, I would like to be able to update what layer is active (effectively, this turns layers into user-definable mappings for applications). This could be done via sending a command by having ktrl see if there is a running instance for the device and executing basically any of the perform commands.
I.e. in firefox, I can't redefine keyboard shortcuts (*easily, i.e. without compiling firefox myself essentially). Its dumb.
So I would like to have a firefox layer that maps the keyboard shortcuts I want to use to the unchangeable firefox ones, same with other apps I use.
It would be great to have bluetooth keyboard support. On arch + Xorg, at least, this is what the wiki says:
Xorg
Device should be added as `/dev/input/event*` and your Xorg should add it automatically if you did not disable such feature.
And the corresponding events:
/dev/input/event0 /dev/input/event11 /dev/input/event14 /dev/input/event17 /dev/input/event2 /dev/input/event22 /dev/input/event25 /dev/input/event4 /dev/input/event7
/dev/input/event1 /dev/input/event12 /dev/input/event15 /dev/input/event18 /dev/input/event20 /dev/input/event23 /dev/input/event26 /dev/input/event5 /dev/input/event8
/dev/input/event10 /dev/input/event13 /dev/input/event16 /dev/input/event19 /dev/input/event21 /dev/input/event24 /dev/input/event3 /dev/input/event6 /dev/input/event9
If you know a workaround, would love to know!
This is pretty bad haha.
I suspect that since we moved to running as the ktrl
user,
the pulse server that's tied to the new user grabs the exclusive control of the output device...
I created a service based on ktrl.service
found in the project's etc
directory.
/etc/systemd/system/ktrl.service
[Unit]
Description=ktrl
[Service]
User=ktrl
Environment=HOME=/opt/ktrl
ExecStart=/home/andrew/.cargo/bin/ktrl -d /dev/input/event3
[Install]
WantedBy=multi-user.target
However, when I try to run the service I get the following error from systemctl status ktrl.service
Loaded: loaded (/etc/systemd/system/ktrl.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Sat 2020-06-27 10:35:08 EDT; 5s ago
Process: 23335 ExecStart=/home/andrew/.cargo/bin/ktrl -d /dev/input/event3 (code=exited, status=203/EXEC)
Main PID: 23335 (code=exited, status=203/EXEC)
CPU: 5ms
Jun 27 10:35:08 localhost.localdomain systemd[1]: Started ktrl.
Jun 27 10:35:08 localhost.localdomain systemd[23335]: ktrl.service: Failed to execute command: Permission denied
Jun 27 10:35:08 localhost.localdomain systemd[23335]: ktrl.service: Failed at step EXEC spawning /home/andrew/.cargo/bin/ktrl: Permission denied
Jun 27 10:35:08 localhost.localdomain systemd[1]: ktrl.service: Main process exited, code=exited, status=203/EXEC
Jun 27 10:35:08 localhost.localdomain systemd[1]: ktrl.service: Failed with result 'exit-code'.
I was able to track the issue down to SELinux. While SELinux is enforced the service does not work, however, while SELinux is disabled the service runs without issue. This is due to the bin being located in the user's home directory.
It would be better to have the bin located in /usr/local/bin/ktrl
then you could update the ktrl.service file to be
[Unit]
Description=ktrl
[Service]
User=ktrl
Environment=/opt/ktrl
ExecStart=/usr/local/bin/ktrl -d /dev/input/event3
[Install]
WantedBy=multi-user.target
That way there would be no issues with SELinux.
Looks like MomentaryLayer
not working properly, or I'm missing how it suppose to work. Consider such config:
// ktrl Example Configuration File
// -------------------------------
//
// ktrl config files use `ron` (Rust Object Notation) to serialize
// the text into the internal `cfg::Cfg` struct.
//
// - The full KEY_... listing can be found inside the `keys::KeyCode` enum
// - Layer entries are mapping between a source `KeyCode` into an `Action` (more on that below)
//
(
// ktrl will register a TapHold as an hold after 300ms
tap_hold_wait_time: 300,
// ktrl will register a TapDance if all taps occur within a 1000ms period (1s)
tap_dance_wait_time: 1000,
layer_aliases: {
"base": 0,
"left_control": 1,
"right_control": 2,
"left_alt": 3,
"right_alt": 4,
},
layer_profiles: {
"left_control": Profile(
indices: [],
aliases: ["left_control"],
),
"right_control": Profile(
indices: [],
aliases: ["right_control"],
),
"left_alt": Profile(
indices: [],
aliases: ["left_alt"],
),
"right_alt": Profile(
indices: [],
aliases: ["right_alt"],
),
},
layers: [
{
KEY_CAPSLOCK: TapHold(Key(KEY_LEFTCTRL), MomentaryLayer(1)),
KEY_APOSTROPHE: TapHold(Key(KEY_RIGHTCTRL), MomentaryLayer(2)),
KEY_TAB: TapHold(Key(KEY_LEFTALT), ToggleLayerAlias("left_alt")),
KEY_LEFTBRACE: TapHold(Key(KEY_RIGHTALT), ToggleLayerAlias("right_alt")),
KEY_SPACE: TapHold(Key(KEY_SPACE), Key(KEY_LEFTSHIFT)),
KEY_Q: Tap(Key(KEY_Q)),
KEY_W: Tap(Key(KEY_W)),
KEY_E: Tap(Key(KEY_F)),
KEY_R: Tap(Key(KEY_P)),
KEY_T: Tap(Key(KEY_G)),
KEY_Y: Tap(Key(KEY_J)),
KEY_U: Tap(Key(KEY_L)),
KEY_I: Tap(Key(KEY_U)),
KEY_O: Tap(Key(KEY_Y)),
KEY_P: Tap(Key(KEY_B)),
KEY_A: Tap(Key(KEY_A)),
KEY_S: Tap(Key(KEY_R)),
KEY_D: Tap(Key(KEY_S)),
KEY_F: Tap(Key(KEY_T)),
KEY_G: Tap(Key(KEY_D)),
KEY_H: Tap(Key(KEY_H)),
KEY_J: Tap(Key(KEY_N)),
KEY_K: Tap(Key(KEY_E)),
KEY_L: Tap(Key(KEY_I)),
KEY_SEMICOLON: Tap(Key(KEY_O)),
KEY_B: Tap(Key(KEY_SPACE)),
KEY_N: Tap(Key(KEY_K)),
},
// left_control
{
KEY_A: Tap(Key(KEY_COMMA)),
KEY_S: Tap(Key(KEY_DOT)),
KEY_D: Tap(Key(KEY_MINUS)),
KEY_F: Tap(Key(KEY_KPPLUS)),
},
// right_control
{
KEY_A: Tap(Key(KEY_COMMA)),
KEY_S: Tap(Key(KEY_DOT)),
KEY_D: Tap(Key(KEY_MINUS)),
KEY_F: Tap(Key(KEY_O)),
},
],
)
As you can see I have 4 layers but describing only two - left_control
enabled by holding CapsLock
and right_control
enabled by holding '
- APOSTROPHE. The strange thing is that when I press CapsLock + a/s/d/f
- it inputs correct remapping and then when I release - it goes back to Layer 0. Strange thing happens when I'm trying ' + a/s/d/f
. If I press it - it work perfectly inputting remapping, but when I release - it moves not to Layer 0 but to Layer 1, and then even if I'm not holding neither CapsLock
nor '
- it still for some reason inserts Layer 1 remappings.
Is it supposed to work this way, and is there a way to just make setup such as:
"Till I hold one button - I get such layer remapping, if I hold another button - I get another layer remapping, and when I release any of such layer-until-holded buttons - it falls back to layer 0" ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.