1.. default-domain:: c
2.. highlight:: c
3
4The core API (excluding backend-specific components) is declared in ``pugl.h``:
5
6.. code-block:: c
7
8   #include <pugl/pugl.h>
9
10The API revolves around two main objects: the `world` and the `view`.
11An application creates a world to manage top-level state,
12then creates one or more views to display.
13
14****************
15Creating a World
16****************
17
18The world is the top-level object which represents an instance of Pugl.
19It handles the connection to the window system,
20and manages views and the event loop.
21
22An application typically has a single world,
23which is constructed once on startup and used to drive the main event loop.
24
25Construction
26============
27
28A world must be created before any views, and it must outlive all of its views.
29A world is created with :func:`puglNewWorld`, for example:
30
31.. code-block:: c
32
33   PuglWorld* world = puglNewWorld(PUGL_PROGRAM, 0);
34
35For a plugin, specify :enumerator:`PUGL_MODULE <PuglWorldType.PUGL_MODULE>` instead.
36In some cases, it is necessary to pass additional flags.
37For example, Vulkan requires thread support:
38
39.. code-block:: c
40
41   PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS)
42
43It is a good idea to set a class name for your project with :func:`puglSetClassName`.
44This allows the window system to distinguish different applications and,
45for example, users to set up rules to manage their windows nicely:
46
47.. code-block:: c
48
49   puglSetClassName(world, "MyAwesomeProject")
50
51Setting Application Data
52========================
53
54Pugl will call an event handler in the application with only a view pointer and an event,
55so there needs to be some way to access the data you use in your application.
56This is done by setting an opaque handle on the world with :func:`puglSetWorldHandle`,
57for example:
58
59.. code-block:: c
60
61   puglSetWorldHandle(world, myApp);
62
63The handle can be later retrieved with :func:`puglGetWorldHandle`:
64
65.. code-block:: c
66
67   MyApp* app = (MyApp*)puglGetWorldHandle(world);
68
69All non-constant data should be accessed via this handle,
70to avoid problems associated with static mutable data.
71
72***************
73Creating a View
74***************
75
76A view is a drawable region that receives events.
77You may think of it as a window,
78though it may be embedded and not represent a top-level system window. [#f1]_
79
80Creating a visible view is a multi-step process.
81When a new view is created with :func:`puglNewView`,
82it does not yet represent a "real" system view:
83
84.. code-block:: c
85
86   PuglView* view = puglNewView(world);
87
88Configuring the Frame
89=====================
90
91Before display,
92the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set.
93These allow the window system (or plugin host) to arrange the view properly.
94For example:
95
96.. code-block:: c
97
98   const double defaultWidth  = 1920.0;
99   const double defaultHeight = 1080.0;
100
101   puglSetWindowTitle(view, "My Window");
102   puglSetDefaultSize(view, defaultWidth, defaultHeight);
103   puglSetMinSize(view, defaultWidth / 4.0, defaultHeight / 4.0);
104   puglSetAspectRatio(view, 1, 1, 16, 9);
105
106There are also several :enum:`hints <PuglViewHint>` for basic attributes that can be set:
107
108.. code-block:: c
109
110   puglSetViewHint(view, PUGL_RESIZABLE, PUGL_TRUE);
111   puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
112
113Embedding
114=========
115
116To embed the view in another window,
117you will need to somehow get the :type:`native view handle <PuglNativeView>` for the parent,
118then set it with :func:`puglSetParentWindow`.
119If the parent is a Pugl view,
120the native handle can be accessed with :func:`puglGetNativeWindow`.
121For example:
122
123.. code-block:: c
124
125   puglSetParentWindow(view, puglGetNativeWindow(parent));
126
127Setting an Event Handler
128========================
129
130In order to actually do anything, a view must process events from the system.
131Pugl dispatches all events to a single :type:`event handling function <PuglEventFunc>`,
132which is set with :func:`puglSetEventFunc`:
133
134.. code-block:: c
135
136   puglSetEventFunc(view, onEvent);
137
138See `Handling Events`_ below for details on writing the event handler itself.
139
140Setting View Data
141=================
142
143Since the event handler is called with only a view pointer and an event,
144there needs to be some way to access application data associated with the view.
145Similar to `Setting Application Data`_ above,
146this is done by setting an opaque handle on the view with :func:`puglSetHandle`,
147for example:
148
149.. code-block:: c
150
151   puglSetHandle(view, myViewData);
152
153The handle can be later retrieved,
154likely in the event handler,
155with :func:`puglGetHandle`:
156
157.. code-block:: c
158
159   MyViewData* data = (MyViewData*)puglGetHandle(view);
160
161All non-constant data should be accessed via this handle,
162to avoid problems associated with static mutable data.
163
164If data is also associated with the world,
165it can be retrieved via the view using :func:`puglGetWorld`:
166
167.. code-block:: c
168
169   PuglWorld* world = puglGetWorld(view);
170   MyApp*     app   = (MyApp*)puglGetWorldHandle(world);
171
172Setting a Backend
173=================
174
175Before being realized, the view must have a backend set with :func:`puglSetBackend`.
176
177The backend manages the graphics API that will be used for drawing.
178Pugl includes backends and supporting API for
179:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`.
180
181Using Cairo
182-----------
183
184Cairo-specific API is declared in the ``cairo.h`` header:
185
186.. code-block:: c
187
188   #include <pugl/cairo.h>
189
190The Cairo backend is provided by :func:`puglCairoBackend()`:
191
192.. code-block:: c
193
194   puglSetBackend(view, puglCairoBackend());
195
196No additional configuration is required for Cairo.
197To draw when handling an expose event,
198the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`puglGetContext`:
199
200.. code-block:: c
201
202   cairo_t* cr = (cairo_t*)puglGetContext(view);
203
204Using OpenGL
205------------
206
207OpenGL-specific API is declared in the ``gl.h`` header:
208
209.. code-block:: c
210
211   #include <pugl/gl.h>
212
213The OpenGL backend is provided by :func:`puglGlBackend()`:
214
215.. code-block:: c
216
217   puglSetBackend(view, puglGlBackend());
218
219Some hints must also be set so that the context can be set up correctly.
220For example, to use OpenGL 3.3 Core Profile:
221
222.. code-block:: c
223
224   puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
225   puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
226   puglSetViewHint(view, PUGL_CONTEXT_VERSION_MINOR, 3);
227
228If you need to perform some setup using the OpenGL API,
229there are two ways to do so.
230
231The OpenGL context is active when
232:enumerator:`PUGL_CREATE <PuglEventType.PUGL_CREATE>` and
233:enumerator:`PUGL_DESTROY <PuglEventType.PUGL_DESTROY>`
234events are dispatched,
235so things like creating and destroying shaders and textures can be done then.
236
237Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler,
238:func:`puglEnterContext` and :func:`puglLeaveContext` can be used to manually activate the OpenGL context during application setup.
239Note, however, that unlike many other APIs, these functions must not be used for drawing.
240It is only valid to use the OpenGL API for configuration in a manually entered context,
241rendering will not work.
242For example:
243
244.. code-block:: c
245
246   puglEnterContext(view);
247   setupOpenGL(myApp);
248   puglLeaveContext(view);
249
250   while (!myApp->quit) {
251     puglUpdate(world, 0.0);
252   }
253
254   puglEnterContext(view);
255   teardownOpenGL(myApp);
256   puglLeaveContext(view);
257
258Using Vulkan
259------------
260
261Vulkan-specific API is declared in the ``vulkan.h`` header.
262This header includes Vulkan headers,
263so if you are dynamically loading Vulkan at runtime,
264you should define ``VK_NO_PROTOTYPES`` before including it.
265
266.. code-block:: c
267
268   #define VK_NO_PROTOTYPES
269
270   #include <pugl/vulkan.h>
271
272The Vulkan backend is provided by :func:`puglVulkanBackend()`:
273
274.. code-block:: c
275
276   puglSetBackend(view, puglVulkanBackend());
277
278Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly.
279Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API.
280
281Loading Vulkan
282^^^^^^^^^^^^^^
283
284For maximum compatibility,
285it is best to not link to Vulkan at compile-time,
286but instead load the Vulkan API at run-time.
287To do so, first create a :struct:`PuglVulkanLoader`:
288
289.. code-block:: c
290
291   PuglVulkanLoader* loader = puglNewVulkanLoader(world);
292
293The loader manages the dynamically loaded Vulkan library,
294so it must be kept alive for as long as the application is using Vulkan.
295You can get the function used to load Vulkan functions with :func:`puglGetInstanceProcAddrFunc`:
296
297.. code-block:: c
298
299   PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
300     puglGetInstanceProcAddrFunc(loader);
301
302This vkGetInstanceProcAddr_ function can be used to load the rest of the Vulkan API.
303For example, you can use it to get the vkCreateInstance_ function,
304then use that to create your Vulkan instance.
305In practice, you will want to use some loader or wrapper API since there are many Vulkan functions.
306
307For advanced situations,
308there is also :func:`puglGetDeviceProcAddrFunc` which retrieves the vkGetDeviceProcAddr_ function instead.
309
310The Vulkan loader is provided for convenience,
311so that applications to not need to write platform-specific code to load Vulkan.
312Its use it not mandatory and Pugl can be used with Vulkan loaded by some other method.
313
314Linking with Vulkan
315^^^^^^^^^^^^^^^^^^^
316
317If you do want to link to the Vulkan library at compile time,
318note that the Pugl Vulkan backend does not depend on it,
319so you will have to do so explicitly.
320
321Creating a Surface
322^^^^^^^^^^^^^^^^^^
323
324The details of using Vulkan are far beyond the scope of this documentation,
325but Pugl provides a portable function, :func:`puglCreateSurface`,
326to get the Vulkan surface for a view.
327Assuming you have somehow created your ``VkInstance``,
328you can get the surface for a view using :func:`puglCreateSurface`:
329
330.. code-block:: c
331
332   VkSurfaceKHR* surface = NULL;
333   puglCreateSurface(puglGetDeviceProcAddrFunc(loader),
334                     view,
335                     vulkanInstance,
336                     NULL,
337                     &surface);
338
339Showing the View
340================
341
342Once the view is configured, it can be "realized" with :func:`puglRealize`.
343This creates a "real" system view, for example:
344
345.. code-block:: c
346
347   PuglStatus status = puglRealize(view);
348   if (status) {
349     fprintf(stderr, "Error realizing view (%s)\n", puglStrerror(status));
350   }
351
352Note that realizing a view can fail for many reasons,
353so the return code should always be checked.
354This is generally the case for any function that interacts with the window system.
355Most functions also return a :enum:`PuglStatus`,
356but these checks are omitted for brevity in the rest of this documentation.
357
358A realized view is not initially visible,
359but can be shown with :func:`puglShow`:
360
361.. code-block:: c
362
363   puglShow(view);
364
365To create an initially visible view,
366it is also possible to simply call :func:`puglShow` right away.
367The view will be automatically realized if necessary.
368
369***************
370Handling Events
371***************
372
373Events are sent to a view when it has received user input,
374must be drawn, or in other situations that may need to be handled such as resizing.
375
376Events are sent to the event handler as a :union:`PuglEvent` union.
377The ``type`` field defines the type of the event and which field of the union is active.
378The application must handle at least :enumerator:`PUGL_CONFIGURE <PuglEventType.PUGL_CONFIGURE>`
379and :enumerator:`PUGL_EXPOSE <PuglEventType.PUGL_EXPOSE>` to draw anything,
380but there are many other :enum:`event types <PuglEventType>`.
381
382For example, a basic event handler might look something like this:
383
384.. code-block:: c
385
386   static PuglStatus
387   onEvent(PuglView* view, const PuglEvent* event)
388   {
389     MyApp* app = (MyApp*)puglGetHandle(view);
390
391     switch (event->type) {
392     case PUGL_CREATE:
393       return setupGraphics(app);
394     case PUGL_DESTROY:
395       return teardownGraphics(app);
396     case PUGL_CONFIGURE:
397       return resize(app, event->configure.width, event->configure.height);
398     case PUGL_EXPOSE:
399       return draw(app, view);
400     case PUGL_CLOSE:
401       return quit(app);
402     case PUGL_BUTTON_PRESS:
403        return onButtonPress(app, view, event->button);
404     default:
405       break;
406     }
407
408     return PUGL_SUCCESS;
409   }
410
411Using the Graphics Context
412==========================
413
414Drawing
415-------
416
417Note that Pugl uses a different drawing model than many libraries,
418particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_.
419
420In that style of code, drawing is performed imperatively in the main loop,
421but with Pugl, the application must draw only while handling an expose event.
422This is because Pugl supports event-driven applications that only draw the damaged region when necessary,
423and handles exposure internally to provide optimized and consistent behavior across platforms.
424
425Cairo Context
426-------------
427
428A Cairo context is created for each :struct:`PuglEventExpose`,
429and only exists during the handling of that event.
430Null is returned by :func:`puglGetContext` at any other time.
431
432OpenGL Context
433--------------
434
435The OpenGL context is only active during the handling of these events:
436
437- :struct:`PuglEventCreate`
438- :struct:`PuglEventDestroy`
439- :struct:`PuglEventConfigure`
440- :struct:`PuglEventExpose`
441
442As always, drawing is only possible during an expose.
443
444Vulkan Context
445--------------
446
447With Vulkan, the graphics context is managed by the application rather than Pugl.
448However, drawing must still only be performed during an expose.
449
450**********************
451Driving the Event Loop
452**********************
453
454Pugl does not contain any threads or other event loop "magic".
455For flexibility, the event loop is driven explicitly by repeatedly calling :func:`puglUpdate`,
456which processes events from the window system and dispatches them to views when necessary.
457
458The exact use of :func:`puglUpdate` depends on the application.
459Plugins should call it with a ``timeout`` of 0 in a callback driven by the host.
460This avoids blocking the main loop,
461since other plugins and the host itself need to run as well.
462
463A program can use whatever timeout is appropriate:
464event-driven applications may wait forever by using a ``timeout`` of -1,
465while those that draw continuously may use a significant fraction of the frame period
466(with enough time left over to render).
467
468Redrawing
469=========
470
471Occasional redrawing can be requested by calling :func:`puglPostRedisplay` or :func:`puglPostRedisplayRect`.
472After these are called,
473a :struct:`PuglEventExpose` will be dispatched on the next call to :func:`puglUpdate`.
474
475For continuous redrawing,
476call :func:`puglPostRedisplay` while handling a :struct:`PuglEventUpdate` event.
477This event is sent just before views are redrawn,
478so it can be used as a hook to expand the update region right before the view is exposed.
479Anything else that needs to be done every frame can be handled similarly.
480
481Event Dispatching
482=================
483
484Ideally, pending events are dispatched during a call to :func:`puglUpdate`,
485directly within the scope of that call.
486
487Unfortunately, this is not universally true due to differences between platforms.
488
489MacOS
490-----
491
492On MacOS, drawing is handled specially and not by the normal event queue mechanism.
493This means that configure and expose events,
494and possibly others,
495may be dispatched to a view outside the scope of a :func:`puglUpdate` call.
496In general, you can not rely on coherent event dispatching semantics on MacOS:
497the operating system can call into application code at "random" times,
498and these calls may result in Pugl events being dispatched.
499
500An application that follows the Pugl guidelines should work fine,
501but there is one significant inconsistency you may encounter on MacOS:
502posting a redisplay will not wake up a blocked :func:`puglUpdate` call.
503
504Windows
505-------
506
507On Windows, the application has relatively tight control over the event loop,
508so events are typically dispatched explicitly by :func:`puglUpdate`.
509Drawing is handled by events,
510so posting a redisplay will wake up a blocked :func:`puglUpdate` call.
511
512However, it is possible for the system to dispatch events at other times.
513So,
514it is possible for events to be dispatched outside the scope of a :func:`puglUpdate` call,
515but this does not happen in normal circumstances and can largely be ignored.
516
517X11
518---
519
520On X11, the application strictly controls event dispatching,
521and there is no way for the system to call into application code at surprising times.
522So, all events are dispatched in the scope of a :func:`puglUpdate` call.
523
524Recursive Event Loops
525---------------------
526
527On Windows and MacOS,
528the event loop is stalled while the user is resizing the window or,
529on Windows,
530has displayed the window menu.
531This means that :func:`puglUpdate` will block until the resize is finished,
532or the menu is closed.
533
534Pugl dispatches :struct:`PuglEventLoopEnter` and :struct:`PuglEventLoopLeave` events to notify the application of this situation.
535If you want to continuously redraw during resizing on these platforms,
536you can schedule a timer with :func:`puglStartTimer` when the recursive loop is entered,
537and post redisplays when handling the :struct:`PuglEventTimer`.
538Be sure to remove the timer with :func:`puglStopTimer` when the recursive loop is finished.
539
540On X11, there are no recursive event loops,
541and everything works as usual while the user is resizing the window.
542There is nothing special about a "live resize" on X11,
543and the above loop events will never be dispatched.
544
545*************
546Shutting Down
547*************
548
549When a view is closed,
550it will receive a :struct:`PuglEventClose`.
551An application may also set a flag based on user input or other conditions,
552which can be used to break out of the main loop and stop calling :func:`puglUpdate`.
553
554When the main event loop has finished running,
555any views and the world need to be destroyed, in that order.
556For example:
557
558.. code-block:: c
559
560   puglFreeView(view);
561   puglFreeWorld(world);
562
563.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/
564
565.. _vkCreateInstance: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html
566
567.. _vkGetDeviceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetDeviceProcAddr.html
568
569.. _vkGetInstanceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html
570
571.. rubric:: Footnotes
572
573.. [#f1] MacOS has a strong distinction between
574   `views <https://developer.apple.com/documentation/appkit/nsview>`_,
575   which may be nested, and
576   `windows <https://developer.apple.com/documentation/appkit/nswindow>`_,
577   which may not.
578   On Windows and X11, everything is a nestable window,
579   but top-level windows are configured differently.
580