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