diff --git a/src/b2/VBlankMonitorOSX.cpp b/src/b2/VBlankMonitorOSX.cpp index d9e88882..0328dc4d 100644 --- a/src/b2/VBlankMonitorOSX.cpp +++ b/src/b2/VBlankMonitorOSX.cpp @@ -6,6 +6,7 @@ #include "VBlankMonitor.h" #include #include "Messages.h" +#include #include #include "VBlankMonitorOSX_private.inl" @@ -14,47 +15,35 @@ ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +LOG_TAGGED_DEFINE(VBLANK,"vblank","VBLANK",&log_printer_stderr_and_debugger) + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + class VBlankMonitorOSX: public VBlankMonitor { public: ~VBlankMonitorOSX() { + CGDisplayRemoveReconfigurationCallback(&ReconfigurationCallback,this); this->ResetDisplaysList(); } bool Init(Handler *handler,Messages *messages) override { - CVReturn cvr; + CGError cge; ASSERT(!m_handler); m_handler=handler; - + if(!this->InitDisplaysList(messages)) { return false; } - for(auto &&display:m_displays) { - cvr=CVDisplayLinkCreateWithCGDisplay(display->id, - &display->link); - if(cvr!=kCVReturnSuccess) { - messages->e.f("CVDisplayLinkCreateWithCGDisplay failed: %s\n",GetCVReturnEnumName(cvr)); - return false; - } - - cvr=CVDisplayLinkSetOutputCallback(display->link, - &OutputCallback, - display.get()); - if(cvr!=kCVReturnSuccess) { - messages->e.f("CVDisplayLinkSetOutputCallback failed: %s\n",GetCVReturnEnumName(cvr)); - return false; - } - - cvr=CVDisplayLinkStart(display->link); - if(cvr!=kCVReturnSuccess) { - messages->e.f("CVDisplayLinkStart failed: %s\n",GetCVReturnEnumName(cvr)); - return false; - } + cge=CGDisplayRegisterReconfigurationCallback(&ReconfigurationCallback,this); + if(cge!=kCGErrorSuccess) { + return false; } - + return true; } @@ -93,12 +82,30 @@ class VBlankMonitorOSX: protected: private: struct Display { - // CGDirectDisplayID=uint32_t - CGDirectDisplayID id=0; + CGDirectDisplayID const id; + VBlankMonitorOSX *const vbm; + CVDisplayLinkRef link=nullptr; void *data=nullptr; - VBlankMonitorOSX *vbm=nullptr; - std::atomic stop_thread{false}; + + Display(CGDirectDisplayID id_,VBlankMonitorOSX *vbm_): + id(id_), + vbm(vbm_) + { + } + + ~Display() { + CVReturn cvr=CVDisplayLinkStop(this->link); + ASSERTF(cvr==kCVReturnSuccess,"%s",GetCVReturnEnumName(cvr)); + this->link=nullptr; + + CVDisplayLinkRelease(this->link); + this->link=nullptr; + + if(this->data) { + this->vbm->m_handler->FreeDisplayData(this->id,this->data); + } + } }; Handler *m_handler=nullptr; std::vector> m_displays; @@ -124,40 +131,79 @@ class VBlankMonitorOSX: } for(uint32_t i=0;i(); - - display->id=cgdisplays[i]; - display->vbm=this; - - display->data=m_handler->AllocateDisplayData(display->id); - if(!display->data) { - messages->e.f("AllocateDisplayData failed\n"); - return false; + if(!this->AddDisplay(cgdisplays[i])) { + // I have no idea how much help this error will be, but at least you'll know. + messages->e.f("Failed to initialise display 0x%" PRIx32 "\n",cgdisplays[i]); } - - m_displays.push_back(std::move(display)); } return true; } void ResetDisplaysList() { + while(!m_displays.empty()) { + this->RemoveDisplay(m_displays[0]->id); + } + } + + bool AddDisplay(CGDirectDisplayID id) { + CVReturn cvr; + for(auto &&display:m_displays) { - if(display->link) { - CVReturn cvr; - - cvr=CVDisplayLinkStop(display->link); - ASSERTF(cvr==kCVReturnSuccess,"%s",GetCVReturnEnumName(cvr)); - - CVDisplayLinkRelease(display->link); - display->link=nullptr; + if(display->id==id) { + LOGF(VBLANK,"Add Display %" PRIx32 ": already added.\n",id); + return true; } - - m_handler->FreeDisplayData(display->id,display->data); - display->data=nullptr; } + + auto &&display=std::make_unique(id,this); - m_displays.clear(); + CGRect bounds=CGDisplayBounds(id); + (void)bounds; + LOGF(VBLANK,"Add Dispay %" PRIx32 ": ",id); + LOGI(VBLANK); + + LOGF(VBLANK,"(%.1f,%.1f), %.1f x %.1f\n",bounds.origin.x,bounds.origin.y,bounds.size.width,bounds.size.height); + + display->data=m_handler->AllocateDisplayData(display->id); + if(!display->data) { + LOGF(VBLANK,"AllocateDisplayData failed\n"); + return false; + } + + cvr=CVDisplayLinkCreateWithCGDisplay(display->id, + &display->link); + if(cvr!=kCVReturnSuccess) { + LOGF(VBLANK,"CVDisplayLinkCreateWithCGDisplay failed: %s\n",GetCVReturnEnumName(cvr)); + return false; + } + + cvr=CVDisplayLinkSetOutputCallback(display->link, + &OutputCallback, + display.get()); + if(cvr!=kCVReturnSuccess) { + LOGF(VBLANK,"CVDisplayLinkSetOutputCallback failed: %s\n",GetCVReturnEnumName(cvr)); + return false; + } + + cvr=CVDisplayLinkStart(display->link); + if(cvr!=kCVReturnSuccess) { + LOGF(VBLANK,"CVDisplayLinkStart failed: %s\n",GetCVReturnEnumName(cvr)); + return false; + } + + m_displays.push_back(std::move(display)); + return true; + } + + void RemoveDisplay(CGDirectDisplayID id) { + for(auto &&display_it=m_displays.begin();display_it!=m_displays.end();++display_it) { + if((*display_it)->id==id) { + LOGF(VBLANK,"Remove Display %" PRIx32 "\n",id); + m_displays.erase(display_it); + return; + } + } } static CVReturn OutputCallback(CVDisplayLinkRef displayLink, @@ -177,6 +223,20 @@ class VBlankMonitorOSX: return kCVReturnSuccess; } + + static void ReconfigurationCallback(CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *userInfo) + { + auto vbm=(VBlankMonitorOSX *)userInfo; + + if(flags&kCGDisplayAddFlag) { + vbm->AddDisplay(display); + } else if(flags&kCGDisplayRemoveFlag) { + vbm->RemoveDisplay(display); + } + } + }; //////////////////////////////////////////////////////////////////////////