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:
parent
c5e9a74c88
commit
1a2545fdea
@ -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")
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user