diff --git a/cmake/documentation.cmake b/cmake/documentation.cmake index 49b0dffaa..953a91ec0 100644 --- a/cmake/documentation.cmake +++ b/cmake/documentation.cmake @@ -30,7 +30,7 @@ endmacro() function(add_documentation) set(options) set(oneValueArgs PROJECT PROJECT_NUMBER OUTPUT_DIRECTORY WORKING_DIRECTORY) - set(multiValueArgs INPUT_DIRECTORY PUBLIC_HEADERS) + set(multiValueArgs INPUT_DIRECTORY PUBLIC_HEADERS EXCLUDE_FILES) cmake_parse_arguments(DOCUMENTATION "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (DOCUMENTATION_PROJECT) @@ -81,6 +81,7 @@ function(add_documentation) set(DOXYGEN_GENERATE_LATEX NO) set(DOXYGEN_GENERATE_XML YES) set(DOXYGEN_XML_OUTPUT xml) + set(DOXYGEN_EXCLUDE ${DOCUMENTATION_EXCLUDE_FILES}) configure_file(${PROJECT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) set(doxygenXmlOutputDir ${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_XML_OUTPUT}) @@ -139,5 +140,8 @@ if(MORPHEUS_BUILD_DOCUMENTATION) INPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/examples INPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libraries OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/documentation + EXCLUDE_FILES + ${PROJECT_SOURCE_DIR}/libraries/gfx/platform/src/morpheus/gfx/platform/macos/application_delegate.h + ${PROJECT_SOURCE_DIR}/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.h ) endif() diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index c5107a55d..c6ad75887 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -100,3 +100,6 @@ GENERATE_XML = @DOXYGEN_GENERATE_XML@ # This tag requires that the tag GENERATE_XML is set to YES. XML_OUTPUT = @DOXYGEN_XML_OUTPUT@ + +# Files to exclude from documentation generation. +EXCLUDE = @DOXYGEN_EXCLUDE@ \ No newline at end of file diff --git a/libraries/gfx/metal/examples/window/main.cpp b/libraries/gfx/metal/examples/window/main.cpp index f74f8ffbb..a90264daf 100644 --- a/libraries/gfx/metal/examples/window/main.cpp +++ b/libraries/gfx/metal/examples/window/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,10 +6,18 @@ #include using namespace morpheus; +using namespace morpheus::application::po; using namespace morpheus::gfx::macos; int main(int argc, const char * argv[]) { + RenderWindow::Config config; + + if (auto const result = parseProgramOptions(argc, argv, HelpDocumentation{}, config)) { + return result.value(); + } + + // NSLog (@"Window name: %@\nWidth: %ld\nHeight: %ld\nColour Depth: %ld\n", windowName, static_cast(width), static_cast(height), static_cast(colourDepth)); for (const auto& monitors : enumerateMonitors()) { @@ -16,6 +25,10 @@ int main(int argc, const char * argv[]) std::cout.flush(); // Ensure it's not buffered } - RenderWindow::Config config; RenderWindow window(config); + + while (!window.shouldClose()) + { + window.pollEvents(); + } } diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.hpp b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.hpp index 25787edfd..f125f3f30 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.hpp +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.hpp @@ -37,11 +37,17 @@ class RenderWindow : protected gfx::RenderWindow { //! The colour depth of the pixels of the render target. [[nodiscard]] std::uint16_t colourDepth() const noexcept { return gfx::RenderTarget::colourDepth(); } + [[nodiscard]] bool shouldClose() const noexcept; + + // bool isHidden() const noexcept // bool isFocus() const noexcept /// \copydoc gfx::RenderWindow::fullScreen() - [[nodiscard]] bool fullScreen() const noexcept { return gfx::RenderWindow::fullScreen(); } + [[nodiscard]] bool fullScreen() const noexcept + { + return gfx::RenderWindow::fullScreen(); + } [[nodiscard]] bool visible() const noexcept; @@ -59,10 +65,14 @@ class RenderWindow : protected gfx::RenderWindow { /// Access the underlying OS Window handle. auto getHandle() const noexcept { return mHandle; } -private: + void pollEvents(); +private: + class Impl; + std::unique_ptr mThis; // The internal implementation. WindowHandle mHandle; + // Should we use a Windows controller subclass to enable unit testing? // https://eschatologist.net/blog/?p=10 }; diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.mm b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.mm index c0c5343cf..824865805 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.mm +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/render_window.mm @@ -1,4 +1,5 @@ +#include #include #include #include @@ -6,21 +7,42 @@ #import "Cocoa/Cocoa.h" +#include + namespace morpheus::gfx::macos { -RenderWindow::RenderWindow(Config const& config) -: gfx::RenderWindow(config) -{ - @autoreleasepool { +class RenderWindow::Impl{ +public: + Impl(Config const& config); + Impl(Impl const&) = delete; + Impl(Impl&&) = default; + Impl& operator=(Impl const&) = delete; + Impl& operator=(Impl&&) = default; - if ([NSThread currentThread] != [NSThread mainThread]) - { - // See https://lists.apple.com/archives/cocoa-dev/2011/Feb/msg00460.html - throwRuntimeException("Cannot create a window from a worker thread. (OS X limitation)"); - } + ~Impl(); + + auto pollEvents() -> void; + auto setTitle(std::string const& title) -> void; - NSString* title = [[NSString alloc] initWithUTF8String:config.windowName.c_str()]; + [[nodiscard]] bool shouldClose() const noexcept { return mShouldClose; } + +private: + /// All window resoures on MacOS must be create on the main thread, this throw an exception if this is not the case.ยง + auto throwExceptionIfNotMainThread() -> void; + + NSWindow* mHandle = nullptr; + WindowDelegate* mWindowDelegate = nullptr; + // ApplicationDelegate* mApplicationDelegate = nullptr; + bool mShouldClose = false; +}; + +RenderWindow::Impl::Impl(Config const& config) +: mHandle(nullptr) +, mWindowDelegate([[WindowDelegate alloc] init]) +{ + @autoreleasepool { + throwExceptionIfNotMainThread(); NSRect frame = NSMakeRect(config.startX, config.startY, config.width, config.height); NSUInteger styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable; @@ -28,7 +50,7 @@ styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; - [(NSWindow *)mHandle setTitle:title]; + setTitle(config.windowName); if (config.fullScreen) { @@ -47,13 +69,107 @@ [(NSWindow *)mHandle setBackgroundColor:[NSColor blackColor]]; // [window setIsVisible:YES]; - // [window makeKeyAndOrderFront:windoAw]; + // [window makeKeyAndOrderFront:window]; + + [mHandle setDelegate: mWindowDelegate]; [[NSApplication sharedApplication] finishLaunching]; - [NSApp run]; - //[(NSWindow *)mHandle setDelegate: [WindowDelegate alloc]]; + // [NSApp run]; + } +} + +RenderWindow::Impl::~Impl() +{ + [mHandle release]; + [mWindowDelegate release]; + // [ApplicationDelegate release]; +} + +void RenderWindow::Impl::pollEvents() +{ + @autoreleasepool { + + NSApplication* app = NSApplication.sharedApplication; + NSEvent* event = [app nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + { + [app sendEvent:event]; + + mShouldClose = [mWindowDelegate shouldClose]; + } + } +} + +auto RenderWindow::Impl::setTitle(std::string const& title) -> void +{ + MORPHEUS_ASSERT(mHandle); + + @autoreleasepool { + NSString* titleStr = [[NSString alloc] initWithUTF8String:title.c_str()]; + [mHandle setTitle:titleStr]; } } + + +auto RenderWindow::Impl::throwExceptionIfNotMainThread() -> void +{ + if ([NSThread currentThread] != [NSThread mainThread]) + { + // See https://lists.apple.com/archives/cocoa-dev/2011/Feb/msg00460.html + throwRuntimeException("Cannot create a window from a worker thread. (OS X limitation)"); + } +} + +RenderWindow::RenderWindow(Config const& config) +: gfx::RenderWindow(config) +, mThis(std::make_unique(config)) +{ + // @autoreleasepool { + + // if ([NSThread currentThread] != [NSThread mainThread]) + // { + // // See https://lists.apple.com/archives/cocoa-dev/2011/Feb/msg00460.html + // throwRuntimeException("Cannot create a window from a worker thread. (OS X limitation)"); + // } + + // NSString* title = [[NSString alloc] initWithUTF8String:config.windowName.c_str()]; + + // NSRect frame = NSMakeRect(config.startX, config.startY, config.width, config.height); + // NSUInteger styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable; + // mHandle = [[NSWindow alloc] initWithContentRect:frame + // styleMask:styleMask + // backing:NSBackingStoreBuffered + // defer:NO]; + // [(NSWindow *)mHandle setTitle:title]; + + // if (config.fullScreen) + // { + // [(NSWindow *)mHandle toggleFullScreen:nil]; + // } + + // if (config.visible) + // { + // [(NSWindow *)mHandle makeKeyAndOrderFront:nil]; + // } + + // [NSApp setDelegate: [ApplicationDelegate alloc]]; + // [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + // [NSApp activateIgnoringOtherApps:YES]; + + + // [(NSWindow *)mHandle setBackgroundColor:[NSColor blackColor]]; + // // [window setIsVisible:YES]; + // // [window makeKeyAndOrderFront:window]; + + // // [(NSWindow *)mHandle setDelegate: [WindowDelegate alloc]]; + // [[NSApplication sharedApplication] finishLaunching]; + // // [NSApp run]; + // } +} + RenderWindow::RenderWindow(WindowHandle const window) : mHandle(window) { @@ -71,9 +187,17 @@ } } + +RenderWindow::RenderWindow(RenderWindow&&) noexcept = default; +RenderWindow& RenderWindow::operator=(RenderWindow&&) noexcept = default; + RenderWindow::~RenderWindow() { @autoreleasepool { + + // WindowDelegate* delegate = (WindowDelegate*)[(NSWindow*)mHandle delegate]; + // [delegate release]; + if (mHandle) { [(NSWindow *)mHandle close]; @@ -95,4 +219,19 @@ [(NSWindow*)mHandle makeKeyAndOrderFront:nil]; } +bool RenderWindow::shouldClose() const noexcept +{ + return mThis->shouldClose(); +} + +// bool RenderWindow::fullScreen() const noexcept +// { +// return gfx::RenderWindow::fullScreen(); +// } + +void RenderWindow::pollEvents() +{ + mThis->pollEvents(); +} + } // namespace morpheus::gfx::macos diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.h b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.h index c5c693b49..bde881cbb 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.h +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.h @@ -3,6 +3,9 @@ #import @interface WindowDelegate : NSResponder +@property (nonatomic) BOOL shouldClose; + +- (void) windowWillClose:(NSNotification *)notification; - (void) windowDidMiniaturize:(NSNotification *)notification; - (void) windowDidDeminiaturize:(NSNotification *)notification; @end diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.mm b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.mm index 09e1b39bb..a5348e628 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.mm +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/macos/window_delegate.mm @@ -4,6 +4,15 @@ #import @implementation WindowDelegate +- (instancetype)init { + self = [super init]; + _shouldClose = NO; + return self; +} +- (void)windowWillClose:(NSNotification *)notification { + _shouldClose = YES; +} + - (void) windowDidMiniaturize: (NSNotification *)notification { diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/pixel.hpp b/libraries/gfx/platform/src/morpheus/gfx/platform/pixel.hpp index 1d12b5b06..fe7c5e067 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/pixel.hpp +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/pixel.hpp @@ -6,10 +6,10 @@ namespace morpheus::gfx { -/*! \enum pixel_format +/*! \enum PixelFormat */ -enum class pixel_format : std::int32_t +enum class PixelFormat : std::int32_t { UNKNOWN, // VK_FORMAT_R8_UNORM diff --git a/libraries/gfx/platform/src/morpheus/gfx/platform/render_window.hpp b/libraries/gfx/platform/src/morpheus/gfx/platform/render_window.hpp index 7395ae96a..dc132d305 100644 --- a/libraries/gfx/platform/src/morpheus/gfx/platform/render_window.hpp +++ b/libraries/gfx/platform/src/morpheus/gfx/platform/render_window.hpp @@ -5,6 +5,8 @@ #include #include + +#include #include namespace morpheus::gfx @@ -28,10 +30,10 @@ struct WindowConfig namespace po = boost::program_options; // clang-format off options.add_options() - ("window-name", po::value(&windowName)->required(), "The title of the Window.") - ("width", po::value(&width)->required(), "Width in pixels of the window.") - ("height", po::value(&height)->required(), "Height in pixels of the window.") - ("colour-depth", po::value(&colourDepth)->required(), "Colour depth in bits per pixel.") + ("window-name", po::value(&windowName)->default_value(windowName), "The title of the Window.") + ("width", po::value(&width)->default_value(width), "Width in pixels of the window.") + ("height", po::value(&height)->default_value(height), "Height in pixels of the window.") + ("colour-depth", po::value(&colourDepth)->default_value(colourDepth), "Colour depth in bits per pixel.") ("start-x", po::value(&startX)->default_value(startX), "Starting pixel in the x-dimension for the Window.") ("start-y", po::value(&startY)->default_value(startY), "Starting pixel in the y-dimension for the Window.") ("full-screen", po::value(&fullScreen)->default_value(fullScreen), "Is the window to be started in full screen mode")