Giter Club home page Giter Club logo

Comments (28)

jtroo avatar jtroo commented on July 21, 2024

Currently macro is quite limited in functionality. It only allows basic typing actions (delays, keys and key-chords), so it probably wouldn't help in emulating tap-macro-release. The multi action might help, though it does tend to have some weird interactions.

Activating arbitrary actions on release is not well supported by the underlying key state machine at present. It does however support emitting a release event for Custom type actions. The Custom action type is what's used for mouse buttons, live reload, unicode, and command execution.

Given that this functionality seems quite specific, it may be sufficient to provide on-release for the few relevant use cases. E.g. just provide an on-release-layer-switch action.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Would I be able to listen to layer switches in order to run a cmd? Because in kmonad, I run a cmd along with the layer switch to change my key layer widget (in AwesomeWM) to display the current layer.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

Yea you could write your own code that connects to the kanata TCP server and runs the commands itself. Or another event could be added for on-release-cmd.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Okay then it seems on-release-layer-switch would be sufficient for me for now. The TCP server is probably the better solution to run the cmd, but I'm not gonna complain if you also added on-release-cmd

Do you think a general on-release would eventually possible in the future?

Because I've this concept I named Proxy Mod where, in the base layer, I hold a key (the proxy mod) -> it changes to a layer that lets you pick modifiers like ctrl (via press-only lctl) on press, then changes back to base layer on release so I can chord with them -> Once I release the proxy mod, it clears all modifiers. Would be nice to be able to do that.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

It's certainly possible if I or someone else can think of a design that fits well with keyberon's state machine.

An idea that popped into my head is that rather than have general-case press-only or on-release, have the ability to create fake keys that have arbitrary actions. And then there can be other actions to tap/press/release these fake keys that are disconnected from the keyboard inputs. A specific on-release case could act on these fake keys.

The concept of fake keys works better in keyberon's state machine than general on-release does.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Not sure I follow the whole fake keys concept.

Also, I just want to differentiate between on-press/on-release vs. press-only/release-only in case of confusion. The former refers to the user's physical press/release of a key. The latter refers to the emitted event.

I believe release-key and release-layer works like release-only.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

The fake keys idea would probably make more sense if you look at the design docs as well as the code.

The kanata code leaves the key state management to the keyberon library. The library doesn't have a good way to handle on-release. It also doesn't have a good way to handle press-only; all key states (not layer though) are tied to a physical key being pressed and the state goes away when the physical key is released.

Fake keys would be a way to use the keyberon library to decouple physical key presses and releases from key state.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Okay I think I understand. Basically, use fake keys as a middle-man (or filtering mechanism) to trick the keyberon's state management.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

I think #92 should be able to replicate all the functionality you want. It'll require a different setup compared to kmonad, but I think it'll work. See the kanata.kbd file changes for examples.

Please test at your leisure and let me know what you think.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Fake keys seem to work as intended. Thanks! I appreciate how quickly you added it in. I also noticed you can map (layer-switch layer_name) to a fake key which is cool.

Now my issues are pretty much API-related. (Note: I haven't really studied the source code as I'm not familiar with Rust as much so I'm looking at this from a more UX perspective. Forgive me for my lack of knowledge of the source code's limitations.)

Fake Keys API

For the average user, I think fake keys are probably better of as an implementation detail for some abstraction rather than a direct API, but here are my suggestions to improve the API anyway:

  1. Remove deffakekeys

From a UX perspective, it just seems redundant having to manually map a normal key to a fake key alias. Perhaps fake-key-op should just take a normal key as an argument, then when parsing it, it would automatically map the normal key to a generated fake key or something.

  1. Rename fake-key-op to on-press-fake-key-op

The added prefix makes it more clear. For instance, (fake-key-op ctl tap) might be confusing whereas with the added prefix, it's not.

A More Composable API

  1. Decouple the press-only/release-only/tap part from the on-press/on-release part and abstract them into their own general actions (while they use fake keys internally).

This would add a lot of composability. You can still keep the fake keys API if you want, but such composable abstractions would be nice. For instance, I could create a bunch of release-only aliases, combine them into a single alias, then pack this with layer-switch into on-release:

(defalias
  ;; GENERAL
  ;;; lock modifiers
  po-lalt (press-only lalt)
  po-lmeta (press-only lmet)
  po-lshift (press-only lsft)
  po-lctrl (press-only lctl)
  ;;; clear modifiers
  ro-lalt (release-only lalt)
  ro-lmeta (release-only lmet)
  ro-lshift (release-only lsft)
  ro-lctrl (release-only lctl)
  lclear (multi @ro-lalt @ro-lmeta @ro-lshift @ro-lctrl)
  ro-ralt (release-only ralt)
  ro-rmeta (release-only rmet)
  ro-rshift (release-only rsft)
  ro-rctrl (release-only rctl)
  rclear (multi @ro-ralt @ro-rmeta @ro-rshift @ro-rctrl)
  clear (multi @lclear @rclear)
  ;;; layers
  base (layer-switch base)
  extend (layer-switch extend)
  media (layer-switch media)
  navigation (layer-switch navigation)
  ;;; control/(escape+clear) key
  esc-from-ctrl (multi (release-only lctl) esc @clear)
  esc-from-ctrl-to-base (multi @esc-from-ctrl @base)
  ctrl_maybe-esc (multi lctl (tap-hold-press 200 200 @esc-from-ctrl XX))
  ctrl_maybe-esc-base (multi lctl (tap-hold-press 200 200 @esc-from-ctrl-to-base XX))
  cte @ctrl_maybe-esc ;; Map to Caps key for base layer
  ctb @ctrl_maybe-esc-base ;; Map to Caps key for other layers
  
  ;; BASE LAYER
  extend-base (multi (on-press @extend) (on-release (multi @base @clear))) ;; The example I was talking about
  alt-extend-base (multi lalt @extend-base)
  alt @alt-extend-base ;; Map to Alt key
  
  ;; EXTEND LAYER
  ;;; sub-layer keys
  ;;;; if you release this before releasing @alt, temporarily keep the layer until @alt is released
  ;;;; if you release this after releasing @alt, permanently keep the layer
  media-media (multi (on-press @media) (on-release @media))
  navigation-navigation (multi (on-press @navigation) (on-release @navigation))
  med @media-media ;; Map to Q key
  nav @navigation-navigation ;; Map to W key
  ;;; proxy mods
  ;;;; Lock a modifier until @alt is released. Go to base layer
  pxc (multi @po-lctrl @base) ;; Map to A key
  pxs (multi @po-lshift @base) ;; Map to S key
  pxa (multi @po-lalt @base) ;; Map to D key
  pxm (multi @po-lmeta @base) ;; Map to F key
)

Other Notes

  • For a general release-only action, I imagine you'd combine release-key, release-layer, and a fake key release into one, maybe using a match statement. Or maybe just use fake key release for all of it.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

The fake keys API should be composable enough to achieve the effects of what you're describing. I think what you're going for is ergonomics instead, and I can see how the kmonad way of doing it could be more ergonomic than fake keys.

The fake keys API is the simplest possible API implementation that works for generic on-release, press-only, and release-only, since it maps very closely to the underlying state machine.

It may be possible to hide the fake keys functionality behind press/release-only + on-release APIs, but the backing code will need to become more complex to do the bookkeeping for which actions map to which fake keys. It may happen one day, but I'm not particularly motivated to do the work to make an already complex use case more ergonomic, particularly since I don't actually use the feature myself.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

One thing to note is that fake key actions currently should also be able to refer to other previously defined fake key, and accept multi as well. This might help get closer to the ergonomics you're describing.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

One thing to note is that fake key actions currently should also be able to refer to other previously defined fake key, and accept multi as well. This might help get closer to the ergonomics you're describing.

Ah, I did not test that, but that's good to know. I'll try to convert my kmonad config to kanata using the fake keys API and see how it goes.

In the mean time, what do you think about my suggestions on improving the fake keys API?

from kanata.

jtroo avatar jtroo commented on July 21, 2024

In the mean time, what do you think about my suggestions on improving the fake keys API?

To summarize my earlier comment, it's certainly possible but at this time I believe the current API and the suggested improvements have the same expressive power - just with different ergonomics - I'm not currently motivated to design and implement any changes.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

I meant just the first 2 suggestions. I assume getting rid of deffakekeys would currently be a hassle, but what about the rename from fake-key-op to on-press-fake-key-op?

from kanata.

jtroo avatar jtroo commented on July 21, 2024

Ah I see. It's unclear how removing deffakekeys would actually be workable without going all-in into implementing the 3rd suggestion.

Renaming to on-press-fake-key-op seems fine to me.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

I've force-pushed the PR branch to make the change to on-press-fake-key-op as well as added a more complex example to kanata.kbd.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Found a bug. It seems a multi containing on-release in deffakekeys doesn't mesh well. You can't refer to it in defalias.

(deffakekeys
  flctl lctl
  flsft lsft
  flmet lmet
  flalt lalt
  flclear (multi
            (on-release-fake-key-op flctl release)
            (on-release-fake-key-op flsft release)
            (on-release-fake-key-op flmet release)
            (on-release-fake-key-op flalt release)
          )
)

(defalias
  pct (on-press-fake-key-op flctl press)
  
  ;; DOESN'T WORK
  clr (on-release-fake-key-op flclear press)

  ;; DOESN'T WORK
  clr (on-release-fake-key-op flclear release)

  ;; DOESN'T WORK
  clr (on-press-fake-key-op flclear press)

  ;; DOESN'T WORK
  clr (on-press-fake-key-op flclear release)
  
  ;; WORKS
  clr (multi
        (on-release-fake-key-op flctl release)
        (on-release-fake-key-op flsft release)
        (on-release-fake-key-op flmet release)
        (on-release-fake-key-op flalt release)
      )
)

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Ah I see. It's unclear how removing deffakekeys would actually be workable without going all-in into implementing the 3rd suggestion.

My suggestion is to use normal key (instead of fake key) as arguments in fake-key-op expressions. Then when parsing it, auto-generate/cache a fake key in place of the normal key.

So anytime you refer to a normal key inside a fake-key-op expression, it would point to the generated fake key.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

Let me check my understanding. The suggestion is that if argument to fake-key-op happens to map to a real key label like lmet then the code should auto-generate a fake key for lmet without needing deffakekeys. This would be convenient, but eliminating deffakekeys entirely would lose its flexibility, so it doesn't seem worth doing.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Maybe I'm missing something, but I'm not sure what flexibility you would lose (at least from a user's perspective).

deffakekeys just feels verbose.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

Regarding the bug, I can't reproduce it. Do note that on-release-fake-key-op doesn't actually activate unless the fake key was actually pressed in the first place. I tested with this:

(deffakekeys
  pal (multi
        (on-press-fake-key-op ctl press)
        (on-press-fake-key-op sft press)
        (on-press-fake-key-op met press)
        (on-press-fake-key-op alt press)
      )
  ral (multi
        (on-release-fake-key-op ctl release)
        (on-release-fake-key-op sft release)
        (on-release-fake-key-op met release)
        (on-release-fake-key-op alt release)
      )
)

(defalias
  pal (on-press-fake-key-op pal tap) ;; works
  ral (on-release-fake-key-op ral tap) ;; releases on press
  rl1 (on-release-fake-key-op ral press) ;; does nothing at first
  rl2 (on-release-fake-key-op ral release) ;; activates if activating @rl1 first then @rl2
)

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

I forgot to try tap. That works for me. Still, it's confusing having to refer to it that way. I think multis are probably better off staying in defalias instead of deffakekeys.

from kanata.

jtroo avatar jtroo commented on July 21, 2024

Maybe I'm missing something, but I'm not sure what flexibility you would lose (at least from a user's perspective).

I think we're missing some details in each other's understanding of how the feature should work.

The simple case which loses flexibility (in my view) is:

  • remove deffakekeys
  • fake-key-op only accepts key actions (e.g. lmet, lctl) and auto-generates a fake key for it

This loses flexibility since fake keys can no longer be arbitrary actions. But it might be that it's unnecessary, not sure.

The complicated case, which if implemented correctly may as well go all the way to suggestion 3 is:

  • remove deffakekeys
  • fake-key-op accepts arbitrary actions and generates fake keys for it

This is complicated because designing the code such that press/tap/release on various physical keys will map to the correct intended common fake keys is complex.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Oh I understand now. I was so fixated on key actions, I forgot some other arbitrary action like a layer-switch action would complicate things.

Maybe you can allow to auto-generate fake keys for normal key actions, but don't remove deffakekeys in case of other arbitrary actions? Or is that still too much?

from kanata.

jtroo avatar jtroo commented on July 21, 2024

It's possible and not so difficult, but to me doesn't seem worth it since the feature then becomes inconsistent, which may harm the user experience more than the inconvenience of always using deffakekeys.

from kanata.

oblitzitate avatar oblitzitate commented on July 21, 2024

Fair enough. Thanks for responding quickly.

I suppose my only suggestion left is probably to shorten fake-key-op expressions even more. Perhaps remove op suffix (I assume it means operation) since it's implied. Perhaps remove the - symbol in fake-key so that it's consistent with deffakekeys. So the final expressions would be on-press-fakekey and on-release-fakekey

from kanata.

jtroo avatar jtroo commented on July 21, 2024

I'm good with shortening the action names. I'll push to the PR shortly.

from kanata.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.