1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Excises dangling references to OpenGLView, reinstates display link.

This commit is contained in:
Thomas Harte 2020-09-13 22:11:51 -04:00
parent c5e9a74c88
commit 1a2545fdea
4 changed files with 38 additions and 128 deletions

View File

@ -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")

View File

@ -149,7 +149,6 @@ struct ActivityObserver: public Activity::Observer {
NSMutableArray<NSString *> *_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();
});
}

View File

@ -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 <NSObject>
/*!
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 <CSOpenGLViewDelegate> delegate;
@property (nonatomic, weak, nullable) id <CSScanTargetViewResponderDelegate> responderDelegate;
@property (atomic, weak, nullable) id <CSScanTargetViewDisplayLinkDelegate> 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.

View File

@ -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 <NSDraggingInfo>)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;
}