Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to change state of virtual keys from an external program #105

Closed
houmain opened this issue Feb 4, 2024 Discussed in #103 · 13 comments
Closed

Allow to change state of virtual keys from an external program #105

houmain opened this issue Feb 4, 2024 Discussed in #103 · 13 comments

Comments

@houmain
Copy link
Owner

houmain commented Feb 4, 2024

Discussed in #103

@houmain
Copy link
Owner Author

houmain commented Mar 16, 2024

This will be available in the upcoming 4.0.0 release which is already documented and testable in the next branch.

@ristomatti
Copy link
Contributor

ristomatti commented Mar 17, 2024

I sadly didn't have time to thoroughly test this during the weekend but I just now got it built and installed. Initial findings:

There seems to be a regression with parsing parameterized aliases. On startup I got ERROR: Unterminated string in line 590 from the ContextActive line:

[class=JetBrainsIDE title=/Diff for Pull Request |Commit: |Shelved: |History: /i]                         
  ContextActive >> context["JetBrainsIDE pull request, commit or shelve diff"]

Removing the comma got rid of the error, so I assume it parsed it as two parameters "JetBrainsIDE pull request and commit or shelve diff".

It also seems the call to context is now handled differently. I have it defined like below, based on the example given here #91 (comment).

context = 50ms log["entered $0"] ^ log["left $0"]                                                                       notify = $(notify-send -t "$1" keymapper "$0")

Previously the ContextActive triggers logged the message I passed to context, e.g.:

entered JetBrainsIDE mappings
entered JetBrainsIDE actions
left JetBrainsIDE mappings
left JetBrainsIDE actions

Now the triggered class matching regex is logged instead:

entered /jetbrains-/i mappings
entered /jetbrains-/i actions
left /jetbrains-/i actions
left /jetbrains-/i mappings

Is this an intentional change or just a change in how ContextActive is handled compared to the initial implementation?

@ristomatti
Copy link
Contributor

ristomatti commented Mar 17, 2024

Due to regressions mentioned here #91 (comment), it might take a bit of time for me to test keymapperctl as I'll need to switch back and forth between the versions (I'm using the broken bits mappings all the time). I'll definitely try to find time to do that regardless as I've quite anxiously waited for this feature. 🙂

As a side note - after I had brought up this feature request, I got a slight feeling of remorse as I feared this would make the code much more complicated and with that risk breaking things up... I hope this won't become a huge PITA for you!

@ristomatti
Copy link
Contributor

Oh, forgot to mention. The descriptions for these two options seem to be mixed around:

  --wait-pressed <key>  waits until a virtual key is released.
  --wait-released <key> waits until a virtual key is pressed.

@ristomatti
Copy link
Contributor

ristomatti commented Mar 18, 2024

I'll definitely try to find time to do that regardless

Ok I couldn't resist. I've quickly tested all keymapperctl options and all seem to work. I suggest a couple of small clarifications to the help text:

Usage: keymapperctl [--timeout <millisecs>] [option]
  --timeout <millisecs> sets a timeout for the following operation.
  --is-pressed <key>    returns 0 when a virtual key is down.
  --is-released <key>   returns 0 when a virtual key is up.
  --press <key>         presses a virtual key.
  --release <key>       releases a virtual key.
  --toggle <key>        toggles a virtual key.
  --wait-pressed <key>  waits until a virtual key is pressed.
  --wait-released <key> waits until a virtual key is released.
  --wait-toggled <key>  waits until a virtual key is toggled.
  -h, --help            print this help.

Changes:

  • swapped pressed/released on wait options
  • added [--timeout <millisecs>] in usage to clarify it's a positional option
  • moved --timeout as the first one listed for the above reason
  • changed [options] to [option] in usage to clarify only one option should be provided

@houmain
Copy link
Owner Author

houmain commented Mar 18, 2024

Is this an intentional change or just a change in how ContextActive is handled compared to the initial implementation?

No, I think it is substituting aliases again in some strings.

Thanks for your improvements of the help text!

changed [options] to [option] in usage to clarify only one option should be provided

But it is possible to pass more than one option.
This nifty line would release Virtual1 5 seconds after it is pressed (assuming that Virtual255 is not used elsewhere):

keymapperctl --wait-pressed Virtual1 --timeout 5000 --wait-pressed Virtual255 --release Virtual1

@ristomatti
Copy link
Contributor

ristomatti commented Mar 18, 2024

Aha! I need to test it more then. It was so late I just did some half brained tests. Something like:

keymapperctl --wait-released Virtual2 --wait-pressed Virtual3; echo $?

..then made a note that nothing happened from Virtual3. I didn't realise they could be chained. I figured the commands would anyway need run within some external script. But now when I think about it more, handling a timeout like suggested would be more inconvenient. In fact I think your example would handle the specific use case I was after here #113 (comment). E.g. something a bit like QMK's one shot keys that timeout after some time, but rather hold the modifier down but timeout after let's say a few minutes to avoid surprises if I forget to toggle it off. 😄

@ristomatti
Copy link
Contributor

ristomatti commented Mar 18, 2024

Hmm this got me thinking. Can you come up with a way these could be used to make a debounce effect on a virtual key? An example, based on my QMK caps word emulation you've seen earlier (which has btw worked more reliably than I would have ever expected given the hacky way it's implemented!):

Digit    = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Letter   = A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
CapsWord = Virtual1

# CapsWord emulation (https://docs.qmk.fm/#/feature_caps_word)
[default]
  CapsWordKey = Letter | Digit | Minus | Backspace

  Shift{200ms}            >> Shift
  (ShiftLeft ShiftRight)  >> CapsWord CapsLock ^
  ShiftLeft               >> ShiftLeft
  ShiftRight              >> ShiftRight

  # Optional external indicator, caps lock should already be lit
  CapsWord >> caps_word_status_on ^ caps_word_status_off

[modifier="CapsWord"]
  Minus                   >> Shift{Minus} # '_'
  CapsWordKey             >> CapsWordKey
  Any                     >> CapsWord CapsLock Any ^

To furher improve this, the mode would deactivate if none of the keys covered by CapsWordKey was pressed within a given timeout. The timeout would restart on each keypress.

And don't get me wrong, I don't want to expand the scope of this feature request!

Edit: Perhaps this would require a way to use explicit keydown events on the output? For example include a keydown event for CapsWord (Virtual1) after these:

  Minus                   >> Shift{Minus} # '_'
  CapsWordKey             >> CapsWordKey

AFAIK, currently it would only be possible to toggle a virtual key in this type of use case(?)

@houmain
Copy link
Owner Author

houmain commented Mar 19, 2024

Very cool your caps word configuration. Yeah, the automatic deactivation should not be a problem with this new feature.

The alias substitution hopefully works correctly now.

I could not resist to add a few more features to keymapperctl. I do not intend to make it touring complete but now one can run:
keymapperctl --instance test --wait 1000 --is-pressed Virtual1 --stdout --restart
This keeps writing once per second to stdout 0 when Virtual1 is down and 1 when it is not.

The instance feature allows to cancel or replace these long running processes. It is stopped when running a new process with the same id:
keymapperctl --instance test

@ristomatti
Copy link
Contributor

With the new changes, the previous behavior of ContextActive debugging works again. But I now got ERROR: Unterminated string from these lines:

[default]
  launch_telegram  >> $(i3-open -c Telegram "gtk-launch $(basename $(rg -l Telegram $HOME/.local/share/applications))")
  launch_whatsapp  >> $(i3-open -t WhatsApp "gtk-launch $(basename $(rg -l WhatsApp $HOME/.local/share/applications))")

In this particular case, I had forgot I wrote i3-open to interpret everything after flags as the command to execute, so it works without quotes. This is still a regression though.

@houmain
Copy link
Owner Author

houmain commented Mar 22, 2024

This should be fixed with the latest commit, thanks for reporting!

@ristomatti
Copy link
Contributor

I see my ridiculously complicated way of launching Telegram has ended up as a unit test. 🤣

Backstory: I haven't figured out a simpler way to launch desktop shortcuts by name. In the case of Telegram, just ~/Applications/Telegram/Telegram would have done the job. WhatsApp on the other hand is a Chrome app, so $(basename $(rg -l WhatsApp $HOME/.local/share/applications)) resolves to chrome-hnpfjngllnobngcgfapefoaidbinmjnm-Profile_8.desktop which gtk-launch accepts. If someone knows a simpler way, I'd be more than happy to hear that.

@houmain houmain closed this as completed Mar 28, 2024
@ristomatti
Copy link
Contributor

I can't reproduce the issue in 4.0, fix confirmed. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants