8000 Change release button handling to issue passthrough events by jchtt · Pull Request #524 · kmonad/kmonad · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Change release button handling to issue passthrough events #524

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

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

jchtt
Copy link
@jchtt jchtt commented Apr 18, 2022

Hi all,

this is meant to address #256, release events not being held when using tap-next-release. From studying the code, this seems to be the least invasive solution to this problem.

The issue stems from the way KMonad handles key releases in relation to the pull chain. The way I understand it, the pull chain consists of dispatch, hooks, and sluice. Dispatch listens to incoming key events, passes them to the hooks layer, where key events can be caught and additional actions triggered. Events that are not caught then arrive at the sluice, where event processing can be held. If held and then released, cached events are being re-inserted into dispatch. If not held, events are being processed and lookup of buttons occurs. In this system, releases are handled via hooks: once a button press is handled after passing through the sluice, a hook is registered that emits the correct key release upon receiving the corresponding event.

The issue is that when a key has already been pressed before processing was held and the key is then released, its release event is being emitted immediately because the release hook has already been registered, and its emit statement is therefore not being held up at the sluice.

My proposed solution seems like the least invasive way to approach this and to reuse most of the existing infrastructure. Instead of having an emit statement in the release hook, we are injecting the corresponding key event into dispatch, therefore allowing it to be blocked by the sluice, just as presses are blocked.

A potential problem with this is that a release event could pass through the hooks layer twice, so one key release could trigger multiple release hooks to be fired. My proposed solution is to flag key events specifically as already having been processed, thus safeguarding them from the hooks and any lookup in the button hashmap. This PR introduces a WrappedKeyEvent type that wraps a key event and additionally has a passthrough field that indicates whether an event should be handled by the hooks system and lookup up in the hashmap or not.

I'm pretty inexperienced with Haskell (just started re-learning it), so the code might be pretty bad. I would consider it a starting point to discuss this or other solutions to #256 and would love some comments to improve my coding style. In particular, one issue I had is that in some places, I wasn't able to use the (lenses type?) lookup with (^.) for the fields of the new type I introduced. I would love to know how to do that better.

Edit: One thing that is not covered by this is other "Button" release actions. Maybe extending the sluice to be able to handle Actions instead of KeyEvents might be the way to go? In that case, a container like WrappedKeyEvent could contains events or actions?

@jchtt
Copy link
Author
jchtt commented Apr 19, 2022

Maybe another way to solve this issue would be to allow a hook to be registered as a sort of "master hook", holding processing of any other hooks. In that case, this would obviate the need for having a sluice: buttons like tap-hold-next could just register themselves as master hooks, taking a record of all events happening while they are active, and then taking care of injecting them into dispatch themselves.

@jchtt
Copy link
Author
jchtt commented Apr 30, 2022

I revisited this question and made another attempt at finding a cleaner solution that would work also with layers switches.

My current proposed solution introduces an additional hooks layer after the sluice in the pull chain and a WrappedEvent data type that can hold either KeyEvents or Uniques (hook tags from expired timers). Regular hooks are registered in this second hooks layer, while any hook that makes use of the hold function is registered in the first hook layer that I renamed inputHooksPrio (for priority). Moreover, timers are now taken out of the injectTmr in Dispatch, not in Hooks.

These changes mean that processing of any kind of event can now be paused, including key release events and timers. When paused, only the priority hooks layer will process these events, taking care of eventually re-opening the sluice.

@david-janssen @slotThe @thoelze1 @MuhammedZakir Let me know what you think!

@MuhammedZakir
Copy link
Contributor
MuhammedZakir commented May 1, 2022

I haven't looked into KMonad's internals nor do I have necessary experience, so I can't comment on your code. But I will test it later; may take a few weeks though.


It looks like you accidentally modified startup/run file? https://github.com/kmonad/kmonad/pull/524/files#diff-fa2b73773005be60f40b7a2ec4ce791b241369aa8fee2b6bfec043145167acf7

@jchtt
Copy link
Author
jchtt commented May 1, 2022

I haven't looked into the KMonad's internals nor do I have necessary experience, so I can't comment on your code. But I will test it later; may take a few weeks though.

Thanks, that would be great!

It looks like you accidentally modified startup/run file? https://github.com/kmonad/kmonad/pull/524/files#diff-fa2b73773005be60f40b7a2ec4ce791b241369aa8fee2b6bfec043145167acf7

Yeah, I don't know what's going on there, it somehow automatically did this whenever I made a new branch. I suspect something weird is going on with my git on Windows. If you check the actual branch, the file is still there…

Edit: Windows had decided to drop the executable flagged, should be fixed now.

@meain
Copy link
meain commented May 2, 2022

Thanks a lot for working on this. I was able to test this out and seems to be working well for me. I'll keep using it and let you know if I run into any issues.

@slotThe
Copy link
Member
slotThe commented May 2, 2022

@jchtt sorry, life got in the way. I will definitely try to get to it this week! Thanks already for the contribution!

@jchtt
Copy link
Author
jchtt commented May 3, 2022

Thanks @slotThe, no rush! Not sure this is the best way to solve this problem, happy to think about other approaches as well.

Copy link
Member
@slotThe slotThe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking in to say that I haven't forgotten about this! I've been running on this PR for a few days now and I quite like the behaviour (and haven't noticed any drawbacks so far).

I would agree with your assessment that it's probably the least invasive solution with the current way that things work. Having this two-hook solution is perhaps conceptually a bit "meh", but in this case I think perfection can wait until after model-refactor is not just a distant dream anymore :)

I plan to read the code in more details (hopefully soon...) but I think the overall idea is sound and from a glance I don't suppose will change much aside from some style considerations. Thanks!

@jchtt
Copy link
Author
jchtt commented May 9, 2022

Thanks @slotThe. One style question I have is the following: if we keep the strategy as is, any hook function that calls hold should register a "priority" hook instead of a regular one. Is there any nice way of enforcing that?

@MuhammedZakir
Copy link
Contributor

I think we should document when to use inputHooksPrio over inputHooks, preferably with an example (use a currently defined button for that). This would help when defining new buttons.

@slotThe
Copy link
Member
slotThe commented May 9, 2022

Thanks @slotThe. One style question I have is the following: if we keep the strategy as is, any hook function that calls hold should register a "priority" hook instead of a regular one. Is there any nice way of enforcing that?

Beyond making it a completely new type? Probably not. A sleep-deprived, rambling, thought: we could perhaps make this work with phantom types, exporting not the data constructors themselves but constructor functions for them. This needs some further deliberation but it's probably worth it

I think we should document when to use inputHooksPrio over inputHooks, preferably with an example (use a currently defined button for that). This would help when defining new buttons.

Definitely yes!

@david-janssen
Copy link
Collaborator

Just popping in to give a strategic answer: if you managed to identify a problem with the pullchain model and found a way to fix it, then I applaud that greatly. If we test things and they concretely fix a problem without introducing any new ones, I am all for it. I have 0 attachment to the direction the pullchain model is taken into, because I think that at its core, it's a very flawed solution written by someone who was still thinking imperatively.

I have loads of work to do in IO at the moment, so that's where I'll be focusing my efforts. If I do ever manage to wrap up that work, I'm turning my attention to a completely different way of handling the model. Until that time, any duct-tape on the pullchain is deeply appreciated :-)

meain added a commit to meain/nur-packages that referenced this pull request Apr 20, 2024
@jokesper jokesper mentioned this pull request Sep 5, 2024
Copy link
Collaborator
@jokesper jokesper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I would have personally changed. Keep in mind, I'm just a contributor I cannot actually merge this.
If I understand it correctly you also did a little refactoring where you touched the code
with which function handles timeout (missing understanding, not against it).
Nitpicking aside, nice work!

| OutputHook -- ^ Install the hook just before emitting a 'KeyEvent'
= InputHookPrio -- ^ Install the hook immediately after receiving a &# E7EE 39;KeyEvent'
-- and before the sluice
| InputHook -- ^ Installthe hook after the sluice
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you missed a space

Suggested change
| InputHook -- ^ Installthe hook after the sluice
| InputHook -- ^ Install the hook after the sluice

Comment on lines +87 to +91
itr <- atomically $ newEmptyTMVar
dsp <- Dp.mkDispatch (awaitKey src) itr
ihkPrio <- Hs.mkHooks (Dp.pull dsp) itr
slc <- Sl.mkSluice $ Hs.pull ihkPrio
ihk <- Hs.mkHooks (Sl.pull slc) itr
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would give itr as the first parameter since it doesn't require parenthesis.
You would also need to change the functions below.

Suggested change
itr <- atomically $ newEmptyTMVar
dsp <- Dp.mkDispatch (awaitKey src) itr
ihkPrio <- Hs.mkHooks (Dp.pull dsp) itr
slc <- Sl.mkSluice $ Hs.pull ihkPrio
ihk <- Hs.mkHooks (Sl.pull slc) itr
itr <- atomically $ newEmptyTMVar
dsp <- Dp.mkDispatch itr $ awaitKey src
ihkPrio <- Hs.mkHooks itr $ Dp.pull dsp
slc <- Sl.mkSluice $ Hs.pull ihkPrio
ihk <- Hs.mkHooks itr $ Sl.pull slc

Comment on lines +100 to +102
= InputHookPrio -- ^ Install the hook immediately after receiving a 'KeyEvent'
-- and before the sluice
| InputHook -- ^ Installthe hook after the sluice
Copy link
Collaborator
@jokesper jokesper Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have added a parmater to the InputHook to differentiate whether before or after the sluice.
This would make the within function more sensical as within OutputHook is a bit wierd.
Though this is the change I'm least sure about


-- Initialize the button environments in the keymap
phl <- Km.mkKeymap (cfg^.firstLayer) (cfg^.keymapCfg)

-- Initialize output components
otv <- lift . atomically $ newEmptyTMVar
ohk <- Hs.mkHooks . atomically . takeTMVar $ otv
otr <- atomically $ newEmptyTMVar
ohk <- Hs.mkHooks ((atomically . takeTMVar) otv >>= pure . (WrappedKeyEvent NoCatch)) otr
Copy link
Collaborator
@jokesper jokesper Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When changing the parameter order of mkHooks as described above this too would be more readable*:

Suggested change
ohk <- Hs.mkHooks ((atomically . takeTMVar) otv >>= pure . (WrappedKeyEvent NoCatch)) otr
ohk <- Hs.mkHooks otr $ pure . WrappedKeyEvent NoCatch <=< atomically . takeTMVar $ otv

* readablitiy is subjective so this is just my opinion.
EDIT:
Or even better don't use monadic stuff when you use pure, just use the functor.

Comment on lines +69 to +70
mkDispatch' :: MonadUnliftIO m => m KeyEvent -> TMVar Unique -> m Dispatch
mkDispatch' s itr = withRunInIO $ \u -> do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above I would change the order of parameters like this and also for mkDispatch, mkHooks, ... (and maybe Dispatch, ...)

Suggested change
mkDispatch' :: MonadUnliftIO m => m KeyEvent -> TMVar Unique -> m Dispatch
mkDispatch' s itr = withRunInIO $ \u -> do
mkDispatch' :: MonadUnliftIO m => TMVar Unique -> m KeyEvent -> m Dispatch
mkDispatch' itr s = withRunInIO $ \u -> do

@@ -82,7 +85,7 @@ mkDispatch = lift . mkDispatch'
-- 1. The next item to be rerun
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add the timer in the comments of what is happening.

dsp <- Dp.mkDispatch $ awaitKey src
ihk <- Hs.mkHooks $ Dp.pull dsp
slc <- Sl.mkSluice $ Hs.pull ihk
itr <- atomically $ newEmptyTMVar
Copy link
Collaborator
@jokesper jokesper Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply hint

Suggested change
itr <- atomically $ newEmptyTMVar
itr <- newEmptyTMVarIO

This is a problem at a few points.
Even better rebase on master since there it's already fixed.

jokesper added a commit to jokesper/kmonad that referenced this pull request Oct 27, 2024
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
jokesper added a commit to jokesper/kmonad that referenced this pull request Dec 22, 2024
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
jokesper added a commit to jokesper/kmonad that referenced this pull request Jan 4, 2025
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
@jokesper
Copy link
Collaborator
jokesper commented Jan 25, 2025

While trying to implement a fix for #955, I noticed that when we hold and catch an event via InputHookPrio, other input hooks can capture it when we stop holding it.
Is this intentional
runHooks runs every hook on an event.
But with holding we do the same, except that there may be a time difference in between.
We could simply not reregister events which have been caught.

This is especially relevant if we register a hook after we process them.
Such hooks will in some cases always immediatly trigged due to receiving an event which actually caused their creation.

If we want to be pedantic about it, hooks should receive the keyevents iff they have been registered before the event.

Since this is a bit harder to implement I suggest simply not reregistering events which have been caught (I hope this doesn't break other things).

jokesper added a commit to jokesper/kmonad that referenced this pull request Jan 26, 2025
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
jokesper added a commit to jokesper/kmonad that referenced this pull request Feb 17, 2025
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
jokesper added a commit to jokesper/kmonad that referenced this pull request Feb 19, 2025
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
jokesper added a commit to jokesper/kmonad that referenced this pull request Mar 7, 2025
This is a long standing issue, so it fixes a few things:
- fixes kmonad#256
- fixes kmonad#466 (duplicate of kmonad#256 or kmonad#111)
- fixes kmonad#523 (duplicate)
- fixes kmonad#705 (already closed)
- fixes kmonad#907 (duplicate)
- resolves kmonad#524 (original merge request, with conflicts)
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

Successfully merging this pull request may close these issues.

6 participants
0