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

tap config-specified key on every mouse cursor movement event #1598

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

wolfwood
Copy link
Contributor

@wolfwood wolfwood commented Apr 6, 2025

Describe your changes. Use imperative present tense.

tap config-specified key on every mouse cursor movement event

used in the example configs to build automousekeys layer triggered by cursor movement, with config level timeout and early exit when typing resumes

Checklist

  • Add documentation to docs/config.adoc
    • Yes or N/A
  • Add example and basic docs to cfg_samples/kanata.kbd
    • Yes or N/A
  • Update error messages
    • Yes or N/A
  • Added tests, or did manual testing
    • Yes

I could use assistance testing Windows w/ interception driver

@wolfwood wolfwood force-pushed the feature/automousekeys-simple branch from 80a87d2 to f97b632 Compare April 6, 2025 04:32
@wolfwood
Copy link
Contributor Author

wolfwood commented Apr 6, 2025

closes #1081

@wolfwood wolfwood force-pushed the feature/automousekeys-simple branch 3 times, most recently from 221091e to b439e4c Compare April 6, 2025 04:55
Copy link
Owner

@jtroo jtroo left a comment

Choose a reason for hiding this comment

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

Thanks! The approach seems reasonable. Seems fine to leave the feature API as-is, so can add the docs to the appropriate locations.

@wolfwood wolfwood force-pushed the feature/automousekeys-simple branch from b439e4c to 07afe04 Compare April 8, 2025 04:12
@wolfwood
Copy link
Contributor Author

wolfwood commented Apr 8, 2025

clippy failures seem to be unrelated to my changes?

@jtroo
Copy link
Owner

jtroo commented Apr 8, 2025

Yea don't worry about clippy; I'll fix that and also hopefully test the interception variant at some point soon.

used in the example configs to build automousekeys layer triggered by cursor movement, with config level timeout and early exit when typing resumes
@wolfwood wolfwood force-pushed the feature/automousekeys-simple branch from c6bbc4e to 0eda538 Compare April 8, 2025 17:36
@jtroo jtroo self-requested a review April 10, 2025 08:47
&mouse_to_intercept_excluded_hwids,
&mut is_dev_interceptable,
) {
if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
Copy link
Owner

@jtroo jtroo Apr 11, 2025

Choose a reason for hiding this comment

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

Curious what the thought is behind doing this in both branches? Since having a copy in the else means all mouse devices get involved in this check, when the user has not configured mouse interception, seems there is no need to filter it here; meaning there could be deduplication by moving this between lines 77,78.

rather than explicit toggling. see cfg_examples/automousekeys-*.kbd for more.

mvmt is a special key provided for this purpose which does not map to an output,
and cannot be supplied as an action, however any key may be used.
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
and cannot be supplied as an action, however any key may be used.
and cannot be supplied as an action; however, any key may be used.

[[mouse-movement-key]]
=== Linux or Windows-interception only: mouse-movement-key

tap a specified key when a mouse cursor movement is received
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
tap a specified key when a mouse cursor movement is received
Accepts a single key name.
When configured, whenever a mouse cursor movement is received,
the configured key name will be "tapped" by Kanata,
activating the key's action.

keys layer while mousing, which can be disabled by a timeout or typing on other keys,
rather than explicit toggling. see cfg_examples/automousekeys-*.kbd for more.

mvmt is a special key provided for this purpose which does not map to an output,
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
mvmt is a special key provided for this purpose which does not map to an output,
The `mvmt` key name is specially intended for this purpose.
It has no output key mapping


tap a specified key when a mouse cursor movement is received

This enables repoorting of every relative mouse movement, which corresponds to
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
This enables repoorting of every relative mouse movement, which corresponds to
This enables reporting of every relative mouse movement, which corresponds to

@@ -295,6 +295,23 @@ If you need help, please feel welcome to ask in the GitHub discussions.
;; The downside of this configuration is that the non-modifier key
;; does not remain held which is important to consider for your use cases.
override-release-on-activation yes

;; tap a specified key when a mouse cursor movement is received
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
;; tap a specified key when a mouse cursor movement is received
;; Tap the specified key when a mouse cursor movement is received.

Copy link
Owner

Choose a reason for hiding this comment

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

Please add this file and also the -only file to the tests to ensure they remain vaild. E.g. see:

new_from_file(&std::path::PathBuf::from("./cfg_samples/kanata.kbd")).unwrap();

@jtroo
Copy link
Owner

jtroo commented Apr 11, 2025

For interception, needed below to work. However, I don't like that we're increasing processing cost by changing the filter from excluding MOVE to including.

I will rescind my former opinion that this should be completely live-reloadable and state that, while the key should be live-reloadable, the enablement of the feature itself should not be. The code should include the !MOVE if the feature is not in use on startup, which I suspect it should be for most users. Seems Linux doesn't have the same issue since it's always processing all the events anyway; which I suppose is a different issue, but not something for this PR.

initial patch
diff --git a/src/kanata/windows/interception.rs b/src/kanata/windows/interception.rs
index 681ccdaf..fc22aa1c 100644
--- a/src/kanata/windows/interception.rs
+++ b/src/kanata/windows/interception.rs
@@ -29,7 +29,7 @@ impl Kanata {
         if mouse_to_intercept_hwids.is_some() || mouse_to_intercept_excluded_hwids.is_some() {
             intrcptn.set_filter(
                 ic::is_mouse,
-                ic::Filter::MouseFilter(ic::MouseState::all() & (!ic::MouseState::MOVE)),
+                ic::Filter::MouseFilter(ic::MouseState::all()),
             );
         }
         let mut is_dev_interceptable: HashMap<ic::Device, bool> = HashMap::default();
@@ -72,22 +72,14 @@ impl Kanata {
                             flags,
                             ..
                         } => {
+                            if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
+                                if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
+                                    tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
+                                }
+                            }
                             if mouse_to_intercept_hwids.is_some()
                                 || mouse_to_intercept_excluded_hwids.is_some()
                             {
-                                if is_device_interceptable(
-                                    dev,
-                                    &intrcptn,
-                                    &mouse_to_intercept_hwids,
-                                    &mouse_to_intercept_excluded_hwids,
-                                    &mut is_dev_interceptable,
-                                ) {
-                                    if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
-                                        if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
-                                            tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
-                                        }
-                                    }
-                                }
                                 log::trace!("checking mouse stroke {:?}", strokes[i]);
                                 if let Some(event) = mouse_state_to_event(
                                     dev,
@@ -104,11 +96,6 @@ impl Kanata {
                                     continue;
                                 }
                             } else {
-                                if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
-                                    if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
-                                        tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
-                                    }
-                                }
                                 intrcptn.send(dev, &strokes[i..i + 1]);
                                 continue;
                             }

The patch that applies the filters on startup is. Please do update the doc to describe this live-reload behaviour; on Interception only changing the key works on live-reload; not enabling/disabling the feature. Thinking through it more, may also be worth an Interception-only warning log regarding this live-reload while this defcfg item is defined.

Partial live-reload disable
diff --git a/src/kanata/windows/interception.rs b/src/kanata/windows/interception.rs
index 681ccdaf..e76bb74a 100644
--- a/src/kanata/windows/interception.rs
+++ b/src/kanata/windows/interception.rs
@@ -27,10 +27,14 @@ impl Kanata {
             kanata.lock().intercept_mouse_hwids_exclude.clone();
         let mouse_movement_key = kanata.lock().mouse_movement_key.clone();
         if mouse_to_intercept_hwids.is_some() || mouse_to_intercept_excluded_hwids.is_some() {
-            intrcptn.set_filter(
-                ic::is_mouse,
-                ic::Filter::MouseFilter(ic::MouseState::all() & (!ic::MouseState::MOVE)),
-            );
+            if mouse_movement_key.lock().is_some() {
+                intrcptn.set_filter(ic::is_mouse, ic::Filter::MouseFilter(ic::MouseState::all()));
+            } else {
+                intrcptn.set_filter(
+                    ic::is_mouse,
+                    ic::Filter::MouseFilter(ic::MouseState::all() & (!ic::MouseState::MOVE)),
+                );
+            }
         }
         let mut is_dev_interceptable: HashMap<ic::Device, bool> = HashMap::default();
         loop {
@@ -72,22 +76,14 @@ impl Kanata {
                             flags,
                             ..
                         } => {
+                            if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
+                                if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
+                                    tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
+                                }
+                            }
                             if mouse_to_intercept_hwids.is_some()
                                 || mouse_to_intercept_excluded_hwids.is_some()
                             {
-                                if is_device_interceptable(
-                                    dev,
-                                    &intrcptn,
-                                    &mouse_to_intercept_hwids,
-                                    &mouse_to_intercept_excluded_hwids,
-                                    &mut is_dev_interceptable,
-                                ) {
-                                    if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
-                                        if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
-                                            tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
-                                        }
-                                    }
-                                }
                                 log::trace!("checking mouse stroke {:?}", strokes[i]);
                                 if let Some(event) = mouse_state_to_event(
                                     dev,
@@ -104,11 +100,6 @@ impl Kanata {
                                     continue;
                                 }
                             } else {
-                                if let Some(ms_mvmt_key) = *mouse_movement_key.lock() {
-                                    if flags.contains(ic::MouseFlags::MOVE_RELATIVE) {
-                                        tx.try_send(KeyEvent::new(ms_mvmt_key, KeyValue::Tap))?;
-                                    }
-                                }
                                 intrcptn.send(dev, &strokes[i..i + 1]);
                                 continue;
                             }

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.

2 participants