v2 / thirdparty / sokol / sokol_app.h
17540 lines · 16142 sloc · 632.95 KB · 161d51f83033318e80299998137d6905cd0cdc9f
Raw
1#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)
2#define SOKOL_APP_IMPL
3#endif
4#ifndef SOKOL_APP_INCLUDED
5
6/*
7 V language IMPORTANT NOTE: all the V patches in this code, are marked with:
8 // __v_ start
9 // __v_ end
10*/
11
12/*
13 sokol_app.h -- cross-platform application wrapper
14
15 Project URL: https://github.com/floooh/sokol
16
17 Do this:
18 #define SOKOL_IMPL or
19 #define SOKOL_APP_IMPL
20 before you include this file in *one* C or C++ file to create the
21 implementation.
22
23 In the same place define one of the following to select the 3D-API
24 which should be initialized by sokol_app.h (this must also match
25 the backend selected for sokol_gfx.h if both are used in the same
26 project):
27
28 #define SOKOL_GLCORE
29 #define SOKOL_GLES3
30 #define SOKOL_D3D11
31 #define SOKOL_METAL
32 #define SOKOL_WGPU
33 #define SOKOL_VULKAN
34 #define SOKOL_NOAPI
35
36 Optionally provide the following defines with your own implementations:
37
38 SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
39 SOKOL_UNREACHABLE() - a guard macro for unreachable code
40 (default: assert(false)) SOKOL_WIN32_FORCE_MAIN - define this on Win32
41 to add a main() entry point SOKOL_WIN32_FORCE_WINMAIN - define this on
42 Win32 to add a WinMain() entry point (enabled by default unless
43 SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY
44 is defined) SOKOL_NO_ENTRY - define this if sokol_app.h
45 shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public
46 function declaration prefix (default: extern) SOKOL_API_DECL -
47 same as SOKOL_APP_API_DECL SOKOL_API_IMPL - public function
48 implementation prefix (default: -)
49
50 Optionally define the following to force debug checks and validations
51 even in release mode:
52
53 SOKOL_DEBUG - by default this is defined if NDEBUG is not
54 defined
55
56 If sokol_app.h is compiled as a DLL, define the following before
57 including the declaration or implementation:
58
59 SOKOL_DLL
60
61 On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as
62 __declspec(dllexport) or __declspec(dllimport) as needed.
63
64 if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined,
65 it is up to the developer to define the desired subsystem.
66
67 On Linux, SOKOL_GLCORE can use either GLX or EGL.
68 GLX is default, set SOKOL_FORCE_EGL to override.
69
70 For example code, see
71 https://github.com/floooh/sokol-samples/tree/master/sapp
72
73 Portions of the Windows and Linux GL initialization, event-, icon- etc...
74 code have been taken from GLFW (http://www.glfw.org/).
75
76 iOS onscreen keyboard support 'inspired' by libgdx.
77
78 Link with the following system libraries:
79
80 - on macOS:
81 - all backends: Foundation, Cocoa, QuartzCore
82 - with SOKOL_METAL: Metal, MetalKit
83 - with SOKOL_GLCORE: OpenGL
84 - with SOKOL_WGPU: a WebGPU implementation library (tested with
85 webgpu_dawn)
86 - on iOS:
87 - all backends: Foundation, UIKit
88 - with SOKOL_METAL: Metal, MetalKit
89 - with SOKOL_GLES3: OpenGLES, GLKit
90 - on Linux:
91 - all backends: X11, Xi, Xcursor, dl, pthread, m
92 - with SOKOL_GLCORE: GL
93 - with SOKOL_GLES3: GLESv2
94 - with SOKOL_WGPU: a WebGPU implementation library (tested with
95 webgpu_dawn)
96 - with SOKOL_VULKAN: vulkan
97 - with EGL: EGL
98 - on Android: GLESv3, EGL, log, android
99 - on Windows:
100 - with MSVC or Clang: library dependencies are defined via `#pragma
101 comment`
102 - with SOKOL_WGPU: a WebGPU implementation library (tested with
103 webgpu_dawn)
104 - with SOKOL_VULKAN:
105 - install the Vulkan SDK
106 - set a header search path to $VULKAN_SDK/Include
107 - set a library search path to $VULKAN_SDK/Lib
108 - link with vulkan-1.lib
109 - with MINGW/MSYS2 gcc:
110 - compile with '-mwin32' so that _WIN32 is defined
111 - link with the following libs: -lkernel32 -luser32 -lshell32
112 - additionally with the GL backend: -lgdi32
113 - additionally with the D3D11 backend: -ld3d11 -ldxgi
114
115 On Linux, you also need to use the -pthread compiler and linker option,
116 otherwise weird things will happen, see here for details:
117 https://github.com/floooh/sokol/issues/376
118
119 For Linux+Vulkan install the following packages (or equivalents):
120 - libvulkan-dev
121 - vulkan-validationlayers
122 - vulkan-tools
123
124 On macOS and iOS, the implementation must be compiled as Objective-C.
125
126 On Emscripten:
127 - for WebGL2: add the linker option `-s USE_WEBGL2=1`
128 - for WebGPU: compile and link with `--use-port=emdawnwebgpu`
129 (for more exotic situations read:
130 https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md)
131
132 FEATURE OVERVIEW
133 ================
134 sokol_app.h provides a minimalistic cross-platform API which
135 implements the 'application-wrapper' parts of a 3D application:
136
137 - a common application entry function
138 - creates a window and 3D-API context/device with a swapchain
139 surface, depth-stencil-buffer surface and optionally MSAA surface
140 - makes the rendered frame visible
141 - provides keyboard-, mouse- and low-level touch-events
142 - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android
143 - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI
144
145 FEATURE/PLATFORM MATRIX
146 =======================
147 | Windows | macOS | Linux | iOS | Android | HTML5
148 --------------------+---------+-------+-------+-------+---------+--------
149 gl 4.x | YES | YES | YES | --- | --- | ---
150 gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES
151 metal | --- | YES | --- | YES | --- | ---
152 d3d11 | YES | --- | --- | --- | --- | ---
153 webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES
154 noapi | YES | TODO | TODO | --- | TODO | ---
155 KEY_DOWN | YES | YES | YES | SOME | TODO | YES
156 KEY_UP | YES | YES | YES | SOME | TODO | YES
157 CHAR | YES | YES | YES | YES | TODO | YES
158 MOUSE_DOWN | YES | YES | YES | --- | --- | YES
159 MOUSE_UP | YES | YES | YES | --- | --- | YES
160 MOUSE_SCROLL | YES | YES | YES | --- | --- | YES
161 MOUSE_MOVE | YES | YES | YES | --- | --- | YES
162 MOUSE_ENTER | YES | YES | YES | --- | --- | YES
163 MOUSE_LEAVE | YES | YES | YES | --- | --- | YES
164 TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES
165 TOUCHES_MOVED | --- | --- | --- | YES | YES | YES
166 TOUCHES_ENDED | --- | --- | --- | YES | YES | YES
167 TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES
168 RESIZED | YES | YES | YES | YES | YES | YES
169 ICONIFIED | YES | YES | YES | --- | --- | ---
170 RESTORED | YES | YES | YES | --- | --- | ---
171 FOCUSED | YES | YES | YES | --- | --- | YES
172 UNFOCUSED | YES | YES | YES | --- | --- | YES
173 SUSPENDED | --- | --- | --- | YES | YES | TODO
174 RESUMED | --- | --- | --- | YES | YES | TODO
175 QUIT_REQUESTED | YES | YES | YES | --- | --- | YES
176 IME | TODO | TODO? | TODO | ??? | TODO | ???
177 key repeat flag | YES | YES | YES | --- | --- | YES
178 windowed | YES | YES | YES | --- | --- | YES
179 fullscreen | YES | YES | YES | YES | YES | YES(3)
180 mouse hide | YES | YES | YES | --- | --- | YES
181 mouse lock | YES | YES | YES | --- | --- | YES
182 set cursor type | YES | YES | YES | --- | --- | YES
183 screen keyboard | --- | --- | --- | YES | TODO | YES
184 swap interval | YES | YES | YES | YES | TODO | YES
185 high-dpi | YES | YES | TODO | YES | YES | YES
186 clipboard | YES | YES | YES | --- | --- | YES
187 MSAA | YES | YES | YES | YES | YES | YES
188 drag'n'drop | YES | YES | YES | --- | --- | YES
189 window icon | YES | YES(1)| YES | --- | --- | YES
190
191 (1) macOS has no regular window icons, instead the dock icon is changed
192 (2) supported with EGL only (not GLX)
193 (3) fullscreen in the browser not supported on iphones
194 (4) WebGPU on native desktop platforms should be considered experimental
195 and mainly useful for debugging and benchmarking
196
197 STEP BY STEP
198 ============
199 --- Add a sokol_main() function to your code which returns a sapp_desc
200 structure with initialization parameters and callback function pointers. This
201 function is called very early, usually at the start of the
202 platform's entry function (e.g. main or WinMain). You should do as
203 little as possible here, since the rest of your code might be called
204 from another thread (this depends on the platform):
205
206 sapp_desc sokol_main(int argc, char* argv[]) {
207 return (sapp_desc) {
208 .width = 640,
209 .height = 480,
210 .init_cb = my_init_func,
211 .frame_cb = my_frame_func,
212 .cleanup_cb = my_cleanup_func,
213 .event_cb = my_event_func,
214 ...
215 };
216 }
217
218 To get any logging output in case of errors you need to provide a log
219 callback. The easiest way is via sokol_log.h:
220
221 #include "sokol_log.h"
222
223 sapp_desc sokol_main(int argc, char* argv[]) {
224 return (sapp_desc) {
225 ...
226 .logger.func = slog_func,
227 };
228 }
229
230 There are many more setup parameters, but these are the most important.
231 For a complete list search for the sapp_desc structure declaration
232 below.
233
234 DO NOT call any sokol-app function from inside sokol_main(), since
235 sokol-app will not be initialized at this point.
236
237 The .width and .height parameters are the preferred size of the 3D
238 rendering canvas. The actual size may differ from this depending on
239 platform and other circumstances. Also the canvas size may change at
240 any time (for instance when the user resizes the application window,
241 or rotates the mobile device). You can just keep .width and .height
242 zero-initialized to open a default-sized window (what "default-size"
243 exactly means is platform-specific, but usually it's a size that covers
244 most of, but not all, of the display).
245
246 All provided function callbacks will be called from the same thread,
247 but this may be different from the thread where sokol_main() was called.
248
249 .init_cb (void (*)(void))
250 This function is called once after the application window,
251 3D rendering context and swap chain have been created. The
252 function takes no arguments and has no return value.
253 .frame_cb (void (*)(void))
254 This is the per-frame callback, which is usually called 60
255 times per second. This is where your application would update
256 most of its state and perform all rendering.
257 .cleanup_cb (void (*)(void))
258 The cleanup callback is called once right before the application
259 quits.
260 .event_cb (void (*)(const sapp_event* event))
261 The event callback is mainly for input handling, but is also
262 used to communicate other types of events to the application. Keep
263 the event_cb struct member zero-initialized if your application doesn't
264 require event handling.
265
266 As you can see, those 'standard callbacks' don't have a user_data
267 argument, so any data that needs to be preserved between callbacks
268 must live in global variables. If keeping state in global variables
269 is not an option, there's an alternative set of callbacks with
270 an additional user_data pointer argument:
271
272 .user_data (void*)
273 The user-data argument for the callbacks below
274 .init_userdata_cb (void (*)(void* user_data))
275 .frame_userdata_cb (void (*)(void* user_data))
276 .cleanup_userdata_cb (void (*)(void* user_data))
277 .event_userdata_cb (void(*)(const sapp_event* event, void* user_data))
278
279 The function sapp_userdata() can be used to query the user_data
280 pointer provided in the sapp_desc struct.
281
282 You can also call sapp_query_desc() to get a copy of the
283 original sapp_desc structure.
284
285 NOTE that there's also an alternative compile mode where sokol_app.h
286 doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
287
288 --- Implement the initialization callback function (init_cb), this is called
289 once after the rendering surface, 3D API and swap chain have been
290 initialized by sokol_app. All sokol-app functions can be called
291 from inside the initialization callback, the most useful functions
292 at this point are:
293
294 int sapp_width(void)
295 int sapp_height(void)
296 Returns the current width and height of the default framebuffer in
297 pixels, this may change from one frame to the next, and it may be different
298 from the initial size provided in the sapp_desc struct.
299
300 float sapp_widthf(void)
301 float sapp_heightf(void)
302 These are alternatives to sapp_width() and sapp_height() which
303 return the default framebuffer size as float values instead of integer. This
304 may help to prevent casting back and forth between int and float
305 in more strongly typed languages than C and C++.
306
307 double sapp_frame_duration(void)
308 Returns the frame duration in seconds averaged over a number of
309 frames to smooth out any jittering spikes.
310
311 int sapp_color_format(void)
312 int sapp_depth_format(void)
313 The color and depth-stencil pixelformats of the default framebuffer,
314 as integer values which are compatible with sokol-gfx's
315 sg_pixel_format enum (so that they can be plugged directly in places
316 where sg_pixel_format is expected). Possible values are:
317
318 23 == SG_PIXELFORMAT_RGBA8
319 28 == SG_PIXELFORMAT_BGRA8
320 42 == SG_PIXELFORMAT_DEPTH
321 43 == SG_PIXELFORMAT_DEPTH_STENCIL
322
323 int sapp_sample_count(void)
324 Return the MSAA sample count of the default framebuffer.
325
326 const void* sapp_metal_get_device(void)
327 const void* sapp_metal_get_current_drawable(void)
328 const void* sapp_metal_get_depth_stencil_texture(void)
329 const void* sapp_metal_get_msaa_color_texture(void)
330 If the Metal backend has been selected, these functions return
331 pointers to various Metal API objects required for rendering, otherwise they
332 return a null pointer. These void pointers are actually Objective-C ids
333 converted with a (ARC) __bridge cast so that the ids can be tunneled through
334 C code. Also note that the returned pointers may change from one frame to the
335 next, only the Metal device object is guaranteed to stay the same.
336
337 const void* sapp_macos_get_window(void)
338 On macOS, get the NSWindow object pointer, otherwise a null pointer.
339 Before being used as Objective-C object, the void* must be converted
340 back with a (ARC) __bridge cast.
341
342 const void* sapp_ios_get_window(void)
343 On iOS, get the UIWindow object pointer, otherwise a null pointer.
344 Before being used as Objective-C object, the void* must be converted
345 back with a (ARC) __bridge cast.
346
347 const void* sapp_d3d11_get_device(void)
348 const void* sapp_d3d11_get_device_context(void)
349 const void* sapp_d3d11_get_render_view(void)
350 const void* sapp_d3d11_get_resolve_view(void);
351 const void* sapp_d3d11_get_depth_stencil_view(void)
352 Similar to the sapp_metal_* functions, the sapp_d3d11_* functions
353 return pointers to D3D11 API objects required for rendering,
354 only if the D3D11 backend has been selected. Otherwise they
355 return a null pointer. Note that the returned pointers to the
356 render-target-view and depth-stencil-view may change from one
357 frame to the next!
358
359 const void* sapp_win32_get_hwnd(void)
360 On Windows, get the window's HWND, otherwise a null pointer. The
361 HWND has been cast to a void pointer in order to be tunneled
362 through code which doesn't include Windows.h.
363
364 const void* sapp_x11_get_window(void)
365 On Linux, get the X11 Window, otherwise a null pointer. The
366 Window has been cast to a void pointer in order to be tunneled
367 through code which doesn't include X11/Xlib.h.
368
369 const void* sapp_x11_get_display(void)
370 On Linux, get the X11 Display, otherwise a null pointer. The
371 Display has been cast to a void pointer in order to be tunneled
372 through code which doesn't include X11/Xlib.h.
373
374 const void* sapp_wgpu_get_device(void)
375 const void* sapp_wgpu_get_render_view(void)
376 const void* sapp_wgpu_get_resolve_view(void)
377 const void* sapp_wgpu_get_depth_stencil_view(void)
378 These are the WebGPU-specific functions to get the WebGPU
379 objects and values required for rendering. If sokol_app.h
380 is not compiled with SOKOL_WGPU, these functions return null.
381
382 uint32_t sapp_gl_get_framebuffer(void)
383 This returns the 'default framebuffer' of the GL context.
384 Typically this will be zero.
385
386 int sapp_gl_get_major_version(void)
387 int sapp_gl_get_minor_version(void)
388 bool sapp_gl_is_gles(void)
389 Returns the major and minor version of the GL context and
390 whether the GL context is a GLES context
391
392 const void* sapp_android_get_native_activity(void);
393 On Android, get the native activity ANativeActivity pointer,
394 otherwise a null pointer.
395
396 --- Implement the frame-callback function, this function will be called
397 on the same thread as the init callback, but might be on a different
398 thread than the sokol_main() function. Note that the size of
399 the rendering framebuffer might have changed since the frame callback
400 was called last. Call the functions sapp_width() and sapp_height()
401 each frame to get the current size.
402
403 --- Optionally implement the event-callback to handle input events.
404 sokol-app provides the following type of input events:
405 - a 'virtual key' was pressed down or released
406 - a single text character was entered (provided as UTF-32 encoded
407 UNICODE code point)
408 - a mouse button was pressed down or released (left, right, middle)
409 - mouse-wheel or 2D scrolling events
410 - the mouse was moved
411 - the mouse has entered or left the application window boundaries
412 - low-level, portable multi-touch events (began, moved, ended,
413 cancelled)
414 - the application window was resized, iconified or restored
415 - the application was suspended or restored (on mobile platforms)
416 - the user or application code has asked to quit the application
417 - a string was pasted to the system clipboard
418 - one or more files have been dropped onto the application window
419
420 To explicitly 'consume' an event and prevent that the event is
421 forwarded for further handling to the operating system, call
422 sapp_consume_event() from inside the event handler (NOTE that
423 this behaviour is currently only implemented for some HTML5
424 events, support for other platforms and event types will
425 be added as needed, please open a GitHub ticket and/or provide
426 a PR if needed).
427
428 NOTE: Do *not* call any 3D API rendering functions in the event
429 callback function, since the 3D API context may not be active when the
430 event callback is called (it may work on some platforms and 3D APIs,
431 but not others, and the exact behaviour may change between
432 sokol-app versions).
433
434 --- Implement the cleanup-callback function, this is called once
435 after the user quits the application (see the section
436 "APPLICATION QUIT" for detailed information on quitting
437 behaviour, and how to intercept a pending quit - for instance to show a
438 "Really Quit?" dialog box). Note that the cleanup-callback isn't
439 guaranteed to be called on the web and mobile platforms.
440
441 MOUSE CURSOR TYPE AND VISIBILITY
442 ================================
443 You can show and hide the mouse cursor with
444
445 void sapp_show_mouse(bool show)
446
447 And to get the current shown status:
448
449 bool sapp_mouse_shown(void)
450
451 NOTE that hiding the mouse cursor is different and independent from
452 the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when
453 active (MOUSE LOCK is described below).
454
455 To change the mouse cursor to one of several predefined types, call
456 the function:
457
458 void sapp_set_mouse_cursor(sapp_mouse_cursor cursor)
459
460 Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore
461 the standard look.
462
463 To get the currently active mouse cursor type, call:
464
465 sapp_mouse_cursor sapp_get_mouse_cursor(void)
466
467 MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE)
468 ================================================
469 In normal mouse mode, no mouse movement events are reported when the
470 mouse leaves the windows client area or hits the screen border (whether
471 it's one or the other depends on the platform), and the mouse move events
472 (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in
473 framebuffer pixels in the sapp_event items mouse_x and mouse_y, and
474 relative movement in framebuffer pixels in the sapp_event items mouse_dx
475 and mouse_dy.
476
477 To get continuous mouse movement (also when the mouse leaves the window
478 client area or hits the screen border), activate mouse-lock mode
479 by calling:
480
481 sapp_lock_mouse(true)
482
483 When mouse lock is activated, the mouse pointer is hidden, the
484 reported absolute mouse position (sapp_event.mouse_x/y) appears
485 frozen, and the relative mouse movement in sapp_event.mouse_dx/dy
486 no longer has a direct relation to framebuffer pixels but instead
487 uses "raw mouse input" (what "raw mouse input" exactly means also
488 differs by platform).
489
490 To deactivate mouse lock and return to normal mouse mode, call
491
492 sapp_lock_mouse(false)
493
494 And finally, to check if mouse lock is currently active, call
495
496 if (sapp_mouse_locked()) { ... }
497
498 Note that mouse-lock state may not change immediately after
499 sapp_lock_mouse(true/false) is called, instead on some platforms the actual
500 state switch may be delayed to the end of the current frame or even to a
501 later frame.
502
503 The mouse may also be unlocked automatically without calling
504 sapp_lock_mouse(false), most notably when the application window becomes
505 inactive.
506
507 On the web platform there are further restrictions to be aware of, caused
508 by the limitations of the HTML5 Pointer Lock API:
509
510 - sapp_lock_mouse(true) can be called at any time, but it will
511 only take effect in a 'short-lived input event handler of a specific
512 type', meaning when one of the following events happens:
513 - SAPP_EVENTTYPE_MOUSE_DOWN
514 - SAPP_EVENTTYPE_MOUSE_UP
515 - SAPP_EVENTTYPE_MOUSE_SCROLL
516 - SAPP_EVENTTYPE_KEY_UP
517 - SAPP_EVENTTYPE_KEY_DOWN
518 - The mouse lock/unlock action on the web platform is asynchronous,
519 this means that sapp_mouse_locked() won't immediately return
520 the new status after calling sapp_lock_mouse(), instead the
521 reported status will only change when the pointer lock has actually
522 been activated or deactivated in the browser.
523 - On the web, mouse lock can be deactivated by the user at any time
524 by pressing the Esc key. When this happens, sokol_app.h behaves
525 the same as if sapp_lock_mouse(false) is called.
526
527 For things like camera manipulation it's most straightforward to lock
528 and unlock the mouse right from the sokol_app.h event handler, for
529 instance the following code enters and leaves mouse lock when the
530 left mouse button is pressed and released, and then uses the relative
531 movement information to manipulate a camera (taken from the
532 cgltf-sapp.c sample in the sokol-samples repository
533 at https://github.com/floooh/sokol-samples):
534
535 static void input(const sapp_event* ev) {
536 switch (ev->type) {
537 case SAPP_EVENTTYPE_MOUSE_DOWN:
538 if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
539 sapp_lock_mouse(true);
540 }
541 break;
542
543 case SAPP_EVENTTYPE_MOUSE_UP:
544 if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
545 sapp_lock_mouse(false);
546 }
547 break;
548
549 case SAPP_EVENTTYPE_MOUSE_MOVE:
550 if (sapp_mouse_locked()) {
551 cam_orbit(&state.camera, ev->mouse_dx * 0.25f,
552 ev->mouse_dy * 0.25f);
553 }
554 break;
555
556 default:
557 break;
558 }
559 }
560
561 For a 'first person shooter mouse' the following code inside the sokol-app
562 event handler is recommended somewhere in your frame callback:
563
564 if (!sapp_mouse_locked()) {
565 sapp_lock_mouse(true);
566 }
567
568 CLIPBOARD SUPPORT
569 =================
570 Applications can send and receive UTF-8 encoded text data from and to the
571 system clipboard. By default, clipboard support is disabled and
572 must be enabled at startup via the following sapp_desc struct
573 members:
574
575 sapp_desc.enable_clipboard - set to true to enable clipboard support
576 sapp_desc.clipboard_size - size of the internal clipboard buffer in
577 bytes
578
579 Enabling the clipboard will dynamically allocate a clipboard buffer
580 for UTF-8 encoded text data of the requested size in bytes, the default
581 size is 8 KBytes. Strings that don't fit into the clipboard buffer
582 (including the terminating zero) will be silently clipped, so it's
583 important that you provide a big enough clipboard size for your
584 use case.
585
586 To send data to the clipboard, call sapp_set_clipboard_string() with
587 a pointer to an UTF-8 encoded, null-terminated C-string.
588
589 NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be
590 called from inside a 'short-lived event handler', and there are a few
591 other HTML5-specific caveats to workaround. You'll basically have to
592 tinker until it works in all browsers :/ (maybe the situation will
593 improve when all browsers agree on and implement the new
594 HTML5 navigator.clipboard API).
595
596 To get data from the clipboard, check for the
597 SAPP_EVENTTYPE_CLIPBOARD_PASTED event in your event handler function, and
598 then call sapp_get_clipboard_string() to obtain the pasted UTF-8 encoded
599 text.
600
601 NOTE that behaviour of sapp_get_clipboard_string() is slightly different
602 depending on platform:
603
604 - on the HTML5 platform, the internal clipboard buffer will only be
605 updated right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, and
606 sapp_get_clipboard_string() will simply return the current content of the
607 clipboard buffer
608 - on 'native' platforms, the call to sapp_get_clipboard_string() will
609 update the internal clipboard buffer with the most recent data
610 from the system clipboard
611
612 Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event,
613 and then call sapp_get_clipboard_string() right in the event handler.
614
615 The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app
616 as follows:
617
618 - on macOS: when the Cmd+V key is pressed down
619 - on HTML5: when the browser sends a 'paste' event to the global
620 'window' object
621 - on all other platforms: when the Ctrl+V key is pressed down
622
623 DRAG AND DROP SUPPORT
624 =====================
625 PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5
626 and on the native desktop platforms (Win32, Linux and macOS) because
627 of security-related restrictions in the HTML5 drag'n'drop API. The
628 WASM/HTML5 specifics are described at the end of this documentation
629 section:
630
631 Like clipboard support, drag'n'drop support must be explicitly enabled
632 at startup in the sapp_desc struct.
633
634 sapp_desc sokol_main(void) {
635 return (sapp_desc) {
636 .enable_dragndrop = true, // default is false
637 ...
638 };
639 }
640
641 You can also adjust the maximum number of files that are accepted
642 in a drop operation, and the maximum path length in bytes if needed:
643
644 sapp_desc sokol_main(void) {
645 return (sapp_desc) {
646 .enable_dragndrop = true, // default is false
647 .max_dropped_files = 8, // default is 1
648 .max_dropped_file_path_length = 8192, // in bytes, default is
649 2048
650 ...
651 };
652 }
653
654 When drag'n'drop is enabled, the event callback will be invoked with an
655 event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on
656 the application window.
657
658 After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the
659 number of dropped files, and their absolute paths by calling separate
660 functions:
661
662 void on_event(const sapp_event* ev) {
663 if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) {
664
665 // the mouse position where the drop happened
666 float x = ev->mouse_x;
667 float y = ev->mouse_y;
668
669 // get the number of files and their paths like this:
670 const int num_dropped_files = sapp_get_num_dropped_files();
671 for (int i = 0; i < num_dropped_files; i++) {
672 const char* path = sapp_get_dropped_file_path(i);
673 ...
674 }
675 }
676 }
677
678 The returned file paths are UTF-8 encoded strings.
679
680 You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path()
681 anywhere, also outside the event handler callback, but be aware that the
682 file path strings will be overwritten with the next drop operation.
683
684 In any case, sapp_get_dropped_file_path() will never return a null pointer,
685 instead an empty string "" will be returned if the drag'n'drop feature
686 hasn't been enabled, the last drop-operation failed, or the file path index
687 is out of range.
688
689 Drag'n'drop caveats:
690
691 - if more files are dropped in a single drop-action
692 than sapp_desc.max_dropped_files, the additional
693 files will be silently ignored
694 - if any of the file paths is longer than
695 sapp_desc.max_dropped_file_path_length (in number of bytes, after
696 UTF-8 encoding) the entire drop operation will be silently ignored (this
697 needs some sort of error feedback in the future)
698 - no mouse positions are reported while the drag is in
699 process, this may change in the future
700
701 Drag'n'drop on HTML5/WASM:
702
703 The HTML5 drag'n'drop API doesn't return file paths, but instead
704 black-box 'file objects' which must be used to load the content
705 of dropped files. This is the reason why sokol_app.h adds two
706 HTML5-specific functions to the drag'n'drop API:
707
708 uint32_t sapp_html5_get_dropped_file_size(int index)
709 Returns the size in bytes of a dropped file.
710
711 void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request*
712 request) Asynchronously loads the content of a dropped file into a provided
713 memory buffer (which must be big enough to hold the file content)
714
715 To start loading the first dropped file after an
716 SAPP_EVENTTYPE_FILES_DROPPED event is received:
717
718 sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){
719 .dropped_file_index = 0,
720 .callback = fetch_cb
721 .buffer = {
722 .ptr = buf,
723 .size = sizeof(buf)
724 },
725 .user_data = ...
726 });
727
728 Make sure that the memory pointed to by 'buf' stays valid until the
729 callback function is called!
730
731 As result of the asynchronous loading operation (no matter if succeeded or
732 failed) the 'fetch_cb' function will be called:
733
734 void fetch_cb(const sapp_html5_fetch_response* response) {
735 // IMPORTANT: check if the loading operation actually succeeded:
736 if (response->succeeded) {
737 // the size of the loaded file:
738 const size_t num_bytes = response->data.size;
739 // and the pointer to the data (same as 'buf' in the
740 fetch-call): const void* ptr = response->data.ptr; } else {
741 // on error check the error code:
742 switch (response->error_code) {
743 case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL:
744 ...
745 break;
746 case SAPP_HTML5_FETCH_ERROR_OTHER:
747 ...
748 break;
749 }
750 }
751 }
752
753 Check the droptest-sapp example for a real-world example which works
754 both on native platforms and the web:
755
756 https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c
757
758 HIGH-DPI RENDERING
759 ==================
760 You can set the sapp_desc.high_dpi flag during initialization to request
761 a full-resolution framebuffer on HighDPI displays. The default behaviour
762 is sapp_desc.high_dpi=false, this means that the application will
763 render to a lower-resolution framebuffer on HighDPI displays and the
764 rendered content will be upscaled by the window system composer.
765
766 In a HighDPI scenario, you still request the same window size during
767 sokol_main(), but the framebuffer sizes returned by sapp_width()
768 and sapp_height() will be scaled up according to the DPI scaling
769 ratio.
770
771 Note that on some platforms the DPI scaling factor may change at any
772 time (for instance when a window is moved from a high-dpi display
773 to a low-dpi display).
774
775 To query the current DPI scaling factor, call the function:
776
777 float sapp_dpi_scale(void);
778
779 For instance on a Retina Mac, returning the following sapp_desc
780 struct from sokol_main():
781
782 sapp_desc sokol_main(void) {
783 return (sapp_desc) {
784 .width = 640,
785 .height = 480,
786 .high_dpi = true,
787 ...
788 };
789 }
790
791 ...the functions the functions sapp_width(), sapp_height()
792 and sapp_dpi_scale() will return the following values:
793
794 sapp_width: 1280
795 sapp_height: 960
796 sapp_dpi_scale: 2.0
797
798 If the high_dpi flag is false, or you're not running on a Retina display,
799 the values would be:
800
801 sapp_width: 640
802 sapp_height: 480
803 sapp_dpi_scale: 1.0
804
805 If the window is moved from the Retina display to a low-dpi external
806 display, the values would change as follows:
807
808 sapp_width: 1280 => 640
809 sapp_height: 960 => 480
810 sapp_dpi_scale: 2.0 => 1.0
811
812 Currently there is no event associated with a DPI change, but an
813 SAPP_EVENTTYPE_RESIZED will be sent as a side effect of the
814 framebuffer size changing.
815
816 Per-monitor DPI is currently supported on macOS and Windows.
817
818 APPLICATION QUIT
819 ================
820 Without special quit handling, a sokol_app.h application will quit
821 'gracefully' when the user clicks the window close-button unless a
822 platform's application model prevents this (e.g. on web or mobile).
823 'Graceful exit' means that the application-provided cleanup callback will
824 be called before the application quits.
825
826 On native desktop platforms sokol_app.h provides more control over the
827 application-quit-process. It's possible to initiate a 'programmatic quit'
828 from the application code, and a quit initiated by the application user can
829 be intercepted (for instance to show a custom dialog box).
830
831 This 'programmatic quit protocol' is implemented through 3 functions
832 and 1 event:
833
834 - sapp_quit(): This function simply quits the application without
835 giving the user a chance to intervene. Usually this might
836 be called when the user clicks the 'Ok' button in a 'Really Quit?'
837 dialog box
838 - sapp_request_quit(): Calling sapp_request_quit() will send the
839 event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler
840 callback, giving the user code a chance to intervene and cancel the
841 pending quit process (for instance to show a 'Really Quit?' dialog
842 box). If the event handler callback does nothing, the application
843 will be quit as usual. To prevent this, call the function
844 sapp_cancel_quit() from inside the event handler.
845 - sapp_cancel_quit(): Cancels a pending quit request, either initiated
846 by the user clicking the window close button, or programmatically
847 by calling sapp_request_quit(). The only place where calling this
848 function makes sense is from inside the event handler callback when
849 the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received.
850 - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user
851 clicks the window's close button or application code calls the
852 sapp_request_quit() function. The event handler callback code can
853 handle this event by calling sapp_cancel_quit() to cancel the quit. If the
854 event is ignored, the application will quit as usual.
855
856 On the web platform, the quit behaviour differs from native platforms,
857 because of web-specific restrictions:
858
859 A `programmatic quit` initiated by calling sapp_quit() or
860 sapp_request_quit() will work as described above: the cleanup callback is
861 called, platform-specific cleanup is performed (on the web
862 this means that JS event handlers are unregistered), and then
863 the request-animation-loop will be exited. However that's all. The
864 web page itself will continue to exist (e.g. it's not possible to
865 programmatically close the browser tab).
866
867 On the web it's also not possible to run custom code when the user
868 closes a browser tab, so it's not possible to prevent this with a
869 fancy custom dialog box.
870
871 Instead the standard "Leave Site?" dialog box can be activated (or
872 deactivated) with the following function:
873
874 sapp_html5_ask_leave_site(bool ask);
875
876 The initial state of the associated internal flag can be provided
877 at startup via sapp_desc.html5.ask_leave_site.
878
879 This feature should only be used sparingly in critical situations - for
880 instance when the user would loose data - since popping up modal dialog
881 boxes is considered quite rude in the web world. Note that there's no way
882 to customize the content of this dialog box or run any code as a result
883 of the user's decision. Also note that the user must have interacted with
884 the site before the dialog box will appear. These are all security measures
885 to prevent fishing.
886
887 The Dear ImGui HighDPI sample contains example code of how to
888 implement a 'Really Quit?' dialog box with Dear ImGui (native desktop
889 platforms only), and for showing the hardwired "Leave Site?" dialog box
890 when running on the web platform:
891
892 https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html
893
894 FULLSCREEN
895 ==========
896 If the sapp_desc.fullscreen flag is true, sokol-app will try to create
897 a fullscreen window on platforms with a 'proper' window system
898 (mobile devices will always use fullscreen). The implementation details
899 depend on the target platform, in general sokol-app will use a
900 'soft approach' which doesn't interfere too much with the platform's
901 window system (for instance borderless fullscreen window instead of
902 a 'real' fullscreen mode). Such details might change over time
903 as sokol-app is adapted for different needs.
904
905 The most important effect of fullscreen mode to keep in mind is that
906 the requested canvas width and height will be ignored for the initial
907 window size, calling sapp_width() and sapp_height() will instead return
908 the resolution of the fullscreen canvas (however the provided size
909 might still be used for the non-fullscreen window, in case the user can
910 switch back from fullscreen- to windowed-mode).
911
912 To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen().
913
914 To check if the application window is currently in fullscreen mode,
915 call sapp_is_fullscreen().
916
917 On the web, sapp_desc.fullscreen will have no effect, and the application
918 will always start in non-fullscreen mode. Call sapp_toggle_fullscreen()
919 from within or 'near' an input event to switch to fullscreen
920 programatically. Note that on the web, the fullscreen state may change back
921 to windowed at any time (either because the browser had rejected switching
922 into fullscreen, or the user leaves fullscreen via Esc), this means that the
923 result of sapp_is_fullscreen() may change also without calling
924 sapp_toggle_fullscreen()!
925
926
927 WINDOW ICON SUPPORT
928 ===================
929 Some sokol_app.h backends allow to change the window icon programmatically:
930
931 - on Win32: the small icon in the window's title bar, and the
932 bigger icon in the task bar
933 - on Linux: highly dependent on the used window manager, but usually
934 the window's title bar icon and/or the task bar icon
935 - on HTML5: the favicon shown in the page's browser tab
936 - on macOS: the application icon shown in the dock, but only
937 for currently running applications
938
939 NOTE that it is not possible to set the actual application icon which is
940 displayed by the operating system on the desktop or 'home screen'. Those
941 icons must be provided 'traditionally' through operating-system-specific
942 resources which are associated with the application (sokol_app.h might
943 later support setting the window icon from platform specific resource data
944 though).
945
946 There are two ways to set the window icon:
947
948 - at application start in the sokol_main() function by initializing
949 the sapp_desc.icon nested struct
950 - or later by calling the function sapp_set_icon()
951
952 As a convenient shortcut, sokol_app.h comes with a builtin default-icon
953 (a rainbow-colored 'S', which at least looks a bit better than the Windows
954 default icon for applications), which can be activated like this:
955
956 At startup in sokol_main():
957
958 sapp_desc sokol_main(...) {
959 return (sapp_desc){
960 ...
961 icon.sokol_default = true
962 };
963 }
964
965 Or later by calling:
966
967 sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true });
968
969 NOTE that a completely zero-initialized sapp_icon_desc struct will not
970 update the window icon in any way. This is an 'escape hatch' so that you
971 can handle the window icon update yourself (or if you do this already,
972 sokol_app.h won't get in your way, in this case just leave the
973 sapp_desc.icon struct zero-initialized).
974
975 Providing your own icon images works exactly like in GLFW (down to the
976 data format):
977
978 You provide one or more 'candidate images' in different sizes, and the
979 sokol_app.h platform backends pick the best match for the specific backend
980 and icon type.
981
982 For each candidate image, you need to provide:
983
984 - the width in pixels
985 - the height in pixels
986 - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844
987 on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88,
988 red=0x44)
989
990 For instance, if you have 3 candidate images (small, medium, big) of
991 sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is
992 setup like this:
993
994 // the actual pixel data (RGBA8, origin top-left)
995 const uint32_t small[16][16] = { ... };
996 const uint32_t medium[32][32] = { ... };
997 const uint32_t big[64][64] = { ... };
998
999 const sapp_icon_desc icon_desc = {
1000 .images = {
1001 { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) },
1002 { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) },
1003 // ...or without the SAPP_RANGE helper macro:
1004 { .width = 64, .height = 64, .pixels = { .ptr=big,
1005 .size=sizeof(big) } }
1006 }
1007 };
1008
1009 An sapp_icon_desc struct initialized like this can then either be applied
1010 at application start in sokol_main:
1011
1012 sapp_desc sokol_main(...) {
1013 return (sapp_desc){
1014 ...
1015 icon = icon_desc
1016 };
1017 }
1018
1019 ...or later by calling sapp_set_icon():
1020
1021 sapp_set_icon(&icon_desc);
1022
1023 Some window icon caveats:
1024
1025 - once the window icon has been updated, there's no way to go back to
1026 the platform's default icon, this is because some platforms (Linux
1027 and HTML5) don't switch the icon visual back to the default even if
1028 the custom icon is deleted or removed
1029 - on HTML5, if the sokol_app.h icon doesn't show up in the browser
1030 tab, check that there's no traditional favicon 'link' element
1031 is defined in the page's index.html, sokol_app.h will only
1032 append a new favicon link element, but not delete any manually
1033 defined favicon in the page
1034
1035 For an example and test of the window icon feature, check out the
1036 'icon-sapp' sample on the sokol-samples git repository.
1037
1038 ONSCREEN KEYBOARD
1039 =================
1040 On some platforms which don't provide a physical keyboard, sokol-app
1041 can display the platform's integrated onscreen keyboard for text
1042 input. To request that the onscreen keyboard is shown, call
1043
1044 sapp_show_keyboard(true);
1045
1046 Likewise, to hide the keyboard call:
1047
1048 sapp_show_keyboard(false);
1049
1050 Note that onscreen keyboard functionality is no longer supported
1051 on the browser platform (the previous hacks and workarounds to make browser
1052 keyboards work for on web applications that don't use HTML UIs
1053 never really worked across browsers).
1054
1055 INPUT EVENT BUBBLING ON THE WEB PLATFORM
1056 ========================================
1057 By default, input event bubbling on the web platform is configured in
1058 a way that makes the most sense for 'full-canvas' apps that cover the
1059 entire browser client window area:
1060
1061 - mouse, touch and wheel events do not bubble up, this prevents various
1062 ugly side events, like:
1063 - HTML text overlays being selected on double- or triple-click into
1064 the canvas
1065 - 'scroll bumping' even when the canvas covers the entire client area
1066 - key_up/down events for 'character keys' *do* bubble up (otherwise
1067 the browser will not generate UNICODE character events)
1068 - all other key events *do not* bubble up by default (this prevents side
1069 effects like F1 opening help, or F7 starting 'caret browsing')
1070 - character events do not bubble up (although I haven't noticed any side
1071 effects otherwise)
1072
1073 Event bubbling can be enabled for input event categories during
1074 initialization in the sapp_desc struct:
1075
1076 sapp_desc sokol_main(int argc, char* argv[]) {
1077 return (sapp_desc){
1078 //...
1079 .html5 = {
1080 .bubble_mouse_events = true,
1081 .bubble_touch_events = true,
1082 .bubble_wheel_events = true,
1083 .bubble_key_events = true,
1084 .bubble_char_events = true,
1085 }
1086 };
1087 }
1088
1089 This basically opens the floodgates and lets *all* input events bubble up to
1090 the browser.
1091
1092 To prevent individual events from bubbling, call sapp_consume_event() from
1093 within the sokol_app.h event callback when that specific event is reported.
1094
1095
1096 SETTING THE CANVAS OBJECT ON THE WEB PLATFORM
1097 =============================================
1098 On the web, sokol_app.h and the Emscripten SDK functions need to find
1099 the WebGL/WebGPU canvas intended for rendering and attaching event
1100 handlers. This can happen in four ways:
1101
1102 1. do nothing and just set the id of the canvas object to 'canvas'
1103 (preferred)
1104 2. via a CSS Selector string (preferred)
1105 3. by setting the `Module.canvas` property to the canvas object
1106 4. by adding the canvas object to the global variable `specialHTMLTargets[]`
1107 (this is a special variable used by the Emscripten runtime to lookup
1108 event target objects for which document.querySelector() cannot be used)
1109
1110 The easiest way is to just name your canvas object 'canvas':
1111
1112 <canvas id="canvas" ...></canvas>
1113
1114 This works because the default css selector string used by sokol_app.h
1115 is '#canvas'.
1116
1117 If you name your canvas differently, you need to communicate that name to
1118 sokol_app.h via `sapp_desc.html5.canvas_selector` as a regular css selector
1119 string that's compatible with `document.querySelector()`. E.g. if your
1120 canvas object looks like this:
1121
1122 <canvas id="bla" ...></canvas>
1123
1124 The `sapp_desc.html5.canvas_selector` string must be set to '#bla':
1125
1126 .html5.canvas_selector = "#bla"
1127
1128 If the canvas object cannot be looked up via `document.querySelector()` you
1129 need to use one of the alternative methods, both involve the special
1130 Emscripten runtime `Module` object which is usually setup in the index.html
1131 like this before the WASM blob is loaded and instantiated:
1132
1133 <script type='text/javascript'>
1134 var Module = {
1135 // ...
1136 };
1137 </script>
1138
1139 The first option is to set the `Module.canvas` property to your canvas
1140 object:
1141
1142 <script type='text/javascript'>
1143 var Module = {
1144 canvas: my_canvas_object,
1145 };
1146 </script>
1147
1148 When sokol_app.h initializes, it will check the global Module object whether
1149 a `Module.canvas` property exists and is an object. This method will add
1150 a new entry to the `specialHTMLTargets[]` object
1151
1152 The other option is to add the canvas under a name chosen by you to the
1153 special `specialHTMLTargets[]` map, which is used by the Emscripten runtime
1154 to lookup 'event target objects' which are not visible to
1155 `document.querySelector()`. Note that `specialHTMLTargets[]` must be updated
1156 after the Emscripten runtime has started but before the WASM code is running.
1157 A good place for this is the special `Module.preRun` array in index.html:
1158
1159 <script type='text/javascript'>
1160 var Module = {
1161 preRun: [
1162 () => {
1163 specialHTMLTargets['my_canvas'] = my_canvas_object;
1164 }
1165 ],
1166 };
1167 </script>
1168
1169 In that case, pass the same string to sokol_app.h which is used as key
1170 in the specialHTMLTargets[] map:
1171
1172 .html5.canvas_selector = "my_canvas"
1173
1174 If sokol_app.h can't find your canvas for some reason check for warning
1175 messages on the browser console.
1176
1177
1178 OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
1179 ======================================================
1180 NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android.
1181
1182 In its default configuration, sokol_app.h "hijacks" the platform's
1183 standard main() function. This was done because different platforms
1184 have different entry point conventions which are not compatible with
1185 C's main() (for instance WinMain on Windows has completely different
1186 arguments). However, this "main hijacking" posed a problem for
1187 usage scenarios like integrating sokol_app.h with other languages than
1188 C or C++, so an alternative SOKOL_NO_ENTRY mode has been added
1189 in which the user code provides the platform's main function:
1190
1191 - define SOKOL_NO_ENTRY before including the sokol_app.h implementation
1192 - do *not* provide a sokol_main() function
1193 - instead provide the standard main() function of the platform
1194 - from the main function, call the function ```sapp_run()``` which
1195 takes a pointer to an ```sapp_desc``` structure.
1196 - from here on```sapp_run()``` takes over control and calls the provided
1197 init-, frame-, event- and cleanup-callbacks just like in the default
1198 model.
1199
1200 sapp_run() behaves differently across platforms:
1201
1202 - on some platforms, sapp_run() will return when the application quits
1203 - on other platforms, sapp_run() will never return, even when the
1204 application quits (the operating system is free to simply terminate
1205 the application at any time)
1206 - on Emscripten specifically, sapp_run() will return immediately while
1207 the frame callback keeps being called
1208
1209 This different behaviour of sapp_run() essentially means that there
1210 shouldn't be any code *after* sapp_run(), because that may either never be
1211 called, or in case of Emscripten will be called at an unexpected time (at
1212 application start).
1213
1214 An application also should not depend on the cleanup-callback being called
1215 when cross-platform compatibility is required.
1216
1217 Since sapp_run() returns immediately on Emscripten you shouldn't activate
1218 the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling
1219 for the browser target), since the C/C++ exit runtime would be called
1220 immediately at application start, causing any global objects to be destroyed
1221 and global variables to be zeroed.
1222
1223 WINDOWS CONSOLE OUTPUT
1224 ======================
1225 On Windows, regular windowed applications don't show any stdout/stderr text
1226 output, which can be a bit of a hassle for printf() debugging or generally
1227 logging text to the console. Also, console output by default uses a local
1228 codepage setting and thus international UTF-8 encoded text is printed
1229 as garbage.
1230
1231 To help with these issues, sokol_app.h can be configured at startup
1232 via the following Windows-specific sapp_desc flags:
1233
1234 sapp_desc.win32.console_utf8 (default: false)
1235 When set to true, the output console codepage will be switched
1236 to UTF-8 (and restored to the original codepage on exit)
1237
1238 sapp_desc.win32.console_attach (default: false)
1239 When set to true, stdout and stderr will be attached to the
1240 console of the parent process (if the parent process actually
1241 has a console). This means that if the application was started
1242 in a command line window, stdout and stderr output will be printed
1243 to the terminal, just like a regular command line program. But if
1244 the application is started via double-click, it will behave like
1245 a regular UI application, and stdout/stderr will not be visible.
1246
1247 sapp_desc.win32.console_create (default: false)
1248 When set to true, a new console window will be created and
1249 stdout/stderr will be redirected to that console window. It
1250 doesn't matter if the application is started from the command
1251 line or via double-click.
1252
1253 NOTE: setting both win32.console_attach and win32.console_create
1254 to true also makes sense and has the effect that output
1255 will appear in the existing terminal when started from the cmdline,
1256 and otherwise (when started via double-click) will open a console window.
1257
1258 MEMORY ALLOCATION OVERRIDE
1259 ==========================
1260 You can override the memory allocation functions at initialization time
1261 like this:
1262
1263 void* my_alloc(size_t size, void* user_data) {
1264 return malloc(size);
1265 }
1266
1267 void my_free(void* ptr, void* user_data) {
1268 free(ptr);
1269 }
1270
1271 sapp_desc sokol_main(int argc, char* argv[]) {
1272 return (sapp_desc){
1273 // ...
1274 .allocator = {
1275 .alloc_fn = my_alloc,
1276 .free_fn = my_free,
1277 .user_data = ...,
1278 }
1279 };
1280 }
1281
1282 If no overrides are provided, malloc and free will be used.
1283
1284 This only affects memory allocation calls done by sokol_app.h
1285 itself though, not any allocations in OS libraries.
1286
1287
1288 ERROR REPORTING AND LOGGING
1289 ===========================
1290 To get any logging information at all you need to provide a logging callback
1291 in the setup call the easiest way is to use sokol_log.h:
1292
1293 #include "sokol_log.h"
1294
1295 sapp_desc sokol_main(int argc, char* argv[]) {
1296 return (sapp_desc) {
1297 ...
1298 .logger.func = slog_func,
1299 };
1300 }
1301
1302 To override logging with your own callback, first write a logging function
1303 like this:
1304
1305 void my_log(const char* tag, // e.g. 'sapp'
1306 uint32_t log_level, // 0=panic, 1=error, 2=warn,
1307 3=info uint32_t log_item_id, // SAPP_LOGITEM_* const char*
1308 message_or_null, // a message string, may be nullptr in release mode
1309 uint32_t line_nr, // line number in
1310 sokol_app.h const char* filename_or_null, // source filename, may be
1311 nullptr in release mode void* user_data)
1312 {
1313 ...
1314 }
1315
1316 ...and then setup sokol-app like this:
1317
1318 sapp_desc sokol_main(int argc, char* argv[]) {
1319 return (sapp_desc) {
1320 ...
1321 .logger = {
1322 .func = my_log,
1323 .user_data = my_user_data,
1324 }
1325 };
1326 }
1327
1328 The provided logging function must be reentrant (e.g. be callable from
1329 different threads).
1330
1331 If you don't want to provide your own custom logger it is highly recommended
1332 to use the standard logger in sokol_log.h instead, otherwise you won't see
1333 any warnings or errors.
1334
1335 TEMP NOTE DUMP
1336 ==============
1337 - sapp_desc needs a bool whether to initialize depth-stencil surface
1338 - the Android implementation calls cleanup_cb() and destroys the egl context
1339 in onDestroy at the latest but should do it earlier, in onStop, as an app is
1340 "killable" after onStop on Android Honeycomb and later (it can't be done at
1341 the moment as the app may be started again after onStop and the sokol
1342 lifecycle does not yet handle context teardown/bringup)
1343
1344
1345 LICENSE
1346 =======
1347 zlib/libpng license
1348
1349 Copyright (c) 2018 Andre Weissflog
1350
1351 This software is provided 'as-is', without any express or implied warranty.
1352 In no event will the authors be held liable for any damages arising from the
1353 use of this software.
1354
1355 Permission is granted to anyone to use this software for any purpose,
1356 including commercial applications, and to alter it and redistribute it
1357 freely, subject to the following restrictions:
1358
1359 1. The origin of this software must not be misrepresented; you must not
1360 claim that you wrote the original software. If you use this software in
1361 a product, an acknowledgment in the product documentation would be
1362 appreciated but is not required.
1363
1364 2. Altered source versions must be plainly marked as such, and must not
1365 be misrepresented as being the original software.
1366
1367 3. This notice may not be removed or altered from any source
1368 distribution.
1369*/
1370#define SOKOL_APP_INCLUDED (1)
1371#include <stdbool.h>
1372#include <stddef.h> // size_t
1373#include <stdint.h>
1374
1375#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL)
1376#define SOKOL_APP_API_DECL SOKOL_API_DECL
1377#endif
1378#ifndef SOKOL_APP_API_DECL
1379#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL)
1380#define SOKOL_APP_API_DECL __declspec(dllexport)
1381#elif defined(_WIN32) && defined(SOKOL_DLL)
1382#define SOKOL_APP_API_DECL __declspec(dllimport)
1383#else
1384#define SOKOL_APP_API_DECL extern
1385#endif
1386#endif
1387
1388#ifdef __cplusplus
1389extern "C" {
1390#endif
1391
1392/* misc constants */
1393enum {
1394 SAPP_MAX_TOUCHPOINTS = 8,
1395 SAPP_MAX_MOUSEBUTTONS = 3,
1396 SAPP_MAX_KEYCODES = 512,
1397 SAPP_MAX_ICONIMAGES = 8,
1398};
1399
1400/*
1401 sapp_event_type
1402
1403 The type of event that's passed to the event handler callback
1404 in the sapp_event.type field. These are not just "traditional"
1405 input events, but also notify the application about state changes
1406 or other user-invoked actions.
1407*/
1408typedef enum sapp_event_type {
1409 SAPP_EVENTTYPE_INVALID,
1410 SAPP_EVENTTYPE_KEY_DOWN,
1411 SAPP_EVENTTYPE_KEY_UP,
1412 SAPP_EVENTTYPE_CHAR,
1413 SAPP_EVENTTYPE_MOUSE_DOWN,
1414 SAPP_EVENTTYPE_MOUSE_UP,
1415 SAPP_EVENTTYPE_MOUSE_SCROLL,
1416 SAPP_EVENTTYPE_MOUSE_MOVE,
1417 SAPP_EVENTTYPE_MOUSE_ENTER,
1418 SAPP_EVENTTYPE_MOUSE_LEAVE,
1419 SAPP_EVENTTYPE_TOUCHES_BEGAN,
1420 SAPP_EVENTTYPE_TOUCHES_MOVED,
1421 SAPP_EVENTTYPE_TOUCHES_ENDED,
1422 SAPP_EVENTTYPE_TOUCHES_CANCELLED,
1423 SAPP_EVENTTYPE_RESIZED,
1424 SAPP_EVENTTYPE_ICONIFIED,
1425 SAPP_EVENTTYPE_RESTORED,
1426 SAPP_EVENTTYPE_FOCUSED,
1427 SAPP_EVENTTYPE_UNFOCUSED,
1428 SAPP_EVENTTYPE_SUSPENDED,
1429 SAPP_EVENTTYPE_RESUMED,
1430 SAPP_EVENTTYPE_QUIT_REQUESTED,
1431 SAPP_EVENTTYPE_CLIPBOARD_PASTED,
1432 SAPP_EVENTTYPE_FILES_DROPPED,
1433 _SAPP_EVENTTYPE_NUM,
1434 _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF
1435} sapp_event_type;
1436
1437/*
1438 sapp_keycode
1439
1440 The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the
1441 struct field sapp_event.key_code.
1442
1443 Note that the keycode values are identical with GLFW.
1444*/
1445typedef enum sapp_keycode {
1446 SAPP_KEYCODE_INVALID = 0,
1447 SAPP_KEYCODE_SPACE = 32,
1448 SAPP_KEYCODE_APOSTROPHE = 39, /* ' */
1449 SAPP_KEYCODE_COMMA = 44, /* , */
1450 SAPP_KEYCODE_MINUS = 45, /* - */
1451 SAPP_KEYCODE_PERIOD = 46, /* . */
1452 SAPP_KEYCODE_SLASH = 47, /* / */
1453 SAPP_KEYCODE_0 = 48,
1454 SAPP_KEYCODE_1 = 49,
1455 SAPP_KEYCODE_2 = 50,
1456 SAPP_KEYCODE_3 = 51,
1457 SAPP_KEYCODE_4 = 52,
1458 SAPP_KEYCODE_5 = 53,
1459 SAPP_KEYCODE_6 = 54,
1460 SAPP_KEYCODE_7 = 55,
1461 SAPP_KEYCODE_8 = 56,
1462 SAPP_KEYCODE_9 = 57,
1463 SAPP_KEYCODE_SEMICOLON = 59, /* ; */
1464 SAPP_KEYCODE_EQUAL = 61, /* = */
1465 SAPP_KEYCODE_A = 65,
1466 SAPP_KEYCODE_B = 66,
1467 SAPP_KEYCODE_C = 67,
1468 SAPP_KEYCODE_D = 68,
1469 SAPP_KEYCODE_E = 69,
1470 SAPP_KEYCODE_F = 70,
1471 SAPP_KEYCODE_G = 71,
1472 SAPP_KEYCODE_H = 72,
1473 SAPP_KEYCODE_I = 73,
1474 SAPP_KEYCODE_J = 74,
1475 SAPP_KEYCODE_K = 75,
1476 SAPP_KEYCODE_L = 76,
1477 SAPP_KEYCODE_M = 77,
1478 SAPP_KEYCODE_N = 78,
1479 SAPP_KEYCODE_O = 79,
1480 SAPP_KEYCODE_P = 80,
1481 SAPP_KEYCODE_Q = 81,
1482 SAPP_KEYCODE_R = 82,
1483 SAPP_KEYCODE_S = 83,
1484 SAPP_KEYCODE_T = 84,
1485 SAPP_KEYCODE_U = 85,
1486 SAPP_KEYCODE_V = 86,
1487 SAPP_KEYCODE_W = 87,
1488 SAPP_KEYCODE_X = 88,
1489 SAPP_KEYCODE_Y = 89,
1490 SAPP_KEYCODE_Z = 90,
1491 SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */
1492 SAPP_KEYCODE_BACKSLASH = 92, /* \ */
1493 SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */
1494 SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */
1495 SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */
1496 SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */
1497 SAPP_KEYCODE_ESCAPE = 256,
1498 SAPP_KEYCODE_ENTER = 257,
1499 SAPP_KEYCODE_TAB = 258,
1500 SAPP_KEYCODE_BACKSPACE = 259,
1501 SAPP_KEYCODE_INSERT = 260,
1502 SAPP_KEYCODE_DELETE = 261,
1503 SAPP_KEYCODE_RIGHT = 262,
1504 SAPP_KEYCODE_LEFT = 263,
1505 SAPP_KEYCODE_DOWN = 264,
1506 SAPP_KEYCODE_UP = 265,
1507 SAPP_KEYCODE_PAGE_UP = 266,
1508 SAPP_KEYCODE_PAGE_DOWN = 267,
1509 SAPP_KEYCODE_HOME = 268,
1510 SAPP_KEYCODE_END = 269,
1511 SAPP_KEYCODE_CAPS_LOCK = 280,
1512 SAPP_KEYCODE_SCROLL_LOCK = 281,
1513 SAPP_KEYCODE_NUM_LOCK = 282,
1514 SAPP_KEYCODE_PRINT_SCREEN = 283,
1515 SAPP_KEYCODE_PAUSE = 284,
1516 SAPP_KEYCODE_F1 = 290,
1517 SAPP_KEYCODE_F2 = 291,
1518 SAPP_KEYCODE_F3 = 292,
1519 SAPP_KEYCODE_F4 = 293,
1520 SAPP_KEYCODE_F5 = 294,
1521 SAPP_KEYCODE_F6 = 295,
1522 SAPP_KEYCODE_F7 = 296,
1523 SAPP_KEYCODE_F8 = 297,
1524 SAPP_KEYCODE_F9 = 298,
1525 SAPP_KEYCODE_F10 = 299,
1526 SAPP_KEYCODE_F11 = 300,
1527 SAPP_KEYCODE_F12 = 301,
1528 SAPP_KEYCODE_F13 = 302,
1529 SAPP_KEYCODE_F14 = 303,
1530 SAPP_KEYCODE_F15 = 304,
1531 SAPP_KEYCODE_F16 = 305,
1532 SAPP_KEYCODE_F17 = 306,
1533 SAPP_KEYCODE_F18 = 307,
1534 SAPP_KEYCODE_F19 = 308,
1535 SAPP_KEYCODE_F20 = 309,
1536 SAPP_KEYCODE_F21 = 310,
1537 SAPP_KEYCODE_F22 = 311,
1538 SAPP_KEYCODE_F23 = 312,
1539 SAPP_KEYCODE_F24 = 313,
1540 SAPP_KEYCODE_F25 = 314,
1541 SAPP_KEYCODE_KP_0 = 320,
1542 SAPP_KEYCODE_KP_1 = 321,
1543 SAPP_KEYCODE_KP_2 = 322,
1544 SAPP_KEYCODE_KP_3 = 323,
1545 SAPP_KEYCODE_KP_4 = 324,
1546 SAPP_KEYCODE_KP_5 = 325,
1547 SAPP_KEYCODE_KP_6 = 326,
1548 SAPP_KEYCODE_KP_7 = 327,
1549 SAPP_KEYCODE_KP_8 = 328,
1550 SAPP_KEYCODE_KP_9 = 329,
1551 SAPP_KEYCODE_KP_DECIMAL = 330,
1552 SAPP_KEYCODE_KP_DIVIDE = 331,
1553 SAPP_KEYCODE_KP_MULTIPLY = 332,
1554 SAPP_KEYCODE_KP_SUBTRACT = 333,
1555 SAPP_KEYCODE_KP_ADD = 334,
1556 SAPP_KEYCODE_KP_ENTER = 335,
1557 SAPP_KEYCODE_KP_EQUAL = 336,
1558 SAPP_KEYCODE_LEFT_SHIFT = 340,
1559 SAPP_KEYCODE_LEFT_CONTROL = 341,
1560 SAPP_KEYCODE_LEFT_ALT = 342,
1561 SAPP_KEYCODE_LEFT_SUPER = 343,
1562 SAPP_KEYCODE_RIGHT_SHIFT = 344,
1563 SAPP_KEYCODE_RIGHT_CONTROL = 345,
1564 SAPP_KEYCODE_RIGHT_ALT = 346,
1565 SAPP_KEYCODE_RIGHT_SUPER = 347,
1566 SAPP_KEYCODE_MENU = 348,
1567} sapp_keycode;
1568
1569/*
1570 Android specific 'tool type' enum for touch events. This lets the
1571 application check what type of input device was used for
1572 touch events.
1573
1574 NOTE: the values must remain in sync with the corresponding
1575 Android SDK type, so don't change those.
1576
1577 See
1578 https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN
1579*/
1580typedef enum sapp_android_tooltype {
1581 SAPP_ANDROIDTOOLTYPE_UNKNOWN = 0, // TOOL_TYPE_UNKNOWN
1582 SAPP_ANDROIDTOOLTYPE_FINGER = 1, // TOOL_TYPE_FINGER
1583 SAPP_ANDROIDTOOLTYPE_STYLUS = 2, // TOOL_TYPE_STYLUS
1584 SAPP_ANDROIDTOOLTYPE_MOUSE = 3, // TOOL_TYPE_MOUSE
1585} sapp_android_tooltype;
1586
1587/*
1588 sapp_touchpoint
1589
1590 Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN,
1591 TOUCHES_MOVED, TOUCHES_ENDED).
1592
1593 Touch points are stored in the nested array sapp_event.touches[],
1594 and the number of touches is stored in sapp_event.num_touches.
1595*/
1596typedef struct sapp_touchpoint {
1597 uintptr_t identifier;
1598 float pos_x;
1599 float pos_y;
1600 sapp_android_tooltype android_tooltype; // only valid on Android
1601 bool changed;
1602} sapp_touchpoint;
1603
1604/*
1605 sapp_mousebutton
1606
1607 The currently pressed mouse button in the events MOUSE_DOWN
1608 and MOUSE_UP, stored in the struct field sapp_event.mouse_button.
1609*/
1610typedef enum sapp_mousebutton {
1611 SAPP_MOUSEBUTTON_LEFT = 0x0,
1612 SAPP_MOUSEBUTTON_RIGHT = 0x1,
1613 SAPP_MOUSEBUTTON_MIDDLE = 0x2,
1614 SAPP_MOUSEBUTTON_INVALID = 0x100,
1615} sapp_mousebutton;
1616
1617/*
1618 These are currently pressed modifier keys (and mouse buttons) which are
1619 passed in the event struct field sapp_event.modifiers.
1620*/
1621enum {
1622 SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key
1623 SAPP_MODIFIER_CTRL = 0x2, // left or right control key
1624 SAPP_MODIFIER_ALT = 0x4, // left or right alt key
1625 SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key
1626 SAPP_MODIFIER_LMB = 0x100, // left mouse button
1627 SAPP_MODIFIER_RMB = 0x200, // right mouse button
1628 SAPP_MODIFIER_MMB = 0x400, // middle mouse button
1629};
1630
1631/*
1632 sapp_event
1633
1634 This is an all-in-one event struct passed to the event handler
1635 user callback function. Note that it depends on the event
1636 type what struct fields actually contain useful values, so you
1637 should first check the event type before reading other struct
1638 fields.
1639*/
1640typedef struct sapp_event {
1641 uint64_t frame_count; // current frame counter, always valid, useful for
1642 // checking if two events were issued in the same frame
1643 sapp_event_type type; // the event type, always valid
1644 sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN
1645 uint32_t char_code; // the UTF-32 character code, only valid in CHAR events
1646 bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP,
1647 // KEY_DOWN and CHAR
1648 uint32_t modifiers; // current modifier keys, valid in all key-, char- and
1649 // mouse-events
1650 sapp_mousebutton mouse_button; // mouse button that was pressed or released,
1651 // valid in MOUSE_DOWN, MOUSE_UP
1652 float mouse_x; // current horizontal mouse position in pixels, always valid
1653 // except during mouse lock
1654 float mouse_y; // current vertical mouse position in pixels, always valid
1655 // except during mouse lock
1656 float mouse_dx; // relative horizontal mouse movement since last frame, always
1657 // valid
1658 float mouse_dy; // relative vertical mouse movement since last frame, always
1659 // valid
1660 float scroll_x; // horizontal mouse wheel scroll distance, valid in
1661 // MOUSE_SCROLL events
1662 float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL
1663 // events
1664 int num_touches; // number of valid items in the touches[] array
1665 sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid
1666 // in TOUCHES_BEGIN,
1667 // TOUCHES_MOVED, TOUCHES_ENDED
1668 int window_width; // current window- and framebuffer sizes in pixels, always
1669 // valid
1670 int window_height;
1671 int framebuffer_width; // = window_width * dpi_scale
1672 int framebuffer_height; // = window_height * dpi_scale
1673} sapp_event;
1674
1675/*
1676 sg_range
1677
1678 A general pointer/size-pair struct and constructor macros for passing binary
1679 blobs into sokol_app.h.
1680*/
1681typedef struct sapp_range {
1682 const void *ptr;
1683 size_t size;
1684} sapp_range;
1685// disabling this for every includer isn't great, but the warnings are also
1686// quite pointless
1687#if defined(_MSC_VER)
1688#pragma warning( \
1689 disable : 4221) /* /W4 only: nonstandard extension used: 'x': cannot be \
1690 initialized using address of automatic variable 'y' */
1691#pragma warning(disable : 4204) /* VS2015: nonstandard extension used: \
1692 non-constant aggregate initializer */
1693#endif
1694#if defined(__cplusplus)
1695#define SAPP_RANGE(x) \
1696 sapp_range { &x, sizeof(x) }
1697#else
1698#define SAPP_RANGE(x) \
1699 (sapp_range) { &x, sizeof(x) }
1700#endif
1701
1702/*
1703 sapp_image_desc
1704
1705 This is used to describe image data to sokol_app.h (window icons and cursor
1706 images).
1707
1708 The pixel format is RGBA8.
1709
1710 cursor_hotspot_x and _y are used only for cursors, to define which pixel
1711 of the image should be aligned with the mouse position.
1712*/
1713typedef struct sapp_image_desc {
1714 int width;
1715 int height;
1716 int cursor_hotspot_x;
1717 int cursor_hotspot_y;
1718 sapp_range pixels;
1719} sapp_image_desc;
1720
1721/*
1722 sapp_icon_desc
1723
1724 An icon description structure for use in sapp_desc.icon and
1725 sapp_set_icon().
1726
1727 When setting a custom image, the application can provide a number of
1728 candidates differing in size, and sokol_app.h will pick the image(s)
1729 closest to the size expected by the platform's window system.
1730
1731 To set sokol-app's default icon, set .sokol_default to true.
1732
1733 Otherwise provide candidate images of different sizes in the
1734 images[] array.
1735
1736 If both the sokol_default flag is set to true, any image candidates
1737 will be ignored and the sokol_app.h default icon will be set.
1738*/
1739typedef struct sapp_icon_desc {
1740 bool sokol_default;
1741 sapp_image_desc images[SAPP_MAX_ICONIMAGES];
1742} sapp_icon_desc;
1743
1744/*
1745 sapp_allocator
1746
1747 Used in sapp_desc to provide custom memory-alloc and -free functions
1748 to sokol_app.h. If memory management should be overridden, both the
1749 alloc_fn and free_fn function must be provided (e.g. it's not valid to
1750 override one function but not the other).
1751*/
1752typedef struct sapp_allocator {
1753 void *(*alloc_fn)(size_t size, void *user_data);
1754 void (*free_fn)(void *ptr, void *user_data);
1755 void *user_data;
1756} sapp_allocator;
1757
1758/*
1759 sapp_log_item
1760
1761 Log items are defined via X-Macros and expanded to an enum
1762 'sapp_log_item', and in debug mode to corresponding
1763 human readable error messages.
1764*/
1765#define _SAPP_LOG_ITEMS \
1766 _SAPP_LOGITEM_XMACRO(OK, "Ok") \
1767 _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \
1768 _SAPP_LOGITEM_XMACRO( \
1769 MACOS_INVALID_NSOPENGL_PROFILE, \
1770 "macos: invalid NSOpenGLProfile (valid choices are 1.0 and 4.1)") \
1771 _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, \
1772 "failed loading opengl32.dll") \
1773 _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, \
1774 "failed to create helper window") \
1775 _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, \
1776 "failed to get helper window DC") \
1777 _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, \
1778 "failed to set pixel format for dummy GL context") \
1779 _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, \
1780 "failed to create dummy GL context") \
1781 _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, \
1782 "failed to make dummy GL context current") \
1783 _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, \
1784 "failed to get WGL pixel format attribute") \
1785 _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, \
1786 "failed to find matching WGL pixel format") \
1787 _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, \
1788 "failed to get pixel format descriptor") \
1789 _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, \
1790 "failed to set selected pixel format") \
1791 _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, \
1792 "ARB_create_context required") \
1793 _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, \
1794 "ARB_create_context_profile required") \
1795 _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED, \
1796 "requested OpenGL version not supported by GL driver " \
1797 "(ERROR_INVALID_VERSION_ARB)") \
1798 _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, \
1799 "requested OpenGL profile not support by GL driver " \
1800 "(ERROR_INVALID_PROFILE_ARB)") \
1801 _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, \
1802 "CreateContextAttribsARB failed with " \
1803 "ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \
1804 _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, \
1805 "CreateContextAttribsARB failed for other reason") \
1806 _SAPP_LOGITEM_XMACRO( \
1807 WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, \
1808 "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG " \
1809 "failed, retrying without debug flag.") \
1810 _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, \
1811 "could not obtain IDXGIFactory object") \
1812 _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, \
1813 "could not obtain IDXGIAdapter object") \
1814 _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, \
1815 "could not obtain IDXGIDevice1 interface") \
1816 _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, \
1817 "RegisterRawInputDevices() failed (on mouse lock)") \
1818 _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, \
1819 "RegisterRawInputDevices() failed (on mouse unlock)") \
1820 _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, \
1821 "GetRawInputData() failed") \
1822 _SAPP_LOGITEM_XMACRO(WIN32_DESTROYICON_FOR_CURSOR_FAILED, \
1823 "DestroyIcon() for a cursor image failed") \
1824 _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \
1825 _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, \
1826 "failed to load GLX entry points") \
1827 _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, \
1828 "GLX extension not found") \
1829 _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, \
1830 "failed to query GLX version") \
1831 _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, \
1832 "GLX version too low (need at least 1.3)") \
1833 _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, \
1834 "glXGetFBConfigs() returned no configs") \
1835 _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, \
1836 "failed to find a suitable GLXFBConfig") \
1837 _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, \
1838 "glXGetVisualFromFBConfig failed") \
1839 _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, \
1840 "GLX extensions ARB_create_context and " \
1841 "ARB_create_context_profile missing") \
1842 _SAPP_LOGITEM_XMACRO( \
1843 LINUX_GLX_CREATE_CONTEXT_FAILED, \
1844 "Failed to create GL context via glXCreateContextAttribsARB") \
1845 _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, \
1846 "glXCreateWindow() failed") \
1847 _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, \
1848 "XCreateWindow() failed") \
1849 _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, \
1850 "eglBindAPI(EGL_OPENGL_API) failed") \
1851 _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, \
1852 "eglBindAPI(EGL_OPENGL_ES_API) failed") \
1853 _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \
1854 _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \
1855 _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, \
1856 "eglChooseConfig() returned no configs") \
1857 _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, \
1858 "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \
1859 _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, \
1860 "XGetVisualInfo() failed") \
1861 _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, \
1862 "eglCreateWindowSurface() failed") \
1863 _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, \
1864 "eglCreateContext() failed") \
1865 _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, \
1866 "eglMakeCurrent() failed") \
1867 _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \
1868 _SAPP_LOGITEM_XMACRO( \
1869 LINUX_X11_QUERY_SYSTEM_DPI_FAILED, \
1870 "failed to query system dpi value, assuming default 96.0") \
1871 _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, \
1872 "dropped file URL doesn't start with 'file://'") \
1873 _SAPP_LOGITEM_XMACRO(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD, \
1874 "X11: Failed to become owner of clipboard selection") \
1875 _SAPP_LOGITEM_XMACRO(LINUX_WAYLAND_CONNECT_DISPLAY_FAILED, \
1876 "Failed to connect to Wayland display") \
1877 _SAPP_LOGITEM_XMACRO( \
1878 LINUX_WAYLAND_NO_REQUIRED_PROTOCOLS, \
1879 "Wayland compositor doesn't support required protocols") \
1880 _SAPP_LOGITEM_XMACRO(LINUX_WAYLAND_CREATE_XKB_CONTEXT_FAILED, \
1881 "Failed to create XKB context") \
1882 _SAPP_LOGITEM_XMACRO(LINUX_WAYLAND_CREATE_WINDOW_FAILED, \
1883 "Failed to create wl_egl_window") \
1884 _SAPP_LOGITEM_XMACRO( \
1885 ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, \
1886 "unsupported input event encountered in _sapp_android_input_cb()") \
1887 _SAPP_LOGITEM_XMACRO( \
1888 ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, \
1889 "unsupported input event encountered in _sapp_android_main_cb()") \
1890 _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, \
1891 "failed to read message in _sapp_android_main_cb()") \
1892 _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, \
1893 "failed to write message in _sapp_android_msg") \
1894 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \
1895 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \
1896 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \
1897 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \
1898 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \
1899 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \
1900 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \
1901 _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \
1902 _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \
1903 _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \
1904 _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \
1905 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, \
1906 "NativeActivity onStart()") \
1907 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, \
1908 "NativeActivity onResume") \
1909 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, \
1910 "NativeActivity onSaveInstanceState") \
1911 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, \
1912 "NativeActivity onWindowFocusChanged") \
1913 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, \
1914 "NativeActivity onPause") \
1915 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, \
1916 "NativeActivity onStop()") \
1917 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, \
1918 "NativeActivity onNativeWindowCreated") \
1919 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, \
1920 "NativeActivity onNativeWindowDestroyed") \
1921 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, \
1922 "NativeActivity onInputQueueCreated") \
1923 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, \
1924 "NativeActivity onInputQueueDestroyed") \
1925 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, \
1926 "NativeActivity onConfigurationChanged") \
1927 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, \
1928 "NativeActivity onLowMemory") \
1929 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, \
1930 "NativeActivity onDestroy") \
1931 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \
1932 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, \
1933 "NativeActivity onCreate") \
1934 _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, \
1935 "failed to create thread pipe") \
1936 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, \
1937 "NativeActivity successfully created") \
1938 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOST, "wgpu: device lost") \
1939 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOG, "wgpu: device log") \
1940 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_UNCAPTURED_ERROR, "wgpu: uncaptured error") \
1941 _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, \
1942 "wgpu: failed to create surface for swapchain") \
1943 _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED, \
1944 "wgpu: wgpuSurfaceGetCapabilities failed") \
1945 _SAPP_LOGITEM_XMACRO( \
1946 WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, \
1947 "wgpu: failed to create depth-stencil texture for swapchain") \
1948 _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED, \
1949 "wgpu: failed to create view object for swapchain " \
1950 "depth-stencil texture") \
1951 _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED, \
1952 "wgpu: failed to create msaa texture for swapchain") \
1953 _SAPP_LOGITEM_XMACRO( \
1954 WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED, \
1955 "wgpu: failed to create view object for swapchain msaa texture") \
1956 _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED, \
1957 "wgpu: wgpuSurfaceGetCurrentTexture() failed") \
1958 _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_ERROR, \
1959 "wgpu: requesting device failed with status 'error'") \
1960 _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN, \
1961 "wgpu: requesting device failed with status 'unknown'") \
1962 _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE, \
1963 "wgpu: requesting adapter failed with 'unavailable'") \
1964 _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_ERROR, \
1965 "wgpu: requesting adapter failed with status 'error'") \
1966 _SAPP_LOGITEM_XMACRO( \
1967 WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN, \
1968 "wgpu: requesting adapter failed with status 'unknown'") \
1969 _SAPP_LOGITEM_XMACRO(WGPU_CREATE_INSTANCE_FAILED, \
1970 "wgpu: failed to create instance") \
1971 _SAPP_LOGITEM_XMACRO(VULKAN_REQUIRED_INSTANCE_EXTENSION_FUNCTION_MISSING, \
1972 "vulkan: could not lookup a required instance " \
1973 "extension function pointer") \
1974 _SAPP_LOGITEM_XMACRO(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE, \
1975 "vulkan: could not find suitable memory type") \
1976 _SAPP_LOGITEM_XMACRO(VULKAN_ALLOCATE_MEMORY_FAILED, \
1977 "vulkan: vkAllocateMemory() failed!") \
1978 _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_INSTANCE_FAILED, \
1979 "vulkan: vkCreateInstance failed") \
1980 _SAPP_LOGITEM_XMACRO(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED, \
1981 "vulkan: vkEnumeratePhysicalDevices failed") \
1982 _SAPP_LOGITEM_XMACRO(VULKAN_NO_PHYSICAL_DEVICES_FOUND, \
1983 "vulkan: vkEnumeratePhysicalDevices return no devices") \
1984 _SAPP_LOGITEM_XMACRO(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND, \
1985 "vulkan: no suitable physical device found") \
1986 _SAPP_LOGITEM_XMACRO( \
1987 VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT, \
1988 "vulkan: vkCreateDevice failed (extension not present)") \
1989 _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT, \
1990 "vulkan: vkCreateDevice failed (feature not present)") \
1991 _SAPP_LOGITEM_XMACRO( \
1992 VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED, \
1993 "vulkan: vkCreateDevice failed (initialization failed)") \
1994 _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_OTHER, \
1995 "vulkan: vkCreateDevice failed (other)") \
1996 _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SURFACE_FAILED, \
1997 "vulkan: vkCreate*SurfaceKHR failed") \
1998 _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SWAPCHAIN_FAILED, \
1999 "vulkan: vkCreateSwapchainKHR failed") \
2000 _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED, \
2001 "vulkan: vkCreateImageView for swapchain image failed") \
2002 _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED, \
2003 "vulkan: vkCreateImage for depth-stencil image failed") \
2004 _SAPP_LOGITEM_XMACRO( \
2005 VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED, \
2006 "vulkan: failed to allocate device memory for depth-stencil image") \
2007 _SAPP_LOGITEM_XMACRO( \
2008 VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED, \
2009 "vulkan: vkBindImageMemory() for depth-stencil image failed") \
2010 _SAPP_LOGITEM_XMACRO(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED, \
2011 "vulkan: vkAcquireNextImageKHR failed") \
2012 _SAPP_LOGITEM_XMACRO(VULKAN_QUEUE_PRESENT_FAILED, \
2013 "vulkan: vkQueuePresentKHR failed") \
2014 _SAPP_LOGITEM_XMACRO( \
2015 IMAGE_DATA_SIZE_MISMATCH, \
2016 "image data size mismatch (must be width*height*4 bytes)") \
2017 _SAPP_LOGITEM_XMACRO( \
2018 DROPPED_FILE_PATH_TOO_LONG, \
2019 "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \
2020 _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, \
2021 "clipboard string didn't fit into clipboard buffer")
2022
2023#define _SAPP_LOGITEM_XMACRO(item, msg) SAPP_LOGITEM_##item,
2024typedef enum sapp_log_item { _SAPP_LOG_ITEMS } sapp_log_item;
2025#undef _SAPP_LOGITEM_XMACRO
2026
2027/*
2028 sapp_pixel_format
2029
2030 Defines the pixel format for swapchain surfaces.
2031
2032 NOTE: when using sokol_gfx.h do not assume that the underlying
2033 values are compatible with sg_pixel_format!
2034
2035*/
2036typedef enum sapp_pixel_format {
2037 _SAPP_PIXELFORMAT_DEFAULT,
2038 SAPP_PIXELFORMAT_NONE,
2039 SAPP_PIXELFORMAT_RGBA8,
2040 SAPP_PIXELFORMAT_SRGB8A8,
2041 SAPP_PIXELFORMAT_BGRA8,
2042 SAPP_PIXELFORMAT_SBGRA8,
2043 SAPP_PIXELFORMAT_DEPTH,
2044 SAPP_PIXELFORMAT_DEPTH_STENCIL,
2045 _SA_PPPIXELFORMAT_FORCE_U32 = 0x7FFFFFFF
2046} sapp_pixel_format;
2047
2048/*
2049 sapp_environment
2050
2051 Used to provide runtime environment information to the
2052 outside world (like default pixel formats and the backend
2053 3D API device pointer) via a call to sapp_get_environment().
2054
2055 NOTE: when using sokol_gfx.h, don't assume that sapp_environment
2056 is binary compatible with sg_environment! Always use a translation
2057 function like sglue_environment() to populate sg_environment
2058 from sapp_environment!
2059*/
2060typedef struct sapp_environment_defaults {
2061 sapp_pixel_format color_format;
2062 sapp_pixel_format depth_format;
2063 int sample_count;
2064} sapp_environment_defaults;
2065
2066typedef struct sapp_metal_environment {
2067 const void *device;
2068} sapp_metal_environment;
2069
2070typedef struct sapp_d3d11_environment {
2071 const void *device;
2072 const void *device_context;
2073} sapp_d3d11_environment;
2074
2075typedef struct sapp_wgpu_environment {
2076 const void *device;
2077} sapp_wgpu_environment;
2078
2079typedef struct sapp_vulkan_environment {
2080 const void *instance;
2081 const void *physical_device;
2082 const void *device;
2083 const void *queue;
2084 uint32_t queue_family_index;
2085} sapp_vulkan_environment;
2086
2087typedef struct sapp_environment {
2088 sapp_environment_defaults defaults;
2089 sapp_metal_environment metal;
2090 sapp_d3d11_environment d3d11;
2091 sapp_wgpu_environment wgpu;
2092 sapp_vulkan_environment vulkan;
2093} sapp_environment;
2094
2095/*
2096 sapp_swapchain
2097
2098 Provides swapchain information for the current frame to the outside
2099 world via a call to sapp_get_swapchain().
2100
2101 NOTE: sapp_get_swapchain() must be called exactly once per frame since
2102 on some backends it will also acquire the next swapchain image.
2103
2104 NOTE: when using sokol_gfx.h, don't assume that the sapp_swapchain struct
2105 has the same memory layout as sg_swapchain! Use the sokol_log.h helper
2106 function sglue_swapchain() to translate sapp_swapchain into a
2107 sg_swapchain instead.
2108*/
2109typedef struct sapp_metal_swapchain {
2110 const void *current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!)
2111 const void *depth_stencil_texture; // MTLTexture
2112 const void *msaa_color_texture; // MTLTexture
2113} sapp_metal_swapchain;
2114
2115typedef struct sapp_d3d11_swapchain {
2116 const void *render_view; // ID3D11RenderTargetView
2117 const void *resolve_view; // ID3D11RenderTargetView
2118 const void *depth_stencil_view; // ID3D11DepthStencilView
2119} sapp_d3d11_swapchain;
2120
2121typedef struct sapp_wgpu_swapchain {
2122 const void *render_view; // WGPUTextureView
2123 const void *resolve_view; // WGPUTextureView
2124 const void *depth_stencil_view; // WGPUTextureView
2125} sapp_wgpu_swapchain;
2126
2127typedef struct sapp_vulkan_swapchain {
2128 const void *render_image; // vkImage
2129 const void *render_view; // vkImageView
2130 const void *resolve_image; // vkImage;
2131 const void *resolve_view; // vkImageView
2132 const void *depth_stencil_image; // vkImage
2133 const void *depth_stencil_view; // vkImageView
2134 const void *render_finished_semaphore; // vkSemaphore
2135 const void *present_complete_semaphore; // vkSemaphore
2136} sapp_vulkan_swapchain;
2137
2138typedef struct sapp_gl_swapchain {
2139 uint32_t framebuffer; // GL framebuffer object
2140} sapp_gl_swapchain;
2141
2142typedef struct sapp_swapchain {
2143 int width;
2144 int height;
2145 int sample_count;
2146 sapp_pixel_format color_format;
2147 sapp_pixel_format depth_format;
2148 sapp_metal_swapchain metal;
2149 sapp_d3d11_swapchain d3d11;
2150 sapp_wgpu_swapchain wgpu;
2151 sapp_vulkan_swapchain vulkan;
2152 sapp_gl_swapchain gl;
2153} sapp_swapchain;
2154
2155/*
2156 sapp_logger
2157
2158 Used in sapp_desc to provide a logging function. Please be aware that
2159 without logging function, sokol-app will be completely silent, e.g. it will
2160 not report errors or warnings. For maximum error verbosity, compile in
2161 debug mode (e.g. NDEBUG *not* defined) and install a logger (for instance
2162 the standard logging function from sokol_log.h).
2163*/
2164typedef struct sapp_logger {
2165 void (*func)(const char *tag, // always "sapp"
2166 uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info
2167 uint32_t log_item_id, // SAPP_LOGITEM_*
2168 const char *message_or_null, // a message string, may be nullptr
2169 // in release mode
2170 uint32_t line_nr, // line number in sokol_app.h
2171 const char *filename_or_null, // source filename, may be nullptr
2172 // in release mode
2173 void *user_data);
2174 void *user_data;
2175} sapp_logger;
2176
2177/*
2178 sokol-app initialization options, used as return value of sokol_main()
2179 or sapp_run() argument.
2180*/
2181typedef struct sapp_gl_desc {
2182 int major_version; // override GL/GLES major and minor version (defaults:
2183 // GL4.1 (macOS) or GL4.3, GLES3.1 (Android) or GLES3.0
2184 int minor_version;
2185} sapp_gl_desc;
2186
2187typedef struct sapp_win32_desc {
2188 bool console_utf8; // if true, set the output console codepage to UTF-8
2189 bool console_create; // if true, attach stdout/stderr to a new console window
2190 bool console_attach; // if true, attach stdout/stderr to parent process
2191} sapp_win32_desc;
2192
2193typedef struct sapp_html5_desc {
2194 const char *canvas_selector; // css selector of the HTML5 canvas element,
2195 // default is "#canvas"
2196 bool
2197 canvas_resize; // if true, the HTML5 canvas size is set to
2198 // sapp_desc.width/height, otherwise canvas size is tracked
2199 bool preserve_drawing_buffer; // HTML5 only: whether to preserve default
2200 // framebuffer content between frames
2201 bool premultiplied_alpha; // HTML5 only: whether the rendered pixels use
2202 // premultiplied alpha convention
2203 bool ask_leave_site; // initial state of the internal html5_ask_leave_site
2204 // flag (see sapp_html5_ask_leave_site())
2205 bool update_document_title; // if true, update the HTML document.title with
2206 // sapp_desc.window_title
2207 bool bubble_mouse_events; // if true, mouse events will bubble up to the web
2208 // page
2209 bool bubble_touch_events; // same for touch events
2210 bool bubble_wheel_events; // same for wheel events
2211 bool bubble_key_events; // if true, bubble up *all* key events to browser, not
2212 // just key events that represent characters
2213 bool bubble_char_events; // if true, bubble up character events to browser
2214 bool
2215 use_emsc_set_main_loop; // if true, use emscripten_set_main_loop() instead
2216 // of emscripten_request_animation_frame_loop()
2217 bool emsc_set_main_loop_simulate_infinite_loop; // this will be passed as the
2218 // simulate_infinite_loop arg
2219 // to
2220 // emscripten_set_main_loop()
2221} sapp_html5_desc;
2222
2223typedef struct sapp_ios_desc {
2224 bool keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the
2225 // canvas
2226} sapp_ios_desc;
2227
2228typedef struct sapp_desc {
2229 void (*init_cb)(
2230 void); // these are the user-provided callbacks without user data
2231 void (*frame_cb)(void);
2232 void (*cleanup_cb)(void);
2233 void (*event_cb)(const sapp_event *);
2234
2235 void *user_data; // these are the user-provided callbacks with user data
2236 void (*init_userdata_cb)(void *);
2237 void (*frame_userdata_cb)(void *);
2238 void (*cleanup_userdata_cb)(void *);
2239 void (*event_userdata_cb)(const sapp_event *, void *);
2240
2241 int width; // the preferred width of the window / canvas
2242 int height; // the preferred height of the window / canvas
2243 int sample_count; // MSAA sample count
2244 int swap_interval; // the preferred swap interval (ignored on some platforms)
2245 bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI
2246 // displays
2247 bool fullscreen; // whether the window should be created in fullscreen mode
2248 bool alpha; // whether the framebuffer should have an alpha channel (ignored
2249 // on some platforms)
2250 const char *window_title; // the window title as UTF-8 encoded string
2251 bool enable_clipboard; // enable clipboard access, default is false
2252 int clipboard_size; // max size of clipboard content in bytes
2253 bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false
2254 int max_dropped_files; // max number of dropped files to process (default: 1)
2255 int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8
2256 // file path (default: 2048)
2257 sapp_icon_desc icon; // the initial window icon to set
2258 sapp_allocator
2259 allocator; // optional memory allocation overrides (default: malloc/free)
2260 sapp_logger logger; // logging callback override (default: NO LOGGING!)
2261
2262 // backend-specific options
2263 sapp_gl_desc gl;
2264 sapp_win32_desc win32;
2265 sapp_html5_desc html5;
2266 sapp_ios_desc ios;
2267
2268 // __v_ start
2269 bool __v_native_render; // V patch to allow for native rendering
2270 int min_width;
2271 int min_height;
2272 bool borderless_window; // V patch to remove native window decorations
2273 // __v_ end
2274} sapp_desc;
2275
2276/* HTML5 specific: request and response structs for
2277 asynchronously loading dropped-file content.
2278*/
2279typedef enum sapp_html5_fetch_error {
2280 SAPP_HTML5_FETCH_ERROR_NO_ERROR,
2281 SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL,
2282 SAPP_HTML5_FETCH_ERROR_OTHER,
2283} sapp_html5_fetch_error;
2284
2285typedef struct sapp_html5_fetch_response {
2286 bool succeeded; // true if the loading operation has succeeded
2287 sapp_html5_fetch_error error_code;
2288 int file_index; // index of the dropped file
2289 // (0..sapp_get_num_dropped_filed()-1)
2290 sapp_range data; // pointer and size of the fetched data (data.ptr ==
2291 // buffer.ptr, data.size <= buffer.size)
2292 sapp_range buffer; // the user-provided buffer ptr/size pair (buffer.ptr ==
2293 // data.ptr, buffer.size >= data.size)
2294 void *user_data; // user-provided user data pointer
2295} sapp_html5_fetch_response;
2296
2297typedef struct sapp_html5_fetch_request {
2298 int dropped_file_index; // 0..sapp_get_num_dropped_files()-1
2299 void (*callback)(const sapp_html5_fetch_response
2300 *); // response callback function pointer (required)
2301 sapp_range buffer; // ptr/size of a memory buffer to load the data into
2302 void *user_data; // optional userdata pointer
2303} sapp_html5_fetch_request;
2304
2305/*
2306 sapp_mouse_cursor
2307
2308 Predefined cursor image definitions, set with
2309 sapp_set_mouse_cursor(sapp_mouse_cursor cursor)
2310*/
2311typedef enum sapp_mouse_cursor {
2312 SAPP_MOUSECURSOR_DEFAULT = 0, // equivalent with system default cursor
2313 SAPP_MOUSECURSOR_ARROW,
2314 SAPP_MOUSECURSOR_IBEAM,
2315 SAPP_MOUSECURSOR_CROSSHAIR,
2316 SAPP_MOUSECURSOR_POINTING_HAND,
2317 SAPP_MOUSECURSOR_RESIZE_EW,
2318 SAPP_MOUSECURSOR_RESIZE_NS,
2319 SAPP_MOUSECURSOR_RESIZE_NWSE,
2320 SAPP_MOUSECURSOR_RESIZE_NESW,
2321 SAPP_MOUSECURSOR_RESIZE_ALL,
2322 SAPP_MOUSECURSOR_NOT_ALLOWED,
2323 SAPP_MOUSECURSOR_CUSTOM_0,
2324 SAPP_MOUSECURSOR_CUSTOM_1,
2325 SAPP_MOUSECURSOR_CUSTOM_2,
2326 SAPP_MOUSECURSOR_CUSTOM_3,
2327 SAPP_MOUSECURSOR_CUSTOM_4,
2328 SAPP_MOUSECURSOR_CUSTOM_5,
2329 SAPP_MOUSECURSOR_CUSTOM_6,
2330 SAPP_MOUSECURSOR_CUSTOM_7,
2331 SAPP_MOUSECURSOR_CUSTOM_8,
2332 SAPP_MOUSECURSOR_CUSTOM_9,
2333 SAPP_MOUSECURSOR_CUSTOM_10,
2334 SAPP_MOUSECURSOR_CUSTOM_11,
2335 SAPP_MOUSECURSOR_CUSTOM_12,
2336 SAPP_MOUSECURSOR_CUSTOM_13,
2337 SAPP_MOUSECURSOR_CUSTOM_14,
2338 SAPP_MOUSECURSOR_CUSTOM_15,
2339 _SAPP_MOUSECURSOR_NUM,
2340} sapp_mouse_cursor;
2341
2342/* user-provided functions */
2343extern sapp_desc sokol_main(int argc, char *argv[]);
2344
2345/* returns true after sokol-app has been initialized */
2346SOKOL_APP_API_DECL bool sapp_isvalid(void);
2347/* returns the current framebuffer width in pixels */
2348SOKOL_APP_API_DECL int sapp_width(void);
2349/* same as sapp_width(), but returns float */
2350SOKOL_APP_API_DECL float sapp_widthf(void);
2351/* returns the current framebuffer height in pixels */
2352SOKOL_APP_API_DECL int sapp_height(void);
2353/* same as sapp_height(), but returns float */
2354SOKOL_APP_API_DECL float sapp_heightf(void);
2355/* get default framebuffer color pixel format */
2356SOKOL_APP_API_DECL sapp_pixel_format sapp_color_format(void);
2357/* get default framebuffer depth pixel format */
2358SOKOL_APP_API_DECL sapp_pixel_format sapp_depth_format(void);
2359/* get default framebuffer sample count */
2360SOKOL_APP_API_DECL int sapp_sample_count(void);
2361/* returns true when high_dpi was requested and actually running in a high-dpi
2362 * scenario */
2363SOKOL_APP_API_DECL bool sapp_high_dpi(void);
2364/* returns the dpi scaling factor (window pixels to framebuffer pixels) */
2365SOKOL_APP_API_DECL float sapp_dpi_scale(void);
2366/* show or hide the mobile device onscreen keyboard */
2367SOKOL_APP_API_DECL void sapp_show_keyboard(bool show);
2368/* return true if the mobile device onscreen keyboard is currently shown */
2369SOKOL_APP_API_DECL bool sapp_keyboard_shown(void);
2370/* query fullscreen mode */
2371SOKOL_APP_API_DECL bool sapp_is_fullscreen(void);
2372/* toggle fullscreen mode */
2373SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void);
2374/* show or hide the mouse cursor */
2375SOKOL_APP_API_DECL void sapp_show_mouse(bool show);
2376/* show or hide the mouse cursor */
2377SOKOL_APP_API_DECL bool sapp_mouse_shown(void);
2378/* enable/disable mouse-pointer-lock mode */
2379SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock);
2380/* return true if in mouse-pointer-lock mode (this may toggle a few frames
2381 * later) */
2382SOKOL_APP_API_DECL bool sapp_mouse_locked(void);
2383/* set mouse cursor type */
2384SOKOL_APP_API_DECL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor);
2385/* get current mouse cursor type */
2386SOKOL_APP_API_DECL sapp_mouse_cursor sapp_get_mouse_cursor(void);
2387/* associate a custom mouse cursor image to a sapp_mouse_cursor enum entry */
2388SOKOL_APP_API_DECL sapp_mouse_cursor sapp_bind_mouse_cursor_image(
2389 sapp_mouse_cursor cursor, const sapp_image_desc *desc);
2390/* restore the sapp_mouse_cursor enum entry to it's default system appearance */
2391SOKOL_APP_API_DECL void
2392sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor);
2393/* return the userdata pointer optionally provided in sapp_desc */
2394SOKOL_APP_API_DECL void *sapp_userdata(void);
2395/* return a copy of the sapp_desc structure */
2396SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void);
2397/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */
2398SOKOL_APP_API_DECL void sapp_request_quit(void);
2399/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received)
2400 */
2401SOKOL_APP_API_DECL void sapp_cancel_quit(void);
2402/* initiate a "hard quit" (quit application without sending
2403 * SAPP_EVENTTYPE_QUIT_REQUESTED) */
2404SOKOL_APP_API_DECL void sapp_quit(void);
2405/* call from inside event callback to consume the current event (don't forward
2406 * to platform) */
2407SOKOL_APP_API_DECL void sapp_consume_event(void);
2408/* get the current frame counter (for comparison with sapp_event.frame_count) */
2409SOKOL_APP_API_DECL uint64_t sapp_frame_count(void);
2410/* get an averaged/smoothed frame duration in seconds */
2411SOKOL_APP_API_DECL double sapp_frame_duration(void);
2412/* write string into clipboard */
2413SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char *str);
2414/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED)
2415 */
2416SOKOL_APP_API_DECL const char *sapp_get_clipboard_string(void);
2417/* set the window title (only on desktop platforms) */
2418SOKOL_APP_API_DECL void sapp_set_window_title(const char *str);
2419/* set the window icon (only on Windows and Linux) */
2420SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc *icon_desc);
2421/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED
2422 * event) */
2423SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void);
2424/* gets the dropped file paths */
2425SOKOL_APP_API_DECL const char *sapp_get_dropped_file_path(int index);
2426
2427/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty
2428 * stub) */
2429SOKOL_APP_API_DECL void sapp_run(const sapp_desc *desc);
2430
2431/* get runtime environment information */
2432sapp_environment sapp_get_environment(void);
2433/* get current frame's swapchain information (call once per frame!) */
2434sapp_swapchain sapp_get_swapchain(void);
2435
2436/* EGL: get EGLDisplay object */
2437SOKOL_APP_API_DECL const void *sapp_egl_get_display(void);
2438/* EGL: get EGLContext object */
2439SOKOL_APP_API_DECL const void *sapp_egl_get_context(void);
2440
2441/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */
2442SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask);
2443/* HTML5: get byte size of a dropped file */
2444SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index);
2445/* HTML5: asynchronously load the content of a dropped file */
2446SOKOL_APP_API_DECL void
2447sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request *request);
2448
2449/* macOS: get bridged pointer to macOS NSWindow */
2450SOKOL_APP_API_DECL const void *sapp_macos_get_window(void);
2451/* iOS: get bridged pointer to iOS UIWindow */
2452SOKOL_APP_API_DECL const void *sapp_ios_get_window(void);
2453
2454/* D3D11: get pointer to IDXGISwapChain object */
2455SOKOL_APP_API_DECL const void *sapp_d3d11_get_swap_chain(void);
2456
2457/* Win32: get the HWND window handle */
2458SOKOL_APP_API_DECL const void *sapp_win32_get_hwnd(void);
2459
2460/* GL: get major version */
2461SOKOL_APP_API_DECL int sapp_gl_get_major_version(void);
2462/* GL: get minor version */
2463SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void);
2464/* GL: return true if the context is GLES */
2465SOKOL_APP_API_DECL bool sapp_gl_is_gles(void);
2466/* GL: get default framebuffer */
2467SOKOL_APP_API_DECL uint32_t sapp_gl_get_framebuffer(void);
2468
2469/* X11: get Window */
2470SOKOL_APP_API_DECL const void *sapp_x11_get_window(void);
2471/* X11: get Display */
2472SOKOL_APP_API_DECL const void *sapp_x11_get_display(void);
2473
2474/* Android: get native activity handle */
2475SOKOL_APP_API_DECL const void *sapp_android_get_native_activity(void);
2476
2477#ifdef __cplusplus
2478} /* extern "C" */
2479
2480/* reference-based equivalents for C++ */
2481inline void sapp_run(const sapp_desc &desc) { return sapp_run(&desc); }
2482
2483#endif
2484
2485#endif // SOKOL_APP_INCLUDED
2486
2487// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████
2488// █████ ████████ ██ ██████ ███ ██ ██ ████ ████ ██ ██ ██ ██ ████
2489// ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
2490// ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████
2491// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
2492// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
2493// ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██
2494// ██████ ██ ████
2495//
2496// >>implementation
2497#ifdef SOKOL_APP_IMPL
2498#ifndef SOKOL_APP_IMPL_INCLUDED
2499#define SOKOL_APP_IMPL_INCLUDED
2500
2501#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
2502#error \
2503 "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sapp_desc.allocator to override memory allocation functions"
2504#endif
2505
2506#include <math.h> // roundf
2507#include <stddef.h> // size_t
2508#include <stdlib.h> // malloc, free
2509#include <string.h> // memset, strncmp
2510
2511// helper macros
2512#define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
2513#define _sapp_absf(a) (((a) < 0.0f) ? -(a) : (a))
2514
2515#ifdef __cplusplus
2516#define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {}
2517#else
2518#define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {0}
2519#endif
2520
2521#define _SAPP_MAX_TITLE_LENGTH (128)
2522#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640)
2523#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480)
2524
2525// check if the config defines are alright
2526#if defined(__APPLE__)
2527// see
2528// https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
2529#if !defined(__cplusplus)
2530#if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
2531#error \
2532 "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
2533#endif
2534#endif
2535#define _SAPP_APPLE (1)
2536#include <TargetConditionals.h>
2537#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
2538// MacOS
2539#define _SAPP_MACOS (1)
2540#if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU)
2541#error ("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL, SOKOL_GLCORE or SOKOL_WGPU")
2542#endif
2543#else
2544// iOS or iOS Simulator
2545#define _SAPP_IOS (1)
2546#if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3)
2547#error ("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3")
2548#endif
2549#if TARGET_OS_TV
2550#define _SAPP_TVOS (1)
2551#endif
2552#endif
2553#elif defined(__EMSCRIPTEN__)
2554// Emscripten
2555#define _SAPP_EMSCRIPTEN (1)
2556#if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU)
2557#error ("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU")
2558#endif
2559#elif defined(_WIN32)
2560// Windows (D3D11 or GL)
2561#define _SAPP_WIN32 (1)
2562#if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) && \
2563 !defined(SOKOL_VULKAN) && !defined(SOKOL_NOAPI)
2564#error ("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE, SOKOL_WGPU, SOKOL_VULKAN or SOKOL_NOAPI")
2565#endif
2566#if defined(SOKOL_VULKAN)
2567#define VK_USE_PLATFORM_WIN32_KHR
2568#include <vulkan/vulkan.h>
2569#endif
2570#elif defined(__ANDROID__)
2571// Android
2572#define _SAPP_ANDROID (1)
2573#if !defined(SOKOL_GLES3)
2574#error ("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3")
2575#endif
2576#if defined(SOKOL_NO_ENTRY)
2577#error ("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android")
2578#endif
2579#elif defined(__linux__) || defined(__unix__)
2580// Linux
2581#define _SAPP_LINUX (1)
2582
2583// Define POSIX features early for clock_gettime and other POSIX functions
2584#ifndef _POSIX_C_SOURCE
2585#define _POSIX_C_SOURCE 199309L
2586#endif
2587
2588// Wayland/X11 platform selection
2589// Default to X11 on Linux unless SOKOL_WAYLAND or SOKOL_X11 is explicitly defined
2590// Note: This must come AFTER sokol_app.h checks for SOKOL_GLCORE etc.
2591#if defined(__linux__) && !defined(SOKOL_WAYLAND) && !defined(SOKOL_X11)
2592#define SOKOL_X11 (1)
2593#endif
2594#if !defined(SOKOL_DISABLE_WAYLAND)
2595#if defined(SOKOL_WAYLAND) || (!defined(SOKOL_X11) && defined(__linux__))
2596#define _SAPP_WAYLAND (1)
2597#endif
2598#endif
2599#if !defined(_SAPP_WAYLAND)
2600#define _SAPP_X11 (1)
2601#endif
2602
2603#if !defined(SOKOL_GLCORE) && !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) && \
2604 !defined(SOKOL_VULKAN)
2605#error ("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3, SOKOL_WGPU or SOKOL_VULKAN")
2606#endif
2607#if defined(SOKOL_GLCORE)
2608#if defined(SOKOL_FORCE_EGL) || defined(_SAPP_WAYLAND)
2609#define _SAPP_EGL (1)
2610#else
2611#define _SAPP_GLX (1)
2612#endif
2613#define GL_GLEXT_PROTOTYPES
2614#include <GL/gl.h>
2615#elif defined(SOKOL_GLES3)
2616#define _SAPP_EGL (1)
2617#include <GLES3/gl3.h>
2618#include <GLES3/gl3ext.h>
2619#elif defined(SOKOL_VULKAN)
2620#if defined(_SAPP_WAYLAND)
2621#define VK_USE_PLATFORM_WAYLAND_KHR
2622#else
2623#define VK_USE_PLATFORM_XLIB_KHR
2624#endif
2625#include <vulkan/vulkan.h>
2626#endif
2627#else
2628#error "sokol_app.h: Unknown platform"
2629#endif
2630
2631#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3)
2632#define _SAPP_ANY_GL (1)
2633#endif
2634
2635#ifndef SOKOL_API_IMPL
2636#define SOKOL_API_IMPL
2637#endif
2638#ifndef SOKOL_DEBUG
2639#ifndef NDEBUG
2640#define SOKOL_DEBUG
2641#endif
2642#endif
2643#ifndef SOKOL_ASSERT
2644#include <assert.h>
2645#define SOKOL_ASSERT(c) assert(c)
2646#endif
2647#ifndef SOKOL_UNREACHABLE
2648#define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
2649#endif
2650
2651#ifndef _SOKOL_PRIVATE
2652#if defined(__GNUC__) || defined(__clang__)
2653#define _SOKOL_PRIVATE __attribute__((unused)) static
2654#else
2655#define _SOKOL_PRIVATE static
2656#endif
2657#endif
2658#ifndef _SOKOL_UNUSED
2659#define _SOKOL_UNUSED(x) (void)(x)
2660#endif
2661
2662#if defined(SOKOL_WGPU)
2663#include <webgpu/webgpu.h>
2664#if !defined(__EMSCRIPTEN__)
2665#define _SAPP_WGPU_HAS_WAIT (1)
2666#endif
2667#endif
2668
2669#if defined(_SAPP_APPLE)
2670#ifndef GL_SILENCE_DEPRECATION
2671#define GL_SILENCE_DEPRECATION
2672#endif
2673#if defined(SOKOL_METAL)
2674#import <Metal/Metal.h>
2675#import <MetalKit/MetalKit.h>
2676#endif
2677#if defined(_SAPP_MACOS)
2678#import <Cocoa/Cocoa.h>
2679#if defined(_SAPP_ANY_GL)
2680#include <OpenGL/gl3.h>
2681#endif
2682#if defined(SOKOL_WGPU)
2683#import <QuartzCore/CADisplayLink.h>
2684#import <QuartzCore/CAMetalLayer.h>
2685#endif
2686#elif defined(_SAPP_IOS)
2687#import <UIKit/UIKit.h>
2688#if defined(_SAPP_ANY_GL)
2689#import <GLKit/GLKit.h>
2690#include <OpenGLES/ES3/gl.h>
2691#endif
2692#endif
2693#include <AvailabilityMacros.h>
2694#include <mach/mach_time.h>
2695#elif defined(_SAPP_EMSCRIPTEN)
2696#if defined(SOKOL_GLES3)
2697#include <GLES3/gl3.h>
2698#endif
2699#include <emscripten/emscripten.h>
2700#include <emscripten/html5.h>
2701#elif defined(_SAPP_WIN32)
2702#ifdef _MSC_VER
2703#pragma warning(push)
2704#pragma warning( \
2705 disable : 4201) /* nonstandard extension used: nameless struct/union */
2706#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \
2707 aggregate initializer */
2708#pragma warning(disable : 4054) /* 'type cast': from function pointer */
2709#pragma warning(disable : 4055) /* 'type cast': from data pointer */
2710#pragma warning(disable \
2711 : 4505) /* unreferenced local function has been removed */
2712#pragma warning(disable : 4115) /* /W4: 'ID3D11ModuleInstance': named type \
2713 definition in parentheses (in d3d11.h) */
2714#endif
2715#ifndef WIN32_LEAN_AND_MEAN
2716#define WIN32_LEAN_AND_MEAN
2717#endif
2718#ifndef NOMINMAX
2719#define NOMINMAX
2720#endif
2721#include <shellapi.h>
2722#include <windows.h>
2723#include <windowsx.h>
2724
2725#if defined(__GNUC__)
2726#pragma GCC diagnostic push
2727#pragma GCC diagnostic ignored "-Wunknown-pragmas"
2728#endif
2729
2730#if !defined( \
2731 SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the application's
2732 // responsibility to use the right subsystem
2733
2734#if defined(SOKOL_WIN32_FORCE_MAIN) && defined(SOKOL_WIN32_FORCE_WINMAIN)
2735// If both are defined, it's the application's responsibility to use the right
2736// subsystem
2737#elif defined(SOKOL_WIN32_FORCE_MAIN)
2738#pragma comment(linker, "/subsystem:console")
2739#else
2740#pragma comment(linker, "/subsystem:windows")
2741#endif
2742#endif
2743#include <stdio.h> /* freopen_s() */
2744#include <wchar.h> /* wcslen() */
2745
2746#pragma comment(lib, "kernel32")
2747#pragma comment(lib, "user32")
2748#pragma comment( \
2749 lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */
2750#pragma comment(lib, "gdi32")
2751#if defined(SOKOL_D3D11)
2752#pragma comment(lib, "dxgi")
2753#pragma comment(lib, "d3d11")
2754#endif
2755
2756#if defined(__GNUC__)
2757#pragma GCC diagnostic pop
2758#endif
2759
2760#if defined(SOKOL_D3D11)
2761#ifndef D3D11_NO_HELPERS
2762#define D3D11_NO_HELPERS
2763#endif
2764#include <d3d11.h>
2765#include <dxgi.h>
2766// DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't
2767// depend on it
2768#define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4)
2769#endif
2770#ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */
2771#define WM_MOUSEHWHEEL (0x020E)
2772#endif
2773#ifndef WM_DPICHANGED
2774#define WM_DPICHANGED (0x02E0)
2775#endif
2776#elif defined(_SAPP_ANDROID)
2777#include <EGL/egl.h>
2778#include <GLES3/gl3.h>
2779#include <android/looper.h>
2780#include <android/native_activity.h>
2781#include <pthread.h>
2782#include <time.h>
2783#include <unistd.h>
2784#elif defined(_SAPP_LINUX)
2785#define GL_GLEXT_PROTOTYPES
2786#if defined(_SAPP_X11)
2787#include <X11/XKBlib.h>
2788#include <X11/Xatom.h>
2789#include <X11/Xcursor/Xcursor.h>
2790#include <X11/Xlib.h>
2791#include <X11/Xmd.h> /* CARD32 */
2792#include <X11/Xresource.h>
2793#include <X11/Xutil.h>
2794#include <X11/cursorfont.h> /* XC_* font cursors */
2795#include <X11/extensions/XInput2.h>
2796#include <X11/keysym.h>
2797#endif
2798#if defined(_SAPP_WAYLAND)
2799#include <linux/input-event-codes.h>
2800#include <string.h>
2801#include <sys/mman.h>
2802#include <sys/timerfd.h>
2803#include <wayland-client.h>
2804#include <wayland-cursor.h>
2805#include <wayland-egl.h>
2806#include <xkbcommon/xkbcommon-compose.h>
2807#include <xkbcommon/xkbcommon.h>
2808
2809// struct forward declarations
2810struct xdg_wm_base;
2811struct xdg_surface;
2812struct xdg_toplevel;
2813struct zwp_pointer_constraints_v1;
2814struct zwp_locked_pointer_v1;
2815struct zwp_relative_pointer_manager_v1;
2816struct zwp_relative_pointer_v1;
2817struct wp_cursor_shape_manager_v1;
2818struct wp_cursor_shape_device_v1;
2819struct wp_fractional_scale_manager_v1;
2820struct wp_fractional_scale_v1;
2821struct zxdg_decoration_manager_v1;
2822struct zxdg_toplevel_decoration_v1;
2823
2824// interface forward declarations
2825extern const struct wl_interface wl_compositor_interface;
2826extern const struct wl_interface wl_surface_interface;
2827extern const struct wl_interface wl_seat_interface;
2828extern const struct wl_interface wl_shm_interface;
2829extern const struct wl_interface xdg_wm_base_interface;
2830extern const struct wl_interface xdg_surface_interface;
2831extern const struct wl_interface xdg_toplevel_interface;
2832extern const struct wl_interface wl_data_device_manager_interface;
2833extern const struct wl_interface zwp_pointer_constraints_v1_interface;
2834extern const struct wl_interface zwp_relative_pointer_manager_v1_interface;
2835extern const struct wl_interface wp_cursor_shape_manager_v1_interface;
2836extern const struct wl_interface wp_fractional_scale_manager_v1_interface;
2837extern const struct wl_interface wp_viewporter_interface;
2838extern const struct wl_interface zxdg_decoration_manager_v1_interface;
2839#endif
2840#if defined(_SAPP_EGL)
2841#include <EGL/egl.h>
2842#endif
2843#if defined(_SAPP_WAYLAND)
2844#include "cursor-shape-v1-client-protocol.h"
2845#include "fractional-scale-v1-client-protocol.h"
2846#include "pointer-constraints-unstable-v1-client-protocol.h"
2847#include "relative-pointer-unstable-v1-client-protocol.h"
2848#include "viewporter-client-protocol.h"
2849#include "xdg-decoration-unstable-v1-client-protocol.h"
2850#include "xdg-shell-client-protocol.h"
2851#endif
2852#include <dlfcn.h> /* dlopen, dlsym, dlclose */
2853#include <limits.h> /* LONG_MAX */
2854#include <poll.h>
2855#include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
2856#include <time.h>
2857#include <unistd.h>
2858#endif
2859
2860#if defined(_SAPP_APPLE)
2861// this is ARC compatible
2862#if defined(__cplusplus)
2863#define _SAPP_CLEAR_ARC_STRUCT(type, item) \
2864 { \
2865 item = type(); \
2866 }
2867#else
2868#define _SAPP_CLEAR_ARC_STRUCT(type, item) \
2869 { \
2870 item = (type){0}; \
2871 }
2872#endif
2873#else
2874#define _SAPP_CLEAR_ARC_STRUCT(type, item) \
2875 { \
2876 _sapp_clear(&item, sizeof(item)); \
2877 }
2878#endif
2879
2880// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███
2881// ██ ██████ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████
2882// ████ ██ ████ ██ ██ █████ ██████ ███████ ██ ████ ██ █████ ██ ██
2883// ██ ████ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
2884// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████
2885// ██ ██ ██ ██ ██ ██ ████ ██████
2886//
2887// >>frame timing
2888#define _SAPP_RING_NUM_SLOTS (256)
2889typedef struct {
2890 int head;
2891 int tail;
2892 double buf[_SAPP_RING_NUM_SLOTS];
2893} _sapp_ring_t;
2894
2895_SOKOL_PRIVATE int _sapp_ring_idx(int i) { return i % _SAPP_RING_NUM_SLOTS; }
2896
2897_SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t *ring) {
2898 ring->head = 0;
2899 ring->tail = 0;
2900}
2901
2902_SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t *ring) {
2903 return _sapp_ring_idx(ring->head + 1) == ring->tail;
2904}
2905
2906_SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t *ring) {
2907 return ring->head == ring->tail;
2908}
2909
2910_SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t *ring) {
2911 int count;
2912 if (ring->head >= ring->tail) {
2913 count = ring->head - ring->tail;
2914 } else {
2915 count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail;
2916 }
2917 SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS));
2918 return count;
2919}
2920
2921_SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t *ring, double val) {
2922 SOKOL_ASSERT(!_sapp_ring_full(ring));
2923 ring->buf[ring->head] = val;
2924 ring->head = _sapp_ring_idx(ring->head + 1);
2925}
2926
2927_SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t *ring) {
2928 SOKOL_ASSERT(!_sapp_ring_empty(ring));
2929 double val = ring->buf[ring->tail];
2930 ring->tail = _sapp_ring_idx(ring->tail + 1);
2931 return val;
2932}
2933
2934/*
2935 NOTE:
2936
2937 Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS?
2938 A: The value appears to be highly unstable during the first few
2939 seconds, sometimes several frames are dropped in sequence, or
2940 switch between 120 and 60 Hz for a few frames. Simply measuring
2941 and averaging the frame time yielded a more stable frame duration.
2942 Maybe switching to CVDisplayLink would yield better results.
2943 Until then just measure the time.
2944*/
2945typedef struct {
2946#if defined(_SAPP_APPLE)
2947 struct {
2948 mach_timebase_info_data_t timebase;
2949 uint64_t start;
2950 } mach;
2951#elif defined(_SAPP_EMSCRIPTEN)
2952 // empty
2953#elif defined(_SAPP_WIN32)
2954 struct {
2955 LARGE_INTEGER freq;
2956 LARGE_INTEGER start;
2957 } win;
2958#else // Linux, Android, ...
2959#ifdef CLOCK_MONOTONIC
2960#define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC
2961#else
2962// on some embedded platforms, CLOCK_MONOTONIC isn't defined
2963#define _SAPP_CLOCK_MONOTONIC (1)
2964#endif
2965 struct {
2966 uint64_t start;
2967 } posix;
2968#endif
2969} _sapp_timestamp_t;
2970
2971_SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer,
2972 int64_t denom) {
2973 int64_t q = value / denom;
2974 int64_t r = value % denom;
2975 return q * numer + r * numer / denom;
2976}
2977
2978_SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t *ts) {
2979#if defined(_SAPP_APPLE)
2980 mach_timebase_info(&ts->mach.timebase);
2981 ts->mach.start = mach_absolute_time();
2982#elif defined(_SAPP_EMSCRIPTEN)
2983 (void)ts;
2984#elif defined(_SAPP_WIN32)
2985 QueryPerformanceFrequency(&ts->win.freq);
2986 QueryPerformanceCounter(&ts->win.start);
2987#else
2988// Ensure clock_gettime is declared
2989#if !defined(CLOCK_MONOTONIC)
2990#define CLOCK_MONOTONIC 1
2991#endif
2992#if !defined(_SAPP_CLOCK_MONOTONIC)
2993#define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC
2994#endif
2995 extern int clock_gettime(int, struct timespec *);
2996 struct timespec tspec;
2997 clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
2998 ts->posix.start =
2999 (uint64_t)tspec.tv_sec * 1000000000 + (uint64_t)tspec.tv_nsec;
3000#endif
3001}
3002
3003_SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t *ts) {
3004#if defined(_SAPP_APPLE)
3005 const uint64_t traw = mach_absolute_time() - ts->mach.start;
3006 const uint64_t now = (uint64_t)_sapp_int64_muldiv(
3007 (int64_t)traw, (int64_t)ts->mach.timebase.numer,
3008 (int64_t)ts->mach.timebase.denom);
3009 return (double)now / 1000000000.0;
3010#elif defined(_SAPP_EMSCRIPTEN)
3011 (void)ts;
3012 SOKOL_ASSERT(false);
3013 return 0.0;
3014#elif defined(_SAPP_WIN32)
3015 LARGE_INTEGER qpc;
3016 QueryPerformanceCounter(&qpc);
3017 const uint64_t now = (uint64_t)_sapp_int64_muldiv(
3018 qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart);
3019 return (double)now / 1000000000.0;
3020#else
3021 extern int clock_gettime(int, struct timespec *);
3022 struct timespec tspec;
3023 clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
3024 const uint64_t now =
3025 (uint64_t)tspec.tv_sec * 1000000000 + (uint64_t)tspec.tv_nsec;
3026 const uint64_t elapsed = now - ts->posix.start;
3027 return (double)elapsed / 1000000000.0;
3028#endif
3029}
3030
3031typedef struct {
3032 double last;
3033 double accum;
3034 double avg;
3035 int spike_count;
3036 int num;
3037 _sapp_timestamp_t timestamp;
3038 _sapp_ring_t ring;
3039} _sapp_timing_t;
3040
3041_SOKOL_PRIVATE void _sapp_timing_reset(_sapp_timing_t *t) {
3042 t->last = 0.0;
3043 t->accum = 0.0;
3044 t->spike_count = 0;
3045 t->num = 0;
3046 _sapp_ring_init(&t->ring);
3047}
3048
3049_SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t *t) {
3050 t->avg = 1.0 / 60.0; // dummy value until first actual value is available
3051 _sapp_timing_reset(t);
3052 _sapp_timestamp_init(&t->timestamp);
3053}
3054
3055_SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t *t, double dur) {
3056 // arbitrary upper limit to ignore outliers (e.g. during window resizing, or
3057 // debugging)
3058 double min_dur = 0.0;
3059 double max_dur = 0.1;
3060 // if we have enough samples for a useful average, use a much tighter 'valid
3061 // window'
3062 if (_sapp_ring_full(&t->ring)) {
3063 min_dur = t->avg * 0.8;
3064 max_dur = t->avg * 1.2;
3065 }
3066 if ((dur < min_dur) || (dur > max_dur)) {
3067 t->spike_count++;
3068 // if there have been many spikes in a row, the display refresh rate
3069 // might have changed, so a timing reset is needed
3070 if (t->spike_count > 20) {
3071 _sapp_timing_reset(t);
3072 }
3073 return;
3074 }
3075 if (_sapp_ring_full(&t->ring)) {
3076 double old_val = _sapp_ring_dequeue(&t->ring);
3077 t->accum -= old_val;
3078 t->num -= 1;
3079 }
3080 _sapp_ring_enqueue(&t->ring, dur);
3081 t->accum += dur;
3082 t->num += 1;
3083 SOKOL_ASSERT(t->num > 0);
3084 t->avg = t->accum / t->num;
3085 t->spike_count = 0;
3086}
3087
3088_SOKOL_PRIVATE void _sapp_timing_discontinuity(_sapp_timing_t *t) {
3089 t->last = 0.0;
3090}
3091
3092_SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t *t) {
3093 const double now = _sapp_timestamp_now(&t->timestamp);
3094 if (t->last > 0.0) {
3095 double dur = now - t->last;
3096 _sapp_timing_put(t, dur);
3097 }
3098 t->last = now;
3099}
3100
3101_SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t *t, double now) {
3102 if (t->last > 0.0) {
3103 double dur = now - t->last;
3104 _sapp_timing_put(t, dur);
3105 }
3106 t->last = now;
3107}
3108
3109_SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t *t) { return t->avg; }
3110
3111// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████
3112// ██ ██ ██ ██ ██ ██ ██ ██ ██
3113// ███████ ██ ██████ ██ ██ ██ ██ ███████
3114// ██ ██ ██ ██ ██ ██ ██ ██ ██
3115// ███████ ██ ██ ██ ██████ ██████ ██ ███████
3116//
3117// >> structs
3118#if defined(SOKOL_WGPU)
3119typedef struct {
3120 WGPUInstance instance;
3121 WGPUAdapter adapter;
3122 WGPUDevice device;
3123 WGPUSurface surface;
3124 WGPUTextureFormat render_format;
3125 WGPUTexture msaa_tex;
3126 WGPUTextureView msaa_view;
3127 WGPUTexture depth_stencil_tex;
3128 WGPUTextureView depth_stencil_view;
3129 WGPUTextureView swapchain_view;
3130 bool init_done;
3131} _sapp_wgpu_t;
3132#endif
3133
3134#if defined(SOKOL_VULKAN)
3135#define _SAPP_VK_MAX_SWAPCHAIN_IMAGES (8)
3136
3137typedef struct {
3138 VkImage img;
3139 VkDeviceMemory mem;
3140 VkImageView view;
3141} _sapp_vk_swapchain_surface_t;
3142
3143typedef struct {
3144 VkInstance instance;
3145 VkSurfaceKHR surface;
3146 VkSurfaceFormatKHR surface_format;
3147 VkPhysicalDevice physical_device;
3148 uint32_t queue_family_index;
3149 VkDevice device;
3150 VkQueue queue;
3151 VkSwapchainKHR swapchain;
3152 uint32_t num_swapchain_images;
3153 uint32_t cur_swapchain_image_index;
3154 VkImage swapchain_images[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
3155 VkImageView swapchain_views[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
3156 _sapp_vk_swapchain_surface_t msaa;
3157 _sapp_vk_swapchain_surface_t depth;
3158 uint32_t sync_slot;
3159 struct {
3160 VkSemaphore render_finished_sem;
3161 VkSemaphore present_complete_sem;
3162 } sync[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
3163 struct {
3164 PFN_vkSetDebugUtilsObjectNameEXT set_debug_utils_object_name_ext;
3165 } ext;
3166} _sapp_vk_t;
3167#endif
3168
3169#if defined(_SAPP_MACOS)
3170@interface _sapp_macos_app_delegate : NSObject <NSApplicationDelegate>
3171@end
3172@interface _sapp_macos_window : NSWindow
3173@end
3174@interface _sapp_macos_window_delegate : NSObject <NSWindowDelegate>
3175@end
3176#if defined(SOKOL_METAL)
3177@interface _sapp_macos_view : MTKView
3178@end
3179#elif defined(SOKOL_GLCORE)
3180@interface _sapp_macos_view : NSOpenGLView
3181- (void)timerFired:(id)sender;
3182@end
3183#elif defined(SOKOL_WGPU)
3184@interface _sapp_macos_view : NSView
3185- (void)displayLinkFired:(id)sender;
3186@end
3187#endif // SOKOL_GLCORE
3188
3189// __v_ start
3190@interface MyView2 : NSView
3191@end
3192
3193static NSView* g_view = nil;
3194// __v_ end
3195
3196typedef struct {
3197 uint32_t flags_changed_store;
3198 uint8_t mouse_buttons;
3199 NSWindow *window;
3200 NSTrackingArea *tracking_area;
3201 id keyup_monitor;
3202 _sapp_macos_app_delegate *app_dlg;
3203 _sapp_macos_window_delegate *win_dlg;
3204 _sapp_macos_view *view;
3205 NSCursor *standard_cursors[_SAPP_MOUSECURSOR_NUM];
3206 NSCursor *custom_cursors[_SAPP_MOUSECURSOR_NUM];
3207#if defined(SOKOL_METAL)
3208 id<MTLDevice> mtl_device;
3209#endif
3210#if defined(SOKOL_WGPU)
3211 struct {
3212 CAMetalLayer *mtl_layer;
3213 CADisplayLink *display_link;
3214 } wgpu;
3215#endif
3216} _sapp_macos_t;
3217
3218#endif // _SAPP_MACOS
3219
3220#if defined(_SAPP_IOS)
3221
3222@interface _sapp_app_delegate : NSObject <UIApplicationDelegate>
3223@end
3224@interface _sapp_textfield_dlg : NSObject <UITextFieldDelegate>
3225- (void)keyboardWasShown:(NSNotification *)notif;
3226- (void)keyboardWillBeHidden:(NSNotification *)notif;
3227- (void)keyboardDidChangeFrame:(NSNotification *)notif;
3228@end
3229#if defined(SOKOL_METAL)
3230@interface _sapp_ios_view : MTKView
3231;
3232@end
3233#else
3234@interface _sapp_ios_view : GLKView
3235@end
3236#endif
3237
3238typedef struct {
3239 UIWindow *window;
3240 _sapp_ios_view *view;
3241 UITextField *textfield;
3242 _sapp_textfield_dlg *textfield_dlg;
3243#if defined(SOKOL_METAL)
3244 UIViewController *view_ctrl;
3245 id<MTLDevice> mtl_device;
3246#else
3247 GLKViewController *view_ctrl;
3248 EAGLContext *eagl_ctx;
3249#endif
3250 bool suspended;
3251} _sapp_ios_t;
3252
3253#endif // _SAPP_IOS
3254
3255#if defined(_SAPP_EMSCRIPTEN)
3256
3257typedef struct {
3258 bool mouse_lock_requested;
3259 uint16_t mouse_buttons;
3260} _sapp_emsc_t;
3261#endif // _SAPP_EMSCRIPTEN
3262
3263#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32)
3264typedef struct {
3265 ID3D11Device *device;
3266 ID3D11DeviceContext *device_context;
3267 ID3D11Texture2D *rt;
3268 ID3D11RenderTargetView *rtv;
3269 ID3D11Texture2D *msaa_rt;
3270 ID3D11RenderTargetView *msaa_rtv;
3271 ID3D11Texture2D *ds;
3272 ID3D11DepthStencilView *dsv;
3273 DXGI_SWAP_CHAIN_DESC swap_chain_desc;
3274 IDXGISwapChain *swap_chain;
3275 IDXGIDevice1 *dxgi_device;
3276 bool use_dxgi_frame_stats;
3277 UINT sync_refresh_count;
3278} _sapp_d3d11_t;
3279#endif
3280
3281#if defined(_SAPP_WIN32)
3282
3283#ifndef DPI_ENUMS_DECLARED
3284typedef enum PROCESS_DPI_AWARENESS {
3285 PROCESS_DPI_UNAWARE = 0,
3286 PROCESS_SYSTEM_DPI_AWARE = 1,
3287 PROCESS_PER_MONITOR_DPI_AWARE = 2
3288} PROCESS_DPI_AWARENESS;
3289typedef enum MONITOR_DPI_TYPE {
3290 MDT_EFFECTIVE_DPI = 0,
3291 MDT_ANGULAR_DPI = 1,
3292 MDT_RAW_DPI = 2,
3293 MDT_DEFAULT = MDT_EFFECTIVE_DPI
3294} MONITOR_DPI_TYPE;
3295#endif // DPI_ENUMS_DECLARED
3296
3297typedef struct {
3298 bool aware;
3299 float content_scale;
3300 float window_scale;
3301 float mouse_scale;
3302} _sapp_win32_dpi_t;
3303
3304typedef struct {
3305 HWND hwnd;
3306 HMONITOR hmonitor;
3307 HDC dc;
3308 HICON big_icon;
3309 HICON small_icon;
3310 HCURSOR standard_cursors[_SAPP_MOUSECURSOR_NUM];
3311 HCURSOR custom_cursors[_SAPP_MOUSECURSOR_NUM];
3312 UINT orig_codepage;
3313 WCHAR surrogate;
3314 RECT stored_window_rect; // used to restore window pos/size when toggling
3315 // fullscreen => windowed
3316 bool is_win10_or_greater;
3317 bool in_create_window;
3318 bool iconified;
3319 _sapp_win32_dpi_t dpi;
3320 struct {
3321 struct {
3322 LONG pos_x, pos_y;
3323 bool pos_valid;
3324 } lock;
3325 struct {
3326 LONG pos_x, pos_y;
3327 bool pos_valid;
3328 } raw_input;
3329 bool requested_lock;
3330 bool tracked;
3331 uint8_t capture_mask;
3332 } mouse;
3333 struct {
3334 size_t size;
3335 void *ptr;
3336 } raw_input_data;
3337} _sapp_win32_t;
3338
3339#if defined(SOKOL_GLCORE)
3340#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
3341#define WGL_SUPPORT_OPENGL_ARB 0x2010
3342#define WGL_DRAW_TO_WINDOW_ARB 0x2001
3343#define WGL_PIXEL_TYPE_ARB 0x2013
3344#define WGL_TYPE_RGBA_ARB 0x202b
3345#define WGL_ACCELERATION_ARB 0x2003
3346#define WGL_NO_ACCELERATION_ARB 0x2025
3347#define WGL_RED_BITS_ARB 0x2015
3348#define WGL_GREEN_BITS_ARB 0x2017
3349#define WGL_BLUE_BITS_ARB 0x2019
3350#define WGL_ALPHA_BITS_ARB 0x201b
3351#define WGL_DEPTH_BITS_ARB 0x2022
3352#define WGL_STENCIL_BITS_ARB 0x2023
3353#define WGL_DOUBLE_BUFFER_ARB 0x2011
3354#define WGL_SAMPLES_ARB 0x2042
3355#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
3356#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
3357#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
3358#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
3359#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
3360#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
3361#define WGL_CONTEXT_FLAGS_ARB 0x2094
3362#define ERROR_INVALID_VERSION_ARB 0x2095
3363#define ERROR_INVALID_PROFILE_ARB 0x2096
3364#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
3365typedef BOOL(WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int);
3366typedef BOOL(WINAPI *PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC, int, int, UINT,
3367 const int *, int *);
3368typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void);
3369typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);
3370typedef HGLRC(WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC,
3371 const int *);
3372typedef HGLRC(WINAPI *PFN_wglCreateContext)(HDC);
3373typedef BOOL(WINAPI *PFN_wglDeleteContext)(HGLRC);
3374typedef PROC(WINAPI *PFN_wglGetProcAddress)(LPCSTR);
3375typedef HDC(WINAPI *PFN_wglGetCurrentDC)(void);
3376typedef BOOL(WINAPI *PFN_wglMakeCurrent)(HDC, HGLRC);
3377
3378typedef struct {
3379 HINSTANCE opengl32;
3380 HGLRC gl_ctx;
3381 PFN_wglCreateContext CreateContext;
3382 PFN_wglDeleteContext DeleteContext;
3383 PFN_wglGetProcAddress GetProcAddress;
3384 PFN_wglGetCurrentDC GetCurrentDC;
3385 PFN_wglMakeCurrent MakeCurrent;
3386 PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
3387 PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB;
3388 PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT;
3389 PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB;
3390 PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
3391 // special case glGetIntegerv
3392 void(WINAPI *GetIntegerv)(uint32_t pname, int32_t *data);
3393 bool ext_swap_control;
3394 bool arb_multisample;
3395 bool arb_pixel_format;
3396 bool arb_create_context;
3397 bool arb_create_context_profile;
3398 HWND msg_hwnd;
3399 HDC msg_dc;
3400} _sapp_wgl_t;
3401#endif // SOKOL_GLCORE
3402
3403#endif // _SAPP_WIN32
3404
3405#if defined(_SAPP_ANDROID)
3406typedef enum {
3407 _SOKOL_ANDROID_MSG_CREATE,
3408 _SOKOL_ANDROID_MSG_RESUME,
3409 _SOKOL_ANDROID_MSG_PAUSE,
3410 _SOKOL_ANDROID_MSG_FOCUS,
3411 _SOKOL_ANDROID_MSG_NO_FOCUS,
3412 _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW,
3413 _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE,
3414 _SOKOL_ANDROID_MSG_DESTROY,
3415} _sapp_android_msg_t;
3416
3417typedef struct {
3418 pthread_t thread;
3419 pthread_mutex_t mutex;
3420 pthread_cond_t cond;
3421 int read_from_main_fd;
3422 int write_from_main_fd;
3423} _sapp_android_pt_t;
3424
3425typedef struct {
3426 ANativeWindow *window;
3427 AInputQueue *input;
3428} _sapp_android_resources_t;
3429
3430typedef struct {
3431 ANativeActivity *activity;
3432 _sapp_android_pt_t pt;
3433 _sapp_android_resources_t pending;
3434 _sapp_android_resources_t current;
3435 ALooper *looper;
3436 bool is_thread_started;
3437 bool is_thread_stopping;
3438 bool is_thread_stopped;
3439 bool has_created;
3440 bool has_resumed;
3441 bool has_focus;
3442 EGLConfig config;
3443 EGLDisplay display;
3444 EGLContext context;
3445 EGLSurface surface;
3446} _sapp_android_t;
3447
3448#endif // _SAPP_ANDROID
3449
3450#if defined(_SAPP_LINUX)
3451
3452#if defined(_SAPP_X11)
3453#define _SAPP_X11_XDND_VERSION (5)
3454#define _SAPP_X11_MAX_X11_KEYCODES (256)
3455
3456#define GLX_VENDOR 1
3457#define GLX_RGBA_BIT 0x00000001
3458#define GLX_WINDOW_BIT 0x00000001
3459#define GLX_DRAWABLE_TYPE 0x8010
3460#define GLX_RENDER_TYPE 0x8011
3461#define GLX_DOUBLEBUFFER 5
3462#define GLX_RED_SIZE 8
3463#define GLX_GREEN_SIZE 9
3464#define GLX_BLUE_SIZE 10
3465#define GLX_ALPHA_SIZE 11
3466#define GLX_DEPTH_SIZE 12
3467#define GLX_STENCIL_SIZE 13
3468#define GLX_SAMPLES 0x186a1
3469#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
3470#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
3471#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
3472#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
3473#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
3474#define GLX_CONTEXT_FLAGS_ARB 0x2094
3475
3476typedef XID GLXWindow;
3477typedef XID GLXDrawable;
3478typedef struct __GLXFBConfig *GLXFBConfig;
3479typedef struct __GLXcontext *GLXContext;
3480typedef void (*__GLXextproc)(void);
3481
3482typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display *, GLXFBConfig, int, int *);
3483typedef const char *(*PFNGLXGETCLIENTSTRINGPROC)(Display *, int);
3484typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display *, int *, int *);
3485typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display *, int *, int *);
3486typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display *, GLXContext);
3487typedef Bool (*PFNGLXMAKECURRENTPROC)(Display *, GLXDrawable, GLXContext);
3488typedef void (*PFNGLXSWAPBUFFERSPROC)(Display *, GLXDrawable);
3489typedef const char *(*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display *, int);
3490typedef GLXFBConfig *(*PFNGLXGETFBCONFIGSPROC)(Display *, int, int *);
3491typedef __GLXextproc (*PFNGLXGETPROCADDRESSPROC)(const char *procName);
3492typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display *, GLXDrawable, int);
3493typedef XVisualInfo *(*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display *, GLXFBConfig);
3494typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display *, GLXFBConfig, Window,
3495 const int *);
3496typedef void (*PFNGLXDESTROYWINDOWPROC)(Display *, GLXWindow);
3497
3498typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
3499typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig,
3500 GLXContext, Bool,
3501 const int *);
3502
3503typedef struct {
3504 bool available;
3505 int major_opcode;
3506 int event_base;
3507 int error_base;
3508 int major;
3509 int minor;
3510} _sapp_xi_t;
3511
3512typedef struct {
3513 int version;
3514 Window source;
3515 Atom format;
3516 Atom XdndAware;
3517 Atom XdndEnter;
3518 Atom XdndPosition;
3519 Atom XdndStatus;
3520 Atom XdndActionCopy;
3521 Atom XdndDrop;
3522 Atom XdndFinished;
3523 Atom XdndSelection;
3524 Atom XdndTypeList;
3525 Atom text_uri_list;
3526} _sapp_xdnd_t;
3527
3528typedef struct {
3529 uint8_t mouse_buttons;
3530 Display *display;
3531 int screen;
3532 Window root;
3533 Colormap colormap;
3534 Window window;
3535 Cursor hidden_cursor;
3536 Cursor standard_cursors[_SAPP_MOUSECURSOR_NUM];
3537 Cursor custom_cursors[_SAPP_MOUSECURSOR_NUM];
3538 int window_state;
3539 float dpi;
3540 unsigned char error_code;
3541 Atom UTF8_STRING;
3542 Atom CLIPBOARD;
3543 Atom TARGETS;
3544 Atom WM_PROTOCOLS;
3545 Atom WM_DELETE_WINDOW;
3546 Atom WM_STATE;
3547 Atom NET_WM_NAME;
3548 Atom NET_WM_ICON_NAME;
3549 Atom NET_WM_ICON;
3550 Atom NET_WM_STATE;
3551 Atom NET_WM_STATE_FULLSCREEN;
3552 _sapp_xi_t xi;
3553 _sapp_xdnd_t xdnd;
3554 // XLib manual says keycodes are in the range [8, 255] inclusive.
3555 // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html
3556 bool key_repeat[_SAPP_X11_MAX_X11_KEYCODES];
3557} _sapp_x11_t;
3558#endif // _SAPP_X11
3559
3560#if defined(_SAPP_WAYLAND)
3561typedef struct {
3562 // Core Wayland objects
3563 struct wl_display *display;
3564 struct wl_registry *registry;
3565 struct wl_compositor *compositor;
3566 struct wl_surface *surface;
3567 struct wl_egl_window *egl_window;
3568 struct wl_shm *shm;
3569
3570 // XDG shell
3571 struct xdg_wm_base *xdg_wm_base;
3572 struct xdg_surface *xdg_surface;
3573 struct xdg_toplevel *xdg_toplevel;
3574
3575 // Input
3576 struct wl_seat *seat;
3577 struct wl_pointer *pointer;
3578 struct wl_keyboard *keyboard;
3579 struct wl_touch *touch;
3580 uint32_t seat_version;
3581
3582 // Pointer constraints and relative pointer (for mouse lock)
3583 struct zwp_pointer_constraints_v1 *pointer_constraints;
3584 struct zwp_locked_pointer_v1 *locked_pointer;
3585 struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
3586 struct zwp_relative_pointer_v1 *relative_pointer;
3587
3588 // Keyboard handling (XKB)
3589 struct xkb_context *xkb_context;
3590 struct xkb_keymap *xkb_keymap;
3591 struct xkb_state *xkb_state;
3592 struct xkb_compose_table *xkb_compose_table;
3593 struct xkb_compose_state *xkb_compose_state;
3594
3595 // Cursor support
3596 struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
3597 struct wp_cursor_shape_device_v1 *cursor_shape_device;
3598
3599 // Fractional scale
3600 struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
3601 struct wp_fractional_scale_v1 *fractional_scale;
3602 uint32_t scale_numerator; // e.g., 120 for 1.2x scale
3603
3604 // Viewporter (for fractional scaling)
3605 struct wp_viewporter *viewporter;
3606 struct wp_viewport *viewport;
3607
3608 // Decorations
3609 struct zxdg_decoration_manager_v1 *decoration_manager;
3610 struct zxdg_toplevel_decoration_v1 *toplevel_decoration;
3611
3612 // Cursor
3613 struct wl_cursor_theme *cursor_theme;
3614 struct wl_cursor *cursor_default;
3615 struct wl_surface *cursor_surface;
3616 int cursor_size;
3617
3618 // Clipboard
3619 struct wl_data_device_manager *data_device_manager;
3620 struct wl_data_device *data_device;
3621 struct wl_data_source *data_source;
3622 struct wl_data_offer *data_offer;
3623 char *clipboard_string;
3624 size_t clipboard_size;
3625
3626 // Window state
3627 int width;
3628 int height;
3629 int fb_width;
3630 int fb_height;
3631 float scale;
3632 bool configured;
3633 bool closed;
3634 bool fullscreen;
3635 bool maximized;
3636 bool focused;
3637
3638 // Pointer state
3639 wl_fixed_t pointer_x;
3640 wl_fixed_t pointer_y;
3641 uint32_t pointer_enter_serial;
3642
3643 // Keyboard state
3644 uint32_t key_repeat_rate;
3645 uint32_t key_repeat_delay;
3646 int key_repeat_timer_fd;
3647 xkb_keycode_t key_repeat_keycode;
3648} _sapp_wl_t;
3649#endif // _SAPP_WAYLAND
3650
3651#if defined(_SAPP_X11) && defined(_SAPP_GLX)
3652
3653typedef struct {
3654 void *libgl;
3655 int major;
3656 int minor;
3657 int event_base;
3658 int error_base;
3659 GLXContext ctx;
3660 GLXWindow window;
3661
3662 // GLX 1.3 functions
3663 PFNGLXGETFBCONFIGSPROC GetFBConfigs;
3664 PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
3665 PFNGLXGETCLIENTSTRINGPROC GetClientString;
3666 PFNGLXQUERYEXTENSIONPROC QueryExtension;
3667 PFNGLXQUERYVERSIONPROC QueryVersion;
3668 PFNGLXDESTROYCONTEXTPROC DestroyContext;
3669 PFNGLXMAKECURRENTPROC MakeCurrent;
3670 PFNGLXSWAPBUFFERSPROC SwapBuffers;
3671 PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString;
3672 PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig;
3673 PFNGLXCREATEWINDOWPROC CreateWindow;
3674 PFNGLXDESTROYWINDOWPROC DestroyWindow;
3675
3676 // GLX 1.4 and extension functions
3677 PFNGLXGETPROCADDRESSPROC GetProcAddress;
3678 PFNGLXGETPROCADDRESSPROC GetProcAddressARB;
3679 PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
3680 PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
3681 PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
3682
3683 // special case glGetIntegerv
3684 void (*GetIntegerv)(uint32_t pname, int32_t *data);
3685
3686 // extension availability
3687 bool EXT_swap_control;
3688 bool MESA_swap_control;
3689 bool ARB_multisample;
3690 bool ARB_create_context;
3691 bool ARB_create_context_profile;
3692} _sapp_glx_t;
3693#endif // _SAPP_GLX
3694
3695#if defined(_SAPP_EGL)
3696typedef struct {
3697 EGLDisplay display;
3698 EGLContext context;
3699 EGLSurface surface;
3700} _sapp_egl_t;
3701#endif // _SAPP_EGL
3702#endif // _SAPP_LINUX (for struct definitions)
3703
3704#if defined(_SAPP_ANY_GL)
3705typedef struct {
3706 uint32_t framebuffer;
3707} _sapp_gl_t;
3708#endif
3709
3710typedef struct {
3711 bool enabled;
3712 int buf_size;
3713 char *buffer;
3714} _sapp_clipboard_t;
3715
3716typedef struct {
3717 bool enabled;
3718 int max_files;
3719 int max_path_length;
3720 int num_files;
3721 int buf_size;
3722 char *buffer;
3723} _sapp_drop_t;
3724
3725typedef struct {
3726 float x, y;
3727 float dx, dy;
3728 bool shown;
3729 bool locked;
3730 bool pos_valid;
3731 sapp_mouse_cursor current_cursor;
3732} _sapp_mouse_t;
3733
3734typedef struct {
3735 sapp_desc desc;
3736 bool valid;
3737 bool fullscreen;
3738 bool first_frame;
3739 bool init_called;
3740 bool cleanup_called;
3741 bool quit_requested;
3742 bool quit_ordered;
3743 bool event_consumed;
3744 bool html5_ask_leave_site;
3745 bool onscreen_keyboard_shown;
3746 int window_width;
3747 int window_height;
3748 int framebuffer_width;
3749 int framebuffer_height;
3750 int sample_count;
3751 int swap_interval;
3752 float dpi_scale;
3753 uint64_t frame_count;
3754 _sapp_timing_t timing;
3755 sapp_event event;
3756 _sapp_mouse_t mouse;
3757 _sapp_clipboard_t clipboard;
3758 _sapp_drop_t drop;
3759 sapp_icon_desc default_icon_desc;
3760 uint32_t *default_icon_pixels;
3761#if defined(SOKOL_WGPU)
3762 _sapp_wgpu_t wgpu;
3763#endif
3764#if defined(SOKOL_VULKAN)
3765 _sapp_vk_t vk;
3766#endif
3767#if defined(_SAPP_MACOS)
3768 _sapp_macos_t macos;
3769#elif defined(_SAPP_IOS)
3770 _sapp_ios_t ios;
3771#elif defined(_SAPP_EMSCRIPTEN)
3772 _sapp_emsc_t emsc;
3773#elif defined(_SAPP_WIN32)
3774 _sapp_win32_t win32;
3775#if defined(SOKOL_D3D11)
3776 _sapp_d3d11_t d3d11;
3777#elif defined(SOKOL_GLCORE)
3778 _sapp_wgl_t wgl;
3779#endif
3780#elif defined(_SAPP_ANDROID)
3781 _sapp_android_t android;
3782#elif defined(_SAPP_LINUX)
3783#if defined(_SAPP_X11)
3784 _sapp_x11_t x11;
3785#if defined(_SAPP_GLX)
3786 _sapp_glx_t glx;
3787#elif defined(_SAPP_EGL)
3788 _sapp_egl_t egl;
3789#endif
3790#elif defined(_SAPP_WAYLAND)
3791 _sapp_wl_t wl;
3792 _sapp_egl_t egl;
3793#endif
3794#endif
3795#if defined(_SAPP_ANY_GL)
3796 _sapp_gl_t gl;
3797#endif
3798 char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH];
3799 char window_title[_SAPP_MAX_TITLE_LENGTH]; // UTF-8
3800 wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; // UTF-32 or UCS-2 */
3801 sapp_keycode keycodes[SAPP_MAX_KEYCODES];
3802 // __v_ start
3803 bool __v_native_render; /* V patch to allow for native rendering */
3804 // __v_ end
3805 bool custom_cursor_bound[_SAPP_MOUSECURSOR_NUM]; // true if a custom mouse
3806 // cursor is bound on that
3807 // slot
3808} _sapp_t;
3809static _sapp_t _sapp;
3810
3811// ██ ██████ ██████ ██████ ██ ███ ██ ██████
3812// ██ ██ ██ ██ ██ ██ ████ ██ ██
3813// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███
3814// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
3815// ███████ ██████ ██████ ██████ ██ ██ ████ ██████
3816//
3817// >>logging
3818#if defined(SOKOL_DEBUG)
3819#define _SAPP_LOGITEM_XMACRO(item, msg) #item ": " msg,
3820static const char *_sapp_log_messages[] = {_SAPP_LOG_ITEMS};
3821#undef _SAPP_LOGITEM_XMACRO
3822#endif // SOKOL_DEBUG
3823
3824#define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_##code, 0, 0, __LINE__)
3825#define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_##code, 1, 0, __LINE__)
3826#define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_##code, 2, 0, __LINE__)
3827#define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_##code, 3, 0, __LINE__)
3828#define _SAPP_PANIC_MSG(code, msg) \
3829 _sapp_log(SAPP_LOGITEM_##code, 0, msg, __LINE__)
3830#define _SAPP_ERROR_MSG(code, msg) \
3831 _sapp_log(SAPP_LOGITEM_##code, 1, msg, __LINE__)
3832#define _SAPP_WARN_MSG(code, msg) \
3833 _sapp_log(SAPP_LOGITEM_##code, 2, msg, __LINE__)
3834#define _SAPP_INFO_MSG(code, msg) \
3835 _sapp_log(SAPP_LOGITEM_##code, 3, msg, __LINE__)
3836
3837static void _sapp_log(sapp_log_item log_item, uint32_t log_level,
3838 const char *msg, uint32_t line_nr) {
3839 if (_sapp.desc.logger.func) {
3840 const char *filename = 0;
3841#if defined(SOKOL_DEBUG)
3842 filename = __FILE__;
3843 if (0 == msg) {
3844 msg = _sapp_log_messages[log_item];
3845 }
3846#endif
3847 _sapp.desc.logger.func("sapp", log_level, (uint32_t)log_item, msg, line_nr,
3848 filename, _sapp.desc.logger.user_data);
3849 } else {
3850 // for log level PANIC it would be 'undefined behaviour' to continue
3851 if (log_level == 0) {
3852 abort();
3853 }
3854 }
3855}
3856
3857// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██
3858// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██
3859// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████
3860// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
3861// ██ ██ ███████ ██ ██ ██████ ██ ██ ██
3862//
3863// >>memory
3864_SOKOL_PRIVATE void _sapp_clear(void *ptr, size_t size) {
3865 SOKOL_ASSERT(ptr && (size > 0));
3866 memset(ptr, 0, size);
3867}
3868
3869_SOKOL_PRIVATE void *_sapp_malloc(size_t size) {
3870 SOKOL_ASSERT(size > 0);
3871 void *ptr;
3872 if (_sapp.desc.allocator.alloc_fn) {
3873 ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data);
3874 } else {
3875 ptr = malloc(size);
3876 }
3877 if (0 == ptr) {
3878 _SAPP_PANIC(MALLOC_FAILED);
3879 }
3880 return ptr;
3881}
3882
3883_SOKOL_PRIVATE void *_sapp_malloc_clear(size_t size) {
3884 void *ptr = _sapp_malloc(size);
3885 _sapp_clear(ptr, size);
3886 return ptr;
3887}
3888
3889_SOKOL_PRIVATE void _sapp_free(void *ptr) {
3890 if (_sapp.desc.allocator.free_fn) {
3891 _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data);
3892 } else {
3893 free(ptr);
3894 }
3895}
3896
3897// ██ ██ ███████ ██ ██████ ███████ ██████ ███████
3898// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
3899// ███████ █████ ██ ██████ █████ ██████ ███████
3900// ██ ██ ██ ██ ██ ██ ██ ██ ██
3901// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████
3902//
3903// >>helpers
3904
3905// round float to int and at least 1
3906_SOKOL_PRIVATE int _sapp_roundf_gzero(float f) {
3907 int val = (int)roundf(f);
3908 if (val <= 0) {
3909 val = 1;
3910 }
3911 return val;
3912}
3913
3914_SOKOL_PRIVATE void _sapp_call_init(void) {
3915 if (_sapp.desc.init_cb) {
3916 _sapp.desc.init_cb();
3917 } else if (_sapp.desc.init_userdata_cb) {
3918 _sapp.desc.init_userdata_cb(_sapp.desc.user_data);
3919 }
3920 _sapp.init_called = true;
3921}
3922
3923_SOKOL_PRIVATE void _sapp_call_frame(void) {
3924 // __v_ start
3925 if (_sapp.__v_native_render) {
3926 return;
3927 }
3928 if (_sapp.init_called && !_sapp.cleanup_called) {
3929 if (_sapp.desc.frame_cb) {
3930 _sapp.desc.frame_cb();
3931 } else if (_sapp.desc.frame_userdata_cb) {
3932 _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
3933 }
3934 }
3935}
3936
3937// __v_ start
3938_SOKOL_PRIVATE void _sapp_call_frame_native(void) {
3939 // puts("_sapp_call_frame_native()");
3940 // printf("init called=%d cleanup_called=%d\n",
3941 // _sapp.init_called,_sapp.cleanup_called);
3942 if (_sapp.init_called && !_sapp.cleanup_called) {
3943 if (_sapp.desc.frame_cb) {
3944 _sapp.desc.frame_cb();
3945 } else if (_sapp.desc.frame_userdata_cb) {
3946 _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
3947 }
3948 }
3949}
3950// __v_ end
3951
3952_SOKOL_PRIVATE void _sapp_call_cleanup(void) {
3953 if (!_sapp.cleanup_called) {
3954 if (_sapp.desc.cleanup_cb) {
3955 _sapp.desc.cleanup_cb();
3956 } else if (_sapp.desc.cleanup_userdata_cb) {
3957 _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
3958 }
3959 _sapp.cleanup_called = true;
3960 }
3961}
3962
3963_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event *e) {
3964 if (!_sapp.cleanup_called) {
3965 if (_sapp.desc.event_cb) {
3966 _sapp.desc.event_cb(e);
3967 } else if (_sapp.desc.event_userdata_cb) {
3968 _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
3969 }
3970 }
3971 if (_sapp.event_consumed) {
3972 _sapp.event_consumed = false;
3973 return true;
3974 } else {
3975 return false;
3976 }
3977}
3978
3979_SOKOL_PRIVATE char *_sapp_dropped_file_path_ptr(int index) {
3980 SOKOL_ASSERT(_sapp.drop.buffer);
3981 SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files));
3982 int offset = index * _sapp.drop.max_path_length;
3983 SOKOL_ASSERT(offset < _sapp.drop.buf_size);
3984 return &_sapp.drop.buffer[offset];
3985}
3986
3987/* Copy a string (either zero-terminated or with explicit length)
3988 into a fixed size buffer with guaranteed zero-termination.
3989
3990 Return false if the string didn't fit into the buffer and had to be clamped.
3991
3992 FIXME: Currently UTF-8 strings might become invalid if the string
3993 is clamped, because the last zero-byte might be written into
3994 the middle of a multi-byte sequence.
3995*/
3996_SOKOL_PRIVATE bool _sapp_strcpy_range(const char *src, size_t src_len,
3997 char *dst, size_t dst_buf_len) {
3998 SOKOL_ASSERT(src && dst && (dst_buf_len > 0));
3999 if (0 == src_len) {
4000 src_len = dst_buf_len;
4001 }
4002 char *const end = &(dst[dst_buf_len - 1]);
4003 char c = 0;
4004 for (size_t i = 0; i < dst_buf_len; i++) {
4005 c = *src;
4006 if (i >= src_len) {
4007 c = 0;
4008 }
4009 if (c != 0) {
4010 src++;
4011 }
4012 *dst++ = c;
4013 }
4014 // truncated?
4015 if (c != 0) {
4016 *end = 0;
4017 return false;
4018 } else {
4019 return true;
4020 }
4021}
4022
4023_SOKOL_PRIVATE bool _sapp_strcpy(const char *src, char *dst,
4024 size_t dst_buf_len) {
4025 return _sapp_strcpy_range(src, 0, dst, dst_buf_len);
4026}
4027
4028_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc *desc) {
4029 SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) ||
4030 (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
4031 sapp_desc res = *desc;
4032 res.sample_count = _sapp_def(res.sample_count, 1);
4033 res.swap_interval = _sapp_def(res.swap_interval, 1);
4034 if (0 == res.gl.major_version) {
4035#if defined(SOKOL_GLCORE)
4036 res.gl.major_version = 4;
4037#if defined(_SAPP_APPLE)
4038 res.gl.minor_version = 1;
4039#else
4040 res.gl.minor_version = 1;
4041#endif
4042#elif defined(SOKOL_GLES3)
4043 res.gl.major_version = 3;
4044#if defined(_SAPP_ANDROID) || defined(_SAPP_LINUX)
4045 res.gl.minor_version = 1;
4046#else
4047 res.gl.minor_version = 0;
4048#endif
4049#endif
4050 }
4051 res.html5.canvas_selector = _sapp_def(res.html5.canvas_selector, "#canvas");
4052 res.clipboard_size = _sapp_def(res.clipboard_size, 8192);
4053 res.max_dropped_files = _sapp_def(res.max_dropped_files, 1);
4054 res.max_dropped_file_path_length =
4055 _sapp_def(res.max_dropped_file_path_length, 2048);
4056 res.window_title = _sapp_def(res.window_title, "sokol");
4057 return res;
4058}
4059
4060_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc *desc) {
4061 SOKOL_ASSERT(desc);
4062 SOKOL_ASSERT(desc->width >= 0);
4063 SOKOL_ASSERT(desc->height >= 0);
4064 SOKOL_ASSERT(desc->sample_count >= 0);
4065 SOKOL_ASSERT(desc->swap_interval >= 0);
4066 SOKOL_ASSERT(desc->clipboard_size >= 0);
4067 SOKOL_ASSERT(desc->max_dropped_files >= 0);
4068 SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0);
4069 _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp);
4070 _sapp.desc = _sapp_desc_defaults(desc);
4071 _sapp.first_frame = true;
4072 // NOTE: _sapp.desc.width/height may be 0! Platform backends need to deal with
4073 // this
4074 _sapp.window_width = _sapp.desc.width;
4075 _sapp.window_height = _sapp.desc.height;
4076 _sapp.framebuffer_width = _sapp.window_width;
4077 _sapp.framebuffer_height = _sapp.window_height;
4078 _sapp.sample_count = _sapp.desc.sample_count;
4079 _sapp.swap_interval = _sapp.desc.swap_interval;
4080 _sapp_strcpy(_sapp.desc.html5.canvas_selector, _sapp.html5_canvas_selector,
4081 sizeof(_sapp.html5_canvas_selector));
4082 _sapp.desc.html5.canvas_selector = _sapp.html5_canvas_selector;
4083 _sapp.html5_ask_leave_site = _sapp.desc.html5.ask_leave_site;
4084 _sapp.clipboard.enabled = _sapp.desc.enable_clipboard;
4085 if (_sapp.clipboard.enabled) {
4086 _sapp.clipboard.buf_size = _sapp.desc.clipboard_size;
4087 _sapp.clipboard.buffer =
4088 (char *)_sapp_malloc_clear((size_t)_sapp.clipboard.buf_size);
4089 }
4090 _sapp.drop.enabled = _sapp.desc.enable_dragndrop;
4091 if (_sapp.drop.enabled) {
4092 _sapp.drop.max_files = _sapp.desc.max_dropped_files;
4093 _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length;
4094 _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length;
4095 _sapp.drop.buffer = (char *)_sapp_malloc_clear((size_t)_sapp.drop.buf_size);
4096 }
4097 _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title,
4098 sizeof(_sapp.window_title));
4099 _sapp.desc.window_title = _sapp.window_title;
4100 _sapp.dpi_scale = 1.0f;
4101 _sapp.fullscreen = _sapp.desc.fullscreen;
4102 _sapp.mouse.shown = true;
4103 _sapp_timing_init(&_sapp.timing);
4104 // __v_ start
4105 _sapp.__v_native_render = _sapp.desc.__v_native_render;
4106 // __v_end
4107}
4108
4109_SOKOL_PRIVATE void _sapp_discard_state(void) {
4110 if (_sapp.clipboard.enabled) {
4111 SOKOL_ASSERT(_sapp.clipboard.buffer);
4112 _sapp_free((void *)_sapp.clipboard.buffer);
4113 }
4114 if (_sapp.drop.enabled) {
4115 SOKOL_ASSERT(_sapp.drop.buffer);
4116 _sapp_free((void *)_sapp.drop.buffer);
4117 }
4118 if (_sapp.default_icon_pixels) {
4119 _sapp_free((void *)_sapp.default_icon_pixels);
4120 }
4121 for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
4122 sapp_unbind_mouse_cursor_image((sapp_mouse_cursor)i);
4123 }
4124 _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp);
4125}
4126
4127_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) {
4128 _sapp_clear(&_sapp.event, sizeof(_sapp.event));
4129 _sapp.event.type = type;
4130 _sapp.event.frame_count = _sapp.frame_count;
4131 _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
4132 _sapp.event.window_width = _sapp.window_width;
4133 _sapp.event.window_height = _sapp.window_height;
4134 _sapp.event.framebuffer_width = _sapp.framebuffer_width;
4135 _sapp.event.framebuffer_height = _sapp.framebuffer_height;
4136 _sapp.event.mouse_x = _sapp.mouse.x;
4137 _sapp.event.mouse_y = _sapp.mouse.y;
4138 _sapp.event.mouse_dx = _sapp.mouse.dx;
4139 _sapp.event.mouse_dy = _sapp.mouse.dy;
4140}
4141
4142_SOKOL_PRIVATE bool _sapp_events_enabled(void) {
4143 /* only send events when an event callback is set, and the init function was
4144 * called */
4145 return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) &&
4146 _sapp.init_called;
4147}
4148
4149_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
4150 if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) {
4151 return _sapp.keycodes[scan_code];
4152 } else {
4153 return SAPP_KEYCODE_INVALID;
4154 }
4155}
4156
4157_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) {
4158 if (_sapp.drop.enabled) {
4159 SOKOL_ASSERT(_sapp.drop.buffer);
4160 _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size);
4161 }
4162}
4163
4164_SOKOL_PRIVATE void _sapp_frame(void) {
4165 if (_sapp.first_frame) {
4166 _sapp.first_frame = false;
4167 _sapp_call_init();
4168 }
4169 _sapp_call_frame();
4170 _sapp.frame_count++;
4171}
4172
4173_SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc *desc) {
4174 SOKOL_ASSERT(desc->width > 0);
4175 SOKOL_ASSERT(desc->height > 0);
4176 SOKOL_ASSERT(desc->pixels.ptr != 0);
4177 SOKOL_ASSERT(desc->pixels.size > 0);
4178 const size_t wh_size =
4179 (size_t)(desc->width * desc->height) * sizeof(uint32_t);
4180 if (wh_size != desc->pixels.size) {
4181 _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH);
4182 return false;
4183 }
4184 return true;
4185}
4186
4187_SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[],
4188 int num_images, int width,
4189 int height) {
4190 int least_diff = 0x7FFFFFFF;
4191 int least_index = 0;
4192 for (int i = 0; i < num_images; i++) {
4193 int diff =
4194 (image_descs[i].width * image_descs[i].height) - (width * height);
4195 if (diff < 0) {
4196 diff = -diff;
4197 }
4198 if (diff < least_diff) {
4199 least_diff = diff;
4200 least_index = i;
4201 }
4202 }
4203 return least_index;
4204}
4205
4206_SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc *desc) {
4207 int index = 0;
4208 for (; index < SAPP_MAX_ICONIMAGES; index++) {
4209 if (0 == desc->images[index].pixels.ptr) {
4210 break;
4211 }
4212 }
4213 return index;
4214}
4215
4216_SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc *desc,
4217 int num_images) {
4218 SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES);
4219 for (int i = 0; i < num_images; i++) {
4220 const sapp_image_desc *img_desc = &desc->images[i];
4221 if (!_sapp_image_validate(img_desc)) {
4222 return false;
4223 }
4224 }
4225 return true;
4226}
4227
4228_SOKOL_PRIVATE void _sapp_setup_default_icon(void) {
4229 SOKOL_ASSERT(0 == _sapp.default_icon_pixels);
4230
4231 const int num_icons = 3;
4232 const int icon_sizes[3] = {16, 32, 64}; // must be multiple of 8!
4233
4234 // allocate a pixel buffer for all icon pixels
4235 int all_num_pixels = 0;
4236 for (int i = 0; i < num_icons; i++) {
4237 all_num_pixels += icon_sizes[i] * icon_sizes[i];
4238 }
4239 _sapp.default_icon_pixels =
4240 (uint32_t *)_sapp_malloc_clear((size_t)all_num_pixels * sizeof(uint32_t));
4241
4242 // initialize default_icon_desc struct
4243 uint32_t *dst = _sapp.default_icon_pixels;
4244 const uint32_t *dst_end = dst + all_num_pixels;
4245 (void)dst_end; // silence unused warning in release mode
4246 for (int i = 0; i < num_icons; i++) {
4247 const int dim = (int)icon_sizes[i];
4248 const int num_pixels = dim * dim;
4249 sapp_image_desc *img_desc = &_sapp.default_icon_desc.images[i];
4250 img_desc->width = dim;
4251 img_desc->height = dim;
4252 img_desc->pixels.ptr = dst;
4253 img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t);
4254 dst += num_pixels;
4255 }
4256 SOKOL_ASSERT(dst == dst_end);
4257
4258 // Amstrad CPC font 'S'
4259 const uint8_t tile[8] = {
4260 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00,
4261 };
4262 // rainbow colors
4263 const uint32_t colors[8] = {
4264 0xFF4370FF, 0xFF26A7FF, 0xFF58EEFF, 0xFF57E1D4,
4265 0xFF65CC9C, 0xFF6ABB66, 0xFFF5A542, 0xFFC2577E,
4266 };
4267 dst = _sapp.default_icon_pixels;
4268 const uint32_t blank = 0x00FFFFFF;
4269 const uint32_t shadow = 0xFF000000;
4270 for (int i = 0; i < num_icons; i++) {
4271 const int dim = icon_sizes[i];
4272 SOKOL_ASSERT((dim % 8) == 0);
4273 const int scale = dim / 8;
4274 for (int ty = 0, y = 0; ty < 8; ty++) {
4275 const uint32_t color = colors[ty];
4276 for (int sy = 0; sy < scale; sy++, y++) {
4277 uint8_t bits = tile[ty];
4278 for (int tx = 0, x = 0; tx < 8; tx++, bits <<= 1) {
4279 uint32_t pixel = (0 == (bits & 0x80)) ? blank : color;
4280 for (int sx = 0; sx < scale; sx++, x++) {
4281 SOKOL_ASSERT(dst < dst_end);
4282 *dst++ = pixel;
4283 }
4284 }
4285 }
4286 }
4287 }
4288 SOKOL_ASSERT(dst == dst_end);
4289
4290 // right shadow
4291 dst = _sapp.default_icon_pixels;
4292 for (int i = 0; i < num_icons; i++) {
4293 const int dim = icon_sizes[i];
4294 for (int y = 0; y < dim; y++) {
4295 uint32_t prev_color = blank;
4296 for (int x = 0; x < dim; x++) {
4297 const int dst_index = y * dim + x;
4298 const uint32_t cur_color = dst[dst_index];
4299 if ((cur_color == blank) && (prev_color != blank)) {
4300 dst[dst_index] = shadow;
4301 }
4302 prev_color = cur_color;
4303 }
4304 }
4305 dst += dim * dim;
4306 }
4307 SOKOL_ASSERT(dst == dst_end);
4308
4309 // bottom shadow
4310 dst = _sapp.default_icon_pixels;
4311 for (int i = 0; i < num_icons; i++) {
4312 const int dim = icon_sizes[i];
4313 for (int x = 0; x < dim; x++) {
4314 uint32_t prev_color = blank;
4315 for (int y = 0; y < dim; y++) {
4316 const int dst_index = y * dim + x;
4317 const uint32_t cur_color = dst[dst_index];
4318 if ((cur_color == blank) && (prev_color != blank)) {
4319 dst[dst_index] = shadow;
4320 }
4321 prev_color = cur_color;
4322 }
4323 }
4324 dst += dim * dim;
4325 }
4326 SOKOL_ASSERT(dst == dst_end);
4327}
4328
4329// ██ ██ ██████ ██████ ██ ██
4330// ██ ██ ██ ██ ██ ██ ██
4331// ██ █ ██ ██ ███ ██████ ██ ██
4332// ██ ███ ██ ██ ██ ██ ██ ██
4333// ███ ███ ██████ ██ ██████
4334//
4335// >>wgpu
4336#if defined(SOKOL_WGPU)
4337
4338_SOKOL_PRIVATE WGPUStringView _sapp_wgpu_stringview(const char *str) {
4339 WGPUStringView res;
4340 if (str) {
4341 res.data = str;
4342 res.length = strlen(str);
4343 } else {
4344 res.data = 0;
4345 res.length = 0;
4346 }
4347 return res;
4348}
4349
4350_SOKOL_PRIVATE WGPUCallbackMode _sapp_wgpu_callbackmode(void) {
4351#if defined(_SAPP_WGPU_HAS_WAIT)
4352 return WGPUCallbackMode_WaitAnyOnly;
4353#else
4354 return WGPUCallbackMode_AllowProcessEvents;
4355#endif
4356}
4357
4358_SOKOL_PRIVATE void _sapp_wgpu_await(WGPUFuture future) {
4359#if defined(_SAPP_WGPU_HAS_WAIT)
4360 SOKOL_ASSERT(_sapp.wgpu.instance);
4361 _SAPP_STRUCT(WGPUFutureWaitInfo, wait_info);
4362 wait_info.future = future;
4363 WGPUWaitStatus res =
4364 wgpuInstanceWaitAny(_sapp.wgpu.instance, 1, &wait_info, UINT64_MAX);
4365 SOKOL_ASSERT(res == WGPUWaitStatus_Success);
4366 _SOKOL_UNUSED(res);
4367#else
4368 // this code path should never be called
4369 _SOKOL_UNUSED(future);
4370 SOKOL_ASSERT(false);
4371#endif
4372}
4373
4374_SOKOL_PRIVATE WGPUTextureFormat
4375_sapp_wgpu_pick_render_format(size_t count, const WGPUTextureFormat *formats) {
4376 // NOTE: only accept non-SRGB formats until sokol_app.h gets proper SRGB
4377 // support
4378 SOKOL_ASSERT((count > 0) && formats);
4379 for (size_t i = 0; i < count; i++) {
4380 const WGPUTextureFormat fmt = formats[i];
4381 switch (fmt) {
4382 case WGPUTextureFormat_RGBA8Unorm:
4383 case WGPUTextureFormat_BGRA8Unorm:
4384 return fmt;
4385 default:
4386 break;
4387 }
4388 }
4389 // FIXME: fallback might still return an SRGB format
4390 return formats[0];
4391}
4392
4393_SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) {
4394 SOKOL_ASSERT(_sapp.wgpu.instance);
4395 SOKOL_ASSERT(_sapp.wgpu.device);
4396 SOKOL_ASSERT(0 == _sapp.wgpu.msaa_tex);
4397 SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view);
4398 SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex);
4399 SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view);
4400
4401 if (!called_from_resize) {
4402 SOKOL_ASSERT(0 == _sapp.wgpu.surface);
4403 _SAPP_STRUCT(WGPUSurfaceDescriptor, surf_desc);
4404#if defined(_SAPP_EMSCRIPTEN)
4405 _SAPP_STRUCT(WGPUEmscriptenSurfaceSourceCanvasHTMLSelector,
4406 html_canvas_desc);
4407 html_canvas_desc.chain.sType =
4408 WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector;
4409 html_canvas_desc.selector =
4410 _sapp_wgpu_stringview(_sapp.html5_canvas_selector);
4411 surf_desc.nextInChain = &html_canvas_desc.chain;
4412#elif defined(_SAPP_MACOS)
4413 _SAPP_STRUCT(WGPUSurfaceSourceMetalLayer, from_metal_layer);
4414 from_metal_layer.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
4415 from_metal_layer.layer = _sapp.macos.view.layer;
4416 surf_desc.nextInChain = &from_metal_layer.chain;
4417#elif defined(_SAPP_WIN32)
4418 _SAPP_STRUCT(WGPUSurfaceSourceWindowsHWND, from_hwnd);
4419 from_hwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
4420 from_hwnd.hinstance = GetModuleHandleW(NULL);
4421 from_hwnd.hwnd = _sapp.win32.hwnd;
4422 surf_desc.nextInChain = &from_hwnd.chain;
4423#elif defined(_SAPP_LINUX)
4424#if defined(_SAPP_X11)
4425 _SAPP_STRUCT(WGPUSurfaceSourceXlibWindow, from_xlib);
4426 from_xlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
4427 from_xlib.display = _sapp.x11.display;
4428 from_xlib.window = _sapp.x11.window;
4429 surf_desc.nextInChain = &from_xlib.chain;
4430#elif defined(_SAPP_WAYLAND)
4431 _SAPP_STRUCT(WGPUSurfaceSourceWaylandSurface, from_wayland);
4432 from_wayland.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
4433 from_wayland.display = _sapp.wl.display;
4434 from_wayland.surface = _sapp.wl.surface;
4435 surf_desc.nextInChain = &from_wayland.chain;
4436#endif
4437#else
4438#error "sokol_app.h: unsupported WebGPU platform"
4439#endif
4440 _sapp.wgpu.surface =
4441 wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc);
4442 if (0 == _sapp.wgpu.surface) {
4443 _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED);
4444 }
4445 _SAPP_STRUCT(WGPUSurfaceCapabilities, surf_caps);
4446 WGPUStatus caps_status = wgpuSurfaceGetCapabilities(
4447 _sapp.wgpu.surface, _sapp.wgpu.adapter, &surf_caps);
4448 if (caps_status != WGPUStatus_Success) {
4449 _SAPP_PANIC(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED);
4450 }
4451 _sapp.wgpu.render_format =
4452 _sapp_wgpu_pick_render_format(surf_caps.formatCount, surf_caps.formats);
4453 }
4454
4455 SOKOL_ASSERT(_sapp.wgpu.surface);
4456 _SAPP_STRUCT(WGPUSurfaceConfiguration, surf_conf);
4457 surf_conf.device = _sapp.wgpu.device;
4458 surf_conf.format = _sapp.wgpu.render_format;
4459 surf_conf.usage = WGPUTextureUsage_RenderAttachment;
4460 surf_conf.width = (uint32_t)_sapp.framebuffer_width;
4461 surf_conf.height = (uint32_t)_sapp.framebuffer_height;
4462 surf_conf.alphaMode = WGPUCompositeAlphaMode_Opaque;
4463#if defined(_SAPP_EMSCRIPTEN)
4464 // FIXME: make this further configurable?
4465 if (_sapp.desc.html5.premultiplied_alpha) {
4466 surf_conf.alphaMode = WGPUCompositeAlphaMode_Premultiplied;
4467 }
4468#endif
4469 surf_conf.presentMode = WGPUPresentMode_Fifo;
4470 wgpuSurfaceConfigure(_sapp.wgpu.surface, &surf_conf);
4471
4472 _SAPP_STRUCT(WGPUTextureDescriptor, ds_desc);
4473 ds_desc.usage = WGPUTextureUsage_RenderAttachment;
4474 ds_desc.dimension = WGPUTextureDimension_2D;
4475 ds_desc.size.width = (uint32_t)_sapp.framebuffer_width;
4476 ds_desc.size.height = (uint32_t)_sapp.framebuffer_height;
4477 ds_desc.size.depthOrArrayLayers = 1;
4478 ds_desc.format = WGPUTextureFormat_Depth32FloatStencil8;
4479 ds_desc.mipLevelCount = 1;
4480 ds_desc.sampleCount = (uint32_t)_sapp.sample_count;
4481 _sapp.wgpu.depth_stencil_tex =
4482 wgpuDeviceCreateTexture(_sapp.wgpu.device, &ds_desc);
4483 if (0 == _sapp.wgpu.depth_stencil_tex) {
4484 _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED);
4485 }
4486 _sapp.wgpu.depth_stencil_view =
4487 wgpuTextureCreateView(_sapp.wgpu.depth_stencil_tex, 0);
4488 if (0 == _sapp.wgpu.depth_stencil_view) {
4489 _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED);
4490 }
4491
4492 if (_sapp.sample_count > 1) {
4493 _SAPP_STRUCT(WGPUTextureDescriptor, msaa_desc);
4494 msaa_desc.usage = WGPUTextureUsage_RenderAttachment;
4495 msaa_desc.dimension = WGPUTextureDimension_2D;
4496 msaa_desc.size.width = (uint32_t)_sapp.framebuffer_width;
4497 msaa_desc.size.height = (uint32_t)_sapp.framebuffer_height;
4498 msaa_desc.size.depthOrArrayLayers = 1;
4499 msaa_desc.format = _sapp.wgpu.render_format;
4500 msaa_desc.mipLevelCount = 1;
4501 msaa_desc.sampleCount = (uint32_t)_sapp.sample_count;
4502 _sapp.wgpu.msaa_tex =
4503 wgpuDeviceCreateTexture(_sapp.wgpu.device, &msaa_desc);
4504 if (0 == _sapp.wgpu.msaa_tex) {
4505 _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED);
4506 }
4507 _sapp.wgpu.msaa_view = wgpuTextureCreateView(_sapp.wgpu.msaa_tex, 0);
4508 if (0 == _sapp.wgpu.msaa_view) {
4509 _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED);
4510 }
4511 }
4512}
4513
4514_SOKOL_PRIVATE void _sapp_wgpu_discard_swapchain(bool called_from_resize) {
4515 if (_sapp.wgpu.msaa_view) {
4516 wgpuTextureViewRelease(_sapp.wgpu.msaa_view);
4517 _sapp.wgpu.msaa_view = 0;
4518 }
4519 if (_sapp.wgpu.msaa_tex) {
4520 wgpuTextureRelease(_sapp.wgpu.msaa_tex);
4521 _sapp.wgpu.msaa_tex = 0;
4522 }
4523 if (_sapp.wgpu.depth_stencil_view) {
4524 wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view);
4525 _sapp.wgpu.depth_stencil_view = 0;
4526 }
4527 if (_sapp.wgpu.depth_stencil_tex) {
4528 wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex);
4529 _sapp.wgpu.depth_stencil_tex = 0;
4530 }
4531 if (!called_from_resize) {
4532 if (_sapp.wgpu.surface) {
4533 wgpuSurfaceRelease(_sapp.wgpu.surface);
4534 _sapp.wgpu.surface = 0;
4535 }
4536 }
4537}
4538
4539_SOKOL_PRIVATE void _sapp_wgpu_swapchain_next(void) {
4540 SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view);
4541 _SAPP_STRUCT(WGPUSurfaceTexture, surf_tex);
4542 wgpuSurfaceGetCurrentTexture(_sapp.wgpu.surface, &surf_tex);
4543 switch (surf_tex.status) {
4544 case WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal:
4545 case WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal:
4546 // all ok
4547 break;
4548 case WGPUSurfaceGetCurrentTextureStatus_Timeout:
4549 case WGPUSurfaceGetCurrentTextureStatus_Outdated:
4550 case WGPUSurfaceGetCurrentTextureStatus_Lost:
4551 if (surf_tex.texture) {
4552 wgpuTextureRelease(surf_tex.texture);
4553 }
4554 _sapp_wgpu_discard_swapchain(false);
4555 _sapp_wgpu_create_swapchain(false);
4556 // FIXME: currently this will assert in the caller
4557 return;
4558 case WGPUSurfaceGetCurrentTextureStatus_Error:
4559 default:
4560 _SAPP_PANIC(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED);
4561 break;
4562 }
4563 _sapp.wgpu.swapchain_view = wgpuTextureCreateView(surf_tex.texture, 0);
4564 SOKOL_ASSERT(_sapp.wgpu.swapchain_view);
4565}
4566
4567_SOKOL_PRIVATE void _sapp_wgpu_swapchain_size_changed(void) {
4568 if (_sapp.wgpu.surface) {
4569 _sapp_wgpu_discard_swapchain(true);
4570 _sapp_wgpu_create_swapchain(true);
4571 }
4572}
4573
4574_SOKOL_PRIVATE void _sapp_wgpu_device_lost_cb(const WGPUDevice *dev,
4575 WGPUDeviceLostReason reason,
4576 WGPUStringView msg, void *ud1,
4577 void *ud2) {
4578 _SOKOL_UNUSED(dev);
4579 _SOKOL_UNUSED(reason);
4580 _SOKOL_UNUSED(ud1);
4581 _SOKOL_UNUSED(ud2);
4582 // NOTE: on wgpuInstanceRelease(), the device lost callback is always called
4583 // with WGPUDeviceLostReason_CallbackCancelled (even though no device should
4584 // exist at that point)
4585 if (reason != WGPUDeviceLostReason_CallbackCancelled) {
4586 SOKOL_ASSERT(msg.data && (msg.length > 0));
4587 char buf[1024];
4588 _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
4589 _SAPP_ERROR_MSG(WGPU_DEVICE_LOST, buf);
4590 }
4591}
4592
4593// NOTE: emdawnwebgpu doesn't seem to have a device logging callback
4594#if !defined(_SAPP_EMSCRIPTEN)
4595_SOKOL_PRIVATE void _sapp_wgpu_device_logging_cb(WGPULoggingType log_type,
4596 WGPUStringView msg, void *ud1,
4597 void *ud2) {
4598 _SOKOL_UNUSED(log_type);
4599 _SOKOL_UNUSED(ud1);
4600 _SOKOL_UNUSED(ud2);
4601 SOKOL_ASSERT(msg.data && (msg.length > 0));
4602 char buf[1024];
4603 _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
4604 switch (log_type) {
4605 case WGPULoggingType_Warning:
4606 _SAPP_WARN_MSG(WGPU_DEVICE_LOG, buf);
4607 break;
4608 case WGPULoggingType_Error:
4609 _SAPP_ERROR_MSG(WGPU_DEVICE_LOG, buf);
4610 break;
4611 default:
4612 _SAPP_INFO_MSG(WGPU_DEVICE_LOG, buf);
4613 break;
4614 }
4615}
4616#endif
4617
4618_SOKOL_PRIVATE void _sapp_wgpu_uncaptured_error_cb(const WGPUDevice *dev,
4619 WGPUErrorType err_type,
4620 WGPUStringView msg,
4621 void *ud1, void *ud2) {
4622 _SOKOL_UNUSED(dev);
4623 _SOKOL_UNUSED(ud1);
4624 _SOKOL_UNUSED(ud2);
4625 if (err_type != WGPUErrorType_NoError) {
4626 SOKOL_ASSERT(msg.data && (msg.length > 0));
4627 char buf[1024];
4628 _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
4629 _SAPP_ERROR_MSG(WGPU_DEVICE_UNCAPTURED_ERROR, buf);
4630 }
4631}
4632
4633_SOKOL_PRIVATE void _sapp_wgpu_request_device_cb(WGPURequestDeviceStatus status,
4634 WGPUDevice device,
4635 WGPUStringView msg,
4636 void *userdata1,
4637 void *userdata2) {
4638 _SOKOL_UNUSED(msg);
4639 _SOKOL_UNUSED(userdata1);
4640 _SOKOL_UNUSED(userdata2);
4641 SOKOL_ASSERT(!_sapp.wgpu.init_done);
4642 if (status != WGPURequestDeviceStatus_Success) {
4643 if (status == WGPURequestDeviceStatus_Error) {
4644 _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR);
4645 } else {
4646 _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN);
4647 }
4648 }
4649 SOKOL_ASSERT(device);
4650 _sapp.wgpu.device = device;
4651#if !defined(_SAPP_EMSCRIPTEN)
4652 _SAPP_STRUCT(WGPULoggingCallbackInfo, cb_info);
4653 cb_info.callback = _sapp_wgpu_device_logging_cb;
4654 wgpuDeviceSetLoggingCallback(_sapp.wgpu.device, cb_info);
4655#endif
4656 _sapp_wgpu_create_swapchain(false);
4657 _sapp.wgpu.init_done = true;
4658}
4659
4660_SOKOL_PRIVATE void _sapp_wgpu_create_device_and_swapchain(void) {
4661 SOKOL_ASSERT(_sapp.wgpu.adapter);
4662 size_t cur_feature_index = 1;
4663#define _SAPP_WGPU_MAX_REQUESTED_FEATURES (16)
4664 WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = {
4665 WGPUFeatureName_Depth32FloatStencil8,
4666 };
4667 // check for optional features we're interested in
4668 if (wgpuAdapterHasFeature(_sapp.wgpu.adapter,
4669 WGPUFeatureName_TextureCompressionBC)) {
4670 SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
4671 requiredFeatures[cur_feature_index++] =
4672 WGPUFeatureName_TextureCompressionBC;
4673 }
4674 if (wgpuAdapterHasFeature(_sapp.wgpu.adapter,
4675 WGPUFeatureName_TextureCompressionETC2)) {
4676 SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
4677 requiredFeatures[cur_feature_index++] =
4678 WGPUFeatureName_TextureCompressionETC2;
4679 }
4680 if (wgpuAdapterHasFeature(_sapp.wgpu.adapter,
4681 WGPUFeatureName_TextureCompressionASTC)) {
4682 SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
4683 requiredFeatures[cur_feature_index++] =
4684 WGPUFeatureName_TextureCompressionASTC;
4685 }
4686 if (wgpuAdapterHasFeature(_sapp.wgpu.adapter,
4687 WGPUFeatureName_Float32Filterable)) {
4688 SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
4689 requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable;
4690 }
4691 if (wgpuAdapterHasFeature(_sapp.wgpu.adapter,
4692 WGPUFeatureName_DualSourceBlending)) {
4693 SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
4694 requiredFeatures[cur_feature_index++] = WGPUFeatureName_DualSourceBlending;
4695 }
4696#undef _SAPP_WGPU_MAX_REQUESTED_FEATURES
4697
4698 WGPULimits adapterLimits = WGPU_LIMITS_INIT;
4699 wgpuAdapterGetLimits(_sapp.wgpu.adapter, &adapterLimits);
4700
4701 WGPULimits requiredLimits = WGPU_LIMITS_INIT;
4702 requiredLimits.maxColorAttachments = adapterLimits.maxColorAttachments;
4703 requiredLimits.maxSampledTexturesPerShaderStage =
4704 adapterLimits.maxSampledTexturesPerShaderStage;
4705 requiredLimits.maxStorageBuffersPerShaderStage =
4706 adapterLimits.maxStorageBuffersPerShaderStage;
4707 requiredLimits.maxStorageTexturesPerShaderStage =
4708 adapterLimits.maxStorageTexturesPerShaderStage;
4709
4710 _SAPP_STRUCT(WGPURequestDeviceCallbackInfo, cb_info);
4711 cb_info.mode = _sapp_wgpu_callbackmode();
4712 cb_info.callback = _sapp_wgpu_request_device_cb;
4713
4714 _SAPP_STRUCT(WGPUDeviceDescriptor, dev_desc);
4715 dev_desc.requiredFeatureCount = cur_feature_index;
4716 dev_desc.requiredFeatures = requiredFeatures;
4717 dev_desc.requiredLimits = &requiredLimits;
4718 dev_desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
4719 dev_desc.deviceLostCallbackInfo.callback = _sapp_wgpu_device_lost_cb;
4720 dev_desc.uncapturedErrorCallbackInfo.callback =
4721 _sapp_wgpu_uncaptured_error_cb;
4722 WGPUFuture future =
4723 wgpuAdapterRequestDevice(_sapp.wgpu.adapter, &dev_desc, cb_info);
4724#if defined(_SAPP_WGPU_HAS_WAIT)
4725 _sapp_wgpu_await(future);
4726#else
4727 _SOKOL_UNUSED(future);
4728#endif
4729}
4730
4731_SOKOL_PRIVATE void
4732_sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus status,
4733 WGPUAdapter adapter, WGPUStringView msg,
4734 void *userdata1, void *userdata2) {
4735 _SOKOL_UNUSED(msg);
4736 _SOKOL_UNUSED(userdata1);
4737 _SOKOL_UNUSED(userdata2);
4738 if (status != WGPURequestAdapterStatus_Success) {
4739 switch (status) {
4740 case WGPURequestAdapterStatus_Unavailable:
4741 _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE);
4742 break;
4743 case WGPURequestAdapterStatus_Error:
4744 _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR);
4745 break;
4746 default:
4747 _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN);
4748 break;
4749 }
4750 }
4751 SOKOL_ASSERT(adapter);
4752 _sapp.wgpu.adapter = adapter;
4753#if !defined(_SAPP_WGPU_HAS_WAIT)
4754 // chain device creation
4755 _sapp_wgpu_create_device_and_swapchain();
4756#endif
4757}
4758
4759_SOKOL_PRIVATE void _sapp_wgpu_create_adapter(void) {
4760 SOKOL_ASSERT(_sapp.wgpu.instance);
4761 // FIXME: power preference?
4762 _SAPP_STRUCT(WGPURequestAdapterCallbackInfo, cb_info);
4763 cb_info.mode = _sapp_wgpu_callbackmode();
4764 cb_info.callback = _sapp_wgpu_request_adapter_cb;
4765 WGPUFuture future =
4766 wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, cb_info);
4767#if defined(_SAPP_WGPU_HAS_WAIT)
4768 _sapp_wgpu_await(future);
4769#else
4770 _SOKOL_UNUSED(future);
4771#endif
4772}
4773
4774_SOKOL_PRIVATE void _sapp_wgpu_init(void) {
4775 SOKOL_ASSERT(0 == _sapp.wgpu.instance);
4776 SOKOL_ASSERT(!_sapp.wgpu.init_done);
4777
4778 _SAPP_STRUCT(WGPUInstanceDescriptor, desc);
4779#if defined(_SAPP_WGPU_HAS_WAIT)
4780 WGPUInstanceFeatureName inst_features[1] = {
4781 WGPUInstanceFeatureName_TimedWaitAny,
4782 };
4783 desc.requiredFeatureCount = 1;
4784 desc.requiredFeatures = inst_features;
4785#endif
4786 _sapp.wgpu.instance = wgpuCreateInstance(&desc);
4787 if (0 == _sapp.wgpu.instance) {
4788 _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED);
4789 }
4790 // NOTE: on Emscripten, device and swapchain creation are chained in the
4791 // callacks
4792 _sapp_wgpu_create_adapter();
4793#if defined(_SAPP_WGPU_HAS_WAIT)
4794 _sapp_wgpu_create_device_and_swapchain();
4795 SOKOL_ASSERT(_sapp.wgpu.init_done);
4796#endif
4797}
4798
4799_SOKOL_PRIVATE void _sapp_wgpu_discard(void) {
4800 _sapp_wgpu_discard_swapchain(false);
4801 if (_sapp.wgpu.device) {
4802 wgpuDeviceRelease(_sapp.wgpu.device);
4803 _sapp.wgpu.device = 0;
4804 }
4805 if (_sapp.wgpu.adapter) {
4806 wgpuAdapterRelease(_sapp.wgpu.adapter);
4807 _sapp.wgpu.adapter = 0;
4808 }
4809 if (_sapp.wgpu.instance) {
4810 wgpuInstanceRelease(_sapp.wgpu.instance);
4811 _sapp.wgpu.instance = 0;
4812 }
4813}
4814
4815_SOKOL_PRIVATE void _sapp_wgpu_frame(void) {
4816 wgpuInstanceProcessEvents(_sapp.wgpu.instance);
4817 if (_sapp.wgpu.init_done) {
4818 _sapp_frame();
4819 if (_sapp.wgpu.swapchain_view) {
4820 wgpuTextureViewRelease(_sapp.wgpu.swapchain_view);
4821 _sapp.wgpu.swapchain_view = 0;
4822 }
4823#if !defined(_SAPP_EMSCRIPTEN)
4824 wgpuSurfacePresent(_sapp.wgpu.surface);
4825#endif
4826 }
4827}
4828#endif // SOKOL_WGPU
4829
4830// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██
4831// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
4832// ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██
4833// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
4834// ████ ██████ ███████ ██ ██ ██ ██ ██ ████
4835//
4836// >>vulkan
4837// >>vk
4838#if defined(SOKOL_VULKAN)
4839
4840#if defined(__cplusplus)
4841#define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) \
4842 uint32_t count_name = 0; \
4843 type array_name[num] = {}
4844#define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) \
4845 uint32_t count_name = num; \
4846 type array_name[num] = {}
4847#else
4848#define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) \
4849 uint32_t count_name = 0; \
4850 type array_name[num] = {0}
4851#define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) \
4852 uint32_t count_name = num; \
4853 type array_name[num] = {0}
4854#endif
4855
4856_SOKOL_PRIVATE void _sapp_vk_load_instance_ext_funcs(void) {
4857 SOKOL_ASSERT(_sapp.vk.instance);
4858#if defined(SOKOL_DEBUG)
4859 _sapp.vk.ext.set_debug_utils_object_name_ext =
4860 (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(
4861 _sapp.vk.instance, "vkSetDebugUtilsObjectNameEXT");
4862 if (0 == _sapp.vk.ext.set_debug_utils_object_name_ext) {
4863 _SAPP_PANIC(VULKAN_REQUIRED_INSTANCE_EXTENSION_FUNCTION_MISSING);
4864 }
4865#endif
4866}
4867
4868_SOKOL_PRIVATE void _sapp_vk_set_object_label(VkObjectType obj_type,
4869 uint64_t obj_handle,
4870 const char *label) {
4871#if defined(SOKOL_DEBUG)
4872 SOKOL_ASSERT(_sapp.vk.device);
4873 SOKOL_ASSERT(_sapp.vk.ext.set_debug_utils_object_name_ext);
4874 SOKOL_ASSERT(obj_handle);
4875 if (label) {
4876 _SAPP_STRUCT(VkDebugUtilsObjectNameInfoEXT, name_info);
4877 name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
4878 name_info.objectType = obj_type;
4879 name_info.objectHandle = obj_handle, name_info.pObjectName = label;
4880 VkResult res = _sapp.vk.ext.set_debug_utils_object_name_ext(_sapp.vk.device,
4881 &name_info);
4882 SOKOL_ASSERT(res == VK_SUCCESS);
4883 }
4884#else
4885 _SOKOL_UNUSED(obj_type);
4886 _SOKOL_UNUSED(obj_handle);
4887 _SOKOL_UNUSED(label);
4888#endif
4889}
4890
4891_SOKOL_PRIVATE int
4892_sapp_vk_mem_find_memory_type_index(uint32_t type_filter,
4893 VkMemoryPropertyFlags props) {
4894 SOKOL_ASSERT(_sapp.vk.physical_device);
4895 _SAPP_STRUCT(VkPhysicalDeviceMemoryProperties, mem_props);
4896 vkGetPhysicalDeviceMemoryProperties(_sapp.vk.physical_device, &mem_props);
4897 for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) {
4898 if ((type_filter & (1 << i)) &&
4899 ((mem_props.memoryTypes[i].propertyFlags & props) == props)) {
4900 return (int)i;
4901 }
4902 }
4903 return -1;
4904}
4905
4906_SOKOL_PRIVATE void _sapp_vk_create_instance(void) {
4907 SOKOL_ASSERT(0 == _sapp.vk.instance);
4908
4909 _SAPP_STRUCT(VkApplicationInfo, app_info);
4910 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
4911 app_info.pApplicationName = "sokol-app"; // FIXME: override via sapp_desc?
4912 app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
4913 app_info.pEngineName = "sokol";
4914 app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
4915 app_info.apiVersion = VK_API_VERSION_1_3;
4916
4917 _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char *, layer_count, layer_names);
4918#if defined(SOKOL_DEBUG)
4919 layer_names[layer_count++] = "VK_LAYER_KHRONOS_validation";
4920 SOKOL_ASSERT(layer_count <= 32);
4921#endif
4922
4923 _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char *, ext_count, ext_names);
4924 ext_names[ext_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
4925#if defined(SOKOL_DEBUG)
4926 ext_names[ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
4927#endif
4928#if defined(VK_USE_PLATFORM_XLIB_KHR)
4929 ext_names[ext_count++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
4930#elif defined(VK_USE_PLATFORM_WIN32_KHR)
4931 ext_names[ext_count++] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
4932#endif
4933 SOKOL_ASSERT(ext_count <= 32);
4934
4935 _SAPP_STRUCT(VkInstanceCreateInfo, create_info);
4936 create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
4937 create_info.flags = 0;
4938 create_info.pApplicationInfo = &app_info;
4939 create_info.enabledLayerCount = layer_count;
4940 create_info.ppEnabledLayerNames = layer_names;
4941 create_info.enabledExtensionCount = ext_count;
4942 create_info.ppEnabledExtensionNames = ext_names;
4943 VkResult res = vkCreateInstance(&create_info, 0, &_sapp.vk.instance);
4944 if (res != VK_SUCCESS) {
4945 _SAPP_PANIC(VULKAN_CREATE_INSTANCE_FAILED);
4946 }
4947 SOKOL_ASSERT(_sapp.vk.instance);
4948}
4949
4950_SOKOL_PRIVATE void _sapp_vk_destroy_instance(void) {
4951 SOKOL_ASSERT(_sapp.vk.instance);
4952 vkDestroyInstance(_sapp.vk.instance, 0);
4953 _sapp.vk.instance = 0;
4954}
4955
4956_SOKOL_PRIVATE uint32_t _sapp_vk_required_device_extensions(
4957 const char **out_names, uint32_t max_count) {
4958 SOKOL_ASSERT(out_names && (max_count > 0));
4959 uint32_t count = 0;
4960 out_names[count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
4961 out_names[count++] = VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME;
4962 SOKOL_ASSERT(count <= max_count);
4963 _SOKOL_UNUSED(max_count);
4964 return count;
4965}
4966
4967_SOKOL_PRIVATE bool
4968_sapp_vk_check_device_extensions(VkPhysicalDevice pdev,
4969 const char **required_exts,
4970 uint32_t num_required_exts) {
4971 SOKOL_ASSERT(pdev && required_exts && num_required_exts > 0);
4972 uint32_t ext_count = 0;
4973 VkResult res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, 0);
4974 SOKOL_ASSERT(res == VK_SUCCESS);
4975 _SOKOL_UNUSED(res);
4976 if (ext_count == 0) {
4977 return false;
4978 }
4979 VkExtensionProperties *ext_props = (VkExtensionProperties *)_sapp_malloc(
4980 sizeof(VkExtensionProperties) * ext_count);
4981 SOKOL_ASSERT(ext_props);
4982 res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, ext_props);
4983 bool all_supported = true;
4984 for (uint32_t req_ext_idx = 0; req_ext_idx < num_required_exts;
4985 req_ext_idx++) {
4986 const char *req_ext_name = required_exts[req_ext_idx];
4987 bool required_ext_supported = false;
4988 for (uint32_t ext_idx = 0; ext_idx < ext_count; ext_idx++) {
4989 const VkExtensionProperties *ext_prop = &ext_props[ext_idx];
4990 if (0 == strcmp(req_ext_name, ext_prop->extensionName)) {
4991 required_ext_supported = true;
4992 break;
4993 }
4994 }
4995 if (!required_ext_supported) {
4996 all_supported = false;
4997 }
4998 }
4999 _sapp_free(ext_props);
5000 return all_supported;
5001}
5002
5003_SOKOL_PRIVATE void _sapp_vk_pick_physical_device(void) {
5004 SOKOL_ASSERT(_sapp.vk.instance);
5005 SOKOL_ASSERT(_sapp.vk.surface);
5006 SOKOL_ASSERT(0 == _sapp.vk.physical_device);
5007 VkResult res = VK_SUCCESS;
5008
5009 _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkPhysicalDevice, physical_device_count,
5010 physical_devices);
5011 res = vkEnumeratePhysicalDevices(_sapp.vk.instance, &physical_device_count,
5012 physical_devices);
5013 if ((res != VK_SUCCESS) && (res != VK_INCOMPLETE)) {
5014 _SAPP_PANIC(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED);
5015 }
5016 if (physical_device_count == 0) {
5017 _SAPP_PANIC(VULKAN_NO_PHYSICAL_DEVICES_FOUND);
5018 }
5019 _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char *, ext_count, ext_names);
5020 ext_count = _sapp_vk_required_device_extensions(ext_names, 32);
5021
5022 VkPhysicalDevice picked_pdev = 0;
5023 for (uint32_t pdev_idx = 0; pdev_idx < physical_device_count; pdev_idx++) {
5024 const VkPhysicalDevice pdev = physical_devices[pdev_idx];
5025 _SAPP_STRUCT(VkPhysicalDeviceProperties, dev_props);
5026 vkGetPhysicalDeviceProperties(pdev, &dev_props);
5027 if (dev_props.apiVersion < VK_API_VERSION_1_3) {
5028 continue;
5029 }
5030 if (!_sapp_vk_check_device_extensions(pdev, ext_names, ext_count)) {
5031 continue;
5032 }
5033 // FIXME: handle theoretical case where graphics and present aren't
5034 // supported by the same queue family index
5035 _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkQueueFamilyProperties,
5036 queue_family_props_count, queue_family_props);
5037 vkGetPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_props_count,
5038 queue_family_props);
5039 bool has_required_queues = false;
5040 const VkQueueFlags required_flags =
5041 VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT;
5042 for (uint32_t qfp_idx = 0; qfp_idx < queue_family_props_count; qfp_idx++) {
5043 const VkQueueFlags queue_flags = queue_family_props[qfp_idx].queueFlags;
5044 if ((queue_flags & required_flags) == required_flags) {
5045 _sapp.vk.queue_family_index = qfp_idx;
5046 has_required_queues = true;
5047 break;
5048 }
5049 }
5050 if (!has_required_queues) {
5051 continue;
5052 }
5053
5054 VkBool32 presentation_supported = false;
5055 res = vkGetPhysicalDeviceSurfaceSupportKHR(
5056 pdev, _sapp.vk.queue_family_index, _sapp.vk.surface,
5057 &presentation_supported);
5058 SOKOL_ASSERT(VK_SUCCESS == res);
5059 if (!presentation_supported) {
5060 continue;
5061 }
5062
5063 // if we arrive here, found a suitable device
5064 picked_pdev = pdev;
5065 break;
5066 }
5067 if (0 == picked_pdev) {
5068 _SAPP_PANIC(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND);
5069 }
5070 _sapp.vk.physical_device = picked_pdev;
5071 SOKOL_ASSERT(_sapp.vk.physical_device);
5072}
5073
5074_SOKOL_PRIVATE void _sapp_vk_create_device(void) {
5075 SOKOL_ASSERT(_sapp.vk.physical_device);
5076 SOKOL_ASSERT(0 == _sapp.vk.device);
5077
5078 const float queue_priority = 0.0f;
5079 _SAPP_STRUCT(VkDeviceQueueCreateInfo, queue_create_info);
5080 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
5081 queue_create_info.queueFamilyIndex = _sapp.vk.queue_family_index;
5082 queue_create_info.queueCount = 1;
5083 queue_create_info.pQueuePriorities = &queue_priority;
5084
5085 _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char *, ext_count, ext_names);
5086 ext_count = _sapp_vk_required_device_extensions(ext_names, 32);
5087
5088 _SAPP_STRUCT(VkPhysicalDeviceFeatures2, supports);
5089 supports.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
5090 vkGetPhysicalDeviceFeatures2(_sapp.vk.physical_device, &supports);
5091
5092 _SAPP_STRUCT(VkPhysicalDeviceDescriptorBufferFeaturesEXT,
5093 descriptor_buffer_features);
5094 descriptor_buffer_features.sType =
5095 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT;
5096 descriptor_buffer_features.descriptorBuffer = VK_TRUE;
5097
5098 _SAPP_STRUCT(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, xds_features);
5099 xds_features.sType =
5100 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
5101 xds_features.pNext = &descriptor_buffer_features;
5102 xds_features.extendedDynamicState = VK_TRUE;
5103
5104 _SAPP_STRUCT(VkPhysicalDeviceVulkan12Features, vk12_features);
5105 vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
5106 vk12_features.pNext = &xds_features;
5107 vk12_features.bufferDeviceAddress = VK_TRUE;
5108
5109 _SAPP_STRUCT(VkPhysicalDeviceVulkan13Features, vk13_features);
5110 vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
5111 vk13_features.pNext = &vk12_features;
5112 vk13_features.dynamicRendering = VK_TRUE;
5113 vk13_features.synchronization2 = VK_TRUE;
5114
5115 _SAPP_STRUCT(VkPhysicalDeviceFeatures2, required);
5116 required.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
5117 required.pNext = &vk13_features;
5118 required.features.samplerAnisotropy = VK_TRUE;
5119 required.features.dualSrcBlend = VK_TRUE;
5120 if (supports.features.textureCompressionBC) {
5121 required.features.textureCompressionBC = VK_TRUE;
5122 }
5123 if (supports.features.textureCompressionETC2) {
5124 required.features.textureCompressionETC2 = VK_TRUE;
5125 }
5126 if (supports.features.textureCompressionASTC_LDR) {
5127 required.features.textureCompressionASTC_LDR = VK_TRUE;
5128 }
5129 _SAPP_STRUCT(VkDeviceCreateInfo, dev_create_info);
5130 dev_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
5131 dev_create_info.pNext = &required;
5132 dev_create_info.queueCreateInfoCount = 1;
5133 dev_create_info.pQueueCreateInfos = &queue_create_info;
5134 dev_create_info.enabledExtensionCount = ext_count;
5135 dev_create_info.ppEnabledExtensionNames = ext_names;
5136
5137 VkResult res = vkCreateDevice(_sapp.vk.physical_device, &dev_create_info, 0,
5138 &_sapp.vk.device);
5139 if (res != VK_SUCCESS) {
5140 switch (res) {
5141 case VK_ERROR_EXTENSION_NOT_PRESENT:
5142 _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT);
5143 break;
5144 case VK_ERROR_FEATURE_NOT_PRESENT:
5145 _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT);
5146 break;
5147 case VK_ERROR_INITIALIZATION_FAILED:
5148 _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED);
5149 break;
5150 default:
5151 _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_OTHER);
5152 break;
5153 }
5154 }
5155 SOKOL_ASSERT(_sapp.vk.device);
5156
5157 SOKOL_ASSERT(0 == _sapp.vk.queue);
5158 vkGetDeviceQueue(_sapp.vk.device, _sapp.vk.queue_family_index, 0,
5159 &_sapp.vk.queue);
5160 SOKOL_ASSERT(_sapp.vk.queue);
5161}
5162
5163_SOKOL_PRIVATE void _sapp_vk_destroy_device(void) {
5164 SOKOL_ASSERT(_sapp.vk.device);
5165 vkDestroyDevice(_sapp.vk.device, 0);
5166 _sapp.vk.device = 0;
5167 _sapp.vk.queue = 0;
5168}
5169
5170_SOKOL_PRIVATE void _sapp_vk_create_surface(void) {
5171 SOKOL_ASSERT(_sapp.vk.instance);
5172 SOKOL_ASSERT(0 == _sapp.vk.surface);
5173 VkResult res = VK_SUCCESS;
5174
5175#if defined(_SAPP_LINUX)
5176#if defined(_SAPP_X11)
5177 _SAPP_STRUCT(VkXlibSurfaceCreateInfoKHR, xlib_info);
5178 xlib_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
5179 xlib_info.dpy = _sapp.x11.display;
5180 xlib_info.window = _sapp.x11.window;
5181 res = vkCreateXlibSurfaceKHR(_sapp.vk.instance, &xlib_info, 0,
5182 &_sapp.vk.surface);
5183#elif defined(_SAPP_WAYLAND)
5184 _SAPP_STRUCT(VkWaylandSurfaceCreateInfoKHR, wl_info);
5185 wl_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
5186 wl_info.display = _sapp.wl.display;
5187 wl_info.surface = _sapp.wl.surface;
5188 res = vkCreateWaylandSurfaceKHR(_sapp.vk.instance, &wl_info, 0,
5189 &_sapp.vk.surface);
5190#endif
5191#elif defined(_SAPP_WIN32)
5192 _SAPP_STRUCT(VkWin32SurfaceCreateInfoKHR, win32_info);
5193 win32_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
5194 win32_info.hinstance = GetModuleHandleW(NULL);
5195 win32_info.hwnd = _sapp.win32.hwnd;
5196 res = vkCreateWin32SurfaceKHR(_sapp.vk.instance, &win32_info, 0,
5197 &_sapp.vk.surface);
5198#else
5199#error "sokol_app.h: unsupported Vulkan platform"
5200#endif
5201 if (res != VK_SUCCESS) {
5202 _SAPP_PANIC(VULKAN_CREATE_SURFACE_FAILED);
5203 }
5204 SOKOL_ASSERT(_sapp.vk.surface);
5205}
5206
5207_SOKOL_PRIVATE void _sapp_vk_destroy_surface(void) {
5208 SOKOL_ASSERT(_sapp.vk.instance);
5209 SOKOL_ASSERT(_sapp.vk.surface);
5210 vkDestroySurfaceKHR(_sapp.vk.instance, _sapp.vk.surface, 0);
5211 _sapp.vk.surface = 0;
5212}
5213
5214_SOKOL_PRIVATE VkSurfaceFormatKHR _sapp_vk_pick_surface_format(void) {
5215 SOKOL_ASSERT(_sapp.vk.instance);
5216 SOKOL_ASSERT(_sapp.vk.surface);
5217 _SAPP_VK_MAX_COUNT_AND_ARRAY(64, VkSurfaceFormatKHR, fmt_count, formats);
5218 VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(
5219 _sapp.vk.physical_device, _sapp.vk.surface, &fmt_count, formats);
5220 SOKOL_ASSERT((res == VK_SUCCESS) || (res == VK_INCOMPLETE));
5221 _SOKOL_UNUSED(res);
5222 SOKOL_ASSERT(fmt_count > 0);
5223 // FIXME: only accept non-SRGB formats until sokol_app.h gets proper SRGB
5224 // support
5225 for (uint32_t i = 0; i < fmt_count; i++) {
5226 switch (formats[i].format) {
5227 case VK_FORMAT_B8G8R8A8_UNORM:
5228 case VK_FORMAT_R8G8B8A8_UNORM:
5229 return formats[i];
5230 default:
5231 break;
5232 }
5233 }
5234 // FIXME: fallback might still return an SRGB format
5235 return formats[0];
5236}
5237
5238_SOKOL_PRIVATE void _sapp_vk_create_sync_objects(void) {
5239 SOKOL_ASSERT(_sapp.vk.device);
5240 _SAPP_STRUCT(VkSemaphoreCreateInfo, create_info);
5241 create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
5242 VkResult res;
5243 _SOKOL_UNUSED(res);
5244 for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
5245 SOKOL_ASSERT(0 == _sapp.vk.sync[i].present_complete_sem);
5246 SOKOL_ASSERT(0 == _sapp.vk.sync[i].render_finished_sem);
5247 res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0,
5248 &_sapp.vk.sync[i].present_complete_sem);
5249 SOKOL_ASSERT((res == VK_SUCCESS) &&
5250 (_sapp.vk.sync[i].present_complete_sem));
5251 _sapp_vk_set_object_label(VK_OBJECT_TYPE_SEMAPHORE,
5252 (uint64_t)_sapp.vk.sync[i].present_complete_sem,
5253 "present_complete_sem");
5254 res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0,
5255 &_sapp.vk.sync[i].render_finished_sem);
5256 SOKOL_ASSERT((res == VK_SUCCESS) && (_sapp.vk.sync[i].render_finished_sem));
5257 _sapp_vk_set_object_label(VK_OBJECT_TYPE_SEMAPHORE,
5258 (uint64_t)_sapp.vk.sync[i].render_finished_sem,
5259 "render_finished_sem");
5260 }
5261}
5262
5263_SOKOL_PRIVATE void _sapp_vk_destroy_sync_objects(void) {
5264 SOKOL_ASSERT(_sapp.vk.device);
5265 for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
5266 SOKOL_ASSERT(_sapp.vk.sync[i].present_complete_sem);
5267 SOKOL_ASSERT(_sapp.vk.sync[i].render_finished_sem);
5268 vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].present_complete_sem,
5269 0);
5270 vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].render_finished_sem,
5271 0);
5272 _sapp.vk.sync[i].present_complete_sem = 0;
5273 _sapp.vk.sync[i].render_finished_sem = 0;
5274 }
5275}
5276
5277_SOKOL_PRIVATE VkDeviceMemory
5278_sapp_vk_mem_alloc_image_memory(const VkMemoryRequirements *mem_reqs) {
5279 SOKOL_ASSERT(_sapp.vk.device);
5280 SOKOL_ASSERT(mem_reqs);
5281 int mem_type_index = _sapp_vk_mem_find_memory_type_index(
5282 mem_reqs->memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
5283 if (-1 == mem_type_index) {
5284 _SAPP_ERROR(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE);
5285 return 0;
5286 }
5287 _SAPP_STRUCT(VkMemoryAllocateInfo, alloc_info);
5288 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5289 alloc_info.allocationSize = mem_reqs->size;
5290 alloc_info.memoryTypeIndex = (uint32_t)mem_type_index;
5291 VkDeviceMemory vk_dev_mem = 0;
5292 VkResult res = vkAllocateMemory(_sapp.vk.device, &alloc_info, 0, &vk_dev_mem);
5293 if (res != VK_SUCCESS) {
5294 _SAPP_ERROR(VULKAN_ALLOCATE_MEMORY_FAILED);
5295 return 0;
5296 }
5297 SOKOL_ASSERT(vk_dev_mem);
5298 return vk_dev_mem;
5299}
5300
5301_SOKOL_PRIVATE void _sapp_vk_mem_free_image_memory(VkDeviceMemory vk_dev_mem) {
5302 SOKOL_ASSERT(_sapp.vk.device);
5303 SOKOL_ASSERT(vk_dev_mem);
5304 vkFreeMemory(_sapp.vk.device, vk_dev_mem, 0);
5305}
5306
5307_SOKOL_PRIVATE void
5308_sapp_vk_swapchain_destroy_surface(_sapp_vk_swapchain_surface_t *surf) {
5309 SOKOL_ASSERT(surf);
5310 SOKOL_ASSERT(surf->img);
5311 SOKOL_ASSERT(surf->mem);
5312 SOKOL_ASSERT(surf->view);
5313 vkDestroyImageView(_sapp.vk.device, surf->view, 0);
5314 surf->view = 0;
5315 _sapp_vk_mem_free_image_memory(surf->mem);
5316 surf->mem = 0;
5317 vkDestroyImage(_sapp.vk.device, surf->img, 0);
5318 surf->img = 0;
5319}
5320
5321_SOKOL_PRIVATE void _sapp_vk_swapchain_create_surface(
5322 _sapp_vk_swapchain_surface_t *surf, bool recreate, VkFormat format,
5323 uint32_t width, uint32_t height, VkSampleCountFlagBits sample_count_flags,
5324 VkImageUsageFlags usage, VkImageAspectFlags aspect_mask,
5325 const char *image_debug_label, const char *view_debug_label) {
5326 SOKOL_ASSERT(_sapp.vk.physical_device);
5327 SOKOL_ASSERT(_sapp.vk.device);
5328 SOKOL_ASSERT(surf);
5329 if (recreate) {
5330 _sapp_vk_swapchain_destroy_surface(surf);
5331 }
5332 SOKOL_ASSERT(0 == surf->img);
5333 SOKOL_ASSERT(0 == surf->mem);
5334 SOKOL_ASSERT(0 == surf->view);
5335 VkResult res;
5336
5337 _SAPP_STRUCT(VkImageCreateInfo, img_create_info);
5338 img_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
5339 img_create_info.imageType = VK_IMAGE_TYPE_2D;
5340 img_create_info.format = format;
5341 img_create_info.extent.width = width;
5342 img_create_info.extent.height = height;
5343 img_create_info.extent.depth = 1;
5344 img_create_info.mipLevels = 1;
5345 img_create_info.arrayLayers = 1;
5346 img_create_info.samples = sample_count_flags;
5347 img_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
5348 img_create_info.usage = usage;
5349 img_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
5350 img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5351 res = vkCreateImage(_sapp.vk.device, &img_create_info, 0, &surf->img);
5352 if (res != VK_SUCCESS) {
5353 _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED);
5354 }
5355 SOKOL_ASSERT(surf->img);
5356 _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE, (uint64_t)surf->img,
5357 image_debug_label);
5358
5359 _SAPP_STRUCT(VkMemoryRequirements, mem_reqs);
5360 vkGetImageMemoryRequirements(_sapp.vk.device, surf->img, &mem_reqs);
5361 surf->mem = _sapp_vk_mem_alloc_image_memory(&mem_reqs);
5362 if (0 == surf->mem) {
5363 _SAPP_PANIC(VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED);
5364 }
5365 res = vkBindImageMemory(_sapp.vk.device, surf->img, surf->mem, 0);
5366 if (res != VK_SUCCESS) {
5367 _SAPP_PANIC(VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED);
5368 }
5369 SOKOL_ASSERT(surf->mem);
5370
5371 _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info);
5372 view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
5373 view_create_info.image = surf->img;
5374 view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
5375 view_create_info.format = format;
5376 view_create_info.subresourceRange.aspectMask = aspect_mask;
5377 view_create_info.subresourceRange.levelCount = 1;
5378 view_create_info.subresourceRange.layerCount = 1;
5379 res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0, &surf->view);
5380 if (res != VK_SUCCESS) {
5381 _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED);
5382 }
5383 SOKOL_ASSERT(surf->view);
5384 _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)surf->view,
5385 view_debug_label);
5386}
5387
5388_SOKOL_PRIVATE uint32_t
5389_sapp_vk_swapchain_min_image_count(const VkSurfaceCapabilitiesKHR *surf_caps) {
5390 // FIXME: figure out why at least 3 swapchain images are required to appease
5391 // the validation layer (on the Linux Intel driver, present-mode-fifo has a
5392 // surf_caps.minImageCount == 3, while on Windows surf_caps.minImageCount ==
5393 // 2, and using this directly causes validation layer errors about the
5394 // present-complete semaphore (to reproduce simply change the '= 3' below to
5395 // '= 2')
5396 SOKOL_ASSERT(surf_caps);
5397 const uint32_t required_image_count = 3;
5398 uint32_t min_image_count = surf_caps->minImageCount;
5399 if (min_image_count < required_image_count) {
5400 min_image_count = required_image_count;
5401 }
5402 return min_image_count;
5403}
5404
5405_SOKOL_PRIVATE void _sapp_vk_create_swapchain_image_view(uint32_t image_index) {
5406 SOKOL_ASSERT(_sapp.vk.device);
5407 SOKOL_ASSERT(image_index < _sapp.vk.num_swapchain_images);
5408 SOKOL_ASSERT(_sapp.vk.swapchain_images[image_index]);
5409 SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[image_index]);
5410
5411 _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info);
5412 view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
5413 view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
5414 view_create_info.format = _sapp.vk.surface_format.format;
5415 view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
5416 view_create_info.subresourceRange.levelCount = 1;
5417 view_create_info.subresourceRange.layerCount = 1;
5418 view_create_info.image = _sapp.vk.swapchain_images[image_index];
5419 VkResult res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0,
5420 &_sapp.vk.swapchain_views[image_index]);
5421 if (res != VK_SUCCESS) {
5422 _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED);
5423 }
5424 SOKOL_ASSERT(_sapp.vk.swapchain_views[image_index]);
5425 _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW,
5426 (uint64_t)_sapp.vk.swapchain_views[image_index],
5427 "swapchain_view");
5428}
5429
5430_SOKOL_PRIVATE void
5431_sapp_vk_destroy_swapchain_image_view(uint32_t image_index) {
5432 SOKOL_ASSERT(_sapp.vk.device);
5433 SOKOL_ASSERT(image_index < _sapp.vk.num_swapchain_images);
5434 SOKOL_ASSERT(_sapp.vk.swapchain_views[image_index]);
5435 vkDestroyImageView(_sapp.vk.device, _sapp.vk.swapchain_views[image_index], 0);
5436 _sapp.vk.swapchain_views[image_index] = 0;
5437}
5438
5439_SOKOL_PRIVATE void _sapp_vk_create_swapchain(bool recreate) {
5440 SOKOL_ASSERT(_sapp.vk.physical_device);
5441 SOKOL_ASSERT(_sapp.vk.surface);
5442 SOKOL_ASSERT(_sapp.vk.device);
5443 if (!recreate) {
5444 SOKOL_ASSERT(0 == _sapp.vk.swapchain);
5445 SOKOL_ASSERT(0 == _sapp.vk.num_swapchain_images);
5446 SOKOL_ASSERT(0 == _sapp.vk.swapchain_images[0]);
5447 SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[0]);
5448 } else {
5449 SOKOL_ASSERT(_sapp.vk.swapchain);
5450 SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0);
5451 SOKOL_ASSERT(_sapp.vk.swapchain_images[0]);
5452 SOKOL_ASSERT(_sapp.vk.swapchain_views[0]);
5453 }
5454
5455 VkSwapchainKHR old_swapchain = _sapp.vk.swapchain;
5456
5457 _SAPP_STRUCT(VkSurfaceCapabilitiesKHR, surf_caps);
5458 VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
5459 _sapp.vk.physical_device, _sapp.vk.surface, &surf_caps);
5460 SOKOL_ASSERT(res == VK_SUCCESS);
5461 const uint32_t fb_width = surf_caps.currentExtent.width;
5462 const uint32_t fb_height = surf_caps.currentExtent.height;
5463
5464 _sapp.vk.surface_format = _sapp_vk_pick_surface_format();
5465 const VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
5466
5467 _SAPP_STRUCT(VkSwapchainCreateInfoKHR, create_info);
5468 create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
5469 create_info.flags = 0;
5470 create_info.surface = _sapp.vk.surface;
5471 create_info.minImageCount = _sapp_vk_swapchain_min_image_count(&surf_caps);
5472 create_info.imageFormat = _sapp.vk.surface_format.format;
5473 create_info.imageColorSpace = _sapp.vk.surface_format.colorSpace;
5474 create_info.imageExtent.width = fb_width;
5475 create_info.imageExtent.height = fb_height;
5476 create_info.imageArrayLayers = 1;
5477 create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5478 create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
5479 create_info.preTransform = surf_caps.currentTransform;
5480 create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
5481 create_info.presentMode = present_mode;
5482 create_info.clipped = true;
5483 create_info.oldSwapchain = old_swapchain;
5484 res = vkCreateSwapchainKHR(_sapp.vk.device, &create_info, 0,
5485 &_sapp.vk.swapchain);
5486 if (res != VK_SUCCESS) {
5487 _SAPP_PANIC(VULKAN_CREATE_SWAPCHAIN_FAILED);
5488 }
5489 SOKOL_ASSERT(_sapp.vk.swapchain);
5490
5491 if (old_swapchain) {
5492 // NOTE: destroying the depth- and msaa-surfaces happens
5493 // down in the respective _sapp_vk_swapchain_create_surface() calls!
5494 for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
5495 _sapp_vk_destroy_swapchain_image_view(i);
5496 }
5497 vkDestroySwapchainKHR(_sapp.vk.device, old_swapchain, 0);
5498 }
5499
5500 _sapp.vk.num_swapchain_images = _SAPP_VK_MAX_SWAPCHAIN_IMAGES;
5501 res = vkGetSwapchainImagesKHR(_sapp.vk.device, _sapp.vk.swapchain,
5502 &_sapp.vk.num_swapchain_images,
5503 _sapp.vk.swapchain_images);
5504 SOKOL_ASSERT(res == VK_SUCCESS);
5505 SOKOL_ASSERT(_sapp.vk.num_swapchain_images >= surf_caps.minImageCount);
5506
5507 for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
5508 _sapp_vk_create_swapchain_image_view(i);
5509 }
5510
5511 // create depth-stencil buffer
5512 _sapp_vk_swapchain_create_surface(
5513 &_sapp.vk.depth, recreate, VK_FORMAT_D32_SFLOAT_S8_UINT, fb_width,
5514 fb_height, (VkSampleCountFlagBits)_sapp.sample_count,
5515 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
5516 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
5517 "swapchain_depthstencil_image", "swapchain_depthstencil_view");
5518
5519 // optionally create MSAA surface
5520 if (_sapp.sample_count > 1) {
5521 _sapp_vk_swapchain_create_surface(
5522 &_sapp.vk.msaa, recreate, _sapp.vk.surface_format.format, fb_width,
5523 fb_height, (VkSampleCountFlagBits)_sapp.sample_count,
5524 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
5525 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
5526 VK_IMAGE_ASPECT_COLOR_BIT, "swapchain_msaa_image",
5527 "swapchain_msaa_view");
5528 }
5529
5530 // this is the only place in the Vulkan code path which updates
5531 // _sapp.framebuffer_width/height
5532 _sapp.framebuffer_width = (int)fb_width;
5533 _sapp.framebuffer_height = (int)fb_height;
5534}
5535
5536_SOKOL_PRIVATE void _sapp_vk_destroy_swapchain(void) {
5537 SOKOL_ASSERT(_sapp.vk.device);
5538 SOKOL_ASSERT(_sapp.vk.swapchain);
5539 SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0);
5540 if (_sapp.vk.msaa.img) {
5541 _sapp_vk_swapchain_destroy_surface(&_sapp.vk.msaa);
5542 }
5543 _sapp_vk_swapchain_destroy_surface(&_sapp.vk.depth);
5544 for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
5545 _sapp_vk_destroy_swapchain_image_view(i);
5546 _sapp.vk.swapchain_images[i] = 0;
5547 }
5548 vkDestroySwapchainKHR(_sapp.vk.device, _sapp.vk.swapchain, 0);
5549 _sapp.vk.swapchain = 0;
5550 _sapp.vk.num_swapchain_images = 0;
5551}
5552
5553#if defined(_SAPP_LINUX)
5554#if defined(_SAPP_X11)
5555_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type);
5556#elif defined(_SAPP_WAYLAND)
5557_SOKOL_PRIVATE void _sapp_wl_app_event(sapp_event_type type);
5558#endif
5559[_sapp.macos.window center];
5560_sapp.valid = true;
5561
5562// __v_ start
5563if (!_sapp.__v_native_render) { // __v_
5564#endif
5565#if defined(_SAPP_WIN32)
5566 _SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type);
5567#endif
5568
5569 _SOKOL_PRIVATE void _sapp_vk_recreate_swapchain(void) {
5570 SOKOL_ASSERT(_sapp.vk.device);
5571 vkDeviceWaitIdle(_sapp.vk.device);
5572 int fb_width = _sapp.framebuffer_width;
5573 int fb_height = _sapp.framebuffer_height;
5574 _sapp_vk_create_swapchain(true);
5575 if ((fb_width != _sapp.framebuffer_width) ||
5576 (fb_height != _sapp.framebuffer_height)) {
5577 if (!_sapp.first_frame) {
5578#if defined(_SAPP_LINUX)
5579#if defined(_SAPP_X11)
5580 _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED);
5581#elif defined(_SAPP_WAYLAND)
5582 _sapp_wl_app_event(SAPP_EVENTTYPE_RESIZED);
5583#endif
5584#endif
5585#if defined(_SAPP_WIN32)
5586 _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED);
5587#endif
5588 }
5589 }
5590
5591 else {
5592 [_sapp.macos.window center];
5593 }
5594 } // __v_
5595 // __v_ end
5596
5597 NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
5598 [NSApp activateIgnoringOtherApps:YES];
5599
5600 // __v start
5601 ///////////////////////////////////////////////////////
5602 // Create a child view for native rendering
5603 if (_sapp.__v_native_render) {
5604
5605 CGRect wRect = _sapp.macos.window.frame;
5606 NSView *contentView = _sapp.macos.window.contentView;
5607 CGRect cRect = contentView.frame;
5608
5609 CGRect rect = CGRectMake(wRect.origin.x, wRect.origin.y, cRect.size.width,
5610 cRect.size.height);
5611 NSWindow *overlayWindow =
5612 [[NSWindow alloc] initWithContentRect:rect
5613 styleMask:NSBorderlessWindowMask
5614 backing:NSBackingStoreBuffered
5615 defer:NO];
5616 // overlayWindow.backgroundColor = [NSColor whiteColor];
5617
5618 // overlayWindow.backgroundColor = [[NSColor whiteColor]
5619 // colorWithAlphaComponent:0];
5620 [overlayWindow setOpaque:YES];
5621 [_sapp.macos.window setIgnoresMouseEvents:NO];
5622 g_view = [[MyView2 alloc] init];
5623 g_view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
5624 overlayWindow.contentView = g_view;
5625
5626 [contentView addSubview:g_view];
5627 //[ _sapp.macos.window addChildWindow:overlayWindow
5628 // ordered:NSWindowAbove];
5629 [_sapp.macos.window center];
5630 }
5631 //////////////////////////////////
5632 // __v_ end
5633
5634 [_sapp.macos.window makeKeyAndOrderFront:nil];
5635 _sapp_macos_update_dimensions();
5636 [NSEvent setMouseCoalescingEnabled:NO];
5637
5638 // __v_ start
5639 /*
5640 NSApplicationPresentationOptions options =
5641 (NSApplicationPresentationAutoHideMenuBar |
5642 NSApplicationPresentationAutoHideDock
5643 | NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock);
5644
5645 [NSApp setPresentationOptions:options];
5646 */
5647
5648 //[NSEvent setMouseCoalescingEnabled:NO];
5649 // __v_ end
5650}
5651
5652_SOKOL_PRIVATE void _sapp_vk_init(void) {
5653 _sapp_vk_create_instance();
5654 _sapp_vk_load_instance_ext_funcs();
5655 _sapp_vk_create_surface();
5656 _sapp_vk_pick_physical_device();
5657 _sapp_vk_create_device();
5658 _sapp_vk_create_swapchain(false);
5659 _sapp_vk_create_sync_objects();
5660}
5661
5662_SOKOL_PRIVATE void _sapp_vk_discard(void) {
5663 SOKOL_ASSERT(_sapp.vk.device);
5664 vkDeviceWaitIdle(_sapp.vk.device);
5665 _sapp_vk_destroy_sync_objects();
5666 _sapp_vk_destroy_swapchain();
5667 _sapp_vk_destroy_device();
5668 _sapp_vk_destroy_surface();
5669 _sapp_vk_destroy_instance();
5670}
5671
5672_SOKOL_PRIVATE void _sapp_vk_swapchain_next(void) {
5673 SOKOL_ASSERT(_sapp.vk.device);
5674 SOKOL_ASSERT(_sapp.vk.swapchain);
5675 VkResult res =
5676 vkAcquireNextImageKHR(_sapp.vk.device, _sapp.vk.swapchain,
5677 UINT64_MAX, // timeout
5678 _sapp.vk.sync[_sapp.vk.sync_slot]
5679 .present_complete_sem, // semaphore to signal
5680 0, // fence to signal
5681 &_sapp.vk.cur_swapchain_image_index);
5682 if ((res != VK_NOT_READY) && (res != VK_SUBOPTIMAL_KHR) &&
5683 (res != VK_SUCCESS) && (res != VK_TIMEOUT)) {
5684 _SAPP_WARN(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED);
5685 }
5686}
5687
5688_SOKOL_PRIVATE void _sapp_vk_present(void) {
5689 SOKOL_ASSERT(_sapp.vk.queue);
5690 _SAPP_STRUCT(VkPresentInfoKHR, present_info);
5691 present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
5692 present_info.waitSemaphoreCount = 1;
5693 // NOTE: using the current swapchain image index here instead of `sync_slot`
5694 // is *NOT* a bug! The render_finished_semaphore *must* be associated with the
5695 // current swapchain image in case the swapchain implementation doesn't return
5696 // swapchain images in order
5697 present_info.pWaitSemaphores =
5698 &_sapp.vk.sync[_sapp.vk.cur_swapchain_image_index].render_finished_sem;
5699 present_info.swapchainCount = 1;
5700 present_info.pSwapchains = &_sapp.vk.swapchain;
5701 present_info.pImageIndices = &_sapp.vk.cur_swapchain_image_index;
5702 VkResult res = vkQueuePresentKHR(_sapp.vk.queue, &present_info);
5703 if ((res == VK_ERROR_OUT_OF_DATE_KHR) || (res == VK_SUBOPTIMAL_KHR)) {
5704 _sapp_vk_recreate_swapchain();
5705 } else if (res != VK_SUCCESS) {
5706 _SAPP_WARN(VULKAN_QUEUE_PRESENT_FAILED);
5707 }
5708}
5709
5710_SOKOL_PRIVATE void _sapp_vk_frame(void) {
5711 _sapp_frame();
5712 _sapp_vk_present();
5713 _sapp.vk.sync_slot = (_sapp.vk.sync_slot + 1) % _sapp.vk.num_swapchain_images;
5714}
5715
5716#endif // SOKOL_VULKAN
5717
5718// █████ ██████ ██████ ██ ███████
5719// ██ ██ ██ ██ ██ ██ ██ ██
5720// ███████ ██████ ██████ ██ █████
5721// ██ ██ ██ ██ ██ ██
5722// ██ ██ ██ ██ ███████ ███████
5723//
5724// >>apple
5725#if defined(_SAPP_APPLE)
5726
5727#if __has_feature(objc_arc)
5728#define _SAPP_OBJC_RELEASE(obj) \
5729 { \
5730 obj = nil; \
5731 }
5732#else
5733#define _SAPP_OBJC_RELEASE(obj) \
5734 { \
5735 [obj release]; \
5736 obj = nil; \
5737 }
5738#endif
5739
5740// ███ ███ █████ ██████ ██████ ███████
5741// ████ ████ ██ ██ ██ ██ ██ ██
5742// ██ ████ ██ ███████ ██ ██ ██ ███████
5743// ██ ██ ██ ██ ██ ██ ██ ██ ██
5744// ██ ██ ██ ██ ██████ ██████ ███████
5745//
5746// >>macos
5747#if defined(_SAPP_MACOS)
5748
5749NSInteger _sapp_macos_max_fps(void) {
5750 NSInteger max_fps = 60;
5751#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000)
5752 if (@available(macOS 12.0, *)) {
5753 max_fps = [NSScreen.mainScreen maximumFramesPerSecond];
5754 }
5755#endif
5756 return max_fps;
5757}
5758
5759#if defined(SOKOL_METAL)
5760_SOKOL_PRIVATE void _sapp_macos_mtl_init(void) {
5761 NSInteger max_fps = _sapp_macos_max_fps();
5762 // NOTE: when eventually switching to CAMetalLayer, use the specialized
5763 // CAMetalDisplayLink instead of CADisplayLink!
5764 _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice();
5765 _sapp.macos.view = [[_sapp_macos_view alloc] init];
5766 [_sapp.macos.view updateTrackingAreas];
5767 _sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
5768 _sapp.macos.view.device = _sapp.macos.mtl_device;
5769 _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
5770 _sapp.macos.view.depthStencilPixelFormat =
5771 MTLPixelFormatDepth32Float_Stencil8;
5772 _sapp.macos.view.sampleCount = (NSUInteger)_sapp.sample_count;
5773 _sapp.macos.view.autoResizeDrawable = false;
5774 _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest;
5775}
5776
5777_SOKOL_PRIVATE void _sapp_macos_mtl_discard_state(void) {
5778 _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device);
5779}
5780
5781_SOKOL_PRIVATE bool
5782_sapp_macos_mtl_update_framebuffer_dimensions(NSRect view_bounds) {
5783 _sapp.framebuffer_width =
5784 _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
5785 _sapp.framebuffer_height =
5786 _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
5787 const CGSize cur_fb_size = _sapp.macos.view.drawableSize;
5788 int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width);
5789 int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height);
5790 bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
5791 (_sapp.framebuffer_height != cur_fb_height);
5792 if (dim_changed) {
5793 const CGSize drawable_size = {(CGFloat)_sapp.framebuffer_width,
5794 (CGFloat)_sapp.framebuffer_height};
5795 _sapp.macos.view.drawableSize = drawable_size;
5796 }
5797 return dim_changed;
5798}
5799#endif
5800
5801#if defined(SOKOL_GLCORE)
5802_SOKOL_PRIVATE void _sapp_macos_gl_init(NSRect window_rect) {
5803 NSOpenGLPixelFormatAttribute attrs[32];
5804 int i = 0;
5805 attrs[i++] = NSOpenGLPFAAccelerated;
5806 attrs[i++] = NSOpenGLPFADoubleBuffer;
5807 attrs[i++] = NSOpenGLPFAOpenGLProfile;
5808 const int glVersion =
5809 _sapp.desc.gl.major_version * 10 + _sapp.desc.gl.minor_version;
5810 switch (glVersion) {
5811 case 10:
5812 attrs[i++] = NSOpenGLProfileVersionLegacy;
5813 break;
5814 case 32:
5815 attrs[i++] = NSOpenGLProfileVersion3_2Core;
5816 break;
5817 case 41:
5818 attrs[i++] = NSOpenGLProfileVersion4_1Core;
5819 break;
5820 default:
5821 _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE);
5822 }
5823 attrs[i++] = NSOpenGLPFAColorSize;
5824 attrs[i++] = 24;
5825 attrs[i++] = NSOpenGLPFAAlphaSize;
5826 attrs[i++] = 8;
5827 attrs[i++] = NSOpenGLPFADepthSize;
5828 attrs[i++] = 24;
5829 attrs[i++] = NSOpenGLPFAStencilSize;
5830 attrs[i++] = 8;
5831 if (_sapp.sample_count > 1) {
5832 attrs[i++] = NSOpenGLPFAMultisample;
5833 attrs[i++] = NSOpenGLPFASampleBuffers;
5834 attrs[i++] = 1;
5835 attrs[i++] = NSOpenGLPFASamples;
5836 attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count;
5837 } else {
5838 attrs[i++] = NSOpenGLPFASampleBuffers;
5839 attrs[i++] = 0;
5840 }
5841 attrs[i++] = 0;
5842 NSOpenGLPixelFormat *glpixelformat_obj =
5843 [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
5844 SOKOL_ASSERT(glpixelformat_obj != nil);
5845
5846 _sapp.macos.view = [[_sapp_macos_view alloc] initWithFrame:window_rect
5847 pixelFormat:glpixelformat_obj];
5848 _SAPP_OBJC_RELEASE(glpixelformat_obj);
5849 [_sapp.macos.view updateTrackingAreas];
5850 if (_sapp.desc.high_dpi) {
5851 [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES];
5852 } else {
5853 [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO];
5854 }
5855
5856 NSTimer *timer_obj = [NSTimer timerWithTimeInterval:0.001
5857 target:_sapp.macos.view
5858 selector:@selector(timerFired:)
5859 userInfo:nil
5860 repeats:YES];
5861 [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode];
5862 timer_obj = nil;
5863}
5864
5865_SOKOL_PRIVATE void _sapp_macos_gl_discard_state(void) {
5866 // nothing to do here
5867}
5868
5869_SOKOL_PRIVATE bool
5870_sapp_macos_gl_update_framebuffer_dimensions(NSRect view_bounds) {
5871 const int cur_fb_width =
5872 _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
5873 const int cur_fb_height =
5874 _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
5875 const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
5876 (_sapp.framebuffer_height != cur_fb_height);
5877 _sapp.framebuffer_width = cur_fb_width;
5878 _sapp.framebuffer_height = cur_fb_height;
5879 return dim_changed;
5880}
5881#endif
5882
5883#if defined(SOKOL_WGPU)
5884_SOKOL_PRIVATE void _sapp_macos_wgpu_init(void) {
5885 NSInteger max_fps = _sapp_macos_max_fps();
5886 _sapp.macos.wgpu.mtl_layer = [CAMetalLayer layer];
5887 _sapp.macos.wgpu.mtl_layer.magnificationFilter = kCAFilterNearest;
5888 _sapp.macos.wgpu.mtl_layer.opaque = true;
5889 // NOTE: might experiment with this, valid values are 2 or 3 (default: 3), I
5890 // don't see any difference tbh
5891 // _sapp.macos.wgpu.mtl_layer.maximumDrawableCount = 2;
5892 _sapp.macos.view = [[_sapp_macos_view alloc] init];
5893 [_sapp.macos.view updateTrackingAreas];
5894 _sapp.macos.view.wantsLayer = YES;
5895 _sapp.macos.view.layer = _sapp.macos.wgpu.mtl_layer;
5896 _sapp.macos.wgpu.display_link =
5897 [_sapp.macos.view displayLinkWithTarget:_sapp.macos.view
5898 selector:@selector(displayLinkFired:)];
5899 float preferred_fps = max_fps / _sapp.swap_interval;
5900 CAFrameRateRange frame_rate_range = {preferred_fps, preferred_fps,
5901 preferred_fps};
5902 _sapp.macos.wgpu.display_link.preferredFrameRateRange = frame_rate_range;
5903 [_sapp.macos.wgpu.display_link addToRunLoop:[NSRunLoop currentRunLoop]
5904 forMode:NSRunLoopCommonModes];
5905 _sapp_wgpu_init();
5906}
5907
5908_SOKOL_PRIVATE void _sapp_macos_wgpu_discard_state(void) {
5909 _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.display_link);
5910 _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.mtl_layer);
5911 _sapp_wgpu_discard();
5912}
5913
5914_SOKOL_PRIVATE bool
5915_sapp_macos_wgpu_update_framebuffer_dimensions(NSRect view_bounds) {
5916 _sapp.framebuffer_width =
5917 _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
5918 _sapp.framebuffer_height =
5919 _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
5920 const CGSize cur_fb_size = _sapp.macos.wgpu.mtl_layer.drawableSize;
5921 int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width);
5922 int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height);
5923 bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
5924 (_sapp.framebuffer_height != cur_fb_height);
5925 if (dim_changed) {
5926 const CGSize drawable_size = {(CGFloat)_sapp.framebuffer_width,
5927 (CGFloat)_sapp.framebuffer_height};
5928 _sapp.macos.wgpu.mtl_layer.drawableSize = drawable_size;
5929 _sapp_wgpu_swapchain_size_changed();
5930 }
5931 return dim_changed;
5932}
5933#endif
5934
5935_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) {
5936 _sapp.keycodes[0x1D] = SAPP_KEYCODE_0;
5937 _sapp.keycodes[0x12] = SAPP_KEYCODE_1;
5938 _sapp.keycodes[0x13] = SAPP_KEYCODE_2;
5939 _sapp.keycodes[0x14] = SAPP_KEYCODE_3;
5940 _sapp.keycodes[0x15] = SAPP_KEYCODE_4;
5941 _sapp.keycodes[0x17] = SAPP_KEYCODE_5;
5942 _sapp.keycodes[0x16] = SAPP_KEYCODE_6;
5943 _sapp.keycodes[0x1A] = SAPP_KEYCODE_7;
5944 _sapp.keycodes[0x1C] = SAPP_KEYCODE_8;
5945 _sapp.keycodes[0x19] = SAPP_KEYCODE_9;
5946 _sapp.keycodes[0x00] = SAPP_KEYCODE_A;
5947 _sapp.keycodes[0x0B] = SAPP_KEYCODE_B;
5948 _sapp.keycodes[0x08] = SAPP_KEYCODE_C;
5949 _sapp.keycodes[0x02] = SAPP_KEYCODE_D;
5950 _sapp.keycodes[0x0E] = SAPP_KEYCODE_E;
5951 _sapp.keycodes[0x03] = SAPP_KEYCODE_F;
5952 _sapp.keycodes[0x05] = SAPP_KEYCODE_G;
5953 _sapp.keycodes[0x04] = SAPP_KEYCODE_H;
5954 _sapp.keycodes[0x22] = SAPP_KEYCODE_I;
5955 _sapp.keycodes[0x26] = SAPP_KEYCODE_J;
5956 _sapp.keycodes[0x28] = SAPP_KEYCODE_K;
5957 _sapp.keycodes[0x25] = SAPP_KEYCODE_L;
5958 _sapp.keycodes[0x2E] = SAPP_KEYCODE_M;
5959 _sapp.keycodes[0x2D] = SAPP_KEYCODE_N;
5960 _sapp.keycodes[0x1F] = SAPP_KEYCODE_O;
5961 _sapp.keycodes[0x23] = SAPP_KEYCODE_P;
5962 _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q;
5963 _sapp.keycodes[0x0F] = SAPP_KEYCODE_R;
5964 _sapp.keycodes[0x01] = SAPP_KEYCODE_S;
5965 _sapp.keycodes[0x11] = SAPP_KEYCODE_T;
5966 _sapp.keycodes[0x20] = SAPP_KEYCODE_U;
5967 _sapp.keycodes[0x09] = SAPP_KEYCODE_V;
5968 _sapp.keycodes[0x0D] = SAPP_KEYCODE_W;
5969 _sapp.keycodes[0x07] = SAPP_KEYCODE_X;
5970 _sapp.keycodes[0x10] = SAPP_KEYCODE_Y;
5971 _sapp.keycodes[0x06] = SAPP_KEYCODE_Z;
5972 _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE;
5973 _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH;
5974 _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA;
5975 _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL;
5976 _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT;
5977 _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET;
5978 _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS;
5979 _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD;
5980 _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET;
5981 _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON;
5982 _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH;
5983 _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1;
5984 _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE;
5985 _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK;
5986 _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE;
5987 _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN;
5988 _sapp.keycodes[0x77] = SAPP_KEYCODE_END;
5989 _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER;
5990 _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE;
5991 _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1;
5992 _sapp.keycodes[0x78] = SAPP_KEYCODE_F2;
5993 _sapp.keycodes[0x63] = SAPP_KEYCODE_F3;
5994 _sapp.keycodes[0x76] = SAPP_KEYCODE_F4;
5995 _sapp.keycodes[0x60] = SAPP_KEYCODE_F5;
5996 _sapp.keycodes[0x61] = SAPP_KEYCODE_F6;
5997 _sapp.keycodes[0x62] = SAPP_KEYCODE_F7;
5998 _sapp.keycodes[0x64] = SAPP_KEYCODE_F8;
5999 _sapp.keycodes[0x65] = SAPP_KEYCODE_F9;
6000 _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10;
6001 _sapp.keycodes[0x67] = SAPP_KEYCODE_F11;
6002 _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12;
6003 _sapp.keycodes[0x69] = SAPP_KEYCODE_F13;
6004 _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14;
6005 _sapp.keycodes[0x71] = SAPP_KEYCODE_F15;
6006 _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16;
6007 _sapp.keycodes[0x40] = SAPP_KEYCODE_F17;
6008 _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18;
6009 _sapp.keycodes[0x50] = SAPP_KEYCODE_F19;
6010 _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20;
6011 _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME;
6012 _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT;
6013 _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT;
6014 _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT;
6015 _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL;
6016 _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT;
6017 _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER;
6018 _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU;
6019 _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK;
6020 _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN;
6021 _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP;
6022 _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT;
6023 _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT;
6024 _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL;
6025 _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT;
6026 _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER;
6027 _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE;
6028 _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB;
6029 _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP;
6030 _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0;
6031 _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1;
6032 _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2;
6033 _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3;
6034 _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4;
6035 _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5;
6036 _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6;
6037 _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7;
6038 _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8;
6039 _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9;
6040 _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD;
6041 _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL;
6042 _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE;
6043 _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER;
6044 _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL;
6045 _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY;
6046 _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT;
6047}
6048
6049_SOKOL_PRIVATE void _sapp_macos_discard_state(void) {
6050 // NOTE: it's safe to call [release] on a nil object
6051 if (_sapp.macos.keyup_monitor != nil) {
6052 [NSEvent removeMonitor:_sapp.macos.keyup_monitor];
6053 // NOTE: removeMonitor also releases the object
6054 _sapp.macos.keyup_monitor = nil;
6055 }
6056 _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area);
6057 _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg);
6058 _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg);
6059 _SAPP_OBJC_RELEASE(_sapp.macos.view);
6060#if defined(SOKOL_METAL)
6061 _sapp_macos_mtl_discard_state();
6062#elif defined(SOKOL_GLCORE)
6063 _sapp_macos_gl_discard_state();
6064#elif defined(SOKOL_WGPU)
6065 _sapp_macos_wgpu_discard_state();
6066#endif
6067 _SAPP_OBJC_RELEASE(_sapp.macos.window);
6068}
6069
6070// undocumented methods for creating cursors (see GLFW 3.4 and
6071// imgui_impl_osx.mm)
6072@interface NSCursor ()
6073+ (id)_windowResizeNorthWestSouthEastCursor;
6074+ (id)_windowResizeNorthEastSouthWestCursor;
6075+ (id)_windowResizeNorthSouthCursor;
6076+ (id)_windowResizeEastWestCursor;
6077@end
6078
6079_SOKOL_PRIVATE void _sapp_macos_init_cursors(void) {
6080 for (size_t i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
6081 _sapp.macos.standard_cursors[i] = nil;
6082 _sapp.macos.custom_cursors[i] = nil;
6083 }
6084 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor];
6085 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor];
6086 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_CROSSHAIR] =
6087 [NSCursor crosshairCursor];
6088 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_POINTING_HAND] =
6089 [NSCursor pointingHandCursor];
6090 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_EW] =
6091 [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)]
6092 ? [NSCursor _windowResizeEastWestCursor]
6093 : [NSCursor resizeLeftRightCursor];
6094 _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NS] =
6095 [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)]
6096 ? [NSCursor _windowResizeNorthSouthCursor]
6097 : [NSCursor resizeUpDownCursor];