diff --git a/VoidLink/Input/SafeTimer.swift b/VoidLink/Input/SafeTimer.swift index 3a9436db..80f69767 100644 --- a/VoidLink/Input/SafeTimer.swift +++ b/VoidLink/Input/SafeTimer.swift @@ -9,8 +9,8 @@ import Foundation import QuartzCore -/// SafeTimer: 高精度、可暂停/重启、可彻底清理 -final class SafeTimer { +@objc(SafeTimer) +final class SafeTimer: NSObject { private var timer: DispatchSourceTimer? private let timerQueue: DispatchQueue // 私有串行队列 private let userHandler: () -> Void @@ -20,7 +20,7 @@ final class SafeTimer { private var shouldRunHandler: Bool = false // 控制逻辑上的暂停/恢复 private var isCleaned: Bool = false // 是否已经 clean - init(interval: TimeInterval = 1.0, + @objc init(interval: TimeInterval = 1.0, delay: TimeInterval = 0, queueLabel: String = "com.example.safetimer", handler: @escaping () -> Void) { @@ -29,6 +29,8 @@ final class SafeTimer { self.delay = delay self.userHandler = handler self.timerQueue = DispatchQueue(label: queueLabel) + + super.init() let t = DispatchSource.makeTimerSource(queue: timerQueue) t.schedule(deadline: .now() + delay, repeating: interval) @@ -48,7 +50,7 @@ final class SafeTimer { } /// 开始逻辑上的计时 - func start() { + @objc func start() { timerQueue.async { [weak self] in guard let self = self, !self.isCleaned else { return } self.shouldRunHandler = true @@ -56,7 +58,7 @@ final class SafeTimer { } /// 暂停逻辑上的计时 - func pause() { + @objc func pause() { timerQueue.async { [weak self] in guard let self = self, !self.isCleaned else { return } self.shouldRunHandler = false @@ -64,7 +66,7 @@ final class SafeTimer { } /// 重置下一次触发时间,并开始执行 handler - func restart() { + @objc func restart() { timerQueue.async { [weak self] in guard let self = self, let t = self.timer, !self.isCleaned else { return } t.schedule(deadline: .now() + self.delay, repeating: self.interval) @@ -73,7 +75,7 @@ final class SafeTimer { } /// 彻底清理 timer,返回后保证 handler 不再执行 - func clean() { + @objc func clean() { if isCleaned { return } // 同步在 timerQueue 执行,保证已排队事件全部处理完(或跳过) timerQueue.sync { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index c994d093..24ecfa8f 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -80,7 +80,7 @@ dispatch_block_t _delayedRemoveExtScreen; VideoDecoderRenderer *_videoRenderer; BOOL _isRestoringFromPiP; - CADisplayLink *_displayLink; + SafeTimer* safeTimer; #if !TARGET_OS_TV CustomEdgeSlideGestureRecognizer *_slideToSettingsRecognizer; @@ -439,11 +439,12 @@ [self.view bringSubviewToFront:self.imguiView.mtkView]; } - [self stopDisplayLink]; + // [self pauseTimer]; if(_settings.sendDummyEvent){ - [self setupDisplayLink]; - [self startDisplayLink]; + if(!safeTimer) [self setupTimer]; + if(!viewJustLoaded) [safeTimer start]; } + else [safeTimer pause]; _motionHandler = [MotionHandler sharedWithProfile: nil]; _motionHandler.gyroBiasX = _settings.gyroBiasX.doubleValue; @@ -461,12 +462,11 @@ } - (void)viewWillAppear:(BOOL)animated { - if(_settings.sendDummyEvent) [self startDisplayLink]; + // if(_settings.sendDummyEvent) [self startTimer]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - [self stopDisplayLink]; } - (void)viewDidAppear:(BOOL)animated @@ -541,7 +541,8 @@ selector:@selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil]; - + + [safeTimer start]; #endif } @@ -841,6 +842,9 @@ for(UIView* view in self.view.subviews){ [view removeFromSuperview]; } + + [safeTimer pause]; + [safeTimer clean]; } } @@ -1698,36 +1702,17 @@ } - (void)dealloc { - [self stopDisplayLink]; NSLog(@"dealloc StreamFrameViewController %f", CACurrentMediaTime()); } -- (void)setupDisplayLink { - if (_displayLink != nil) return; +- (void)setupTimer { TemporarySettings* tempSettings = [[[DataManager alloc] init] getSettings]; //StreamFrameViewController retrieve the settings here. - if (@available(iOS 15.0, tvOS 15.0, *)) { - [_displayLink setPreferredFrameRateRange:CAFrameRateRangeMake(tempSettings.framerate.intValue,tempSettings.framerate.intValue, tempSettings.framerate.intValue)]; - } - else { - _displayLink.preferredFramesPerSecond = tempSettings.framerate.intValue; - } - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTick:)]; - [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; -} - -- (void)startDisplayLink { - _displayLink.paused = NO; -} - -- (void)stopDisplayLink { - [_displayLink invalidate]; - _displayLink = nil; -} - -- (void)displayLinkTick:(CADisplayLink *)link { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - LiSendKeyboardEvent(0xFF, KEY_ACTION_UP, 0); - }); + safeTimer = [[SafeTimer alloc] initWithInterval:1.0/tempSettings.framerate.intValue delay:0 queueLabel:@"streamview.timer" handler:^{ + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ + LiSendKeyboardEvent(0xFF, KEY_ACTION_UP, 0); + // LiSendTouchEvent(LI_TOUCH_EVENT_UP, 200, 1, 1, 0, 0, 0, 0); + }); + }]; } @end