houmain / keymapper

A cross-platform context-aware key remapper.
GNU General Public License v3.0
298 stars 25 forks source link

Feature Request: Execute terminal commands #5

Closed Knyffen closed 3 years ago

Knyffen commented 3 years ago

The virtual keys being toggle-able is awesome, but it leaves a problem of knowing which state it is currently in. Personally, I would use my caps lock light for this (bash: xset led named "Caps Lock"), but in general this and many other use cases could be accounted for if we had access to terminal commands.

Regarding the fork UniversalHotkeyer, while it has support for terminal commands, it doesn't seem to allow chain terminal commands with regular keymapper features (or I might just be misunderstanding the config file syntax...). This means that while I can turn on the LED, I cannot simultaneously toggle my virtual key, which defeats the purpose. So basically, it is worth keeping in mind if you decide to implement the feature. :-)

Thank you for making an awesome program! :D

Azarattum commented 3 years ago

@Knyffen you understood the config syntax correctly. You are unable to chain a shell script with a virtual key with my current implementation. However, you still can chain keystrokes with commands (like xdotool key A).

Regarding your led problem, you can make it work even without shell command by letting the CapsLock key through (like CapsLock >> CapsLock Virtual1). This way you get the led but with a side effect of having the actual CapsLock enabled. I guess you can mitigate capitalization with something like Virtual1{Any} >> Shift{Any}, but that isn't a pretty solution. You can take a look at my configuration where I have a similar use case of making CapsLock trigger 'VimMode' for my keyboard and still having the light to indicate that mode. https://github.com/Azarattum/DotFiles/blob/master/hotkeyer/.config/hotkeyer.conf lines 59, 63 and below.

@houmain if you'll decide to implement this feature, I'm happy to merge any of my changes and archive my fork in favor of your project.

houmain commented 3 years ago

The first version of keymapper had some functionality, like executing terminal commands or macro recording, which was removed some time before the first public version was released. It took me quite some time to get the logic right and to simplify things I removed everything that was not absolutely necessary (I have mapped a few shortcuts to terminal commands using my window manager, so there was no immediate need to to implement this feature...). But I agree that it would be handy to also have these mappings in the keymapper configuration file (especially on Windows). So I am thinking about implementing it.

@Azarattum I think it would be better if on Linux, where the program is split in a client and a daemon part, the client would execute the terminal command (no problems with permissions, environment...). So the client, which reads the configuration file, could generate a keycode for each terminal command, send them as part of the output sequences to the daemon and the daemon would only inform the client about these virtual key presses, which in turn would execute the terminal commands. And maybe also to use a $ instead of the / to define a terminal command? It could look like this:

Virtual1{ScrollLock} >> turn_off_led Virtual1
ScrollLock >> turn_on_led Virtual1
turn_on_led >>$ xset led named "Scroll Lock"
turn_off_led >>$ xset -led named "Scroll Lock"

What do you think?

p.s. xset led named "Caps Lock" has no effect on my machine but xset led named "Scroll Lock" works.

Azarattum commented 3 years ago

I absolutely agree about making the client executing commands. I thought of that while implementing my solution, but unfortunately I needed to get it working quickly, because I literally cannot do anything on my WM without it. I am using hotkeyer as my primary keyboard manager to do stuff like closing, switching windows, opening apps etc. So I went with a dirtier solution... I regret about it, but don't have time to reimplement it properly.

Regarding configuration, your idea looks very nice. However, I have some suggestions. How about using $> or >_ instead of >>$? So it would look like a terminal prompt and would be easier to distinguish from a key binding. Also I see you are using abstract command instead of mapping directly. How would it behave in context scenarios? Do we need to map it twice then?

A >> cmd

[title="name"]
cmd >> A exec
exec >_ echo hello

That would solve the chaining problem, but will be an overkill for simple scenarios where we don't need chaining (which is most likely).

It also would be really nice to have optional direct mappings in contexts. Which should be considered a separate feature of it's own. But isn't that cool:

[title="weird app"]
Meta{D} >_ do stuff

It would also be applied for regular bindings. When abstract commands could be treated like aliases for the key binds to be extra explicit or to do some complex chaining.

Azarattum commented 3 years ago
A >> something 

[title="somewhere"]
something >> B

would be equal to

[title="somewhere"]
A >> B

A >> something 
something >_ echo B

would be equal to

A >_ echo B

But here we have to be explicit:

A >> something B
something >_ echo B
Azarattum commented 3 years ago

A config preprocessor logic might be implemented to perform the explicit expansion. That way there will be no need to change the parser itself.

Just a speculation...

Knyffen commented 3 years ago

While I can't comment meaningfully on the design, regarding the Caps Lock LED I had to edit /usr/share/X11/xkb/compat/ledcaps and remove the exclamation mark in !allowExplicit to enable modifying the LED in user space. I don't know about Scroll Lock, since I don't have it. :P

houmain commented 3 years ago

How about using $> or >_ instead of >>$ ?

A >>$ ls -la
A $> ls -la
A >_ ls -la

Mhh, I think I will postpone this decision. I took inspiration from the Bash prompt...

Also I see you are using abstract command instead of mapping directly. How would it behave in context scenarios? Do we need to map it twice then?

No, I don't think so. You just have to give the command a name if you want to output a key and a terminal command at once. Just as in your example, right:

A >> something B something >_ echo B

It also would be really nice to have optional direct mappings in contexts. Which should be considered a separate feature of it's own.

Yes, I also was surprised once, that this was not possible yet and tried to implement it. I stopped when I noticed it was more work than expected. I will open a new issue for this.

@Knyffen Good to know how to get this working.

houmain commented 3 years ago

This feature is testable now. I already tried the mentioned keyboard LED use case with this config:

  Boss = Virtual1
  Boss{ScrollLock} >> Boss $(xset -led named "Scroll Lock")
  ScrollLock       >> Boss $(xset led named "Scroll Lock")

As you can see, terminal commands can be embedded in the output sequence using $(). I hope you are pleased with the syntax.

@Azarattum Mapping sequences in context blocks should also work now as expected:

[system="Linux"]
A >> B
[system="Windows"]
A >> C

I already updated the documentation but have not created a release yet.

Knyffen commented 3 years ago

Thank you! It looks awesome! Now its time for me to tell all my friends about this project and brag about my now perfect system-wide vim config :D

Azarattum commented 3 years ago

@houmain that looks great! I haven't tested it yet, but I glanced through the code.

Have you seen exclusive key syntax in my fork (~Meta >> ...)? It kinda makes sense to implement something like this here, since we can execute commands now. Bind on output works kinda similar, but it was very buggy last time I tested it. Especially with conflicting key combinations. I need to do the more testing then I'll provide a detailed description in a separate issue. But basically what I mean is:

~Meta >> $(A)
Meta{X} >> $(B)

A would execute ONLY when • No other keys pressed • Meta Down • No other keys pressed • Meta Up

While • Meta Down • X Down • Meta Up Would execute only B. So, you have Windows like start menu that doesn't interrupt any Meta shortcuts and doesn't open randomly with them.

@Knyffen could you share your vim config? I have one myself with some complex features like character replace, find next, delete expressions etc. I'm curious what you came up with to bring vim system-wide.

houmain commented 3 years ago

Thank you for your appreciation!

@Azarattum Maybe rearranging would solve this? Have you tried:

Meta{X} >> $(B)
Meta >> $(A)

I need to do the more testing then I'll provide a detailed description in a separate issue.

All reports of shortcomings are welcome!

Knyffen commented 3 years ago

@Azarattum You can find my config here, but I would recommend waiting a couple days. While I have started now, it was kind of a deal breaker that I never knew what mode I was in. :-) (My earlier message about having a config was more of a hypothetical.)

As any further discussion should probably be happening in different issues, I will close this one before it scope-creeps too much.

Edit: Fixed the link.