Skip to content

[Bug]: Software scroll inversion (#847) is lost when smoothing or sensitivity intercepts the wheel #1737

Description

@isleofgreg

#847 reverses the wheel in the capture layer for mice whose firmware has no 0x2121 invert bit. On macOS and Linux it only rewrites the event when the agent's callback returns EventDisposition::PassThrough. If the callback suppresses the event, the inversion is skipped, and the agent's own scroll output never applies it either. So whenever the agent intercepts the wheel, the setting does nothing. I found this by reading the code on master 0f69d75b, not on hardware.

When the callback suppresses. In crates/openlogi-agent-core/src/runtime/hook.rs, the MouseEvent::Scroll arm calls scroll.try_hook_scroll(delta) for any source where scroll_source_may_intercept is true, and maps true to Suppress. try_hook_scroll (runtime/scroll/worker.rs) accepts the impulse when:

  • smooth scrolling is on (ScrollOutputMode::Smooth), or
  • vertical sensitivity is anything other than 1× (ScrollOutputMode::Direct).

The scroll worker then emits from the original delta, which hasn't been negated.

Why every software-inverted mouse is exposed. wheel_scroll_to_invert (openlogi-hook/src/macos.rs) requires a non-trackpad source that passes source_is_remappable. scroll_source_may_intercept checks the same thing through button_source_may_remap. So any wheel event eligible for software inversion can also be taken by the smoother.

Steps (expected from the code):

  1. Use a mouse without 0x2121, so the GUI shows the software-inversion description.
  2. Turn on "invert scroll" for it.
  3. Turn on smooth scrolling, or set vertical sensitivity to anything but 1×.
  4. Scroll. The direction is not reversed. Turn smoothing off and set sensitivity back to 1×, and it reverses again.

On macOS, run_tap_callback decides invert before it calls the callback, but only acts on it in the PassThrough arm. On Linux, invert_wheel also runs only in the PassThrough arm of the evdev loop.

Fix idea: negate delta.y in the HookEvent the callback receives whenever invert is true. Then a suppressed impulse gets queued already reversed, and a passed-through one still has its native fields negated in place, so the two paths agree. The alternative is for the agent's Scroll arm to consult openlogi_hook::scroll_inversion() before try_hook_scroll. That works too, but leaves two places that each have to remember the flag. Native 0x2121 devices aren't affected, because the firmware inverts before anything reaches the hook.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions