diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 643e57d32..fffea8e3a 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -14,7 +14,6 @@ class MachineDocument: NSDocument, NSWindowDelegate, CSMachineDelegate, -// CSOpenGLViewDelegate, CSScanTargetViewResponderDelegate, CSAudioQueueDelegate, CSROMReciverViewDelegate @@ -100,8 +99,6 @@ class MachineDocument: actionLock.lock() drawLock.lock() machine = nil -// openGLView.delegate = nil -// openGLView.invalidate() scanTargetView.invalidate() actionLock.unlock() drawLock.unlock() @@ -253,18 +250,6 @@ class MachineDocument: final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) { } - /// Responds to the CSOpenGLViewDelegate redraw message by requesting a machine update if this is a timed - /// request, and ordering a redraw regardless of the motivation. -// final func openGLViewRedraw(_ view: CSOpenGLView, event redrawEvent: CSOpenGLViewRedrawEvent) { -// if drawLock.try() { -// if redrawEvent == .timer { -// machine.updateView(forPixelSize: view.backingSize) -// } -// machine.drawView(forPixelSize: view.backingSize) -// drawLock.unlock() -// } -// } - // MARK: - Pasteboard Forwarding. /// Forwards any text currently on the pasteboard into the active machine. @@ -278,7 +263,7 @@ class MachineDocument: // MARK: - Runtime Media Insertion. /// Delegate message to receive drag and drop files. - final func openGLView(_ view: CSScanTargetView, didReceiveFileAt URL: URL) { + final func scanTargetView(_ view: CSScanTargetView, didReceiveFileAt URL: URL) { let mediaSet = CSMediaSet(fileAt: URL) if let mediaSet = mediaSet { mediaSet.apply(to: self.machine) @@ -618,11 +603,11 @@ class MachineDocument: // MARK: - Window Title Updates. private var unadornedWindowTitle = "" - internal func openGLViewDidCaptureMouse(_ view: CSScanTargetView) { + internal func scanTargetViewDidCaptureMouse(_ view: CSScanTargetView) { self.windowControllers[0].window?.title = self.unadornedWindowTitle + " (press ⌘+control to release mouse)" } - internal func openGLViewDidReleaseMouse(_ view: CSScanTargetView) { + internal func scanTargetViewDidReleaseMouse(_ view: CSScanTargetView) { self.windowControllers[0].window?.title = self.unadornedWindowTitle } @@ -748,7 +733,7 @@ class MachineDocument: } fileprivate var animationFader: ViewFader? = nil - internal func openGLViewDidShowOSMouseCursor(_ view: CSScanTargetView) { + internal func scanTargetViewDidShowOSMouseCursor(_ view: CSScanTargetView) { // The OS mouse cursor became visible, so show the volume controls. animationFader = nil volumeView.layer?.removeAllAnimations() @@ -756,7 +741,7 @@ class MachineDocument: volumeView.layer?.opacity = 1.0 } - internal func openGLViewWillHideOSMouseCursor(_ view: CSScanTargetView) { + internal func scanTargetViewWillHideOSMouseCursor(_ view: CSScanTargetView) { // The OS mouse cursor will be hidden, so hide the volume controls. if !volumeView.isHidden && volumeView.layer?.animation(forKey: "opacity") == nil { let fadeAnimation = CABasicAnimation(keyPath: "opacity") diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index a93328811..f10b2d81e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -149,7 +149,6 @@ struct ActivityObserver: public Activity::Observer { NSMutableArray *_leds; CSHighPrecisionTimer *_timer; - CGSize _pixelSize; std::atomic_flag _isUpdating; Time::Nanos _syncTime; Time::Nanos _timeDiff; @@ -725,11 +724,10 @@ struct ActivityObserver: public Activity::Observer { #pragma mark - Timer -- (void)openGLViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime { +- (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime { // First order of business: grab a timestamp. const auto timeNow = Time::nanos_now(); - CGSize pixelSize = view.backingSize; BOOL isSyncLocking; @synchronized(self) { // Store a means to map from CVTimeStamp.hostTime to Time::Nanos; @@ -741,9 +739,6 @@ struct ActivityObserver: public Activity::Observer { // Store the next end-of-frame time. TODO: and start of next and implied visible duration, if raster racing? _syncTime = int64_t(now->hostTime) + _timeDiff; - // Also crib the current view pixel size. - _pixelSize = pixelSize; - // Set the current refresh period. _refreshPeriod = double(now->videoRefreshPeriod) / double(now->videoTimeScale); @@ -753,9 +748,7 @@ struct ActivityObserver: public Activity::Observer { // Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching). if(!isSyncLocking) { -// [self.view performWithGLContext:^{ -// self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height); -// } flushDrawable:YES]; + [self.view draw]; } } @@ -771,7 +764,6 @@ struct ActivityObserver: public Activity::Observer { lastTime = std::max(timeNow - Time::Nanos(10'000'000'000 / TICKS), lastTime); const auto duration = timeNow - lastTime; - CGSize pixelSize; BOOL splitAndSync = NO; @synchronized(self) { // Post on input events. @@ -802,7 +794,6 @@ struct ActivityObserver: public Activity::Observer { if(!splitAndSync) { self->_machine->timed_machine()->run_for((double)duration / 1e9); } - pixelSize = self->_pixelSize; } // If this was not a split-and-sync then dispatch the update request asynchronously, unless @@ -819,13 +810,9 @@ struct ActivityObserver: public Activity::Observer { if(!wasUpdating) { dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ [self.view updateBacking]; -// [self.view performWithGLContext:^{ -// self->_scanTarget->update((int)pixelSize.width, (int)pixelSize.height); - -// if(splitAndSync) { -// self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height); -// } -// } flushDrawable:splitAndSync]; + if(splitAndSync) { + [self.view draw]; + } self->_isUpdating.clear(); }); } diff --git a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h index a8d3f4ac3..4b1a1c14e 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h @@ -13,28 +13,6 @@ @class CSScanTargetView; @class CSScanTarget; -typedef NS_ENUM(NSInteger, CSScanTargetViewRedrawEvent) { - /// Indicates that AppKit requested a redraw for some reason (mostly likely, the window is being resized). So, - /// if the delegate doesn't redraw the view, the user is likely to see a graphical flaw. - CSScanTargetViewRedrawEventAppKit, - /// Indicates that the view's display-linked timer has triggered a redraw request. So, if the delegate doesn't - /// redraw the view, the user will just see the previous drawing without interruption. - CSScanTargetViewRedrawEventTimer -}; - -//@protocol CSScanTargetViewDelegate -///*! -// Requests that the delegate produce an image of its current output state. May be called on -// any queue or thread. -// @param view The view making the request. -// @param redrawEvent If @c YES then the delegate may decline to redraw if its output would be -// identical to the previous frame. If @c NO then the delegate must draw. -//*/ -//- (void)openGLViewRedraw:(nonnull CSScanTargetView *)view event:(CSScanTargetViewRedrawEvent)redrawEvent; -// -// -//@end - @protocol CSScanTargetViewResponderDelegate /*! Supplies a keyDown event to the delegate. @@ -87,32 +65,32 @@ typedef NS_ENUM(NSInteger, CSScanTargetViewRedrawEvent) { the window, in order to forward continuous mouse motion. @param view The view making the announcement. */ -- (void)openGLViewDidCaptureMouse:(nonnull CSScanTargetView *)view; +- (void)scanTargetViewDidCaptureMouse:(nonnull CSScanTargetView *)view; /*! Announces that the mouse is no longer captured. @param view The view making the announcement. */ -- (void)openGLViewDidReleaseMouse:(nonnull CSScanTargetView *)view; +- (void)scanTargetViewDidReleaseMouse:(nonnull CSScanTargetView *)view; /*! Announces that the OS mouse cursor is now being displayed again, after having been invisible. @param view The view making the announcement. */ -- (void)openGLViewDidShowOSMouseCursor:(nonnull CSScanTargetView *)view; +- (void)scanTargetViewDidShowOSMouseCursor:(nonnull CSScanTargetView *)view; /*! Announces that the OS mouse cursor will now be hidden. @param view The view making the announcement. */ -- (void)openGLViewWillHideOSMouseCursor:(nonnull CSScanTargetView *)view; +- (void)scanTargetViewWillHideOSMouseCursor:(nonnull CSScanTargetView *)view; /*! Announces receipt of a file by drag and drop to the delegate. @param view The view making the request. @param URL The file URL of the received file. */ -- (void)openGLView:(nonnull CSScanTargetView *)view didReceiveFileAtURL:(nonnull NSURL *)URL; +- (void)scanTargetView:(nonnull CSScanTargetView *)view didReceiveFileAtURL:(nonnull NSURL *)URL; @end @@ -129,17 +107,16 @@ typedef NS_ENUM(NSInteger, CSScanTargetViewRedrawEvent) { /*! Informs the delegate that the display link has fired. */ -- (void)openGLViewDisplayLinkDidFire:(nonnull CSScanTargetView *)view now:(nonnull const CVTimeStamp *)now outputTime:(nonnull const CVTimeStamp *)outputTime; +- (void)scanTargetViewDisplayLinkDidFire:(nonnull CSScanTargetView *)view now:(nonnull const CVTimeStamp *)now outputTime:(nonnull const CVTimeStamp *)outputTime; @end /*! - Provides an OpenGL canvas with a refresh-linked update timer that can forward a subset + Provides a visible scan target with a refresh-linked update timer that can forward a subset of typical first-responder actions. */ @interface CSScanTargetView : MTKView -//@property (atomic, weak, nullable) id delegate; @property (nonatomic, weak, nullable) id responderDelegate; @property (atomic, weak, nullable) id displayLinkDelegate; @@ -165,11 +142,10 @@ typedef NS_ENUM(NSInteger, CSScanTargetViewRedrawEvent) { */ - (void)invalidate; -/// The size in pixels of the OpenGL canvas, factoring in screen pixel density and view size in points. -@property (nonatomic, readonly) CGSize backingSize; - +/*! + Ensures output begins on all pending scans. +*/ - (void)updateBacking; -//- (void)performWithGLContext:(nonnull dispatch_block_t)action; /*! Instructs that the mouse cursor, if currently captured, should be released. diff --git a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m index 4048b2745..4b69fda90 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m @@ -19,7 +19,6 @@ @implementation CSScanTargetView { CVDisplayLinkRef _displayLink; - CGSize _backingSize; NSNumber *_currentScreenNumber; NSTrackingArea *_mouseTrackingArea; @@ -32,20 +31,6 @@ CSScanTarget *_scanTarget; } -//- (void)prepareOpenGL { -// [super prepareOpenGL]; -// -// // Prepare the atomic int. -// atomic_init(&_isDrawingFlag, 0); -// -// // Set the clear colour. -// [self.openGLContext makeCurrentContext]; -// glClearColor(0.0, 0.0, 0.0, 1.0); -// -// // Setup the [initial] display link. -// [self setupDisplayLink]; -//} - - (void)setupDisplayLink { // Kill the existing link if there is one. if(_displayLink) { @@ -61,11 +46,6 @@ // Set the renderer output callback function. CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self)); - // Set the display link for the current renderer. -// CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; -// CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; -// CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat); - // Activate the display link. CVDisplayLinkStart(_displayLink); } @@ -81,7 +61,7 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const // Ensure _isDrawingFlag has value 1 when drawing, 0 otherwise. atomic_store(&view->_isDrawingFlag, 1); - [view.displayLinkDelegate openGLViewDisplayLinkDidFire:view now:now outputTime:outputTime]; + [view.displayLinkDelegate scanTargetViewDisplayLinkDidFire:view now:now outputTime:outputTime]; /* Do not touch the display link from after this call; there's a bit of a race condition with setupDisplayLink. Specifically: Apple provides CVDisplayLinkStop but a call to that merely prevents future calls to the callback, @@ -109,31 +89,12 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const // feels fine. NSNumber *const screenNumber = self.window.screen.deviceDescription[@"NSScreenNumber"]; if(![_currentScreenNumber isEqual:screenNumber]) { - // Issue a reshape, in case a switch to/from a Retina display has - // happened, changing the results of -convertSizeToBacking:, etc. -// [self reshape]; - // Also switch display links, to make sure synchronisation is with the display // the window is actually on, and at its rate. [self setupDisplayLink]; } } -//- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency { -// [self redrawWithEvent:CSScanTargetViewRedrawEventTimer]; -//} - -//- (void)drawRect:(NSRect)dirtyRect { -// [self redrawWithEvent:CSScanTargetViewRedrawEventAppKit]; -// NSLog(@"..."); -//} - -//- (void)redrawWithEvent:(CSScanTargetViewRedrawEvent)event { -// [self performWithGLContext:^{ -//// [self.delegate openGLViewRedraw:self event:event]; -// } flushDrawable:YES]; -//} - - (void)invalidate { _isInvalid = YES; [self stopDisplayLink]; @@ -168,12 +129,6 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const return _scanTarget; } -- (CGSize)backingSize { - @synchronized(self) { - return _backingSize; - } -} - - (void)updateBacking { [_scanTarget updateFrameBuffer]; } @@ -186,12 +141,19 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const self.device = MTLCreateSystemDefaultDevice(); } + // Configure for explicit drawing. + self.paused = YES; + self.enableSetNeedsDisplay = NO; + // Create the scan target. _scanTarget = [[CSScanTarget alloc] initWithView:self]; self.delegate = _scanTarget; // Register to receive dragged and dropped file URLs. [self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]]; + + // Setup the [initial] display link. + [self setupDisplayLink]; } #pragma mark - NSResponder @@ -237,7 +199,7 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const - (BOOL)performDragOperation:(id )sender { for(NSPasteboardItem *item in [[sender draggingPasteboard] pasteboardItems]) { NSURL *URL = [NSURL URLWithString:[item stringForType:(__bridge NSString *)kUTTypeFileURL]]; - [self.responderDelegate openGLView:self didReceiveFileAtURL:URL]; + [self.responderDelegate scanTargetView:self didReceiveFileAtURL:URL]; } return YES; } @@ -273,13 +235,13 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const _mouseHideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(__unused NSTimer * _Nonnull timer) { [NSCursor setHiddenUntilMouseMoves:YES]; - [self.responderDelegate openGLViewWillHideOSMouseCursor:self]; + [self.responderDelegate scanTargetViewWillHideOSMouseCursor:self]; }]; } } - (void)mouseEntered:(NSEvent *)event { - [self.responderDelegate openGLViewDidShowOSMouseCursor:self]; + [self.responderDelegate scanTargetViewDidShowOSMouseCursor:self]; [super mouseEntered:event]; [self scheduleMouseHide]; } @@ -288,7 +250,7 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const [super mouseExited:event]; [_mouseHideTimer invalidate]; _mouseHideTimer = nil; - [self.responderDelegate openGLViewWillHideOSMouseCursor:self]; + [self.responderDelegate scanTargetViewWillHideOSMouseCursor:self]; } - (void)releaseMouse { @@ -296,8 +258,8 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const _mouseIsCaptured = NO; CGAssociateMouseAndMouseCursorPosition(true); [NSCursor unhide]; - [self.responderDelegate openGLViewDidReleaseMouse:self]; - [self.responderDelegate openGLViewDidShowOSMouseCursor:self]; + [self.responderDelegate scanTargetViewDidReleaseMouse:self]; + [self.responderDelegate scanTargetViewDidShowOSMouseCursor:self]; ((CSApplication *)[NSApplication sharedApplication]).eventDelegate = nil; } } @@ -309,7 +271,7 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const // Mouse capture is off, so don't play games with the cursor, just schedule it to // hide in the near future. [self scheduleMouseHide]; - [self.responderDelegate openGLViewDidShowOSMouseCursor:self]; + [self.responderDelegate scanTargetViewDidShowOSMouseCursor:self]; } else { if(_mouseIsCaptured) { // Mouse capture is on, so move the cursor back to the middle of the window, and @@ -327,7 +289,7 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const [self.responderDelegate mouseMoved:event]; } else { - [self.responderDelegate openGLViewDidShowOSMouseCursor:self]; + [self.responderDelegate scanTargetViewDidShowOSMouseCursor:self]; } } } @@ -360,8 +322,8 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const _mouseIsCaptured = YES; [NSCursor hide]; CGAssociateMouseAndMouseCursorPosition(false); - [self.responderDelegate openGLViewWillHideOSMouseCursor:self]; - [self.responderDelegate openGLViewDidCaptureMouse:self]; + [self.responderDelegate scanTargetViewWillHideOSMouseCursor:self]; + [self.responderDelegate scanTargetViewDidCaptureMouse:self]; if(self.shouldUsurpCommand) { ((CSApplication *)[NSApplication sharedApplication]).eventDelegate = self; }