aboutsummaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/c/event-loop.rst101
-rw-r--r--doc/c/events.rst84
-rw-r--r--doc/c/overview.rst581
-rw-r--r--doc/c/shutting-down.rst20
-rw-r--r--doc/c/view.rst321
-rw-r--r--doc/c/world.rst65
-rw-r--r--doc/c/wscript5
-rw-r--r--doc/cpp/event-loop.rst37
-rw-r--r--doc/cpp/events.rst43
-rw-r--r--doc/cpp/overview.rst407
-rw-r--r--doc/cpp/view.rst299
-rw-r--r--doc/cpp/world.rst41
-rw-r--r--doc/cpp/wscript4
-rw-r--r--doc/summary.rst22
14 files changed, 1062 insertions, 968 deletions
diff --git a/doc/c/event-loop.rst b/doc/c/event-loop.rst
new file mode 100644
index 0000000..3b9915f
--- /dev/null
+++ b/doc/c/event-loop.rst
@@ -0,0 +1,101 @@
+.. default-domain:: c
+.. highlight:: c
+
+######################
+Driving the Event Loop
+######################
+
+Pugl does not contain any threads or other event loop "magic".
+For flexibility, the event loop is driven explicitly by repeatedly calling :func:`puglUpdate`,
+which processes events from the window system and dispatches them to views when necessary.
+
+The exact use of :func:`puglUpdate` depends on the application.
+Plugins should 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:`puglPostRedisplay` or :func:`puglPostRedisplayRect`.
+After these are called,
+a :struct:`PuglEventExpose` will be dispatched on the next call to :func:`puglUpdate`.
+
+For continuous redrawing,
+call :func:`puglPostRedisplay` while handling a :struct:`PuglEventUpdate` event.
+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.
+
+*****************
+Event Dispatching
+*****************
+
+Ideally, pending events are dispatched during a call to :func:`puglUpdate`,
+directly within the scope of that call.
+
+Unfortunately, this is not universally true due to differences between platforms.
+
+MacOS
+=====
+
+On MacOS, drawing is handled specially and not by the normal event queue mechanism.
+This means that configure and expose events,
+and possibly others,
+may be dispatched to a view outside the scope of a :func:`puglUpdate` call.
+In general, you can not rely on coherent event dispatching semantics on MacOS:
+the operating system can call into application code at "random" times,
+and these calls may result in Pugl events being dispatched.
+
+An application that follows the Pugl guidelines should work fine,
+but there is one significant inconsistency you may encounter on MacOS:
+posting a redisplay will not wake up a blocked :func:`puglUpdate` call.
+
+Windows
+=======
+
+On Windows, the application has relatively tight control over the event loop,
+so events are typically dispatched explicitly by :func:`puglUpdate`.
+Drawing is handled by events,
+so posting a redisplay will wake up a blocked :func:`puglUpdate` call.
+
+However, it is possible for the system to dispatch events at other times.
+So,
+it is possible for events to be dispatched outside the scope of a :func:`puglUpdate` call,
+but this does not happen in normal circumstances and can largely be ignored.
+
+X11
+===
+
+On X11, the application strictly controls event dispatching,
+and there is no way for the system to call into application code at surprising times.
+So, all events are dispatched in the scope of a :func:`puglUpdate` call.
+
+*********************
+Recursive Event Loops
+*********************
+
+On Windows and MacOS,
+the event loop is stalled while the user is resizing the window or,
+on Windows,
+has displayed the window menu.
+This means that :func:`puglUpdate` will block until the resize is finished,
+or the menu is closed.
+
+Pugl dispatches :struct:`PuglEventLoopEnter` and :struct:`PuglEventLoopLeave` events to notify the application of this situation.
+If you want to continuously redraw during resizing on these platforms,
+you can schedule a timer with :func:`puglStartTimer` when the recursive loop is entered,
+and post redisplays when handling the :struct:`PuglEventTimer`.
+Be sure to remove the timer with :func:`puglStopTimer` when the recursive loop is finished.
+
+On X11, there are no recursive event loops,
+and everything works as usual while the user is resizing the window.
+There is nothing special about a "live resize" on X11,
+and the above loop events will never be dispatched.
+
diff --git a/doc/c/events.rst b/doc/c/events.rst
new file mode 100644
index 0000000..bf964db
--- /dev/null
+++ b/doc/c/events.rst
@@ -0,0 +1,84 @@
+.. default-domain:: c
+.. highlight:: c
+
+***************
+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 event handler as a :union:`PuglEvent` union.
+The ``type`` field defines the type of the event and which field of the union is active.
+The application must handle at least :enumerator:`PUGL_CONFIGURE <PuglEventType.PUGL_CONFIGURE>`
+and :enumerator:`PUGL_EXPOSE <PuglEventType.PUGL_EXPOSE>` to draw anything,
+but there are many other :enum:`event types <PuglEventType>`.
+
+For example, a basic event handler might look something like this:
+
+.. code-block:: c
+
+ static PuglStatus
+ onEvent(PuglView* view, const PuglEvent* event)
+ {
+ MyApp* app = (MyApp*)puglGetHandle(view);
+
+ switch (event->type) {
+ case PUGL_CREATE:
+ return setupGraphics(app);
+ case PUGL_DESTROY:
+ return teardownGraphics(app);
+ case PUGL_CONFIGURE:
+ return resize(app, event->configure.width, event->configure.height);
+ case PUGL_EXPOSE:
+ return draw(app, view);
+ case PUGL_CLOSE:
+ return quit(app);
+ case PUGL_BUTTON_PRESS:
+ return onButtonPress(app, view, event->button);
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+ }
+
+Using the Graphics Context
+==========================
+
+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.
+
+Cairo Context
+-------------
+
+A Cairo context is created for each :struct:`PuglEventExpose`,
+and only exists during the handling of that event.
+Null is returned by :func:`puglGetContext` at any other time.
+
+OpenGL Context
+--------------
+
+The OpenGL context is only active during the handling of these events:
+
+- :struct:`PuglEventCreate`
+- :struct:`PuglEventDestroy`
+- :struct:`PuglEventConfigure`
+- :struct:`PuglEventExpose`
+
+As always, drawing is only possible during an expose.
+
+Vulkan Context
+--------------
+
+With Vulkan, the graphics context is managed by the application rather than Pugl.
+However, drawing must still only be performed during an expose.
+
diff --git a/doc/c/overview.rst b/doc/c/overview.rst
index 3ebe21e..f597aab 100644
--- a/doc/c/overview.rst
+++ b/doc/c/overview.rst
@@ -1,579 +1,18 @@
.. default-domain:: c
.. highlight:: c
-The core API (excluding backend-specific components) is declared in ``pugl.h``:
+########
+Overview
+########
-.. code-block:: c
-
- #include <pugl/pugl.h>
-
-The API revolves around two main objects: the `world` and the `view`.
+The Pugl 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.
-A world is created with :func:`puglNewWorld`, for example:
-
-.. code-block:: c
-
- PuglWorld* world = puglNewWorld(PUGL_PROGRAM, 0);
-
-For a plugin, specify :enumerator:`PUGL_MODULE <PuglWorldType.PUGL_MODULE>` instead.
-In some cases, it is necessary to pass additional flags.
-For example, Vulkan requires thread support:
-
-.. code-block:: c
-
- PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS)
-
-It is a good idea to set a class name for your project with :func:`puglSetClassName`.
-This allows the window system to distinguish different applications and,
-for example, users to set up rules to manage their windows nicely:
-
-.. code-block:: c
-
- puglSetClassName(world, "MyAwesomeProject")
-
-Setting Application Data
-========================
-
-Pugl will call an event handler in the application with only a view pointer and an event,
-so there needs to be some way to access the data you use in your application.
-This is done by setting an opaque handle on the world with :func:`puglSetWorldHandle`,
-for example:
-
-.. code-block:: c
-
- puglSetWorldHandle(world, myApp);
-
-The handle can be later retrieved with :func:`puglGetWorldHandle`:
-
-.. code-block:: c
-
- MyApp* app = (MyApp*)puglGetWorldHandle(world);
-
-All non-constant data should be accessed via this handle,
-to avoid problems associated with static mutable data.
-
-***************
-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]_
-
-Creating a visible view is a multi-step process.
-When a new view is created with :func:`puglNewView`,
-it does not yet represent a "real" system view:
-
-.. code-block:: c
-
- PuglView* view = puglNewView(world);
-
-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.
-For example:
-
-.. code-block:: c
-
- const double defaultWidth = 1920.0;
- const double defaultHeight = 1080.0;
-
- puglSetWindowTitle(view, "My Window");
- puglSetDefaultSize(view, defaultWidth, defaultHeight);
- puglSetMinSize(view, defaultWidth / 4.0, defaultHeight / 4.0);
- puglSetAspectRatio(view, 1, 1, 16, 9);
-
-There are also several :enum:`hints <PuglViewHint>` for basic attributes that can be set:
-
-.. code-block:: c
-
- puglSetViewHint(view, PUGL_RESIZABLE, PUGL_TRUE);
- puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
-
-Embedding
-=========
-
-To embed the view in another window,
-you will need to somehow get the :type:`native view handle <PuglNativeView>` for the parent,
-then set it with :func:`puglSetParentWindow`.
-If the parent is a Pugl view,
-the native handle can be accessed with :func:`puglGetNativeWindow`.
-For example:
-
-.. code-block:: c
-
- puglSetParentWindow(view, puglGetNativeWindow(parent));
-
-Setting an Event Handler
-========================
-
-In order to actually do anything, a view must process events from the system.
-Pugl dispatches all events to a single :type:`event handling function <PuglEventFunc>`,
-which is set with :func:`puglSetEventFunc`:
-
-.. code-block:: c
-
- puglSetEventFunc(view, onEvent);
-
-See `Handling Events`_ below for details on writing the event handler itself.
-
-Setting View Data
-=================
-
-Since the event handler is called with only a view pointer and an event,
-there needs to be some way to access application data associated with the view.
-Similar to `Setting Application Data`_ above,
-this is done by setting an opaque handle on the view with :func:`puglSetHandle`,
-for example:
-
-.. code-block:: c
-
- puglSetHandle(view, myViewData);
-
-The handle can be later retrieved,
-likely in the event handler,
-with :func:`puglGetHandle`:
-
-.. code-block:: c
-
- MyViewData* data = (MyViewData*)puglGetHandle(view);
-
-All non-constant data should be accessed via this handle,
-to avoid problems associated with static mutable data.
-
-If data is also associated with the world,
-it can be retrieved via the view using :func:`puglGetWorld`:
-
-.. code-block:: c
-
- PuglWorld* world = puglGetWorld(view);
- MyApp* app = (MyApp*)puglGetWorldHandle(world);
-
-Setting a Backend
-=================
-
-Before being realized, the view must have a backend set with :func:`puglSetBackend`.
-
-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.h`` header:
-
-.. code-block:: c
-
- #include <pugl/cairo.h>
-
-The Cairo backend is provided by :func:`puglCairoBackend()`:
-
-.. code-block:: c
-
- puglSetBackend(view, puglCairoBackend());
-
-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:`puglGetContext`:
-
-.. code-block:: c
-
- cairo_t* cr = (cairo_t*)puglGetContext(view);
-
-Using OpenGL
-------------
-
-OpenGL-specific API is declared in the ``gl.h`` header:
-
-.. code-block:: c
-
- #include <pugl/gl.h>
-
-The OpenGL backend is provided by :func:`puglGlBackend()`:
-
-.. code-block:: c
-
- puglSetBackend(view, puglGlBackend());
-
-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:: c
-
- puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
- puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
- puglSetViewHint(view, PUGL_CONTEXT_VERSION_MINOR, 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
-:enumerator:`PUGL_CREATE <PuglEventType.PUGL_CREATE>` and
-:enumerator:`PUGL_DESTROY <PuglEventType.PUGL_DESTROY>`
-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:`puglEnterContext` and :func:`puglLeaveContext` 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:: c
-
- puglEnterContext(view);
- setupOpenGL(myApp);
- puglLeaveContext(view);
-
- while (!myApp->quit) {
- puglUpdate(world, 0.0);
- }
-
- puglEnterContext(view);
- teardownOpenGL(myApp);
- puglLeaveContext(view);
-
-Using Vulkan
-------------
-
-Vulkan-specific API is declared in the ``vulkan.h`` 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:: c
-
- #define VK_NO_PROTOTYPES
-
- #include <pugl/vulkan.h>
-
-The Vulkan backend is provided by :func:`puglVulkanBackend()`:
-
-.. code-block:: c
-
- puglSetBackend(view, puglVulkanBackend());
-
-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 :struct:`PuglVulkanLoader`:
-
-.. code-block:: c
-
- PuglVulkanLoader* loader = puglNewVulkanLoader(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:`puglGetInstanceProcAddrFunc`:
-
-.. code-block:: c
-
- PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
- puglGetInstanceProcAddrFunc(loader);
-
-This vkGetInstanceProcAddr_ function can be used to load the rest of the Vulkan API.
-For example, you can use it to get the vkCreateInstance_ function,
-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.
-
-For advanced situations,
-there is also :func:`puglGetDeviceProcAddrFunc` which retrieves the vkGetDeviceProcAddr_ function instead.
-
-The Vulkan loader is provided for convenience,
-so that applications to not need to write platform-specific code to load Vulkan.
-Its use it not mandatory and Pugl can be used with Vulkan loaded by some other method.
-
-Linking with Vulkan
-^^^^^^^^^^^^^^^^^^^
-
-If you do want to link to the Vulkan library at compile time,
-note that the Pugl Vulkan backend does not depend on it,
-so you will have to do so explicitly.
-
-Creating a Surface
-^^^^^^^^^^^^^^^^^^
-
-The details of using Vulkan are far beyond the scope of this documentation,
-but Pugl provides a portable function, :func:`puglCreateSurface`,
-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:`puglCreateSurface`:
-
-.. code-block:: c
-
- VkSurfaceKHR* surface = NULL;
- puglCreateSurface(puglGetDeviceProcAddrFunc(loader),
- view,
- vulkanInstance,
- NULL,
- &surface);
-
-Showing the View
-================
-
-Once the view is configured, it can be "realized" with :func:`puglRealize`.
-This creates a "real" system view, for example:
-
-.. code-block:: c
-
- PuglStatus status = puglRealize(view);
- if (status) {
- fprintf(stderr, "Error realizing view (%s)\n", puglStrerror(status));
- }
-
-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:`PuglStatus`,
-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:`puglShow`:
-
-.. code-block:: c
-
- puglShow(view);
-
-To create an initially visible view,
-it is also possible to simply call :func:`puglShow` 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 event handler as a :union:`PuglEvent` union.
-The ``type`` field defines the type of the event and which field of the union is active.
-The application must handle at least :enumerator:`PUGL_CONFIGURE <PuglEventType.PUGL_CONFIGURE>`
-and :enumerator:`PUGL_EXPOSE <PuglEventType.PUGL_EXPOSE>` to draw anything,
-but there are many other :enum:`event types <PuglEventType>`.
-
-For example, a basic event handler might look something like this:
-
-.. code-block:: c
-
- static PuglStatus
- onEvent(PuglView* view, const PuglEvent* event)
- {
- MyApp* app = (MyApp*)puglGetHandle(view);
-
- switch (event->type) {
- case PUGL_CREATE:
- return setupGraphics(app);
- case PUGL_DESTROY:
- return teardownGraphics(app);
- case PUGL_CONFIGURE:
- return resize(app, event->configure.width, event->configure.height);
- case PUGL_EXPOSE:
- return draw(app, view);
- case PUGL_CLOSE:
- return quit(app);
- case PUGL_BUTTON_PRESS:
- return onButtonPress(app, view, event->button);
- default:
- break;
- }
-
- return PUGL_SUCCESS;
- }
-
-Using the Graphics Context
-==========================
-
-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.
-
-Cairo Context
--------------
-
-A Cairo context is created for each :struct:`PuglEventExpose`,
-and only exists during the handling of that event.
-Null is returned by :func:`puglGetContext` at any other time.
-
-OpenGL Context
---------------
-
-The OpenGL context is only active during the handling of these events:
-
-- :struct:`PuglEventCreate`
-- :struct:`PuglEventDestroy`
-- :struct:`PuglEventConfigure`
-- :struct:`PuglEventExpose`
-
-As always, drawing is only possible during an expose.
-
-Vulkan Context
---------------
-
-With Vulkan, the graphics context is managed by the application rather than Pugl.
-However, drawing must still only be performed during an expose.
-
-**********************
-Driving the Event Loop
-**********************
-
-Pugl does not contain any threads or other event loop "magic".
-For flexibility, the event loop is driven explicitly by repeatedly calling :func:`puglUpdate`,
-which processes events from the window system and dispatches them to views when necessary.
-
-The exact use of :func:`puglUpdate` depends on the application.
-Plugins should 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:`puglPostRedisplay` or :func:`puglPostRedisplayRect`.
-After these are called,
-a :struct:`PuglEventExpose` will be dispatched on the next call to :func:`puglUpdate`.
-
-For continuous redrawing,
-call :func:`puglPostRedisplay` while handling a :struct:`PuglEventUpdate` event.
-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.
-
-Event Dispatching
-=================
-
-Ideally, pending events are dispatched during a call to :func:`puglUpdate`,
-directly within the scope of that call.
-
-Unfortunately, this is not universally true due to differences between platforms.
-
-MacOS
------
-
-On MacOS, drawing is handled specially and not by the normal event queue mechanism.
-This means that configure and expose events,
-and possibly others,
-may be dispatched to a view outside the scope of a :func:`puglUpdate` call.
-In general, you can not rely on coherent event dispatching semantics on MacOS:
-the operating system can call into application code at "random" times,
-and these calls may result in Pugl events being dispatched.
-
-An application that follows the Pugl guidelines should work fine,
-but there is one significant inconsistency you may encounter on MacOS:
-posting a redisplay will not wake up a blocked :func:`puglUpdate` call.
-
-Windows
--------
-
-On Windows, the application has relatively tight control over the event loop,
-so events are typically dispatched explicitly by :func:`puglUpdate`.
-Drawing is handled by events,
-so posting a redisplay will wake up a blocked :func:`puglUpdate` call.
-
-However, it is possible for the system to dispatch events at other times.
-So,
-it is possible for events to be dispatched outside the scope of a :func:`puglUpdate` call,
-but this does not happen in normal circumstances and can largely be ignored.
-
-X11
----
-
-On X11, the application strictly controls event dispatching,
-and there is no way for the system to call into application code at surprising times.
-So, all events are dispatched in the scope of a :func:`puglUpdate` call.
-
-Recursive Event Loops
----------------------
-
-On Windows and MacOS,
-the event loop is stalled while the user is resizing the window or,
-on Windows,
-has displayed the window menu.
-This means that :func:`puglUpdate` will block until the resize is finished,
-or the menu is closed.
-
-Pugl dispatches :struct:`PuglEventLoopEnter` and :struct:`PuglEventLoopLeave` events to notify the application of this situation.
-If you want to continuously redraw during resizing on these platforms,
-you can schedule a timer with :func:`puglStartTimer` when the recursive loop is entered,
-and post redisplays when handling the :struct:`PuglEventTimer`.
-Be sure to remove the timer with :func:`puglStopTimer` when the recursive loop is finished.
-
-On X11, there are no recursive event loops,
-and everything works as usual while the user is resizing the window.
-There is nothing special about a "live resize" on X11,
-and the above loop events will never be dispatched.
-
-*************
-Shutting Down
-*************
-
-When a view is closed,
-it will receive a :struct:`PuglEventClose`.
-An application may also set a flag based on user input or other conditions,
-which can be used to break out of the main loop and stop calling :func:`puglUpdate`.
-
-When the main event loop has finished running,
-any views and the world need to be destroyed, in that order.
-For example:
-
-.. code-block:: c
-
- puglFreeView(view);
- puglFreeWorld(world);
-
-.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/
-
-.. _vkCreateInstance: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html
-
-.. _vkGetDeviceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetDeviceProcAddr.html
-
-.. _vkGetInstanceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html
-
-.. 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/c/shutting-down.rst b/doc/c/shutting-down.rst
new file mode 100644
index 0000000..dfb56cd
--- /dev/null
+++ b/doc/c/shutting-down.rst
@@ -0,0 +1,20 @@
+.. default-domain:: c
+.. highlight:: c
+
+#############
+Shutting Down
+#############
+
+When a view is closed,
+it will receive a :struct:`PuglEventClose`.
+An application may also set a flag based on user input or other conditions,
+which can be used to break out of the main loop and stop calling :func:`puglUpdate`.
+
+When the main event loop has finished running,
+any views and the world need to be destroyed, in that order.
+For example:
+
+.. code-block:: c
+
+ puglFreeView(view);
+ puglFreeWorld(world);
diff --git a/doc/c/view.rst b/doc/c/view.rst
new file mode 100644
index 0000000..12f146d
--- /dev/null
+++ b/doc/c/view.rst
@@ -0,0 +1,321 @@
+.. default-domain:: c
+.. highlight:: c
+
+###############
+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]_
+
+Creating a visible view is a multi-step process.
+When a new view is created with :func:`puglNewView`,
+it does not yet represent a "real" system view:
+
+.. code-block:: c
+
+ PuglView* view = puglNewView(world);
+
+*********************
+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.
+For example:
+
+.. code-block:: c
+
+ const double defaultWidth = 1920.0;
+ const double defaultHeight = 1080.0;
+
+ puglSetWindowTitle(view, "My Window");
+ puglSetDefaultSize(view, defaultWidth, defaultHeight);
+ puglSetMinSize(view, defaultWidth / 4.0, defaultHeight / 4.0);
+ puglSetAspectRatio(view, 1, 1, 16, 9);
+
+There are also several :enum:`hints <PuglViewHint>` for basic attributes that can be set:
+
+.. code-block:: c
+
+ puglSetViewHint(view, PUGL_RESIZABLE, PUGL_TRUE);
+ puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
+
+*********
+Embedding
+*********
+
+To embed the view in another window,
+you will need to somehow get the :type:`native view handle <PuglNativeView>` for the parent,
+then set it with :func:`puglSetParentWindow`.
+If the parent is a Pugl view,
+the native handle can be accessed with :func:`puglGetNativeWindow`.
+For example:
+
+.. code-block:: c
+
+ puglSetParentWindow(view, puglGetNativeWindow(parent));
+
+************************
+Setting an Event Handler
+************************
+
+In order to actually do anything, a view must process events from the system.
+Pugl dispatches all events to a single :type:`event handling function <PuglEventFunc>`,
+which is set with :func:`puglSetEventFunc`:
+
+.. code-block:: c
+
+ puglSetEventFunc(view, onEvent);
+
+See :doc:`events` for details on writing the event handler itself.
+
+*****************
+Setting View Data
+*****************
+
+Since the event handler is called with only a view pointer and an event,
+there needs to be some way to access application data associated with the view.
+Similar to :ref:`setting application data <setting-application-data>`,
+this is done by setting an opaque handle on the view with :func:`puglSetHandle`,
+for example:
+
+.. code-block:: c
+
+ puglSetHandle(view, myViewData);
+
+The handle can be later retrieved,
+likely in the event handler,
+with :func:`puglGetHandle`:
+
+.. code-block:: c
+
+ MyViewData* data = (MyViewData*)puglGetHandle(view);
+
+All non-constant data should be accessed via this handle,
+to avoid problems associated with static mutable data.
+
+If data is also associated with the world,
+it can be retrieved via the view using :func:`puglGetWorld`:
+
+.. code-block:: c
+
+ PuglWorld* world = puglGetWorld(view);
+ MyApp* app = (MyApp*)puglGetWorldHandle(world);
+
+*****************
+Setting a Backend
+*****************
+
+Before being realized, the view must have a backend set with :func:`puglSetBackend`.
+
+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.h`` header:
+
+.. code-block:: c
+
+ #include <pugl/cairo.h>
+
+The Cairo backend is provided by :func:`puglCairoBackend()`:
+
+.. code-block:: c
+
+ puglSetBackend(view, puglCairoBackend());
+
+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:`puglGetContext`:
+
+.. code-block:: c
+
+ cairo_t* cr = (cairo_t*)puglGetContext(view);
+
+Using OpenGL
+============
+
+OpenGL-specific API is declared in the ``gl.h`` header:
+
+.. code-block:: c
+
+ #include <pugl/gl.h>
+
+The OpenGL backend is provided by :func:`puglGlBackend()`:
+
+.. code-block:: c
+
+ puglSetBackend(view, puglGlBackend());
+
+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:: c
+
+ puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
+ puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
+ puglSetViewHint(view, PUGL_CONTEXT_VERSION_MINOR, 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
+:enumerator:`PUGL_CREATE <PuglEventType.PUGL_CREATE>` and
+:enumerator:`PUGL_DESTROY <PuglEventType.PUGL_DESTROY>`
+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:`puglEnterContext` and :func:`puglLeaveContext` 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:: c
+
+ puglEnterContext(view);
+ setupOpenGL(myApp);
+ puglLeaveContext(view);
+
+ while (!myApp->quit) {
+ puglUpdate(world, 0.0);
+ }
+
+ puglEnterContext(view);
+ teardownOpenGL(myApp);
+ puglLeaveContext(view);
+
+Using Vulkan
+============
+
+Vulkan-specific API is declared in the ``vulkan.h`` 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:: c
+
+ #define VK_NO_PROTOTYPES
+
+ #include <pugl/vulkan.h>
+
+The Vulkan backend is provided by :func:`puglVulkanBackend()`:
+
+.. code-block:: c
+
+ puglSetBackend(view, puglVulkanBackend());
+
+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 :struct:`PuglVulkanLoader`:
+
+.. code-block:: c
+
+ PuglVulkanLoader* loader = puglNewVulkanLoader(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:`puglGetInstanceProcAddrFunc`:
+
+.. code-block:: c
+
+ PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
+ puglGetInstanceProcAddrFunc(loader);
+
+This vkGetInstanceProcAddr_ function can be used to load the rest of the Vulkan API.
+For example, you can use it to get the vkCreateInstance_ function,
+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.
+
+For advanced situations,
+there is also :func:`puglGetDeviceProcAddrFunc` which retrieves the vkGetDeviceProcAddr_ function instead.
+
+The Vulkan loader is provided for convenience,
+so that applications to not need to write platform-specific code to load Vulkan.
+Its use it not mandatory and Pugl can be used with Vulkan loaded by some other method.
+
+Linking with Vulkan
+-------------------
+
+If you do want to link to the Vulkan library at compile time,
+note that the Pugl Vulkan backend does not depend on it,
+so you will have to do so explicitly.
+
+Creating a Surface
+------------------
+
+The details of using Vulkan are far beyond the scope of this documentation,
+but Pugl provides a portable function, :func:`puglCreateSurface`,
+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:`puglCreateSurface`:
+
+.. code-block:: c
+
+ VkSurfaceKHR* surface = NULL;
+ puglCreateSurface(puglGetDeviceProcAddrFunc(loader),
+ view,
+ vulkanInstance,
+ NULL,
+ &surface);
+
+****************
+Showing the View
+****************
+
+Once the view is configured, it can be "realized" with :func:`puglRealize`.
+This creates a "real" system view, for example:
+
+.. code-block:: c
+
+ PuglStatus status = puglRealize(view);
+ if (status) {
+ fprintf(stderr, "Error realizing view (%s)\n", puglStrerror(status));
+ }
+
+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:`PuglStatus`,
+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:`puglShow`:
+
+.. code-block:: c
+
+ puglShow(view);
+
+To create an initially visible view,
+it is also possible to simply call :func:`puglShow` 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.
+
+.. _vkCreateInstance: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html
+
+.. _vkGetDeviceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetDeviceProcAddr.html
+
+.. _vkGetInstanceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html
diff --git a/doc/c/world.rst b/doc/c/world.rst
new file mode 100644
index 0000000..83d9dbd
--- /dev/null
+++ b/doc/c/world.rst
@@ -0,0 +1,65 @@
+################
+Creating a World
+################
+
+.. default-domain:: c
+.. highlight:: c
+
+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.
+A world is created with :func:`puglNewWorld`, for example:
+
+.. code-block:: c
+
+ PuglWorld* world = puglNewWorld(PUGL_PROGRAM, 0);
+
+For a plugin, specify :enumerator:`PUGL_MODULE <PuglWorldType.PUGL_MODULE>` instead.
+In some cases, it is necessary to pass additional flags.
+For example, Vulkan requires thread support:
+
+.. code-block:: c
+
+ PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS)
+
+It is a good idea to set a class name for your project with :func:`puglSetClassName`.
+This allows the window system to distinguish different applications and,
+for example, users to set up rules to manage their windows nicely:
+
+.. code-block:: c
+
+ puglSetClassName(world, "MyAwesomeProject")
+
+.. _setting-application-data:
+
+************************
+Setting Application Data
+************************
+
+Pugl will call an event handler in the application with only a view pointer and an event,
+so there needs to be some way to access the data you use in your application.
+This is done by setting an opaque handle on the world with :func:`puglSetWorldHandle`,
+for example:
+
+.. code-block:: c
+
+ puglSetWorldHandle(world, myApp);
+
+The handle can be later retrieved with :func:`puglGetWorldHandle`:
+
+.. code-block:: c
+
+ MyApp* app = (MyApp*)puglGetWorldHandle(world);
+
+All non-constant data should be accessed via this handle,
+to avoid problems associated with static mutable data.
+
diff --git a/doc/c/wscript b/doc/c/wscript
index 4e0fbc9..389c43f 100644
--- a/doc/c/wscript
+++ b/doc/c/wscript
@@ -10,9 +10,14 @@ def build(bld):
("../_templates/about.html", "sphinx/_templates/about.html"),
("../deployment.rst", "sphinx/deployment.rst"),
("../pugl.rst", "sphinx/pugl.rst"),
+ ("event-loop.rst", "sphinx/event-loop.rst"),
+ ("events.rst", "sphinx/events.rst"),
("index.rst", "sphinx/index.rst"),
("overview.rst", "sphinx/overview.rst"),
("reference.rst", "sphinx/reference.rst"),
+ ("shutting-down.rst", "sphinx/shutting-down.rst"),
+ ("view.rst", "sphinx/view.rst"),
+ ("world.rst", "sphinx/world.rst"),
]
# Run Doxygen to generate XML documentation
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
diff --git a/doc/summary.rst b/doc/summary.rst
new file mode 100644
index 0000000..f05515f
--- /dev/null
+++ b/doc/summary.rst
@@ -0,0 +1,22 @@
+Pugl is an API for writing portable and embeddable GUIs.
+Pugl is not a toolkit or framework,
+but a minimal portability layer that sets up a drawing context and delivers events.
+
+Compared to other libraries,
+Pugl is particularly suitable for use in plugins or other loadable modules.
+There is no implicit context or static data in the library,
+so it may be statically linked and used multiple times in the same process.
+
+Pugl has a modular design that separates the core library from graphics backends.
+The core library is graphics agnostic,
+it implements platform support and depends only on standard system libraries.
+MacOS, Windows, and X11 are currently supported as platforms.
+
+Graphics backends are separate so that applications only depend on the API that they use.
+Pugl includes graphics backends for Cairo_, OpenGL_, and Vulkan_.
+It is also possible to use some other graphics API by implementing a custom backend,
+or simply accessing the native platform handle for a window.
+
+.. _Cairo: https://www.cairographics.org/
+.. _OpenGL: https://www.opengl.org/
+.. _Vulkan: https://www.khronos.org/vulkan/