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

修复不同刷新率下导致的滚动行为异常问题,尝试修复了异常闪退 #646

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions Mos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */
0B12EF1A203AD9C80058B169 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B12EF19203AD9C80058B169 /* Options.swift */; };
0B12EF1D203ADD230058B169 /* ScrollUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B12EF1C203ADD230058B169 /* ScrollUtils.swift */; };
0B12EF1F203ADFB10058B169 /* Interpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B12EF1E203ADFB10058B169 /* Interpolator.swift */; };
0B12EF5E203C7D650058B169 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B12EF5D203C7D650058B169 /* Logger.swift */; };
0B474329258E12A200A5F8A3 /* ScrollPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B474328258E12A200A5F8A3 /* ScrollPhase.swift */; };
0B4C37AF2575E0DD00D4A390 /* IntroductionWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B4C37AE2575E0DD00D4A390 /* IntroductionWindowController.swift */; };
Expand Down Expand Up @@ -55,6 +54,8 @@
23BB4D3121D775D4009B51E9 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23BB4D3021D775D4009B51E9 /* EventMonitor.swift */; };
23E3C96D21DCE8F10098B107 /* StatusItemPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E3C96C21DCE8F10098B107 /* StatusItemPopoverView.swift */; };
23E9673F23613D1100E652F6 /* PreferencesExceptionInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E9673E23613D1100E652F6 /* PreferencesExceptionInputViewController.swift */; };
B408F4922BDE87BF00C87412 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B408F4912BDE87BF00C87412 /* Extensions.swift */; };
B433B3B52BDF784B002D293C /* Tween.swift in Sources */ = {isa = PBXBuildFile; fileRef = B433B3B42BDF784B002D293C /* Tween.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -66,7 +67,6 @@
0B0D5B61206630EE0077C2AC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
0B12EF19203AD9C80058B169 /* Options.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = "<group>"; };
0B12EF1C203ADD230058B169 /* ScrollUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollUtils.swift; sourceTree = "<group>"; };
0B12EF1E203ADFB10058B169 /* Interpolator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interpolator.swift; sourceTree = "<group>"; };
0B12EF5D203C7D650058B169 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
0B474328258E12A200A5F8A3 /* ScrollPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollPhase.swift; sourceTree = "<group>"; };
0B4C37AE2575E0DD00D4A390 /* IntroductionWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroductionWindowController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -125,6 +125,8 @@
23BB4D3021D775D4009B51E9 /* EventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = "<group>"; };
23E3C96C21DCE8F10098B107 /* StatusItemPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemPopoverView.swift; sourceTree = "<group>"; };
23E9673E23613D1100E652F6 /* PreferencesExceptionInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesExceptionInputViewController.swift; sourceTree = "<group>"; };
B408F4912BDE87BF00C87412 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
B433B3B42BDF784B002D293C /* Tween.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tween.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -149,7 +151,6 @@
0B6D3B692041C348004CC064 /* ScrollEvent.swift */,
0B474328258E12A200A5F8A3 /* ScrollPhase.swift */,
0B99C5FB2577FC9200021578 /* ScrollPoster.swift */,
0B12EF1E203ADFB10058B169 /* Interpolator.swift */,
);
path = ScrollCore;
sourceTree = "<group>";
Expand Down Expand Up @@ -202,6 +203,7 @@
0B6D3B6420419EF5004CC064 /* Utils */ = {
isa = PBXGroup;
children = (
B433B3B42BDF784B002D293C /* Tween.swift */,
22003C6D1E8555F0001220BC /* Utils.swift */,
0B12EF5D203C7D650058B169 /* Logger.swift */,
0B56DE3F2568DFDB00B4D9AE /* Archieve.swift */,
Expand Down Expand Up @@ -262,6 +264,7 @@
22142DA91E25344E00E4BFBF /* Main.storyboard */,
23529E0B237087A400806D92 /* Assets.xcassets */,
22142DAC1E25344E00E4BFBF /* Info.plist */,
B408F4912BDE87BF00C87412 /* Extensions.swift */,
);
path = Mos;
sourceTree = "<group>";
Expand Down Expand Up @@ -412,6 +415,7 @@
buildActionMask = 2147483647;
files = (
23399B58237EDC17004935CA /* IntroductionViewController.swift in Sources */,
B433B3B52BDF784B002D293C /* Tween.swift in Sources */,
2225F8DD1E39C8D60020DD22 /* PreferencesAdvanceViewController.swift in Sources */,
2399BFD8222CDB7D008FA807 /* Constants.swift in Sources */,
23E3C96D21DCE8F10098B107 /* StatusItemPopoverView.swift in Sources */,
Expand All @@ -424,7 +428,6 @@
0B12EF5E203C7D650058B169 /* Logger.swift in Sources */,
0B6D3B702042BBCD004CC064 /* Localizable.swift in Sources */,
0BC2AD2F20F3BA090084416B /* PreferencesContributorsViewController.swift in Sources */,
0B12EF1F203ADFB10058B169 /* Interpolator.swift in Sources */,
2391FB322354D59500ECC8C7 /* StatusItemMainPanelView.swift in Sources */,
23BB4D2F21D754D3009B51E9 /* PopoverManager.swift in Sources */,
0B74C441205E386D00E3F942 /* Interceptor.swift in Sources */,
Expand All @@ -442,6 +445,7 @@
2278EE991E3DF770000AB25B /* PreferencesExceptionViewController.swift in Sources */,
0BD39F68204EFC6C000CCC42 /* StatusItemManager.swift in Sources */,
23E9673F23613D1100E652F6 /* PreferencesExceptionInputViewController.swift in Sources */,
B408F4922BDE87BF00C87412 /* Extensions.swift in Sources */,
232E50A7236B372100265C58 /* PreferencesAdvanceWithApplicationViewController.swift in Sources */,
22A32B331E334B2E00A5CF38 /* PreferencesAboutViewController.swift in Sources */,
0BE4C93B20617C6E0001EA0E /* ScrollFilter.swift in Sources */,
Expand Down Expand Up @@ -630,7 +634,7 @@
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 2H959AEV62;
DEVELOPMENT_TEAM = S5DJZV2NWX;
ENABLE_HARDENED_RUNTIME = NO;
INFOPLIST_FILE = Mos/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down Expand Up @@ -665,7 +669,7 @@
CURRENT_PROJECT_VERSION = 2022.10.08;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 2H959AEV62;
DEVELOPMENT_TEAM = S5DJZV2NWX;
ENABLE_HARDENED_RUNTIME = NO;
GCC_OPTIMIZATION_LEVEL = fast;
INFOPLIST_FILE = Mos/Info.plist;
Expand Down
19 changes: 19 additions & 0 deletions Mos/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Extensions.swift
// Mos
//
// Created by Xiaojun Zhou on 2024/4/28.
// Copyright © 2024 Caldis. All rights reserved.
//

extension Comparable {
func clamped(to limits: ClosedRange<Self>) -> Self {
return min(max(self, limits.lowerBound), limits.upperBound)
}
}

extension Strideable where Stride: SignedInteger {
func clamped(to limits: CountableClosedRange<Self>) -> Self {
return min(max(self, limits.lowerBound), limits.upperBound)
}
}
31 changes: 0 additions & 31 deletions Mos/ScrollCore/Interpolator.swift

This file was deleted.

6 changes: 3 additions & 3 deletions Mos/ScrollCore/ScrollCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class ScrollCore {
x: scrollEvent.X.usableValue,
speed: speed,
amplification: ScrollCore.shared.dashAmplification
).tryStart()
)
}
// 返回事件对象
if returnOriginalEvent {
Expand Down Expand Up @@ -251,8 +251,8 @@ class ScrollCore {
scrollEventInterceptor = Interceptor(
event: scrollEventMask,
handleBy: scrollEventCallBack,
listenOn: .cgAnnotatedSessionEventTap,
placeAt: .tailAppendEventTap,
listenOn: .cghidEventTap,
placeAt: .headInsertEventTap,
for: .defaultTap
)
hotkeyEventInterceptor = Interceptor(
Expand Down
113 changes: 72 additions & 41 deletions Mos/ScrollCore/ScrollPoster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,86 @@

import Cocoa


class ScrollPoster {

// 单例
static let shared = ScrollPoster()
init() { NSLog("Module initialized: ScrollPoster") }

// 插值器
private let filter = ScrollFilter()
// 发送器
private var poster: CVDisplayLink?
// 滚动数据
private var startTime = 0.0 // 起始滚动时间
private var start = (y: 0.0, x: 0.0) // 起始滚动位置
private var current = (y: 0.0, x: 0.0) // 当前滚动距离
private var delta = (y: 0.0, x: 0.0) // 滚动方向记录
private var buffer = (y: 0.0, x: 0.0) // 滚动缓冲距离
private var duration = 300.0 // 最终计算后的过渡时长
// 滚动配置
private var shifting = false
private var duration = Options.shared.scrollAdvanced.durationTransition
// 外部依赖
var ref: (event: CGEvent?, proxy: CGEventTapProxy?) = (event: nil, proxy: nil)

// 是否处于平滑滚动中
var isScrolling: Bool {
if let validPoster = poster {
return CVDisplayLinkIsRunning(validPoster)
}
return false
}
}

// MARK: - 滚动数据更新控制
extension ScrollPoster {
func update(event: CGEvent, proxy: CGEventTapProxy, duration: Double, y: Double, x: Double, speed: Double, amplification: Double = 1) -> Self {
func update(event: CGEvent, proxy: CGEventTapProxy, duration: Double, y: Double, x: Double, speed: Double, amplification: Double = 1) {
// 停止循环
if isScrolling {
CVDisplayLinkStop(poster!)
}

// 更新依赖数据
ref.event = event
ref.proxy = proxy
// 更新滚动配置
self.duration = duration

let newDeltaY = y * speed * amplification
let newDeltaX = x * speed * amplification

// 根据待滚动距离计算所需过渡时长,以 300ms 为最小持续时长,到达 10000px 时持续时长达到最大,时长非线性递增
// x,y 所需时长谁大取谁
var dur = 0.0

// 更新滚动数据
if y*delta.y > 0 {
buffer.y += y * speed * amplification
let remaining = abs(buffer.y - current.y)
start.y = current.y
buffer.y += newDeltaY
dur = max(dur, 300 + Tween.easeOutExpo(x: (abs(newDeltaY) + remaining).clamped(to: 0 ... 10000) / 10000) * duration)
} else {
buffer.y = y * speed * amplification
start.y = 0.0
current.y = 0.0
buffer.y = newDeltaY
dur = max(dur, 300 + Tween.easeOutExpo(x: abs(newDeltaY).clamped(to: 0 ... 10000) / 10000) * duration)
}
if x*delta.x > 0 {
buffer.x += x * speed * amplification
} else {
buffer.x = x * speed * amplification
let remaining = abs(buffer.x - current.x)
start.x = current.x
current.x = 0.0
buffer.x += newDeltaX
dur = max(dur, 300 + Tween.easeOutExpo(x: (abs(newDeltaX) + remaining).clamped(to: 0 ... 10000) / 10000) * duration)
} else {
start.x = 0.0
buffer.x = newDeltaX
dur = max(dur, 300 + Tween.easeOutExpo(x: abs(newDeltaX).clamped(to: 0 ... 10000) / 10000) * duration)
}

delta = (y: y, x: x)
return self
startTime = NSDate().timeIntervalSince1970
self.duration = dur

CVDisplayLinkStart(poster!)
}
func updateShifting(enable: Bool) {
shifting = enable
Expand All @@ -71,7 +107,9 @@ extension ScrollPoster {
}
}
func brake() {
ScrollPoster.shared.buffer = ScrollPoster.shared.current
if isScrolling {
stop()
}
}
func reset() {
// 重置数值
Expand All @@ -97,14 +135,6 @@ extension ScrollPoster {
}, nil)
}
}
// 启动事件发送器
func tryStart() {
if let validPoster = poster {
if !CVDisplayLinkIsRunning(validPoster) {
CVDisplayLinkStart(validPoster)
}
}
}
// 停止事件发送器
func stop(_ phase: Phase = Phase.PauseManual) {
// 停止循环
Expand All @@ -129,27 +159,28 @@ extension ScrollPoster {
private extension ScrollPoster {
// 处理滚动事件
func processing() {
// 计算插值
let frame = (
y: Interpolator.lerp(src: current.y, dest: buffer.y, trans: duration),
x: Interpolator.lerp(src: current.x, dest: buffer.x, trans: duration)
)
// 更新滚动位置
current = (
y: current.y + frame.y,
x: current.x + frame.x
)
// 平滑滚动结果
let filledValue = filter.fill(with: frame)
// 变换滚动结果
let shiftedValue = shift(with: filledValue)
// 发送滚动结果
post(ref, shiftedValue)
// 如果临近目标距离小于精确度门限则暂停滚动
if (
frame.y.magnitude <= Options.shared.scrollAdvanced.precision &&
frame.x.magnitude <= Options.shared.scrollAdvanced.precision
) {
let now = NSDate().timeIntervalSince1970
let diffMs = (now - startTime) * 1000

if (diffMs <= duration) {
let perc = Tween.easeOutExpo(x: diffMs / duration)
let oldCurrent = current
// 计算插值
current = (
y: (buffer.y - start.y) * perc + start.y,
x: (buffer.x - start.x) * perc + start.x
)
let nextDelta = (
y: current.y - oldCurrent.y,
x: current.x - oldCurrent.x
)
// 平滑滚动结果
let filledValue = filter.fill(with: nextDelta)
// 变换滚动结果
let shiftedValue = shift(with: filledValue)
// 发送滚动结果
post(ref, shiftedValue)
} else {
stop(Phase.PauseAuto)
}
}
Expand Down
11 changes: 3 additions & 8 deletions Mos/Utils/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,24 +118,19 @@ class OPTIONS_SCROLL_ADVANCED_DEFAULT: Codable {
var speed = 3.00 {
didSet {Options.shared.saveOptions()}
}
var duration = 3.90 {
var duration = 2.00 {
willSet {self.durationTransition = OPTIONS_SCROLL_ADVANCED_DEFAULT.generateDurationTransition(with: newValue)}
didSet {Options.shared.saveOptions()}
}
var durationTransition = 0.1340 {
var durationTransition = 500.0 {
didSet {}
}
var precision = 1.00 {
didSet {Options.shared.saveOptions()}
}
// 工具
static func generateDurationTransition(with duration: Double) -> Double {
// 上界, 此处需要与界面的 Slider 上界保持同步, 并添加 0.2 的偏移令结果不为 0
let upperLimit = 5.0 + 0.2
// 生成数据 (https://www.wolframalpha.com/input/?i=1+-+(sqrt+x%2F5)+%3D+y)
let val = 1-(duration/upperLimit).squareRoot()
// 三位小数
return Double(round(1000 * val)/1000)
return Double((duration - 1) * 500)
}
}
extension OPTIONS_SCROLL_ADVANCED_DEFAULT: Equatable {
Expand Down
15 changes: 15 additions & 0 deletions Mos/Utils/Tween.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Tween.swift
// Mos
//
// Created by Xiaojun Zhou on 2024/4/29.
// Copyright © 2024 Caldis. All rights reserved.
//

import Cocoa

class Tween: NSObject {
class func easeOutExpo(x: Double) -> Double {
return x == 1 ? 1 : 1 - pow(2, -10 * x)
}
}