-
-
Save CanTheAlmighty/ee76fbf701a61651fe439fcd6d25f41d to your computer and use it in GitHub Desktop.
// | |
// DisplayLink.swift | |
// MetalMac | |
// | |
// Created by Jose Canepa on 8/18/16. | |
// Copyright © 2016 Jose Canepa. All rights reserved. | |
// | |
import AppKit | |
/** | |
Analog to the CADisplayLink in iOS. | |
*/ | |
class DisplayLink | |
{ | |
let timer : CVDisplayLink | |
let source : DispatchSourceUserDataAdd | |
var callback : Optional<() -> ()> = nil | |
var running : Bool { return CVDisplayLinkIsRunning(timer) } | |
/** | |
Creates a new DisplayLink that gets executed on the given queue | |
- Parameters: | |
- queue: Queue which will receive the callback calls | |
*/ | |
init?(onQueue queue: DispatchQueue = DispatchQueue.main) | |
{ | |
// Source | |
source = DispatchSource.makeUserDataAddSource(queue: queue) | |
// Timer | |
var timerRef : CVDisplayLink? = nil | |
// Create timer | |
var successLink = CVDisplayLinkCreateWithActiveCGDisplays(&timerRef) | |
if let timer = timerRef | |
{ | |
// Set Output | |
successLink = CVDisplayLinkSetOutputCallback(timer, | |
{ | |
(timer : CVDisplayLink, currentTime : UnsafePointer<CVTimeStamp>, outputTime : UnsafePointer<CVTimeStamp>, _ : CVOptionFlags, _ : UnsafeMutablePointer<CVOptionFlags>, sourceUnsafeRaw : UnsafeMutableRawPointer?) -> CVReturn in | |
// Un-opaque the source | |
if let sourceUnsafeRaw = sourceUnsafeRaw | |
{ | |
// Update the value of the source, thus, triggering a handle call on the timer | |
let sourceUnmanaged = Unmanaged<DispatchSourceUserDataAdd>.fromOpaque(sourceUnsafeRaw) | |
sourceUnmanaged.takeUnretainedValue().add(data: 1) | |
} | |
return kCVReturnSuccess | |
}, Unmanaged.passUnretained(source).toOpaque()) | |
guard successLink == kCVReturnSuccess else | |
{ | |
NSLog("Failed to create timer with active display") | |
return nil | |
} | |
// Connect to display | |
successLink = CVDisplayLinkSetCurrentCGDisplay(timer, CGMainDisplayID()) | |
guard successLink == kCVReturnSuccess else | |
{ | |
NSLog("Failed to connect to display") | |
return nil | |
} | |
self.timer = timer | |
} | |
else | |
{ | |
NSLog("Failed to create timer with active display") | |
return nil | |
} | |
// Timer setup | |
source.setEventHandler(handler: | |
{ | |
[weak self] in self?.callback?() | |
}) | |
} | |
/// Starts the timer | |
func start() | |
{ | |
guard !running else { return } | |
CVDisplayLinkStart(timer) | |
source.resume() | |
} | |
/// Cancels the timer, can be restarted aftewards | |
func cancel() | |
{ | |
guard running else { return } | |
CVDisplayLinkStop(timer) | |
source.cancel() | |
} | |
deinit | |
{ | |
if running | |
{ | |
cancel() | |
} | |
} | |
} |
And they say swift is easy and requires fewer lines of code than objc... I setup a DisplayLink in about 5 lines of code in ObJC.
Swift is for people who love writing complicated code
This is based on the implementation done by Apple for MetalBasic3D example: https://developer.apple.com/library/ios/samplecode/MetalBasic3D/Introduction/Intro.html
I took that code and rewrote it in swift and moved into its own class, for easier integration when using Metal.
The Code example you're referring to isn't using a DisplayLink
Note that since mac 10.11 there's CVDisplayLinkSetOutputHandler
which makes setting up of the callback simpler.
And they say swift is easy and requires fewer lines of code than objc... I setup a DisplayLink in about 5 lines of code in ObJC.
Swift is for people who love writing complicated code
This is for the Mac though. CADisplayLink for iOS is easy in either Swift or ObjC.
I believe you want source.suspend() if you want to be able to restart the timer.