Baby steps: the output window is at least now populated with OpenGL content. Just with glClear to create an exciting off-pink colour though. But it’s wired up to a CVDisplayLink and as the window delegate to redraw upon resizes.
Whoops; switched to async dispatches from the display link’s thread to the main thread, with a guard against dispatching more than one simultaneously. Which is what dispatch_sync would have done in practice because CVDisplayLink is smart, but caused deadlock versus CVDisplayLinkStop — it would try to wait until the callback was done to stop the link, but the callback would be waiting until the main queue is clear to process.