diff options
author | David Robillard <d@drobilla.net> | 2021-01-06 23:53:31 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2021-01-06 23:53:31 +0100 |
commit | d2a6fd3f725f7f174535c49d78d07567e12c84d8 (patch) | |
tree | 8ebbe1d224363f94610e64af167a2592390787b4 /doc/cpp | |
parent | 31d059bf849045f45c8c9db361efd093e88a109c (diff) | |
download | pugl-d2a6fd3f725f7f174535c49d78d07567e12c84d8.tar.gz pugl-d2a6fd3f725f7f174535c49d78d07567e12c84d8.tar.bz2 pugl-d2a6fd3f725f7f174535c49d78d07567e12c84d8.zip |
Split overview into multiple documents
Diffstat (limited to 'doc/cpp')
-rw-r--r-- | doc/cpp/event-loop.rst | 37 | ||||
-rw-r--r-- | doc/cpp/events.rst | 43 | ||||
-rw-r--r-- | doc/cpp/overview.rst | 407 | ||||
-rw-r--r-- | doc/cpp/view.rst | 299 | ||||
-rw-r--r-- | doc/cpp/world.rst | 41 | ||||
-rw-r--r-- | doc/cpp/wscript | 4 |
6 files changed, 434 insertions, 397 deletions
diff --git a/doc/cpp/event-loop.rst b/doc/cpp/event-loop.rst new file mode 100644 index 0000000..1d2ac41 --- /dev/null +++ b/doc/cpp/event-loop.rst @@ -0,0 +1,37 @@ +.. default-domain:: cpp +.. highlight:: cpp +.. namespace:: pugl + +###################### +Driving the Event Loop +###################### + +Pugl does not contain any threads or other event loop "magic". +For flexibility, the event loop is driven manually by repeatedly calling :func:`World::update`, +which processes events from the window system and dispatches them to views when necessary. + +The exact use of :func:`World::update` depends on the application. +Plugins typically call it with a ``timeout`` of 0 in a callback driven by the host. +This avoids blocking the main loop, +since other plugins and the host itself need to run as well. + +A program can use whatever timeout is appropriate: +event-driven applications may wait forever by using a ``timeout`` of -1, +while those that draw continuously may use a significant fraction of the frame period +(with enough time left over to render). + +********* +Redrawing +********* + +Occasional redrawing can be requested by calling :func:`View::postRedisplay` or :func:`View::postRedisplayRect`. +After these are called, +a :type:`ExposeEvent` will be dispatched on the next call to :func:`World::update`. +Note, however, that this will not wake up a blocked :func:`World::update` call on MacOS +(which does not handle drawing via events). + +For continuous redrawing, +call :func:`View::postRedisplay` while handling a :type:`UpdateEvent`. +This event is sent just before views are redrawn, +so it can be used as a hook to expand the update region right before the view is exposed. +Anything else that needs to be done every frame can be handled similarly. diff --git a/doc/cpp/events.rst b/doc/cpp/events.rst new file mode 100644 index 0000000..72c396c --- /dev/null +++ b/doc/cpp/events.rst @@ -0,0 +1,43 @@ +.. default-domain:: cpp +.. highlight:: cpp +.. namespace:: pugl + +############### +Handling Events +############### + +Events are sent to a view when it has received user input, +must be drawn, or in other situations that may need to be handled such as resizing. + +Events are sent to the ``onEvent`` method that takes the matching event type. +The application must handle at least :type:`ConfigureEvent` +and :type:`ExposeEvent` to draw anything, +but there are many other :type:`event types <pugl::EventType>`. + +For example, basic event handling for our above class might look something like: + +.. code-block:: cpp + + pugl::Status + MyView::onEvent(const pugl::ConfigureEvent& event) noexcept + { + return resize(event.width, event.height); + } + + pugl::Status + MyView::onEvent(const pugl::ExposeEvent& event) noexcept + { + return drawMyAwesomeInterface(event.x, event.y, event.width, event.height); + } + +******* +Drawing +******* + +Note that Pugl uses a different drawing model than many libraries, +particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_. + +In that style of code, drawing is performed imperatively in the main loop, +but with Pugl, the application must draw only while handling an expose event. +This is because Pugl supports event-driven applications that only draw the damaged region when necessary, +and handles exposure internally to provide optimized and consistent behavior across platforms. diff --git a/doc/cpp/overview.rst b/doc/cpp/overview.rst index 5fffe37..4ffc3da 100644 --- a/doc/cpp/overview.rst +++ b/doc/cpp/overview.rst @@ -2,6 +2,10 @@ .. highlight:: cpp .. namespace:: pugl +######## +Overview +######## + Pugl is a C library, but the bindings documented here provide a more idiomatic and type-safe API for C++. If you would rather use C, @@ -23,401 +27,10 @@ The API revolves around two main objects: the `world` and the `view`. An application creates a world to manage top-level state, then creates one or more views to display. -Creating a World -================ - -The world is the top-level object which represents an instance of Pugl. -It handles the connection to the window system, -and manages views and the event loop. - -An application typically has a single world, -which is constructed once on startup and used to drive the main event loop. - -Construction ------------- - -A world must be created before any views, and it must outlive all of its views. -The world constructor requires an argument to specify the application type: - -.. code-block:: cpp - - pugl::World world{pugl::WorldType::program}; - -For a plugin, specify :enumerator:`WorldType::module` instead. -In some cases, it is necessary to pass additional flags. -For example, Vulkan requires thread support: - -.. code-block:: cpp - - pugl::World world{pugl::WorldType::program, pugl::WorldFlag::threads}; - -It is a good idea to set a class name for your project with :func:`World::setClassName`. -This allows the window system to distinguish different applications and, -for example, users to set up rules to manage their windows nicely: - -.. code-block:: cpp - - world.setClassName("MyAwesomeProject"); - -Creating a View -=============== - -A `view` is a drawable region that receives events. -You may think of it as a window, -though it may be embedded and not represent a top-level system window. [#f1]_ - -Pugl communicates with views by dispatching events. -For flexibility, the event handler can be a different object than the view. -This allows using :class:`View` along with a separate event handler class. -Alternatively, a view class can inherit from :class:`View` and set itself as its event handler, -for a more object-oriented style. - -This documentation will use the latter approach, -so we will define a class for our view that contains everything needed: - -.. code-block:: cpp - - class MyView : public pugl::View - { - public: - explicit MyView(pugl::World& world) - : pugl::View{world} - { - setEventHandler(*this); - } - - pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; - pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; - - // With other handlers here as needed... - - // Fallback handler for all other events - template<PuglEventType t, class Base> - pugl::Status onEvent(const pugl::Event<t, Base>&) noexcept - { - return pugl::Status::success; - } - - private: - // Some data... - }; - -Pugl will call an ``onEvent`` method of the event handler (the view in this case) for every event. - -Note that Pugl uses a static dispatching mechanism rather than virtual functions to minimize overhead. -It is therefore necessary for the final class to define a handler for every event type. -A terse way to do this without writing every implementation is to define a fallback handler as a template, -as in the example above. -Alternatively, you can define an explicit handler for each event that simply returns :enumerator:`Status::success`. -This way, it will be a compile error if any event is not explicitly handled. - -Configuring the Frame ---------------------- - -Before display, -the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set. -These allow the window system (or plugin host) to arrange the view properly. - -Derived classes can configure themselves during construction, -but we assume here that configuration is being done outside the view. -For example: - -.. code-block:: cpp - - const double defaultWidth = 1920.0; - const double defaultHeight = 1080.0; - - view.setWindowTitle("My Window"); - view.setDefaultSize(defaultWidth, defaultHeight); - view.setMinSize(defaultWidth / 4.0, defaultHeight / 4.0); - view.setAspectRatio(1, 1, 16, 9); - -There are also several :type:`hints <PuglViewHint>` for basic attributes that can be set: - -.. code-block:: cpp - - view.setHint(pugl::ViewHint::resizable, true); - view.setHint(pugl::ViewHint::ignoreKeyRepeat, true); - -Embedding ---------- - -To embed the view in another window, -you will need to somehow get the :type:`native view handle <pugl::NativeView>` for the parent, -then set it with :func:`View::setParentWindow`. -If the parent is a Pugl view, -the native handle can be accessed with :func:`View::nativeWindow`. -For example: - -.. code-block:: cpp - - view.setParentWindow(view, parent.getNativeWindow()); - -Setting a Backend ------------------ - -Before being realized, the view must have a backend set with :func:`View::setBackend`. - -The backend manages the graphics API that will be used for drawing. -Pugl includes backends and supporting API for -:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`. - -Using Cairo -^^^^^^^^^^^ - -Cairo-specific API is declared in the ``cairo.hpp`` header: - -.. code-block:: cpp - - #include <pugl/cairo.hpp> - -The Cairo backend is provided by :func:`cairoBackend()`: - -.. code-block:: cpp - - view.setBackend(pugl::cairoBackend()); - -No additional configuration is required for Cairo. -To draw when handling an expose event, -the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`View::context`: - -.. code-block:: cpp - - cairo_t* cr = static_cast<cairo_t*>(view.context()); - -Using OpenGL -^^^^^^^^^^^^ - -OpenGL-specific API is declared in the ``gl.hpp`` header: - -.. code-block:: cpp - - #include <pugl/gl.hpp> - -The OpenGL backend is provided by :func:`glBackend()`: - -.. code-block:: cpp - - view.setBackend(pugl::glBackend()); - -Some hints must also be set so that the context can be set up correctly. -For example, to use OpenGL 3.3 Core Profile: - -.. code-block:: cpp - - view.setHint(pugl::ViewHint::useCompatProfile, false); - view.setHint(pugl::ViewHint::contextVersionMajor, 3); - view.setHint(pugl::ViewHint::contextVersionMinor, 3); - -If you need to perform some setup using the OpenGL API, -there are two ways to do so. - -The OpenGL context is active when -:type:`CreateEvent` and -:type:`DestroyEvent` -events are dispatched, -so things like creating and destroying shaders and textures can be done then. - -Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler, -:func:`enterContext` and :func:`leaveContext` can be used to manually activate the OpenGL context during application setup. -Note, however, that unlike many other APIs, these functions must not be used for drawing. -It is only valid to use the OpenGL API for configuration in a manually entered context, -rendering will not work. -For example: - -.. code-block:: cpp - - pugl::enterContext(view); - myApp.setupOpenGL(); - pugl::leaveContext(view); - - while (!myApp.quit()) { - world.update(0.0); - } - - pugl::enterContext(view); - myApp.teardownOpenGL(); - pugl::leaveContext(view); - -Using Vulkan -^^^^^^^^^^^^ - -Vulkan-specific API is declared in the ``vulkan.hpp`` header. -This header includes Vulkan headers, -so if you are dynamically loading Vulkan at runtime, -you should define ``VK_NO_PROTOTYPES`` before including it. - -.. code-block:: cpp - - #define VK_NO_PROTOTYPES - - #include <pugl/vulkan.hpp> - -The Vulkan backend is provided by :func:`vulkanBackend()`: - -.. code-block:: cpp - - view.setBackend(pugl::vulkanBackend()); - -Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly. -Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API. - -Loading Vulkan -^^^^^^^^^^^^^^ - -For maximum compatibility, -it is best to not link to Vulkan at compile-time, -but instead load the Vulkan API at run-time. -To do so, first create a :class:`VulkanLoader`: - -.. code-block:: cpp - - pugl::VulkanLoader loader{world}; - -The loader manages the dynamically loaded Vulkan library, -so it must be kept alive for as long as the application is using Vulkan. -You can get the function used to load Vulkan functions with :func:`VulkanLoader::getInstanceProcAddrFunc`: - -.. code-block:: cpp - - auto vkGetInstanceProcAddr = loader.getInstanceProcAddrFunc(); - -It is best to use this function to load everything at run time, -rather than link to the Vulkan library at run time. -You can, for example, pass this to get the ``vkCreateInstance`` function using this, -then use that to create your Vulkan instance. -In practice, you will want to use some loader or wrapper API since there are many Vulkan functions. - -It is not necessary to use :class:`VulkanLoader`, -you can, for example, use the ``DynamicLoader`` from ``vulkan.hpp`` in the Vulkan SDK instead. - -The details of using Vulkan are far beyond the scope of this documentation, -but Pugl provides a portable function, :func:`createSurface`, -to get the Vulkan surface for a view. -Assuming you have somehow created your ``VkInstance``, -you can get the surface for a view using :func:`createSurface`: - -.. code-block:: cpp - - VkSurfaceKHR* surface = nullptr; - puglCreateSurface(loader.getDeviceProcAddrFunc(), - view, - vulkanInstance, - nullptr, - &surface); - -Pugl does not provide API that uses ``vulkan.hpp`` to avoid the onerous dependency, -but if you are using it with exceptions and unique handles, -it is straightforward to wrap the surface handle yourself. - -Showing the View ----------------- - -Once the view is configured, it can be "realized" with :func:`View::realize`. -This creates a "real" system view, for example: - -.. code-block:: cpp - - pugl::Status status = view.realize(); - if (status != pugl::Status::success) { - std::cerr << "Error realizing view: " << pugl::strerror(status) << "\n"; - } - -Note that realizing a view can fail for many reasons, -so the return code should always be checked. -This is generally the case for any function that interacts with the window system. -Most functions also return a :enum:`Status`, -but these checks are omitted for brevity in the rest of this documentation. - -A realized view is not initially visible, -but can be shown with :func:`View::show`: - -.. code-block:: cpp - - view.show(); - -To create an initially visible view, -it is also possible to simply call :func:`View::show()` right away. -The view will be automatically realized if necessary. - -Handling Events -=============== - -Events are sent to a view when it has received user input, -must be drawn, or in other situations that may need to be handled such as resizing. - -Events are sent to the ``onEvent`` method that takes the matching event type. -The application must handle at least :type:`ConfigureEvent` -and :type:`ExposeEvent` to draw anything, -but there are many other :type:`event types <pugl::EventType>`. - -For example, basic event handling for our above class might look something like: - -.. code-block:: cpp - - pugl::Status - MyView::onEvent(const pugl::ConfigureEvent& event) noexcept - { - return resize(event.width, event.height); - } - - pugl::Status - MyView::onEvent(const pugl::ExposeEvent& event) noexcept - { - return drawMyAwesomeInterface(event.x, event.y, event.width, event.height); - } - -Drawing -------- - -Note that Pugl uses a different drawing model than many libraries, -particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_. - -In that style of code, drawing is performed imperatively in the main loop, -but with Pugl, the application must draw only while handling an expose event. -This is because Pugl supports event-driven applications that only draw the damaged region when necessary, -and handles exposure internally to provide optimized and consistent behavior across platforms. - -Driving the Event Loop -====================== - -Pugl does not contain any threads or other event loop "magic". -For flexibility, the event loop is driven manually by repeatedly calling :func:`World::update`, -which processes events from the window system and dispatches them to views when necessary. - -The exact use of :func:`World::update` depends on the application. -Plugins typically call it with a ``timeout`` of 0 in a callback driven by the host. -This avoids blocking the main loop, -since other plugins and the host itself need to run as well. - -A program can use whatever timeout is appropriate: -event-driven applications may wait forever by using a ``timeout`` of -1, -while those that draw continuously may use a significant fraction of the frame period -(with enough time left over to render). - -Redrawing ---------- - -Occasional redrawing can be requested by calling :func:`View::postRedisplay` or :func:`View::postRedisplayRect`. -After these are called, -a :type:`ExposeEvent` will be dispatched on the next call to :func:`World::update`. -Note, however, that this will not wake up a blocked :func:`World::update` call on MacOS -(which does not handle drawing via events). - -For continuous redrawing, -call :func:`View::postRedisplay` while handling a :type:`UpdateEvent`. -This event is sent just before views are redrawn, -so it can be used as a hook to expand the update region right before the view is exposed. -Anything else that needs to be done every frame can be handled similarly. - -.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/ - -.. rubric:: Footnotes +.. toctree:: -.. [#f1] MacOS has a strong distinction between - `views <https://developer.apple.com/documentation/appkit/nsview>`_, - which may be nested, and - `windows <https://developer.apple.com/documentation/appkit/nswindow>`_, - which may not. - On Windows and X11, everything is a nestable window, - but top-level windows are configured differently. + world + view + events + event-loop + shutting-down diff --git a/doc/cpp/view.rst b/doc/cpp/view.rst new file mode 100644 index 0000000..3f5aee8 --- /dev/null +++ b/doc/cpp/view.rst @@ -0,0 +1,299 @@ +.. default-domain:: cpp +.. highlight:: cpp +.. namespace:: pugl + +############### +Creating a View +############### + +A `view` is a drawable region that receives events. +You may think of it as a window, +though it may be embedded and not represent a top-level system window. [#f1]_ + +Pugl communicates with views by dispatching events. +For flexibility, the event handler can be a different object than the view. +This allows using :class:`View` along with a separate event handler class. +Alternatively, a view class can inherit from :class:`View` and set itself as its event handler, +for a more object-oriented style. + +This documentation will use the latter approach, +so we will define a class for our view that contains everything needed: + +.. code-block:: cpp + + class MyView : public pugl::View + { + public: + explicit MyView(pugl::World& world) + : pugl::View{world} + { + setEventHandler(*this); + } + + pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; + pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; + + // With other handlers here as needed... + + // Fallback handler for all other events + template<PuglEventType t, class Base> + pugl::Status onEvent(const pugl::Event<t, Base>&) noexcept + { + return pugl::Status::success; + } + + private: + // Some data... + }; + +Pugl will call an ``onEvent`` method of the event handler (the view in this case) for every event. + +Note that Pugl uses a static dispatching mechanism rather than virtual functions to minimize overhead. +It is therefore necessary for the final class to define a handler for every event type. +A terse way to do this without writing every implementation is to define a fallback handler as a template, +as in the example above. +Alternatively, you can define an explicit handler for each event that simply returns :enumerator:`Status::success`. +This way, it will be a compile error if any event is not explicitly handled. + +********************* +Configuring the Frame +********************* + +Before display, +the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set. +These allow the window system (or plugin host) to arrange the view properly. + +Derived classes can configure themselves during construction, +but we assume here that configuration is being done outside the view. +For example: + +.. code-block:: cpp + + const double defaultWidth = 1920.0; + const double defaultHeight = 1080.0; + + view.setWindowTitle("My Window"); + view.setDefaultSize(defaultWidth, defaultHeight); + view.setMinSize(defaultWidth / 4.0, defaultHeight / 4.0); + view.setAspectRatio(1, 1, 16, 9); + +There are also several :type:`hints <PuglViewHint>` for basic attributes that can be set: + +.. code-block:: cpp + + view.setHint(pugl::ViewHint::resizable, true); + view.setHint(pugl::ViewHint::ignoreKeyRepeat, true); + +********* +Embedding +********* + +To embed the view in another window, +you will need to somehow get the :type:`native view handle <pugl::NativeView>` for the parent, +then set it with :func:`View::setParentWindow`. +If the parent is a Pugl view, +the native handle can be accessed with :func:`View::nativeWindow`. +For example: + +.. code-block:: cpp + + view.setParentWindow(view, parent.getNativeWindow()); + +***************** +Setting a Backend +***************** + +Before being realized, the view must have a backend set with :func:`View::setBackend`. + +The backend manages the graphics API that will be used for drawing. +Pugl includes backends and supporting API for +:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`. + +Using Cairo +=========== + +Cairo-specific API is declared in the ``cairo.hpp`` header: + +.. code-block:: cpp + + #include <pugl/cairo.hpp> + +The Cairo backend is provided by :func:`cairoBackend()`: + +.. code-block:: cpp + + view.setBackend(pugl::cairoBackend()); + +No additional configuration is required for Cairo. +To draw when handling an expose event, +the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`View::context`: + +.. code-block:: cpp + + cairo_t* cr = static_cast<cairo_t*>(view.context()); + +Using OpenGL +============ + +OpenGL-specific API is declared in the ``gl.hpp`` header: + +.. code-block:: cpp + + #include <pugl/gl.hpp> + +The OpenGL backend is provided by :func:`glBackend()`: + +.. code-block:: cpp + + view.setBackend(pugl::glBackend()); + +Some hints must also be set so that the context can be set up correctly. +For example, to use OpenGL 3.3 Core Profile: + +.. code-block:: cpp + + view.setHint(pugl::ViewHint::useCompatProfile, false); + view.setHint(pugl::ViewHint::contextVersionMajor, 3); + view.setHint(pugl::ViewHint::contextVersionMinor, 3); + +If you need to perform some setup using the OpenGL API, +there are two ways to do so. + +The OpenGL context is active when +:type:`CreateEvent` and +:type:`DestroyEvent` +events are dispatched, +so things like creating and destroying shaders and textures can be done then. + +Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler, +:func:`enterContext` and :func:`leaveContext` can be used to manually activate the OpenGL context during application setup. +Note, however, that unlike many other APIs, these functions must not be used for drawing. +It is only valid to use the OpenGL API for configuration in a manually entered context, +rendering will not work. +For example: + +.. code-block:: cpp + + pugl::enterContext(view); + myApp.setupOpenGL(); + pugl::leaveContext(view); + + while (!myApp.quit()) { + world.update(0.0); + } + + pugl::enterContext(view); + myApp.teardownOpenGL(); + pugl::leaveContext(view); + +Using Vulkan +============ + +Vulkan-specific API is declared in the ``vulkan.hpp`` header. +This header includes Vulkan headers, +so if you are dynamically loading Vulkan at runtime, +you should define ``VK_NO_PROTOTYPES`` before including it. + +.. code-block:: cpp + + #define VK_NO_PROTOTYPES + + #include <pugl/vulkan.hpp> + +The Vulkan backend is provided by :func:`vulkanBackend()`: + +.. code-block:: cpp + + view.setBackend(pugl::vulkanBackend()); + +Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly. +Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API. + +Loading Vulkan +-------------- + +For maximum compatibility, +it is best to not link to Vulkan at compile-time, +but instead load the Vulkan API at run-time. +To do so, first create a :class:`VulkanLoader`: + +.. code-block:: cpp + + pugl::VulkanLoader loader{world}; + +The loader manages the dynamically loaded Vulkan library, +so it must be kept alive for as long as the application is using Vulkan. +You can get the function used to load Vulkan functions with :func:`VulkanLoader::getInstanceProcAddrFunc`: + +.. code-block:: cpp + + auto vkGetInstanceProcAddr = loader.getInstanceProcAddrFunc(); + +It is best to use this function to load everything at run time, +rather than link to the Vulkan library at run time. +You can, for example, pass this to get the ``vkCreateInstance`` function using this, +then use that to create your Vulkan instance. +In practice, you will want to use some loader or wrapper API since there are many Vulkan functions. + +It is not necessary to use :class:`VulkanLoader`, +you can, for example, use the ``DynamicLoader`` from ``vulkan.hpp`` in the Vulkan SDK instead. + +The details of using Vulkan are far beyond the scope of this documentation, +but Pugl provides a portable function, :func:`createSurface`, +to get the Vulkan surface for a view. +Assuming you have somehow created your ``VkInstance``, +you can get the surface for a view using :func:`createSurface`: + +.. code-block:: cpp + + VkSurfaceKHR* surface = nullptr; + puglCreateSurface(loader.getDeviceProcAddrFunc(), + view, + vulkanInstance, + nullptr, + &surface); + +Pugl does not provide API that uses ``vulkan.hpp`` to avoid the onerous dependency, +but if you are using it with exceptions and unique handles, +it is straightforward to wrap the surface handle yourself. + +**************** +Showing the View +**************** + +Once the view is configured, it can be "realized" with :func:`View::realize`. +This creates a "real" system view, for example: + +.. code-block:: cpp + + pugl::Status status = view.realize(); + if (status != pugl::Status::success) { + std::cerr << "Error realizing view: " << pugl::strerror(status) << "\n"; + } + +Note that realizing a view can fail for many reasons, +so the return code should always be checked. +This is generally the case for any function that interacts with the window system. +Most functions also return a :enum:`Status`, +but these checks are omitted for brevity in the rest of this documentation. + +A realized view is not initially visible, +but can be shown with :func:`View::show`: + +.. code-block:: cpp + + view.show(); + +To create an initially visible view, +it is also possible to simply call :func:`View::show()` right away. +The view will be automatically realized if necessary. + +.. rubric:: Footnotes + +.. [#f1] MacOS has a strong distinction between + `views <https://developer.apple.com/documentation/appkit/nsview>`_, + which may be nested, and + `windows <https://developer.apple.com/documentation/appkit/nswindow>`_, + which may not. + On Windows and X11, everything is a nestable window, + but top-level windows are configured differently. diff --git a/doc/cpp/world.rst b/doc/cpp/world.rst new file mode 100644 index 0000000..1a3b432 --- /dev/null +++ b/doc/cpp/world.rst @@ -0,0 +1,41 @@ +.. default-domain:: cpp +.. highlight:: cpp +.. namespace:: pugl + +################ +Creating a World +################ + +The world is the top-level object which represents an instance of Pugl. +It handles the connection to the window system, +and manages views and the event loop. + +An application typically has a single world, +which is constructed once on startup and used to drive the main event loop. + +************ +Construction +************ + +A world must be created before any views, and it must outlive all of its views. +The world constructor requires an argument to specify the application type: + +.. code-block:: cpp + + pugl::World world{pugl::WorldType::program}; + +For a plugin, specify :enumerator:`WorldType::module` instead. +In some cases, it is necessary to pass additional flags. +For example, Vulkan requires thread support: + +.. code-block:: cpp + + pugl::World world{pugl::WorldType::program, pugl::WorldFlag::threads}; + +It is a good idea to set a class name for your project with :func:`World::setClassName`. +This allows the window system to distinguish different applications and, +for example, users to set up rules to manage their windows nicely: + +.. code-block:: cpp + + world.setClassName("MyAwesomeProject"); diff --git a/doc/cpp/wscript b/doc/cpp/wscript index a7aefc1..3ccb8c7 100644 --- a/doc/cpp/wscript +++ b/doc/cpp/wscript @@ -12,8 +12,12 @@ def build(bld): ("../pugl.rst", "sphinx/pugl.rst"), ("c-reference.rst", "sphinx/c-reference.rst"), ("cpp-reference.rst", "sphinx/cpp-reference.rst"), + ("event-loop.rst", "sphinx/event-loop.rst"), + ("events.rst", "sphinx/events.rst"), ("index.rst", "sphinx/index.rst"), ("overview.rst", "sphinx/overview.rst"), + ("view.rst", "sphinx/view.rst"), + ("world.rst", "sphinx/world.rst"), ] # Run Doxygen to generate XML documentation |