mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-30 19:30:17 +00:00
Merge pull request #743 from TomHarte/macOSScreens
Ensures macOS window can properly be dragged between screens, Retina or not.
This commit is contained in:
commit
50be991415
@ -17,6 +17,7 @@
|
||||
@implementation CSOpenGLView {
|
||||
CVDisplayLinkRef _displayLink;
|
||||
CGSize _backingSize;
|
||||
NSScreen *_currentScreen;
|
||||
|
||||
NSTrackingArea *_mouseTrackingArea;
|
||||
NSTimer *_mouseHideTimer;
|
||||
@ -26,12 +27,38 @@
|
||||
- (void)prepareOpenGL {
|
||||
[super prepareOpenGL];
|
||||
|
||||
// Synchronize buffer swaps with vertical refresh rate
|
||||
// Note the initial screen.
|
||||
_currentScreen = self.window.screen;
|
||||
|
||||
// Synchronize buffer swaps with vertical refresh rate.
|
||||
// TODO: discard this, once scheduling is sufficiently intelligent?
|
||||
GLint swapInt = 1;
|
||||
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
|
||||
// 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, then wait until its final shout is definitely done.
|
||||
if(_displayLink) {
|
||||
const double duration = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_displayLink);
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
|
||||
// This is a workaround; I could find no way to ensure that a callback from the display
|
||||
// link is not currently ongoing.
|
||||
usleep((useconds_t)ceil(duration * 1000000.0));
|
||||
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
}
|
||||
|
||||
// Create a display link capable of being used with all active displays
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||
NSNumber *const screenNumber = self.window.screen.deviceDescription[@"NSScreenNumber"];
|
||||
CVDisplayLinkCreateWithCGDisplay(screenNumber.unsignedIntValue, &_displayLink);
|
||||
|
||||
// Set the renderer output callback function
|
||||
CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
|
||||
@ -41,21 +68,44 @@
|
||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
||||
|
||||
// set the clear colour
|
||||
[self.openGLContext makeCurrentContext];
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
// Activate the display link
|
||||
CVDisplayLinkStart(_displayLink);
|
||||
}
|
||||
|
||||
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
|
||||
CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext;
|
||||
|
||||
[view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)];
|
||||
/*
|
||||
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,
|
||||
it doesn't wait for completion of any current calls. So I've set up a usleep for one callback's duration,
|
||||
so code in here gets one callback's duration to access the display link.
|
||||
|
||||
In practice, it should do so only upon entry, and before calling into the view. The view promises not to
|
||||
access the display link itself as part of -drawAtTime:frequency:.
|
||||
*/
|
||||
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency {
|
||||
// Test now whether the screen this view is on has changed since last time it was checked.
|
||||
// There's likely a callback available for this, on NSWindow if nowhere else, or an NSNotification,
|
||||
// but since this method is going to be called repeatedly anyway, and the test is cheap, polling
|
||||
// feels fine.
|
||||
if(self.window.screen != _currentScreen) {
|
||||
_currentScreen = self.window.screen;
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
[self redrawWithEvent:CSOpenGLViewRedrawEventTimer];
|
||||
}
|
||||
|
||||
@ -75,7 +125,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// Release the display link
|
||||
// Stop and release the display link
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user