diff --git a/include/vsg/platform/macos/MacOS_Window.h b/include/vsg/platform/macos/MacOS_Window.h index 21b9331680..ddaeaaf0e9 100644 --- a/include/vsg/platform/macos/MacOS_Window.h +++ b/include/vsg/platform/macos/MacOS_Window.h @@ -57,7 +57,7 @@ namespace vsgMacOS const char* instanceExtensionSurfaceName() const override { return "VK_MVK_macos_surface"; } - bool valid() const override { return _window; } + bool valid() const override { return _window || _view; } bool pollEvents(vsg::UIEvents& events) override; diff --git a/src/vsg/platform/macos/MacOS_Window.mm b/src/vsg/platform/macos/MacOS_Window.mm index 8b0da4bea9..4df5927d08 100644 --- a/src/vsg/platform/macos/MacOS_Window.mm +++ b/src/vsg/platform/macos/MacOS_Window.mm @@ -708,10 +708,58 @@ void createApplicationMenus(void) } MacOS_Window::MacOS_Window(vsg::ref_ptr traits) : - Inherit(traits) + Inherit(traits), + _window(nil), + _view(nil), + _metalLayer(nil) { _keyboard = new KeyboardMap; + // When nativeWindow is set, embed into the provided NSView rather than + // creating a standalone NSWindow. This mirrors Win32_Window's handling + // of an external HWND. + if (traits->nativeWindow.has_value()) + { + auto nativeHandle = std::any_cast(traits->nativeWindow); + if (nativeHandle) + { + NSView* externalView = reinterpret_cast(nativeHandle); + _view = (vsg_MacOS_NSView*)externalView; + [_view setWantsLayer:YES]; + + _metalLayer = (CAMetalLayer*)[_view layer]; + if (!_metalLayer || ![_metalLayer isKindOfClass:[CAMetalLayer class]]) + { + _metalLayer = [[CAMetalLayer alloc] init]; + if (!_metalLayer) + { + throw Exception{"Error: vsg::MacOS_Window::MacOS_Window(...) failed to create CAMetalLayer for embedded view.", VK_ERROR_INVALID_EXTERNAL_HANDLE}; + } + [_view setLayer:_metalLayer]; + } + + auto devicePixelScale = _traits->hdpi ? [[_view window] backingScaleFactor] : 1.0f; + [_metalLayer setContentsScale:devicePixelScale]; + + uint32_t finalwidth = traits->width * devicePixelScale; + uint32_t finalheight = traits->height * devicePixelScale; + + if (traits->device) share(traits->device); + + _extent2D.width = finalwidth; + _extent2D.height = finalheight; + + _first_macos_timestamp = [[NSProcessInfo processInfo] systemUptime]; + _first_macos_time_point = vsg::clock::now(); + + vsg::clock::time_point event_time = vsg::clock::now(); + bufferedEvents.emplace_back(vsg::ConfigureWindowEvent::create(this, event_time, _traits->x, _traits->y, finalwidth, finalheight)); + + return; + } + } + + // Standalone window path NSRect contentRect = NSMakeRect(0, 0, traits->width, traits->height); NSWindowStyleMask styleMask = 0; @@ -822,16 +870,20 @@ void createApplicationMenus(void) bool MacOS_Window::pollEvents(vsg::UIEvents& events) { - for (;;) + // Skip NSApp event polling when embedded — the host owns the event loop. + if (_window) { - NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event == nil) - break; - - [NSApp sendEvent:event]; + for (;;) + { + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + break; + + [NSApp sendEvent:event]; + } } return Window::pollEvents(events); @@ -841,7 +893,8 @@ void createApplicationMenus(void) { const NSRect contentRect = [_view frame]; - auto devicePixelScale = _traits->hdpi ? [_window backingScaleFactor] : 1.0f; + NSWindow* hostWindow = _window ? _window : [_view window]; + auto devicePixelScale = _traits->hdpi ? [hostWindow backingScaleFactor] : 1.0f; //[_metalLayer setContentsScale:devicePixelScale]; _extent2D.width = contentRect.size.width * devicePixelScale;