pilotmoon / Scroll-Reverser

Per-device scrolling prefs on macOS.

Home Page:https://pilotmoon.com/scrollreverser/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scroll Reverse bounce bug in macOS 12.2

xszuflax opened this issue · comments

Hello,

after an update to macOS Monterey 12.2 Scroll Reverse is bouncing when scrolling with the Magic Mouse 2. I guess the video will show it best. I use reverse vertical scroll for a mouse. The touchpad is OK.

Best,
Marcin

ScrollReverse_bounce_bug.mp4

Confirmed, same for me.

commented

Can confirm

Hello,

it looks like a Safari specific issue. I do not see this problem in Chrome and Electron Apps. Finder is OK, too.

Best,
MT

After latest updates for Big Sur, the issue is now also present on macOS Big Sur.

macOS 11.6.2 (20G314)
Safari Version 15.3 (16612.4.9.1.7, 16612)

I can confirm, it is Safari specific.

It happens with Trackpad here.

same issue, but with Magic Mouse 1

it happened before
#58

Hello,

I believe it is a different thing. It does not affect the trackpad only a Magic Mouse 2 not even an external USB mouse with a wheel. It only happens in Safari 15.3 (17612.4.9.1.5). Scrolling in other apps is OK.

If I was to blame something I would point fingers at scroll APIs for ProMotion. I don't have a mac that supports ProMotion thus I don't know other apps than Safari which supports 120 Hz screen refresh rates but I would be interesting to check if one could observe similar behaviour.

I guess ProMotion isn't a point of problem. I had such issues on just a regular Mac and an hakintosh

I do have this problem on my M1 MBP 13". What I'm trying to say is that maybe when Apple tried to implement better ProMotion they screwed the API for non ProMotion machines and now ScrollReverse is messed up. I do not know any other ProMotion ready app to test my theory. The fact is that this is broken only in Safari and Apple was working in 12.2 on better ProMotion support.

I'm experiencing the same bug on my ProMotion M1 Max MBP. Only happens in Safari. Versions:

  • Safari 15.3 (17612.4.9.1.5)
  • macOS 12.2 (21D49)

I'm experiencing the same bug on my MacBook Pro (16-inch, 2019). Happens in Safari and DingTalk.

Versions:

  • Safari 15.3 (17612.4.9.1.5)

  • macOS 12.2 (21D49)

  • DingTalk 6.3.25 (d456fd)

commented

Can confirm

Did you encounter any difficulties with bug fixes? Or is there any other alternative solution?

Did you encounter any difficulties with bug fixes? Or is there any other alternative solution?

Yes, great difficulty. I can't see any way to "fix" this in Scroll Reverser. As far as I can tell Scroll Reverser is continuing to do the correct thing, and Safari is interpreting the result weirdly, in a way I just don't understand. It's extremely frustrating. I'll write up in more detail if I can put it together in a way that makes sense.

Confirmed as well. Bug occurs with trackpad and mouse when reverse scroll is enabled.

Safari Version 15.3 (17612.4.9.1.5)
Mac OS Monterey 12.2
M1 Max Macbook pro 16" with ProMotion

commented

Did you encounter any difficulties with bug fixes? Or is there any other alternative solution?

Yes, great difficulty. I can't see any way to "fix" this in Scroll Reverser. As far as I can tell Scroll Reverser is continuing to do the correct thing, and Safari is interpreting the result weirdly, in a way I just don't understand. It's extremely frustrating. I'll write up in more detail if I can put it together in a way that makes sense.

I wish you the best of luck.
I had to turn off the functionality of Scroll Reverser.
In adapting to mouse rollback. :~(

Another clue:Mac Quicklook is also affected..such as PDF preview with space bar.

It's such a weird issue. Everything works as expected when you scroll and don't let the macOS inertia to kick in. Once it begins to move by inertia the bounce effect appears.

To test it try to disable inertia in accessibility and try by yourself:

System Preferences > Accessibility > Pointer Control > Mouse (or Trackpad, whatever you use) Options > Scrolling without inertia.

Hope it helps the developer debugging the issue 😄

EDIT: As mentioned by others, I could only observe the issue while using Safari.

12.2.1 does not fix the issue :-/

12.2.1 does not fix the issue :-/

Yes. It is the same for me unfortunately.

Some more info in case it is interesting for Nick or anybody else.

I tested Scroll Reverse against latest:

  • Safari Technology Review: Release 140 (Safari 15.4, WebKit 16614.1.1.4)
  • Safari Beta: Version 15.4 (16613.1.14.11.2, 16613)

Both have the same bouncing issue so whatever change they introduced in Safari/WebKit it's still there and is not Safari 15.3 specific.

I've just added the following update to the main README and website:

Summary: Bad news. Scroll Reverser isn't working in Safari and there is no fix.

On macOS Monterey 12.2, Scroll Reverser is not working in Safari when using smooth scrolling devices — that is, trackpads and the Magic Mouse. The effect is a kind of "snap back" where the scrolling direction flips, as if it fighting you. The problem does not occur with scroll wheel devices.

I have not been able to find any way to modify Scroll Reverser to overcome this problem. (It seems Safari is ignoring the direction of the scrolling event input during the momentum phase of the scroll, and instead it is deriving it from some other source. That means whatever Scroll Reverser does, it can't reverse the momentum part of the scroll, which is giving the "snap back" effect. Speculatively, this is something to do with recent work done to to improve Safari scrolling on ProMotion displays.)

For now we wait and see if the changes in 12.2 were an unintentional bug, or if this is the way it is now. If anyone has any technical info on all this, or solutions, please let me know. I do not plan do do any more work on Scroll Reverser unless this situation is resolved.

A note on alternative apps: MOS and UnnaturalScrollWheels are excellent alternatives to Scroll Reverser that reverse wheel mouse scrolling independently of the trackpad. However, neither of them can distinguish the Magic Mouse from the trackpad — that has always been Scroll Reverser's speciality. It's specifically trackpad/Magic Mouse reversing that is now not working.

commented

This may just be adding to the pile, but FWIW even with "scrolling with inertia" enabled, scrolling slowly is a way to avoid the same 'rubber-banding' that is otherwise experienced. I also tried using UnnaturalScrollWheels, but it appears to be having similar issues, at least for me.... Here's hoping this is just a temporary Safari glitch that will be fixed in the next update! Thanks for looking into this!

This has been happening to me for months

Update: The issue is not present on the start page for safari. Scroll reverser works as normal on this page.
It seems like the issue only presents itself once you go to any other web page.

The safari start page also seems to ignore whichever setting you have for scrolling with inertia. Turning it off will have no effect on the start page and inertia will still be present.

Hey! I'm the author of LinearMouse.

I'd like to share my approach to resolving this problem. That is, instead of modifying the event data in the event tap callback, subscribe to the device events and update the system-level scroll direction when the active device changes.

By using this approach, you can even configure different settings for different devices.

Hey! I'm the author of LinearMouse.

hi, it didn't work for me, https://www.youtube.com/watch?v=GC1Jq6kkYWw

Hey! I'm the author of LinearMouse.

hi, it didn't work for me, https://www.youtube.com/watch?v=GC1Jq6kkYWw

Hi, @svnty!

Thank you very much for your video!

You may want to disable ‘Linear scrolling’ for Magic Mouse in the LinearMouse Preference.

Linear scrolling may have a similar issue that has yet to be resolved.

Hey! I'm the author of LinearMouse.

I'd like to share my approach to resolving this problem. That is, instead of modifying the event data in the event tap callback, subscribe to the device events and update the system-level scroll direction when the active device changes.

By using this approach, you can even configure different settings for different devices.

Yep, for some period of time, I'm stick with yours product. Works almost perfect for now.

Hey! I'm the author of LinearMouse.
I'd like to share my approach to resolving this problem. That is, instead of modifying the event data in the event tap callback, subscribe to the device events and update the system-level scroll direction when the active device changes.
By using this approach, you can even configure different settings for different devices.

Yep, for some period of time, I'm stick with yours product. Works almost perfect for now.

Unfortunately I cannot use LinearMouse for my workflow. I need to reverse the Trackpad, not a mouse. And I need to do it with Scroll-Reverser and not via the MacOS-Trackpad setting, because I want that the gesture to swipe to another mission control desktop stays "natural" (not reversed) while the scrolling is reversed (legacy style scrolling).

I've found some magic bytes in the data of CGEvent. There is a signed int16 representing the delta (?). It seems that this issue could be resolved by simply modifying that number.

Here's a PoC written in Swift. You may save it as main.swift and run swift main.swift. Tested on macOS 12.2.1. Not sure if it works on earlier macOS versions.

#!/usr/bin/swift
import Foundation

let offset = 170

let eventTapCallback: CGEventTapCallBack = { (_, _, event, _) in
    let delta = event.getIntegerValueField(.scrollWheelEventDeltaAxis1)
    let fixedPtDelta = event.getDoubleValueField(.scrollWheelEventFixedPtDeltaAxis1)
    let pointDelta = event.getDoubleValueField(.scrollWheelEventPointDeltaAxis1)
    event.setIntegerValueField(.scrollWheelEventDeltaAxis1, value: -delta)
    event.setDoubleValueField(.scrollWheelEventFixedPtDeltaAxis1, value: -fixedPtDelta)
    event.setDoubleValueField(.scrollWheelEventPointDeltaAxis1, value: -pointDelta)
    var data = event.__data(allocator: kCFAllocatorDefault)! as Data
    let momentumPhase = event.getIntegerValueField(.scrollWheelEventMomentumPhase)
    if momentumPhase == CGMomentumScrollPhase.begin.rawValue {
        data.withUnsafeMutableBytes {
            let value = $0.load(fromByteOffset: offset, as: Int16.self)
            $0.storeBytes(of: -value, toByteOffset: offset, as: Int16.self)
        }
    }
    let event = CGEvent(withDataAllocator: kCFAllocatorDefault, data: data as CFData)!
    event.post(tap: .cgSessionEventTap)
    return nil
}

let eventsOfInterest: CGEventMask = 1 << CGEventType.scrollWheel.rawValue
let eventTap = CGEvent.tapCreate(
    tap: .cghidEventTap,
    place: .tailAppendEventTap,
    options: .defaultTap,
    eventsOfInterest: eventsOfInterest,
    callback: eventTapCallback,
    userInfo: nil
)
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes)
CFRunLoopRun()

I've found some magic bytes in the data of CGEvent. There is a signed int16 representing the delta (?). It seems that this issue could be resolved by simply modifying that number.

That's a huge improvement but it's still not a natural scroll, it will work for now

Not only Safari, but Typora has the same problem.

Another clue:Mac Quicklook is also affected..such as PDF preview with space bar.

It seems that I don't see this problem.

Not only Safari, but Typora has the same problem.

Do you happen to know if Typora uses Webkit as part of their codebase?

Did a bit of research myself. Looks like Typora does use Webkit for the macOS version. Check the Basic Rules chapter:
https://theme.typora.io/doc/Write-Custom-Theme/

The document is quite old so I hope that is still valid. If that's the case then indeed seems like a Webkit related issue.

I looked at the WebKit source code and finally knew how WebKit recognizes the scroll direction.

It seems if we convert CGEvent to IOHIDEvent and fix its scrollY field, everything will work fine.

Tested on trackpad since I don't have a Magic Mouse.

import CoreGraphics
import Foundation

@objc protocol IOHIDEvent: NSObjectProtocol {}

let cgHandle = dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", RTLD_NOW)
typealias CGEventCopyIOHIDEventType = @convention(c) (_ cgEvent: CGEvent) -> IOHIDEvent
let CGEventCopyIOHIDEvent = unsafeBitCast(dlsym(cgHandle, "CGEventCopyIOHIDEvent"), to: CGEventCopyIOHIDEventType.self)

let ioKitHandle = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW)
typealias IOHIDEventGetFloatValueType = @convention(c) (_ event: IOHIDEvent, UInt32) -> Double
let IOHIDEventGetFloatValue = unsafeBitCast(dlsym(ioKitHandle, "IOHIDEventGetFloatValue"), to: IOHIDEventGetFloatValueType.self)
typealias IOHIDEventSetFloatValueType = @convention(c) (_ event: IOHIDEvent, UInt32, Double) -> Void
let IOHIDEventSetFloatValue = unsafeBitCast(dlsym(ioKitHandle, "IOHIDEventSetFloatValue"), to: IOHIDEventSetFloatValueType.self)

let kIOHIDEventFieldScrollY: UInt32 = (6 << 16) | 1

let eventTapCallback: CGEventTapCallBack = { _, _, event, _ in
    // Reverse scrolling
    let delta = event.getIntegerValueField(.scrollWheelEventDeltaAxis1)
    let fixedPtDelta = event.getDoubleValueField(.scrollWheelEventFixedPtDeltaAxis1)
    let pointDelta = event.getDoubleValueField(.scrollWheelEventPointDeltaAxis1)
    event.setIntegerValueField(.scrollWheelEventDeltaAxis1, value: -delta)
    event.setDoubleValueField(.scrollWheelEventFixedPtDeltaAxis1, value: -fixedPtDelta)
    event.setDoubleValueField(.scrollWheelEventPointDeltaAxis1, value: -pointDelta)

    // Fix IOHIDEventFieldScrollY
    let iohidEvent = CGEventCopyIOHIDEvent(event)
    let scrollY = IOHIDEventGetFloatValue(iohidEvent, kIOHIDEventFieldScrollY)
    IOHIDEventSetFloatValue(iohidEvent, kIOHIDEventFieldScrollY, -scrollY)

    return Unmanaged.passUnretained(event)
}

let eventsOfInterest: CGEventMask = 1 << CGEventType.scrollWheel.rawValue
let eventTap = CGEvent.tapCreate(
    tap: .cghidEventTap,
    place: .tailAppendEventTap,
    options: .defaultTap,
    eventsOfInterest: eventsOfInterest,
    callback: eventTapCallback,
    userInfo: nil
)
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes)
CFRunLoopRun()

I've submitted a pull request. It would be great if someone could test it with a Magic Mouse.

I've submitted a pull request. It would be great if someone could test it with a Magic Mouse.

it would be great to get compiled build with that fix

it would be great to get compiled build with that fix

@invariant Would you like to build a pre-release for #141?

I have published the new build with @lujjjh 's fix, as Build 10505.
It is available now via in-app update with the "Include beta updates" check box toggled on.
It seems to work really well for me, but I'll leave this issue open until we gather more confirmations whether it is working well.
Edit: I've tested with Magic Mouse and Magic Trackpad.

commented

Just updated to the beta and it's working perfectly for me now! Nice job to you both!

(Running macOS 12.2.1, Safari Version 15.3)

I have published the new build with @lujjjh 's fix, as Build 10505.
It is available now via in-app update with the "Include beta updates" check box toggled on.
It seems to work really well for me, but I'll leave this issue open until we gather more confirmations whether it is working well.

Just updated the beta version! Works perfectly and solves this problem finally! Thanks a lot!!

Using external Trackpad or internal Trackpad of Macbook Pro Retina 2015. Safari 15.3 on Big Sur.
The Build fixes the issue for me. Well done!🥳🥂Thank you!

Cool! I already checked the new beta and everything is working again with Safari beta 15.4 and Big Sur. Tested with a Magic Mouse.
Thanks a lot folks 👍🏼

Newest beta update fixed the issue for me as well! No issues across the board.

Magic Mouse
Scroll Reverser Beta 10505
Safari Version 15.3 (17612.4.9.1.8)
Mac OS 12.2.1 (21D62)
M1 Max with ProMotion

Newest Beta update also fixed the issue for me on the trackpad for my 16" M1 Max on macOS 12.2.1

I'm experiencing the same bug on my ProMotion M1 Max MBP. Only happens in Safari. Versions:

  • Safari 15.3 (17612.4.9.1.5)
  • macOS 12.2 (21D49)

The new beta release fixed everything for me. Thanks so much to those who worked to get to the bottom of this! 🙌

commented

Fantastic, the beta fixed it! Thanks to everybody who contributed here 👍

Thanks for the feedback, everyone. Since no problems were reported and it seem to work for everyone, I have now pushed out the update as v1.8.2 and I will close this issue.