v / thirdparty / sokol /
1#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)
2#define SOKOL_APP_IMPL
3#endif
4#ifndef SOKOL_APP_INCLUDED
5/*
6 sokol_app.h -- cross-platform application wrapper
7
8 Project URL: https://github.com/floooh/sokol
9
10 Do this:
11 #define SOKOL_IMPL or
12 #define SOKOL_APP_IMPL
13 before you include this file in *one* C or C++ file to create the
14 implementation.
15
16 In the same place define one of the following to select the 3D-API
17 which should be initialized by sokol_app.h (this must also match
18 the backend selected for sokol_gfx.h if both are used in the same
19 project):
20
21 #define SOKOL_GLCORE33
22 #define SOKOL_GLES2
23 #define SOKOL_GLES3
24 #define SOKOL_D3D11
25 #define SOKOL_METAL
26 #define SOKOL_WGPU
27
28 Optionally provide the following defines with your own implementations:
29
30 SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
31 SOKOL_LOG(msg) - your own logging function (default: puts(msg))
32 SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
33 SOKOL_ABORT() - called after an unrecoverable error (default: abort())
34 SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain
35 SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function
36 SOKOL_APP_API_DECL - public function declaration prefix (default: extern)
37 SOKOL_API_DECL - same as SOKOL_APP_API_DECL
38 SOKOL_API_IMPL - public function implementation prefix (default: -)
39 SOKOL_CALLOC - your own calloc function (default: calloc(n, s))
40 SOKOL_FREE - your own free function (default: free(p))
41
42 Optionally define the following to force debug checks and validations
43 even in release mode:
44
45 SOKOL_DEBUG - by default this is defined if _DEBUG is defined
46
47 If sokol_app.h is compiled as a DLL, define the following before
48 including the declaration or implementation:
49
50 SOKOL_DLL
51
52 On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport)
53 or __declspec(dllimport) as needed.
54
55 For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
56
57 Portions of the Windows and Linux GL initialization and event code have been
58 taken from GLFW (http://www.glfw.org/)
59
60 iOS onscreen keyboard support 'inspired' by libgdx.
61
62 Link with the following system libraries:
63
64 - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit
65 - on macOS with GL: Cocoa, QuartzCore, OpenGL
66 - on iOS with Metal: Foundation, UIKit, Metal, MetalKit
67 - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit
68 - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?)
69 - on Android: GLESv3, EGL, log, android
70 - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib
71 - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined
72 - link with the following libs: -lkernel32 -luser32 -lshell32
73 - additionally with the GL backend: -lgdi32
74 - additionally with the D3D11 backend: -ld3d11 -ldxgi -dxguid
75
76 On Linux, you also need to use the -pthread compiler and linker option, otherwise weird
77 things will happen, see here for details: https://github.com/floooh/sokol/issues/376
78
79 Building for UWP requires a recent Visual Studio toolchain and Windows SDK
80 (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is
81 selected, the sokol_app.h implementation must be compiled as C++17.
82
83 On macOS and iOS, the implementation must be compiled as Objective-C.
84
85 FEATURE OVERVIEW
86 ================
87 sokol_app.h provides a minimalistic cross-platform API which
88 implements the 'application-wrapper' parts of a 3D application:
89
90 - a common application entry function
91 - creates a window and 3D-API context/device with a 'default framebuffer'
92 - makes the rendered frame visible
93 - provides keyboard-, mouse- and low-level touch-events
94 - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi)
95 - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2
96
97 FEATURE/PLATFORM MATRIX
98 =======================
99 | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5
100 --------------------+---------+-------+-------+-------+---------+------+-------+-------
101 gl 3.x | YES | YES | YES | --- | --- | --- | --- | ---
102 gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES
103 gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES
104 metal | --- | YES | --- | YES | --- | --- | --- | ---
105 d3d11 | YES | --- | --- | --- | --- | YES | --- | ---
106 KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES
107 KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES
108 CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES
109 MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES
110 MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES
111 MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES
112 MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES
113 MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES
114 MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES
115 TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES
116 TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES
117 TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES
118 TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES
119 RESIZED | YES | YES | YES | YES | YES | YES | --- | YES
120 ICONIFIED | YES | YES | YES | --- | --- | YES | --- | ---
121 RESTORED | YES | YES | YES | --- | --- | YES | --- | ---
122 SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO
123 RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO
124 QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES
125 UPDATE_CURSOR | YES | YES | TODO | --- | --- | TODO | --- | TODO
126 IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ???
127 key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES
128 windowed | YES | YES | YES | --- | --- | YES | TODO | YES
129 fullscreen | YES | YES | YES | YES | YES | YES | TODO | ---
130 mouse hide | YES | YES | YES | --- | --- | YES | TODO | TODO
131 mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES
132 screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES
133 swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES
134 high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES
135 clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES
136 MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES
137 drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES
138
139 TODO
140 ====
141 - Linux:
142 - clipboard support
143 - UWP:
144 - clipboard, mouselock
145 - sapp_consume_event() on non-web platforms?
146
147 STEP BY STEP
148 ============
149 --- Add a sokol_main() function to your code which returns a sapp_desc structure
150 with initialization parameters and callback function pointers. This
151 function is called very early, usually at the start of the
152 platform's entry function (e.g. main or WinMain). You should do as
153 little as possible here, since the rest of your code might be called
154 from another thread (this depends on the platform):
155
156 sapp_desc sokol_main(int argc, char* argv[]) {
157 return (sapp_desc) {
158 .width = 640,
159 .height = 480,
160 .init_cb = my_init_func,
161 .frame_cb = my_frame_func,
162 .cleanup_cb = my_cleanup_func,
163 .event_cb = my_event_func,
164 ...
165 };
166 }
167
168 There are many more setup parameters, but these are the most important.
169 For a complete list search for the sapp_desc structure declaration
170 below.
171
172 DO NOT call any sokol-app function from inside sokol_main(), since
173 sokol-app will not be initialized at this point.
174
175 The .width and .height parameters are the preferred size of the 3D
176 rendering canvas. The actual size may differ from this depending on
177 platform and other circumstances. Also the canvas size may change at
178 any time (for instance when the user resizes the application window,
179 or rotates the mobile device).
180
181 All provided function callbacks will be called from the same thread,
182 but this may be different from the thread where sokol_main() was called.
183
184 .init_cb (void (*)(void))
185 This function is called once after the application window,
186 3D rendering context and swap chain have been created. The
187 function takes no arguments and has no return value.
188 .frame_cb (void (*)(void))
189 This is the per-frame callback, which is usually called 60
190 times per second. This is where your application would update
191 most of its state and perform all rendering.
192 .cleanup_cb (void (*)(void))
193 The cleanup callback is called once right before the application
194 quits.
195 .event_cb (void (*)(const sapp_event* event))
196 The event callback is mainly for input handling, but is also
197 used to communicate other types of events to the application. Keep the
198 event_cb struct member zero-initialized if your application doesn't require
199 event handling.
200 .fail_cb (void (*)(const char* msg))
201 The fail callback is called when a fatal error is encountered
202 during start which doesn't allow the program to continue.
203 Providing a callback here gives you a chance to show an error message
204 to the user. The default behaviour is SOKOL_LOG(msg)
205
206 As you can see, those 'standard callbacks' don't have a user_data
207 argument, so any data that needs to be preserved between callbacks
208 must live in global variables. If keeping state in global variables
209 is not an option, there's an alternative set of callbacks with
210 an additional user_data pointer argument:
211
212 .user_data (void*)
213 The user-data argument for the callbacks below
214 .init_userdata_cb (void (*)(void* user_data))
215 .frame_userdata_cb (void (*)(void* user_data))
216 .cleanup_userdata_cb (void (*)(void* user_data))
217 .event_cb (void(*)(const sapp_event* event, void* user_data))
218 .fail_cb (void(*)(const char* msg, void* user_data))
219 These are the user-data versions of the callback functions. You
220 can mix those with the standard callbacks that don't have the
221 user_data argument.
222
223 The function sapp_userdata() can be used to query the user_data
224 pointer provided in the sapp_desc struct.
225
226 You can also call sapp_query_desc() to get a copy of the
227 original sapp_desc structure.
228
229 NOTE that there's also an alternative compile mode where sokol_app.h
230 doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
231
232 --- Implement the initialization callback function (init_cb), this is called
233 once after the rendering surface, 3D API and swap chain have been
234 initialized by sokol_app. All sokol-app functions can be called
235 from inside the initialization callback, the most useful functions
236 at this point are:
237
238 int sapp_width(void)
239 int sapp_height(void)
240 Returns the current width and height of the default framebuffer in pixels,
241 this may change from one frame to the next, and it may be different
242 from the initial size provided in the sapp_desc struct.
243
244 float sapp_widthf(void)
245 float sapp_heightf(void)
246 These are alternatives to sapp_width() and sapp_height() which return
247 the default framebuffer size as float values instead of integer. This
248 may help to prevent casting back and forth between int and float
249 in more strongly typed languages than C and C++.
250
251 int sapp_color_format(void)
252 int sapp_depth_format(void)
253 The color and depth-stencil pixelformats of the default framebuffer,
254 as integer values which are compatible with sokol-gfx's
255 sg_pixel_format enum (so that they can be plugged directly in places
256 where sg_pixel_format is expected). Possible values are:
257
258 23 == SG_PIXELFORMAT_RGBA8
259 27 == SG_PIXELFORMAT_BGRA8
260 41 == SG_PIXELFORMAT_DEPTH
261 42 == SG_PIXELFORMAT_DEPTH_STENCIL
262
263 int sapp_sample_count(void)
264 Return the MSAA sample count of the default framebuffer.
265
266 bool sapp_gles2(void)
267 Returns true if a GLES2 or WebGL context has been created. This
268 is useful when a GLES3/WebGL2 context was requested but is not
269 available so that sokol_app.h had to fallback to GLES2/WebGL.
270
271 const void* sapp_metal_get_device(void)
272 const void* sapp_metal_get_renderpass_descriptor(void)
273 const void* sapp_metal_get_drawable(void)
274 If the Metal backend has been selected, these functions return pointers
275 to various Metal API objects required for rendering, otherwise
276 they return a null pointer. These void pointers are actually
277 Objective-C ids converted with a (ARC) __bridge cast so that
278 the ids can be tunnel through C code. Also note that the returned
279 pointers to the renderpass-descriptor and drawable may change from one
280 frame to the next, only the Metal device object is guaranteed to
281 stay the same.
282
283 const void* sapp_macos_get_window(void)
284 On macOS, get the NSWindow object pointer, otherwise a null pointer.
285 Before being used as Objective-C object, the void* must be converted
286 back with a (ARC) __bridge cast.
287
288 const void* sapp_ios_get_window(void)
289 On iOS, get the UIWindow object pointer, otherwise a null pointer.
290 Before being used as Objective-C object, the void* must be converted
291 back with a (ARC) __bridge cast.
292
293 const void* sapp_win32_get_hwnd(void)
294 On Windows, get the window's HWND, otherwise a null pointer. The
295 HWND has been cast to a void pointer in order to be tunneled
296 through code which doesn't include Windows.h.
297
298 const void* sapp_d3d11_get_device(void)
299 const void* sapp_d3d11_get_device_context(void)
300 const void* sapp_d3d11_get_render_target_view(void)
301 const void* sapp_d3d11_get_depth_stencil_view(void)
302 Similar to the sapp_metal_* functions, the sapp_d3d11_* functions
303 return pointers to D3D11 API objects required for rendering,
304 only if the D3D11 backend has been selected. Otherwise they
305 return a null pointer. Note that the returned pointers to the
306 render-target-view and depth-stencil-view may change from one
307 frame to the next!
308
309 const void* sapp_wgpu_get_device(void)
310 const void* sapp_wgpu_get_render_view(void)
311 const void* sapp_wgpu_get_resolve_view(void)
312 const void* sapp_wgpu_get_depth_stencil_view(void)
313 These are the WebGPU-specific functions to get the WebGPU
314 objects and values required for rendering. If sokol_app.h
315 is not compiled with SOKOL_WGPU, these functions return null.
316
317 const void* sapp_android_get_native_activity(void);
318 On Android, get the native activity ANativeActivity pointer, otherwise
319 a null pointer.
320
321 --- Implement the frame-callback function, this function will be called
322 on the same thread as the init callback, but might be on a different
323 thread than the sokol_main() function. Note that the size of
324 the rendering framebuffer might have changed since the frame callback
325 was called last. Call the functions sapp_width() and sapp_height()
326 each frame to get the current size.
327
328 --- Optionally implement the event-callback to handle input events.
329 sokol-app provides the following type of input events:
330 - a 'virtual key' was pressed down or released
331 - a single text character was entered (provided as UTF-32 code point)
332 - a mouse button was pressed down or released (left, right, middle)
333 - mouse-wheel or 2D scrolling events
334 - the mouse was moved
335 - the mouse has entered or left the application window boundaries
336 - low-level, portable multi-touch events (began, moved, ended, cancelled)
337 - the application window was resized, iconified or restored
338 - the application was suspended or restored (on mobile platforms)
339 - the user or application code has asked to quit the application
340 - a string was pasted to the system clipboard
341 - one or more files have been dropped onto the application window
342
343 To explicitly 'consume' an event and prevent that the event is
344 forwarded for further handling to the operating system, call
345 sapp_consume_event() from inside the event handler (NOTE that
346 this behaviour is currently only implemented for some HTML5
347 events, support for other platforms and event types will
348 be added as needed, please open a github ticket and/or provide
349 a PR if needed).
350
351 NOTE: Do *not* call any 3D API rendering functions in the event
352 callback function, since the 3D API context may not be active when the
353 event callback is called (it may work on some platforms and 3D APIs,
354 but not others, and the exact behaviour may change between
355 sokol-app versions).
356
357 --- Implement the cleanup-callback function, this is called once
358 after the user quits the application (see the section
359 "APPLICATION QUIT" for detailed information on quitting
360 behaviour, and how to intercept a pending quit - for instance to show a
361 "Really Quit?" dialog box). Note that the cleanup-callback isn't
362 guaranteed to be called on the web and mobile platforms.
363
364 MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE)
365 ================================================
366 In normal mouse mode, no mouse movement events are reported when the
367 mouse leaves the windows client area or hits the screen border (whether
368 it's one or the other depends on the platform), and the mouse move events
369 (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in
370 framebuffer pixels in the sapp_event items mouse_x and mouse_y, and
371 relative movement in framebuffer pixels in the sapp_event items mouse_dx
372 and mouse_dy.
373
374 To get continuous mouse movement (also when the mouse leaves the window
375 client area or hits the screen border), activate mouse-lock mode
376 by calling:
377
378 sapp_lock_mouse(true)
379
380 When mouse lock is activated, the mouse pointer is hidden, the
381 reported absolute mouse position (sapp_event.mouse_x/y) appears
382 frozen, and the relative mouse movement in sapp_event.mouse_dx/dy
383 no longer has a direct relation to framebuffer pixels but instead
384 uses "raw mouse input" (what "raw mouse input" exactly means also
385 differs by platform).
386
387 To deactivate mouse lock and return to normal mouse mode, call
388
389 sapp_lock_mouse(false)
390
391 And finally, to check if mouse lock is currently active, call
392
393 if (sapp_mouse_locked()) { ... }
394
395 On native platforms, the sapp_lock_mouse() and sapp_mouse_locked()
396 functions work as expected (mouse lock is activated or deactivated
397 immediately when sapp_lock_mouse() is called, and sapp_mouse_locked()
398 also immediately returns the new state after sapp_lock_mouse()
399 is called.
400
401 On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave
402 differently, as dictated by the limitations of the HTML5 Pointer Lock API:
403
404 - sapp_lock_mouse(true) can be called at any time, but it will
405 only take effect in a 'short-lived input event handler of a specific
406 type', meaning when one of the following events happens:
407 - SAPP_EVENTTYPE_MOUSE_DOWN
408 - SAPP_EVENTTYPE_MOUSE_UP
409 - SAPP_EVENTTYPE_MOUSE_SCROLL
410 - SAPP_EVENTYTPE_KEY_UP
411 - SAPP_EVENTTYPE_KEY_DOWN
412 - The mouse lock/unlock action on the web platform is asynchronous,
413 this means that sapp_mouse_locked() won't immediately return
414 the new status after calling sapp_lock_mouse(), instead the
415 reported status will only change when the pointer lock has actually
416 been activated or deactivated in the browser.
417 - On the web, mouse lock can be deactivated by the user at any time
418 by pressing the Esc key. When this happens, sokol_app.h behaves
419 the same as if sapp_lock_mouse(false) is called.
420
421 For things like camera manipulation it's most straightforward to lock
422 and unlock the mouse right from the sokol_app.h event handler, for
423 instance the following code enters and leaves mouse lock when the
424 left mouse button is pressed and released, and then uses the relative
425 movement information to manipulate a camera (taken from the
426 cgltf-sapp.c sample in the sokol-samples repository
427 at https://github.com/floooh/sokol-samples):
428
429 static void input(const sapp_event* ev) {
430 switch (ev->type) {
431 case SAPP_EVENTTYPE_MOUSE_DOWN:
432 if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
433 sapp_lock_mouse(true);
434 }
435 break;
436
437 case SAPP_EVENTTYPE_MOUSE_UP:
438 if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
439 sapp_lock_mouse(false);
440 }
441 break;
442
443 case SAPP_EVENTTYPE_MOUSE_MOVE:
444 if (sapp_mouse_locked()) {
445 cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f);
446 }
447 break;
448
449 default:
450 break;
451 }
452 }
453
454 CLIPBOARD SUPPORT
455 =================
456 Applications can send and receive UTF-8 encoded text data from and to the
457 system clipboard. By default, clipboard support is disabled and
458 must be enabled at startup via the following sapp_desc struct
459 members:
460
461 sapp_desc.enable_clipboard - set to true to enable clipboard support
462 sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes
463
464 Enabling the clipboard will dynamically allocate a clipboard buffer
465 for UTF-8 encoded text data of the requested size in bytes, the default
466 size is 8 KBytes. Strings that don't fit into the clipboard buffer
467 (including the terminating zero) will be silently clipped, so it's
468 important that you provide a big enough clipboard size for your
469 use case.
470
471 To send data to the clipboard, call sapp_set_clipboard_string() with
472 a pointer to an UTF-8 encoded, null-terminated C-string.
473
474 NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be
475 called from inside a 'short-lived event handler', and there are a few
476 other HTML5-specific caveats to workaround. You'll basically have to
477 tinker until it works in all browsers :/ (maybe the situation will
478 improve when all browsers agree on and implement the new
479 HTML5 navigator.clipboard API).
480
481 To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED
482 event in your event handler function, and then call sapp_get_clipboard_string()
483 to obtain the pasted UTF-8 encoded text.
484
485 NOTE that behaviour of sapp_get_clipboard_string() is slightly different
486 depending on platform:
487
488 - on the HTML5 platform, the internal clipboard buffer will only be updated
489 right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent,
490 and sapp_get_clipboard_string() will simply return the current content
491 of the clipboard buffer
492 - on 'native' platforms, the call to sapp_get_clipboard_string() will
493 update the internal clipboard buffer with the most recent data
494 from the system clipboard
495
496 Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event,
497 and then call sapp_get_clipboard_string() right in the event handler.
498
499 The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app
500 as follows:
501
502 - on macOS: when the Cmd+V key is pressed down
503 - on HTML5: when the browser sends a 'paste' event to the global 'window' object
504 - on all other platforms: when the Ctrl+V key is pressed down
505
506 DRAG AND DROP SUPPORT
507 =====================
508 PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5
509 and on the native desktop platforms (Win32, Linux and macOS) because
510 of security-related restrictions in the HTML5 drag'n'drop API. The
511 WASM/HTML5 specifics are described at the end of this documentation
512 section:
513
514 Like clipboard support, drag'n'drop support must be explicitly enabled
515 at startup in the sapp_desc struct.
516
517 sapp_desc sokol_main() {
518 return (sapp_desc) {
519 .enable_dragndrop = true, // default is false
520 ...
521 };
522 }
523
524 You can also adjust the maximum number of files that are accepted
525 in a drop operation, and the maximum path length in bytes if needed:
526
527 sapp_desc sokol_main() {
528 return (sapp_desc) {
529 .enable_dragndrop = true, // default is false
530 .max_dropped_files = 8, // default is 1
531 .max_dropped_file_path_length = 8192, // in bytes, default is 2048
532 ...
533 };
534 }
535
536 When drag'n'drop is enabled, the event callback will be invoked with an
537 event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on
538 the application window.
539
540 After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the
541 number of dropped files, and their absolute paths by calling separate
542 functions:
543
544 void on_event(const sapp_event* ev) {
545 if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) {
546
547 // the mouse position where the drop happened
548 float x = ev->mouse_x;
549 float y = ev->mouse_y;
550
551 // get the number of files and their paths like this:
552 const int num_dropped_files = sapp_get_num_dropped_files();
553 for (int i = 0; i < num_dropped_files; i++) {
554 const char* path = sapp_get_dropped_file_path(i);
555 ...
556 }
557 }
558 }
559
560 The returned file paths are UTF-8 encoded strings.
561
562 You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path()
563 anywhere, also outside the event handler callback, but be aware that the
564 file path strings will be overwritten with the next drop operation.
565
566 In any case, sapp_get_dropped_file_path() will never return a null pointer,
567 instead an empty string "" will be returned if the drag'n'drop feature
568 hasn't been enabled, the last drop-operation failed, or the file path index
569 is out of range.
570
571 Drag'n'drop caveats:
572
573 - if more files are dropped in a single drop-action
574 than sapp_desc.max_dropped_files, the additional
575 files will be silently ignored
576 - if any of the file paths is longer than
577 sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8
578 encoding) the entire drop operation will be silently ignored (this
579 needs some sort of error feedback in the future)
580 - no mouse positions are reported while the drag is in
581 process, this may change in the future
582
583 Drag'n'drop on HTML5/WASM:
584
585 The HTML5 drag'n'drop API doesn't return file paths, but instead
586 black-box 'file objects' which must be used to load the content
587 of dropped files. This is the reason why sokol_app.h adds two
588 HTML5-specific functions to the drag'n'drop API:
589
590 uint32_t sapp_html5_get_dropped_file_size(int index)
591 Returns the size in bytes of a dropped file.
592
593 void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request)
594 Asynchronously loads the content of a dropped file into a
595 provided memory buffer (which must be big enough to hold
596 the file content)
597
598 To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED
599 event is received:
600
601 sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){
602 .dropped_file_index = 0,
603 .callback = fetch_cb
604 .buffer_ptr = buf,
605 .buffer_size = buf_size,
606 .user_data = ...
607 });
608
609 Make sure that the memory pointed to by 'buf' stays valid until the
610 callback function is called!
611
612 As result of the asynchronous loading operation (no matter if succeeded or
613 failed) the 'fetch_cb' function will be called:
614
615 void fetch_cb(const sapp_html5_fetch_response* response) {
616 // IMPORTANT: check if the loading operation actually succeeded:
617 if (response->succeeded) {
618 // the size of the loaded file:
619 const uint32_t num_bytes = response->fetched_size;
620 // and the pointer to the data (same as 'buf' in the fetch-call):
621 const void* ptr = response->buffer_ptr;
622 }
623 else {
624 // on error check the error code:
625 switch (response->error_code) {
626 case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL:
627 ...
628 break;
629 case SAPP_HTML5_FETCH_ERROR_OTHER:
630 ...
631 break;
632 }
633 }
634 }
635
636 Check the droptest-sapp example for a real-world example which works
637 both on native platforms and the web:
638
639 https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c
640
641 HIGH-DPI RENDERING
642 ==================
643 You can set the sapp_desc.high_dpi flag during initialization to request
644 a full-resolution framebuffer on HighDPI displays. The default behaviour
645 is sapp_desc.high_dpi=false, this means that the application will
646 render to a lower-resolution framebuffer on HighDPI displays and the
647 rendered content will be upscaled by the window system composer.
648
649 In a HighDPI scenario, you still request the same window size during
650 sokol_main(), but the framebuffer sizes returned by sapp_width()
651 and sapp_height() will be scaled up according to the DPI scaling
652 ratio. You can also get a DPI scaling factor with the function
653 sapp_dpi_scale().
654
655 Here's an example on a Mac with Retina display:
656
657 sapp_desc sokol_main() {
658 return (sapp_desc) {
659 .width = 640,
660 .height = 480,
661 .high_dpi = true,
662 ...
663 };
664 }
665
666 The functions sapp_width(), sapp_height() and sapp_dpi_scale() will
667 return the following values:
668
669 sapp_width -> 1280
670 sapp_height -> 960
671 sapp_dpi_scale -> 2.0
672
673 If the high_dpi flag is false, or you're not running on a Retina display,
674 the values would be:
675
676 sapp_width -> 640
677 sapp_height -> 480
678 sapp_dpi_scale -> 1.0
679
680 APPLICATION QUIT
681 ================
682 Without special quit handling, a sokol_app.h application will quit
683 'gracefully' when the user clicks the window close-button unless a
684 platform's application model prevents this (e.g. on web or mobile).
685 'Graceful exit' means that the application-provided cleanup callback will
686 be called before the application quits.
687
688 On native desktop platforms sokol_app.h provides more control over the
689 application-quit-process. It's possible to initiate a 'programmatic quit'
690 from the application code, and a quit initiated by the application user can
691 be intercepted (for instance to show a custom dialog box).
692
693 This 'programmatic quit protocol' is implemented through 3 functions
694 and 1 event:
695
696 - sapp_quit(): This function simply quits the application without
697 giving the user a chance to intervene. Usually this might
698 be called when the user clicks the 'Ok' button in a 'Really Quit?'
699 dialog box
700 - sapp_request_quit(): Calling sapp_request_quit() will send the
701 event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler
702 callback, giving the user code a chance to intervene and cancel the
703 pending quit process (for instance to show a 'Really Quit?' dialog
704 box). If the event handler callback does nothing, the application
705 will be quit as usual. To prevent this, call the function
706 sapp_cancel_quit() from inside the event handler.
707 - sapp_cancel_quit(): Cancels a pending quit request, either initiated
708 by the user clicking the window close button, or programmatically
709 by calling sapp_request_quit(). The only place where calling this
710 function makes sense is from inside the event handler callback when
711 the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received.
712 - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user
713 clicks the window's close button or application code calls the
714 sapp_request_quit() function. The event handler callback code can handle
715 this event by calling sapp_cancel_quit() to cancel the quit.
716 If the event is ignored, the application will quit as usual.
717
718 On the web platform, the quit behaviour differs from native platforms,
719 because of web-specific restrictions:
720
721 A `programmatic quit` initiated by calling sapp_quit() or
722 sapp_request_quit() will work as described above: the cleanup callback is
723 called, platform-specific cleanup is performed (on the web
724 this means that JS event handlers are unregisters), and then
725 the request-animation-loop will be exited. However that's all. The
726 web page itself will continue to exist (e.g. it's not possible to
727 programmatically close the browser tab).
728
729 On the web it's also not possible to run custom code when the user
730 closes a brower tab, so it's not possible to prevent this with a
731 fancy custom dialog box.
732
733 Instead the standard "Leave Site?" dialog box can be activated (or
734 deactivated) with the following function:
735
736 sapp_html5_ask_leave_site(bool ask);
737
738 The initial state of the associated internal flag can be provided
739 at startup via sapp_desc.html5_ask_leave_site.
740
741 This feature should only be used sparingly in critical situations - for
742 instance when the user would loose data - since popping up modal dialog
743 boxes is considered quite rude in the web world. Note that there's no way
744 to customize the content of this dialog box or run any code as a result
745 of the user's decision. Also note that the user must have interacted with
746 the site before the dialog box will appear. These are all security measures
747 to prevent fishing.
748
749 The Dear ImGui HighDPI sample contains example code of how to
750 implement a 'Really Quit?' dialog box with Dear ImGui (native desktop
751 platforms only), and for showing the hardwired "Leave Site?" dialog box
752 when running on the web platform:
753
754 https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html
755
756 FULLSCREEN
757 ==========
758 If the sapp_desc.fullscreen flag is true, sokol-app will try to create
759 a fullscreen window on platforms with a 'proper' window system
760 (mobile devices will always use fullscreen). The implementation details
761 depend on the target platform, in general sokol-app will use a
762 'soft approach' which doesn't interfere too much with the platform's
763 window system (for instance borderless fullscreen window instead of
764 a 'real' fullscreen mode). Such details might change over time
765 as sokol-app is adapted for different needs.
766
767 The most important effect of fullscreen mode to keep in mind is that
768 the requested canvas width and height will be ignored for the initial
769 window size, calling sapp_width() and sapp_height() will instead return
770 the resolution of the fullscreen canvas (however the provided size
771 might still be used for the non-fullscreen window, in case the user can
772 switch back from fullscreen- to windowed-mode).
773
774 To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen().
775
776 To check if the application window is currently in fullscreen mode,
777 call sapp_is_fullscreen().
778
779 ONSCREEN KEYBOARD
780 =================
781 On some platforms which don't provide a physical keyboard, sokol-app
782 can display the platform's integrated onscreen keyboard for text
783 input. To request that the onscreen keyboard is shown, call
784
785 sapp_show_keyboard(true);
786
787 Likewise, to hide the keyboard call:
788
789 sapp_show_keyboard(false);
790
791 Note that on the web platform, the keyboard can only be shown from
792 inside an input handler. On such platforms, sapp_show_keyboard()
793 will only work as expected when it is called from inside the
794 sokol-app event callback function. When called from other places,
795 an internal flag will be set, and the onscreen keyboard will be
796 called at the next 'legal' opportunity (when the next input event
797 is handled).
798
799 OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
800 ======================================================
801 In its default configuration, sokol_app.h "hijacks" the platform's
802 standard main() function. This was done because different platforms
803 have different main functions which are not compatible with
804 C's main() (for instance WinMain on Windows has completely different
805 arguments). However, this "main hijacking" posed a problem for
806 usage scenarios like integrating sokol_app.h with other languages than
807 C or C++, so an alternative SOKOL_NO_ENTRY mode has been added
808 in which the user code provides the platform's main function:
809
810 - define SOKOL_NO_ENTRY before including the sokol_app.h implementation
811 - do *not* provide a sokol_main() function
812 - instead provide the standard main() function of the platform
813 - from the main function, call the function ```sapp_run()``` which
814 takes a pointer to an ```sapp_desc``` structure.
815 - ```sapp_run()``` takes over control and calls the provided init-, frame-,
816 shutdown- and event-callbacks just like in the default model, it
817 will only return when the application quits (or not at all on some
818 platforms, like emscripten)
819
820 NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
821
822 WINDOWS CONSOLE OUTPUT
823 ======================
824 On Windows, regular windowed applications don't show any stdout/stderr text
825 output, which can be a bit of a hassle for printf() debugging or generally
826 logging text to the console. Also, console output by default uses a local
827 codepage setting and thus international UTF-8 encoded text is printed
828 as garbage.
829
830 To help with these issues, sokol_app.h can be configured at startup
831 via the following Windows-specific sapp_desc flags:
832
833 sapp_desc.win32_console_utf8 (default: false)
834 When set to true, the output console codepage will be switched
835 to UTF-8 (and restored to the original codepage on exit)
836
837 sapp_desc.win32_console_attach (default: false)
838 When set to true, stdout and stderr will be attached to the
839 console of the parent process (if the parent process actually
840 has a console). This means that if the application was started
841 in a command line window, stdout and stderr output will be printed
842 to the terminal, just like a regular command line program. But if
843 the application is started via double-click, it will behave like
844 a regular UI application, and stdout/stderr will not be visible.
845
846 sapp_desc.win32_console_create (default: false)
847 When set to true, a new console window will be created and
848 stdout/stderr will be redirected to that console window. It
849 doesn't matter if the application is started from the command
850 line or via double-click.
851
852 TEMP NOTE DUMP
853 ==============
854 - onscreen keyboard support on Android requires Java :(, should we even bother?
855 - sapp_desc needs a bool whether to initialize depth-stencil surface
856 - GL context initialization needs more control (at least what GL version to initialize)
857 - application icon
858 - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX
859 (Win32 sends the event each frame when the mouse moves and is inside the window
860 client area, OSX sends it only once when the mouse enters the client area)
861 - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy
862 at the latest but should do it earlier, in onStop, as an app is "killable" after onStop
863 on Android Honeycomb and later (it can't be done at the moment as the app may be started
864 again after onStop and the sokol lifecycle does not yet handle context teardown/bringup)
865
866
867 LICENSE
868 =======
869 zlib/libpng license
870
871 Copyright (c) 2018 Andre Weissflog
872
873 This software is provided 'as-is', without any express or implied warranty.
874 In no event will the authors be held liable for any damages arising from the
875 use of this software.
876
877 Permission is granted to anyone to use this software for any purpose,
878 including commercial applications, and to alter it and redistribute it
879 freely, subject to the following restrictions:
880
881 1. The origin of this software must not be misrepresented; you must not
882 claim that you wrote the original software. If you use this software in a
883 product, an acknowledgment in the product documentation would be
884 appreciated but is not required.
885
886 2. Altered source versions must be plainly marked as such, and must not
887 be misrepresented as being the original software.
888
889 3. This notice may not be removed or altered from any source
890 distribution.
891*/
892#define SOKOL_APP_INCLUDED (1)
893#include <stdint.h>
894#include <stdbool.h>
895
896#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL)
897#define SOKOL_APP_API_DECL SOKOL_API_DECL
898#endif
899#ifndef SOKOL_APP_API_DECL
900#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL)
901#define SOKOL_APP_API_DECL __declspec(dllexport)
902#elif defined(_WIN32) && defined(SOKOL_DLL)
903#define SOKOL_APP_API_DECL __declspec(dllimport)
904#else
905#define SOKOL_APP_API_DECL extern
906#endif
907#endif
908
909#ifdef __cplusplus
910extern "C" {
911#endif
912
913enum {
914 SAPP_MAX_TOUCHPOINTS = 8,
915 SAPP_MAX_MOUSEBUTTONS = 3,
916 SAPP_MAX_KEYCODES = 512,
917};
918
919typedef enum sapp_event_type {
920 SAPP_EVENTTYPE_INVALID,
921 SAPP_EVENTTYPE_KEY_DOWN,
922 SAPP_EVENTTYPE_KEY_UP,
923 SAPP_EVENTTYPE_CHAR,
924 SAPP_EVENTTYPE_MOUSE_DOWN,
925 SAPP_EVENTTYPE_MOUSE_UP,
926 SAPP_EVENTTYPE_MOUSE_SCROLL,
927 SAPP_EVENTTYPE_MOUSE_MOVE,
928 SAPP_EVENTTYPE_MOUSE_ENTER,
929 SAPP_EVENTTYPE_MOUSE_LEAVE,
930 SAPP_EVENTTYPE_TOUCHES_BEGAN,
931 SAPP_EVENTTYPE_TOUCHES_MOVED,
932 SAPP_EVENTTYPE_TOUCHES_ENDED,
933 SAPP_EVENTTYPE_TOUCHES_CANCELLED,
934 SAPP_EVENTTYPE_RESIZED,
935 SAPP_EVENTTYPE_ICONIFIED,
936 SAPP_EVENTTYPE_RESTORED,
937 SAPP_EVENTTYPE_SUSPENDED,
938 SAPP_EVENTTYPE_RESUMED,
939 SAPP_EVENTTYPE_UPDATE_CURSOR,
940 SAPP_EVENTTYPE_QUIT_REQUESTED,
941 SAPP_EVENTTYPE_CLIPBOARD_PASTED,
942 SAPP_EVENTTYPE_FILES_DROPPED,
943 _SAPP_EVENTTYPE_NUM,
944 _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF
945} sapp_event_type;
946
947/* key codes are the same names and values as GLFW */
948typedef enum sapp_keycode {
949 SAPP_KEYCODE_INVALID = 0,
950 SAPP_KEYCODE_SPACE = 32,
951 SAPP_KEYCODE_APOSTROPHE = 39, /* ' */
952 SAPP_KEYCODE_COMMA = 44, /* , */
953 SAPP_KEYCODE_MINUS = 45, /* - */
954 SAPP_KEYCODE_PERIOD = 46, /* . */
955 SAPP_KEYCODE_SLASH = 47, /* / */
956 SAPP_KEYCODE_0 = 48,
957 SAPP_KEYCODE_1 = 49,
958 SAPP_KEYCODE_2 = 50,
959 SAPP_KEYCODE_3 = 51,
960 SAPP_KEYCODE_4 = 52,
961 SAPP_KEYCODE_5 = 53,
962 SAPP_KEYCODE_6 = 54,
963 SAPP_KEYCODE_7 = 55,
964 SAPP_KEYCODE_8 = 56,
965 SAPP_KEYCODE_9 = 57,
966 SAPP_KEYCODE_SEMICOLON = 59, /* ; */
967 SAPP_KEYCODE_EQUAL = 61, /* = */
968 SAPP_KEYCODE_A = 65,
969 SAPP_KEYCODE_B = 66,
970 SAPP_KEYCODE_C = 67,
971 SAPP_KEYCODE_D = 68,
972 SAPP_KEYCODE_E = 69,
973 SAPP_KEYCODE_F = 70,
974 SAPP_KEYCODE_G = 71,
975 SAPP_KEYCODE_H = 72,
976 SAPP_KEYCODE_I = 73,
977 SAPP_KEYCODE_J = 74,
978 SAPP_KEYCODE_K = 75,
979 SAPP_KEYCODE_L = 76,
980 SAPP_KEYCODE_M = 77,
981 SAPP_KEYCODE_N = 78,
982 SAPP_KEYCODE_O = 79,
983 SAPP_KEYCODE_P = 80,
984 SAPP_KEYCODE_Q = 81,
985 SAPP_KEYCODE_R = 82,
986 SAPP_KEYCODE_S = 83,
987 SAPP_KEYCODE_T = 84,
988 SAPP_KEYCODE_U = 85,
989 SAPP_KEYCODE_V = 86,
990 SAPP_KEYCODE_W = 87,
991 SAPP_KEYCODE_X = 88,
992 SAPP_KEYCODE_Y = 89,
993 SAPP_KEYCODE_Z = 90,
994 SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */
995 SAPP_KEYCODE_BACKSLASH = 92, /* \ */
996 SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */
997 SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */
998 SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */
999 SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */
1000 SAPP_KEYCODE_ESCAPE = 256,
1001 SAPP_KEYCODE_ENTER = 257,
1002 SAPP_KEYCODE_TAB = 258,
1003 SAPP_KEYCODE_BACKSPACE = 259,
1004 SAPP_KEYCODE_INSERT = 260,
1005 SAPP_KEYCODE_DELETE = 261,
1006 SAPP_KEYCODE_RIGHT = 262,
1007 SAPP_KEYCODE_LEFT = 263,
1008 SAPP_KEYCODE_DOWN = 264,
1009 SAPP_KEYCODE_UP = 265,
1010 SAPP_KEYCODE_PAGE_UP = 266,
1011 SAPP_KEYCODE_PAGE_DOWN = 267,
1012 SAPP_KEYCODE_HOME = 268,
1013 SAPP_KEYCODE_END = 269,
1014 SAPP_KEYCODE_CAPS_LOCK = 280,
1015 SAPP_KEYCODE_SCROLL_LOCK = 281,
1016 SAPP_KEYCODE_NUM_LOCK = 282,
1017 SAPP_KEYCODE_PRINT_SCREEN = 283,
1018 SAPP_KEYCODE_PAUSE = 284,
1019 SAPP_KEYCODE_F1 = 290,
1020 SAPP_KEYCODE_F2 = 291,
1021 SAPP_KEYCODE_F3 = 292,
1022 SAPP_KEYCODE_F4 = 293,
1023 SAPP_KEYCODE_F5 = 294,
1024 SAPP_KEYCODE_F6 = 295,
1025 SAPP_KEYCODE_F7 = 296,
1026 SAPP_KEYCODE_F8 = 297,
1027 SAPP_KEYCODE_F9 = 298,
1028 SAPP_KEYCODE_F10 = 299,
1029 SAPP_KEYCODE_F11 = 300,
1030 SAPP_KEYCODE_F12 = 301,
1031 SAPP_KEYCODE_F13 = 302,
1032 SAPP_KEYCODE_F14 = 303,
1033 SAPP_KEYCODE_F15 = 304,
1034 SAPP_KEYCODE_F16 = 305,
1035 SAPP_KEYCODE_F17 = 306,
1036 SAPP_KEYCODE_F18 = 307,
1037 SAPP_KEYCODE_F19 = 308,
1038 SAPP_KEYCODE_F20 = 309,
1039 SAPP_KEYCODE_F21 = 310,
1040 SAPP_KEYCODE_F22 = 311,
1041 SAPP_KEYCODE_F23 = 312,
1042 SAPP_KEYCODE_F24 = 313,
1043 SAPP_KEYCODE_F25 = 314,
1044 SAPP_KEYCODE_KP_0 = 320,
1045 SAPP_KEYCODE_KP_1 = 321,
1046 SAPP_KEYCODE_KP_2 = 322,
1047 SAPP_KEYCODE_KP_3 = 323,
1048 SAPP_KEYCODE_KP_4 = 324,
1049 SAPP_KEYCODE_KP_5 = 325,
1050 SAPP_KEYCODE_KP_6 = 326,
1051 SAPP_KEYCODE_KP_7 = 327,
1052 SAPP_KEYCODE_KP_8 = 328,
1053 SAPP_KEYCODE_KP_9 = 329,
1054 SAPP_KEYCODE_KP_DECIMAL = 330,
1055 SAPP_KEYCODE_KP_DIVIDE = 331,
1056 SAPP_KEYCODE_KP_MULTIPLY = 332,
1057 SAPP_KEYCODE_KP_SUBTRACT = 333,
1058 SAPP_KEYCODE_KP_ADD = 334,
1059 SAPP_KEYCODE_KP_ENTER = 335,
1060 SAPP_KEYCODE_KP_EQUAL = 336,
1061 SAPP_KEYCODE_LEFT_SHIFT = 340,
1062 SAPP_KEYCODE_LEFT_CONTROL = 341,
1063 SAPP_KEYCODE_LEFT_ALT = 342,
1064 SAPP_KEYCODE_LEFT_SUPER = 343,
1065 SAPP_KEYCODE_RIGHT_SHIFT = 344,
1066 SAPP_KEYCODE_RIGHT_CONTROL = 345,
1067 SAPP_KEYCODE_RIGHT_ALT = 346,
1068 SAPP_KEYCODE_RIGHT_SUPER = 347,
1069 SAPP_KEYCODE_MENU = 348,
1070} sapp_keycode;
1071
1072typedef struct sapp_touchpoint {
1073 uintptr_t identifier;
1074 float pos_x;
1075 float pos_y;
1076 bool changed;
1077} sapp_touchpoint;
1078
1079typedef enum sapp_mousebutton {
1080 SAPP_MOUSEBUTTON_LEFT = 0x0,
1081 SAPP_MOUSEBUTTON_RIGHT = 0x1,
1082 SAPP_MOUSEBUTTON_MIDDLE = 0x2,
1083 SAPP_MOUSEBUTTON_INVALID = 0x100,
1084} sapp_mousebutton;
1085
1086enum {
1087 SAPP_MODIFIER_SHIFT = 0x1,
1088 SAPP_MODIFIER_CTRL = 0x2,
1089 SAPP_MODIFIER_ALT = 0x4,
1090 SAPP_MODIFIER_SUPER = 0x8
1091};
1092
1093typedef struct sapp_event {
1094 uint64_t frame_count;
1095 sapp_event_type type;
1096 sapp_keycode key_code;
1097 uint32_t char_code;
1098 bool key_repeat;
1099 uint32_t modifiers;
1100 sapp_mousebutton mouse_button;
1101 float mouse_x;
1102 float mouse_y;
1103 float mouse_dx;
1104 float mouse_dy;
1105 float scroll_x;
1106 float scroll_y;
1107 int num_touches;
1108 sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS];
1109 int window_width;
1110 int window_height;
1111 int framebuffer_width;
1112 int framebuffer_height;
1113} sapp_event;
1114
1115typedef struct sapp_desc {
1116 void (*init_cb)(void); /* these are the user-provided callbacks without user data */
1117 void (*frame_cb)(void);
1118 void (*cleanup_cb)(void);
1119 void (*event_cb)(const sapp_event*);
1120 void (*fail_cb)(const char*);
1121
1122 void* user_data; /* these are the user-provided callbacks with user data */
1123 void (*init_userdata_cb)(void*);
1124 void (*frame_userdata_cb)(void*);
1125 void (*cleanup_userdata_cb)(void*);
1126 void (*event_userdata_cb)(const sapp_event*, void*);
1127 void (*fail_userdata_cb)(const char*, void*);
1128
1129 int width; /* the preferred width of the window / canvas */
1130 int height; /* the preferred height of the window / canvas */
1131 int sample_count; /* MSAA sample count */
1132 int swap_interval; /* the preferred swap interval (ignored on some platforms) */
1133 bool high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */
1134 bool fullscreen; /* whether the window should be created in fullscreen mode */
1135 bool alpha; /* whether the framebuffer should have an alpha channel (ignored on some platforms) */
1136 const char* window_title; /* the window title as UTF-8 encoded string */
1137 bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */
1138 bool enable_clipboard; /* enable clipboard access, default is false */
1139 int clipboard_size; /* max size of clipboard content in bytes */
1140 bool enable_dragndrop; /* enable file dropping (drag'n'drop), default is false */
1141 int max_dropped_files; /* max number of dropped files to process (default: 1) */
1142 int max_dropped_file_path_length; /* max length in bytes of a dropped UTF-8 file path (default: 2048) */
1143
1144 /* backend-specific options */
1145 bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */
1146 bool win32_console_utf8; /* if true, set the output console codepage to UTF-8 */
1147 bool win32_console_create; /* if true, attach stdout/stderr to a new console window */
1148 bool win32_console_attach; /* if true, attach stdout/stderr to parent process */
1149 const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */
1150 bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */
1151 bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */
1152 bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */
1153 bool html5_ask_leave_site; /* initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) */
1154 bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */
1155
1156 /* V patches */
1157 bool __v_native_render; /* V patch to allow for native rendering */
1158} sapp_desc;
1159
1160/* HTML5 specific: request and response structs for
1161 asynchronously loading dropped-file content.
1162*/
1163typedef enum sapp_html5_fetch_error {
1164 SAPP_HTML5_FETCH_ERROR_NO_ERROR,
1165 SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL,
1166 SAPP_HTML5_FETCH_ERROR_OTHER,
1167} sapp_html5_fetch_error;
1168
1169typedef struct sapp_html5_fetch_response {
1170 bool succeeded; /* true if the loading operation has succeeded */
1171 sapp_html5_fetch_error error_code;
1172 int file_index; /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */
1173 uint32_t fetched_size; /* size in bytes of loaded data */
1174 void* buffer_ptr; /* pointer to user-provided buffer which contains the loaded data */
1175 uint32_t buffer_size; /* size of user-provided buffer (buffer_size >= fetched_size) */
1176 void* user_data; /* user-provided user data pointer */
1177} sapp_html5_fetch_response;
1178
1179typedef struct sapp_html5_fetch_request {
1180 int dropped_file_index; /* 0..sapp_get_num_dropped_files()-1 */
1181 void (*callback)(const sapp_html5_fetch_response*); /* response callback function pointer (required) */
1182 void* buffer_ptr; /* pointer to buffer to load data into */
1183 uint32_t buffer_size; /* size in bytes of buffer */
1184 void* user_data; /* optional userdata pointer */
1185} sapp_html5_fetch_request;
1186
1187/* user-provided functions */
1188extern sapp_desc sokol_main(int argc, char* argv[]);
1189
1190/* returns true after sokol-app has been initialized */
1191SOKOL_APP_API_DECL bool sapp_isvalid(void);
1192/* returns the current framebuffer width in pixels */
1193SOKOL_APP_API_DECL int sapp_width(void);
1194/* same as sapp_width(), but returns float */
1195SOKOL_APP_API_DECL float sapp_widthf(void);
1196/* returns the current framebuffer height in pixels */
1197SOKOL_APP_API_DECL int sapp_height(void);
1198/* same as sapp_height(), but returns float */
1199SOKOL_APP_API_DECL float sapp_heightf(void);
1200/* get default framebuffer color pixel format */
1201SOKOL_APP_API_DECL int sapp_color_format(void);
1202/* get default framebuffer depth pixel format */
1203SOKOL_APP_API_DECL int sapp_depth_format(void);
1204/* get default framebuffer sample count */
1205SOKOL_APP_API_DECL int sapp_sample_count(void);
1206/* returns true when high_dpi was requested and actually running in a high-dpi scenario */
1207SOKOL_APP_API_DECL bool sapp_high_dpi(void);
1208/* returns the dpi scaling factor (window pixels to framebuffer pixels) */
1209SOKOL_APP_API_DECL float sapp_dpi_scale(void);
1210/* show or hide the mobile device onscreen keyboard */
1211SOKOL_APP_API_DECL void sapp_show_keyboard(bool show);
1212/* return true if the mobile device onscreen keyboard is currently shown */
1213SOKOL_APP_API_DECL bool sapp_keyboard_shown(void);
1214/* query fullscreen mode */
1215SOKOL_APP_API_DECL bool sapp_is_fullscreen(void);
1216/* toggle fullscreen mode */
1217SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void);
1218/* show or hide the mouse cursor */
1219SOKOL_APP_API_DECL void sapp_show_mouse(bool show);
1220/* show or hide the mouse cursor */
1221SOKOL_APP_API_DECL bool sapp_mouse_shown();
1222/* enable/disable mouse-pointer-lock mode */
1223SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock);
1224/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */
1225SOKOL_APP_API_DECL bool sapp_mouse_locked(void);
1226/* return the userdata pointer optionally provided in sapp_desc */
1227SOKOL_APP_API_DECL void* sapp_userdata(void);
1228/* return a copy of the sapp_desc structure */
1229SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void);
1230/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */
1231SOKOL_APP_API_DECL void sapp_request_quit(void);
1232/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */
1233SOKOL_APP_API_DECL void sapp_cancel_quit(void);
1234/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */
1235SOKOL_APP_API_DECL void sapp_quit(void);
1236/* call from inside event callback to consume the current event (don't forward to platform) */
1237SOKOL_APP_API_DECL void sapp_consume_event(void);
1238/* get the current frame counter (for comparison with sapp_event.frame_count) */
1239SOKOL_APP_API_DECL uint64_t sapp_frame_count(void);
1240/* write string into clipboard */
1241SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str);
1242/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */
1243SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void);
1244/* set the window title (only on desktop platforms) */
1245SOKOL_APP_API_DECL void sapp_set_window_title(const char* str);
1246/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */
1247SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void);
1248/* gets the dropped file paths */
1249SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index);
1250
1251/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */
1252SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc);
1253
1254/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */
1255SOKOL_APP_API_DECL bool sapp_gles2(void);
1256
1257/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */
1258SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask);
1259/* HTML5: get byte size of a dropped file */
1260SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index);
1261/* HTML5: asynchronously load the content of a dropped file */
1262SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request);
1263
1264/* Metal: get bridged pointer to Metal device object */
1265SOKOL_APP_API_DECL const void* sapp_metal_get_device(void);
1266/* Metal: get bridged pointer to this frame's renderpass descriptor */
1267SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void);
1268/* Metal: get bridged pointer to current drawable */
1269SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void);
1270/* macOS: get bridged pointer to macOS NSWindow */
1271SOKOL_APP_API_DECL const void* sapp_macos_get_window(void);
1272/* iOS: get bridged pointer to iOS UIWindow */
1273SOKOL_APP_API_DECL const void* sapp_ios_get_window(void);
1274
1275/* D3D11: get pointer to ID3D11Device object */
1276SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void);
1277/* D3D11: get pointer to ID3D11DeviceContext object */
1278SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void);
1279/* D3D11: get pointer to ID3D11RenderTargetView object */
1280SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void);
1281/* D3D11: get pointer to ID3D11DepthStencilView */
1282SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void);
1283/* Win32: get the HWND window handle */
1284SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void);
1285
1286/* WebGPU: get WGPUDevice handle */
1287SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void);
1288/* WebGPU: get swapchain's WGPUTextureView handle for rendering */
1289SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void);
1290/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */
1291SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void);
1292/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */
1293SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void);
1294
1295/* Android: get native activity handle */
1296SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void);
1297
1298#ifdef __cplusplus
1299} /* extern "C" */
1300
1301/* reference-based equivalents for C++ */
1302inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
1303
1304#endif
1305
1306// this WinRT specific hack is required when wWinMain is in a static library
1307#if defined(_MSC_VER) && defined(UNICODE)
1308#include <winapifamily.h>
1309#if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
1310#pragma comment(linker, "/include:wWinMain")
1311#endif
1312#endif
1313
1314#endif // SOKOL_APP_INCLUDED
1315
1316/*-- IMPLEMENTATION ----------------------------------------------------------*/
1317#ifdef SOKOL_APP_IMPL
1318#define SOKOL_APP_IMPL_INCLUDED (1)
1319
1320#include <string.h> // memset
1321#include <stddef.h> // size_t
1322
1323/* check if the config defines are alright */
1324#if defined(__APPLE__)
1325 // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
1326 #if !defined(__cplusplus)
1327 #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
1328 #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
1329 #endif
1330 #endif
1331 #define _SAPP_APPLE (1)
1332 #include <TargetConditionals.h>
1333 #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
1334 /* MacOS */
1335 #define _SAPP_MACOS (1)
1336 #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33)
1337 #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33")
1338 #endif
1339 #else
1340 /* iOS or iOS Simulator */
1341 #define _SAPP_IOS (1)
1342 #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3)
1343 #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3")
1344 #endif
1345 #endif
1346#elif defined(__EMSCRIPTEN__)
1347 /* emscripten (asm.js or wasm) */
1348 #define _SAPP_EMSCRIPTEN (1)
1349 #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU)
1350 #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU")
1351 #endif
1352#elif defined(_WIN32)
1353 /* Windows (D3D11 or GL) */
1354 #include <winapifamily.h>
1355 #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
1356 #define _SAPP_UWP (1)
1357 #if !defined(SOKOL_D3D11)
1358 #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11")
1359 #endif
1360 #if !defined(__cplusplus)
1361 #error("sokol_app.h: UWP bindings require C++/17")
1362 #endif
1363 #else
1364 #define _SAPP_WIN32 (1)
1365 #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33)
1366 #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33")
1367 #endif
1368 #endif
1369#elif defined(__ANDROID__)
1370 /* Android */
1371 #define _SAPP_ANDROID (1)
1372 #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2)
1373 #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2")
1374 #endif
1375 #if defined(SOKOL_NO_ENTRY)
1376 #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android")
1377 #endif
1378#elif defined(__linux__) || defined(__unix__)
1379 /* Linux */
1380 #define _SAPP_LINUX (1)
1381 #if !defined(SOKOL_GLCORE33)
1382 #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33")
1383 #endif
1384#else
1385#error "sokol_app.h: Unknown platform"
1386#endif
1387
1388#ifndef SOKOL_API_IMPL
1389 #define SOKOL_API_IMPL
1390#endif
1391#ifndef SOKOL_DEBUG
1392 #ifndef NDEBUG
1393 #define SOKOL_DEBUG (1)
1394 #endif
1395#endif
1396#ifndef SOKOL_ASSERT
1397 #include <assert.h>
1398 #define SOKOL_ASSERT(c) assert(c)
1399#endif
1400#ifndef SOKOL_UNREACHABLE
1401 #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
1402#endif
1403#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE)
1404 #include <stdlib.h>
1405#endif
1406#if !defined(SOKOL_CALLOC)
1407 #define SOKOL_CALLOC(n,s) calloc(n,s)
1408#endif
1409#if !defined(SOKOL_FREE)
1410 #define SOKOL_FREE(p) free(p)
1411#endif
1412#ifndef SOKOL_LOG
1413 #ifdef SOKOL_DEBUG
1414 #if defined(__ANDROID__)
1415 #include <android/log.h>
1416 #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); }
1417 #else
1418 #include <stdio.h>
1419 #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
1420 #endif
1421 #else
1422 #define SOKOL_LOG(s)
1423 #endif
1424#endif
1425#ifndef SOKOL_ABORT
1426 #include <stdlib.h>
1427 #define SOKOL_ABORT() abort()
1428#endif
1429#ifndef _SOKOL_PRIVATE
1430 #if defined(__GNUC__) || defined(__clang__)
1431 #define _SOKOL_PRIVATE __attribute__((unused)) static
1432 #else
1433 #define _SOKOL_PRIVATE static
1434 #endif
1435#endif
1436#ifndef _SOKOL_UNUSED
1437 #define _SOKOL_UNUSED(x) (void)(x)
1438#endif
1439
1440/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/
1441#if defined(_SAPP_APPLE)
1442 #if defined(SOKOL_METAL)
1443 #import <Metal/Metal.h>
1444 #import <MetalKit/MetalKit.h>
1445 #endif
1446 #if defined(_SAPP_MACOS)
1447 #if !defined(SOKOL_METAL)
1448 #ifndef GL_SILENCE_DEPRECATION
1449 #define GL_SILENCE_DEPRECATION
1450 #endif
1451 #include <Cocoa/Cocoa.h>
1452 #endif
1453 #elif defined(_SAPP_IOS)
1454 #import <UIKit/UIKit.h>
1455 #if !defined(SOKOL_METAL)
1456 #import <GLKit/GLKit.h>
1457 #endif
1458 #endif
1459#elif defined(_SAPP_EMSCRIPTEN)
1460 #if defined(SOKOL_WGPU)
1461 #include <webgpu/webgpu.h>
1462 #endif
1463 #include <emscripten/emscripten.h>
1464 #include <emscripten/html5.h>
1465#elif defined(_SAPP_WIN32)
1466 #ifdef _MSC_VER
1467 #pragma warning(push)
1468 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1469 #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
1470 #pragma warning(disable:4054) /* 'type cast': from function pointer */
1471 #pragma warning(disable:4055) /* 'type cast': from data pointer */
1472 #pragma warning(disable:4505) /* unreferenced local function has been removed */
1473 #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
1474 #endif
1475 #ifndef WIN32_LEAN_AND_MEAN
1476 #define WIN32_LEAN_AND_MEAN
1477 #endif
1478 #ifndef NOMINMAX
1479 #define NOMINMAX
1480 #endif
1481 #include <windows.h>
1482 #include <windowsx.h>
1483 #include <shellapi.h>
1484 #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem
1485 #if defined(SOKOL_WIN32_FORCE_MAIN)
1486 #pragma comment (linker, "/subsystem:console")
1487 #else
1488 #pragma comment (linker, "/subsystem:windows")
1489 #endif
1490 #endif
1491 #include <stdio.h> /* freopen_s() */
1492 #include <wchar.h> /* wcslen() */
1493
1494 #pragma comment (lib, "kernel32")
1495 #pragma comment (lib, "user32")
1496 #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */
1497 #if defined(SOKOL_D3D11)
1498 #pragma comment (lib, "dxgi")
1499 #pragma comment (lib, "d3d11")
1500 #pragma comment (lib, "dxguid")
1501 #endif
1502 #if defined(SOKOL_GLCORE33)
1503 #pragma comment (lib, "gdi32")
1504 #endif
1505
1506 #if defined(SOKOL_D3D11)
1507 #ifndef D3D11_NO_HELPERS
1508 #define D3D11_NO_HELPERS
1509 #endif
1510 #include <d3d11.h>
1511 #include <dxgi.h>
1512 // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it
1513 #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4)
1514 #endif
1515 #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */
1516 #define WM_MOUSEHWHEEL (0x020E)
1517 #endif
1518#elif defined(_SAPP_UWP)
1519 #ifndef NOMINMAX
1520 #define NOMINMAX
1521 #endif
1522 #ifdef _MSC_VER
1523 #pragma warning(push)
1524 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1525 #pragma warning(disable:4054) /* 'type cast': from function pointer */
1526 #pragma warning(disable:4055) /* 'type cast': from data pointer */
1527 #pragma warning(disable:4505) /* unreferenced local function has been removed */
1528 #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
1529 #endif
1530 #include <windows.h>
1531 #include <winrt/Windows.ApplicationModel.Core.h>
1532 #include <winrt/Windows.Foundation.h>
1533 #include <winrt/Windows.Foundation.Collections.h>
1534 #include <winrt/Windows.Graphics.Display.h>
1535 #include <winrt/Windows.UI.Core.h>
1536 #include <winrt/Windows.UI.Composition.h>
1537 #include <winrt/Windows.UI.Input.h>
1538 #include <winrt/Windows.UI.ViewManagement.h>
1539 #include <winrt/Windows.System.h>
1540 #include <ppltasks.h>
1541
1542 #include <dxgi1_4.h>
1543 #include <d3d11_3.h>
1544 #include <DirectXMath.h>
1545
1546 #pragma comment (lib, "WindowsApp")
1547 #pragma comment (lib, "dxguid")
1548#elif defined(_SAPP_ANDROID)
1549 #include <pthread.h>
1550 #include <unistd.h>
1551 #include <android/native_activity.h>
1552 #include <android/looper.h>
1553 #include <EGL/egl.h>
1554#elif defined(_SAPP_LINUX)
1555 #define GL_GLEXT_PROTOTYPES
1556 #include <X11/Xlib.h>
1557 #include <X11/Xutil.h>
1558 #include <X11/XKBlib.h>
1559 #include <X11/keysym.h>
1560 #include <X11/Xresource.h>
1561 #include <X11/Xatom.h>
1562 #include <X11/extensions/XInput2.h>
1563 #include <X11/Xcursor/Xcursor.h>
1564 #include <X11/Xmd.h> /* CARD32 */
1565 #include <dlfcn.h> /* dlopen, dlsym, dlclose */
1566 #include <limits.h> /* LONG_MAX */
1567 #include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
1568#endif
1569
1570/*== MACOS DECLARATIONS ======================================================*/
1571#if defined(_SAPP_MACOS)
1572// __v_
1573@interface SokolWindow : NSWindow {
1574}
1575@end
1576@interface MyView2 : NSView
1577@end
1578
1579MyView2* g_view;
1580
1581// A custom NSWindow interface to handle events in borderless windows.
1582@implementation SokolWindow
1583- (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless
1584- (BOOL)canBecomeMainWindow { return YES; }
1585@end
1586// __v_
1587
1588@interface _sapp_macos_app_delegate : NSObject<NSApplicationDelegate>
1589@end
1590@interface _sapp_macos_window : NSWindow
1591@end
1592@interface _sapp_macos_window_delegate : NSObject<NSWindowDelegate>
1593@end
1594#if defined(SOKOL_METAL)
1595 @interface _sapp_macos_view : MTKView
1596 @end
1597#elif defined(SOKOL_GLCORE33)
1598 @interface _sapp_macos_view : NSOpenGLView
1599 - (void)timerFired:(id)sender;
1600 @end
1601#endif // SOKOL_GLCORE33
1602
1603typedef struct {
1604 uint32_t flags_changed_store;
1605 uint8_t mouse_buttons;
1606// NSWindow* window;
1607// SokolWindow* window; // __v_
1608 _sapp_macos_window* window; // __v_
1609 NSTrackingArea* tracking_area;
1610 _sapp_macos_app_delegate* app_dlg;
1611 _sapp_macos_window_delegate* win_dlg;
1612 _sapp_macos_view* view;
1613 #if defined(SOKOL_METAL)
1614 id<MTLDevice> mtl_device;
1615 #endif
1616} _sapp_macos_t;
1617
1618#endif // _SAPP_MACOS
1619
1620/*== IOS DECLARATIONS ========================================================*/
1621#if defined(_SAPP_IOS)
1622
1623@interface _sapp_app_delegate : NSObject<UIApplicationDelegate>
1624@end
1625@interface _sapp_textfield_dlg : NSObject<UITextFieldDelegate>
1626- (void)keyboardWasShown:(NSNotification*)notif;
1627- (void)keyboardWillBeHidden:(NSNotification*)notif;
1628- (void)keyboardDidChangeFrame:(NSNotification*)notif;
1629@end
1630#if defined(SOKOL_METAL)
1631 @interface _sapp_ios_view : MTKView;
1632 @end
1633#else
1634 @interface _sapp_ios_view : GLKView
1635 @end
1636#endif
1637
1638typedef struct {
1639 UIWindow* window;
1640 _sapp_ios_view* view;
1641 UITextField* textfield;
1642 _sapp_textfield_dlg* textfield_dlg;
1643 #if defined(SOKOL_METAL)
1644 UIViewController* view_ctrl;
1645 id<MTLDevice> mtl_device;
1646 #else
1647 GLKViewController* view_ctrl;
1648 EAGLContext* eagl_ctx;
1649 #endif
1650 bool suspended;
1651} _sapp_ios_t;
1652
1653#endif // _SAPP_IOS
1654
1655/*== EMSCRIPTEN DECLARATIONS =================================================*/
1656#if defined(_SAPP_EMSCRIPTEN)
1657
1658#if defined(SOKOL_WGPU)
1659typedef struct {
1660 int state;
1661 WGPUDevice device;
1662 WGPUSwapChain swapchain;
1663 WGPUTextureFormat render_format;
1664 WGPUTexture msaa_tex;
1665 WGPUTexture depth_stencil_tex;
1666 WGPUTextureView swapchain_view;
1667 WGPUTextureView msaa_view;
1668 WGPUTextureView depth_stencil_view;
1669} _sapp_wgpu_t;
1670#endif
1671
1672typedef struct {
1673 bool textfield_created;
1674 bool wants_show_keyboard;
1675 bool wants_hide_keyboard;
1676 bool mouse_lock_requested;
1677 #if defined(SOKOL_WGPU)
1678 _sapp_wgpu_t wgpu;
1679 #endif
1680} _sapp_emsc_t;
1681#endif // _SAPP_EMSCRIPTEN
1682
1683/*== WIN32 DECLARATIONS ======================================================*/
1684#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP))
1685typedef struct {
1686 ID3D11Device* device;
1687 ID3D11DeviceContext* device_context;
1688 ID3D11Texture2D* rt;
1689 ID3D11RenderTargetView* rtv;
1690 ID3D11Texture2D* msaa_rt;
1691 ID3D11RenderTargetView* msaa_rtv;
1692 ID3D11Texture2D* ds;
1693 ID3D11DepthStencilView* dsv;
1694 DXGI_SWAP_CHAIN_DESC swap_chain_desc;
1695 IDXGISwapChain* swap_chain;
1696} _sapp_d3d11_t;
1697#endif
1698
1699/*== WIN32 DECLARATIONS ======================================================*/
1700#if defined(_SAPP_WIN32)
1701
1702#ifndef DPI_ENUMS_DECLARED
1703typedef enum PROCESS_DPI_AWARENESS
1704{
1705 PROCESS_DPI_UNAWARE = 0,
1706 PROCESS_SYSTEM_DPI_AWARE = 1,
1707 PROCESS_PER_MONITOR_DPI_AWARE = 2
1708} PROCESS_DPI_AWARENESS;
1709typedef enum MONITOR_DPI_TYPE {
1710 MDT_EFFECTIVE_DPI = 0,
1711 MDT_ANGULAR_DPI = 1,
1712 MDT_RAW_DPI = 2,
1713 MDT_DEFAULT = MDT_EFFECTIVE_DPI
1714} MONITOR_DPI_TYPE;
1715#endif /*DPI_ENUMS_DECLARED*/
1716
1717typedef struct {
1718 bool aware;
1719 float content_scale;
1720 float window_scale;
1721 float mouse_scale;
1722} _sapp_win32_dpi_t;
1723
1724typedef struct {
1725 HWND hwnd;
1726 HDC dc;
1727 UINT orig_codepage;
1728 LONG mouse_locked_x, mouse_locked_y;
1729 bool is_win10_or_greater;
1730 bool in_create_window;
1731 bool iconified;
1732 bool mouse_tracked;
1733 uint8_t mouse_capture_mask;
1734 _sapp_win32_dpi_t dpi;
1735 bool raw_input_mousepos_valid;
1736 LONG raw_input_mousepos_x;
1737 LONG raw_input_mousepos_y;
1738 uint8_t raw_input_data[256];
1739} _sapp_win32_t;
1740
1741#if defined(SOKOL_GLCORE33)
1742#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
1743#define WGL_SUPPORT_OPENGL_ARB 0x2010
1744#define WGL_DRAW_TO_WINDOW_ARB 0x2001
1745#define WGL_PIXEL_TYPE_ARB 0x2013
1746#define WGL_TYPE_RGBA_ARB 0x202b
1747#define WGL_ACCELERATION_ARB 0x2003
1748#define WGL_NO_ACCELERATION_ARB 0x2025
1749#define WGL_RED_BITS_ARB 0x2015
1750#define WGL_GREEN_BITS_ARB 0x2017
1751#define WGL_BLUE_BITS_ARB 0x2019
1752#define WGL_ALPHA_BITS_ARB 0x201b
1753#define WGL_DEPTH_BITS_ARB 0x2022
1754#define WGL_STENCIL_BITS_ARB 0x2023
1755#define WGL_DOUBLE_BUFFER_ARB 0x2011
1756#define WGL_SAMPLES_ARB 0x2042
1757#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
1758#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
1759#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
1760#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
1761#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
1762#define WGL_CONTEXT_FLAGS_ARB 0x2094
1763#define ERROR_INVALID_VERSION_ARB 0x2095
1764#define ERROR_INVALID_PROFILE_ARB 0x2096
1765#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
1766typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
1767typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*);
1768typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void);
1769typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);
1770typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*);
1771typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC);
1772typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC);
1773typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR);
1774typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void);
1775typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC);
1776
1777typedef struct {
1778 HINSTANCE opengl32;
1779 HGLRC gl_ctx;
1780 PFN_wglCreateContext CreateContext;
1781 PFN_wglDeleteContext DeleteContext;
1782 PFN_wglGetProcAddress GetProcAddress;
1783 PFN_wglGetCurrentDC GetCurrentDC;
1784 PFN_wglMakeCurrent MakeCurrent;
1785 PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
1786 PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB;
1787 PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT;
1788 PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB;
1789 PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
1790 bool ext_swap_control;
1791 bool arb_multisample;
1792 bool arb_pixel_format;
1793 bool arb_create_context;
1794 bool arb_create_context_profile;
1795 HWND msg_hwnd;
1796 HDC msg_dc;
1797} _sapp_wgl_t;
1798#endif // SOKOL_GLCORE33
1799
1800#endif // _SAPP_WIN32
1801
1802/*== UWP DECLARATIONS ======================================================*/
1803#if defined(_SAPP_UWP)
1804
1805typedef struct {
1806 float content_scale;
1807 float window_scale;
1808 float mouse_scale;
1809} _sapp_uwp_dpi_t;
1810
1811typedef struct {
1812 bool mouse_tracked;
1813 uint8_t mouse_buttons;
1814 _sapp_uwp_dpi_t dpi;
1815} _sapp_uwp_t;
1816
1817#endif // _SAPP_UWP
1818
1819/*== ANDROID DECLARATIONS ====================================================*/
1820
1821#if defined(_SAPP_ANDROID)
1822typedef enum {
1823 _SOKOL_ANDROID_MSG_CREATE,
1824 _SOKOL_ANDROID_MSG_RESUME,
1825 _SOKOL_ANDROID_MSG_PAUSE,
1826 _SOKOL_ANDROID_MSG_FOCUS,
1827 _SOKOL_ANDROID_MSG_NO_FOCUS,
1828 _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW,
1829 _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE,
1830 _SOKOL_ANDROID_MSG_DESTROY,
1831} _sapp_android_msg_t;
1832
1833typedef struct {
1834 pthread_t thread;
1835 pthread_mutex_t mutex;
1836 pthread_cond_t cond;
1837 int read_from_main_fd;
1838 int write_from_main_fd;
1839} _sapp_android_pt_t;
1840
1841typedef struct {
1842 ANativeWindow* window;
1843 AInputQueue* input;
1844} _sapp_android_resources_t;
1845
1846typedef struct {
1847 ANativeActivity* activity;
1848 _sapp_android_pt_t pt;
1849 _sapp_android_resources_t pending;
1850 _sapp_android_resources_t current;
1851 ALooper* looper;
1852 bool is_thread_started;
1853 bool is_thread_stopping;
1854 bool is_thread_stopped;
1855 bool has_created;
1856 bool has_resumed;
1857 bool has_focus;
1858 EGLConfig config;
1859 EGLDisplay display;
1860 EGLContext context;
1861 EGLSurface surface;
1862} _sapp_android_t;
1863
1864#endif // _SAPP_ANDROID
1865
1866/*== LINUX DECLARATIONS ======================================================*/
1867#if defined(_SAPP_LINUX)
1868
1869#define _SAPP_X11_XDND_VERSION (5)
1870
1871#define GLX_VENDOR 1
1872#define GLX_RGBA_BIT 0x00000001
1873#define GLX_WINDOW_BIT 0x00000001
1874#define GLX_DRAWABLE_TYPE 0x8010
1875#define GLX_RENDER_TYPE 0x8011
1876#define GLX_DOUBLEBUFFER 5
1877#define GLX_RED_SIZE 8
1878#define GLX_GREEN_SIZE 9
1879#define GLX_BLUE_SIZE 10
1880#define GLX_ALPHA_SIZE 11
1881#define GLX_DEPTH_SIZE 12
1882#define GLX_STENCIL_SIZE 13
1883#define GLX_SAMPLES 0x186a1
1884#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
1885#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
1886#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
1887#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
1888#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
1889#define GLX_CONTEXT_FLAGS_ARB 0x2094
1890
1891typedef XID GLXWindow;
1892typedef XID GLXDrawable;
1893typedef struct __GLXFBConfig* GLXFBConfig;
1894typedef struct __GLXcontext* GLXContext;
1895typedef void (*__GLXextproc)(void);
1896
1897typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
1898typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
1899typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
1900typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
1901typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);
1902typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);
1903typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);
1904typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);
1905typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);
1906typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName);
1907typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
1908typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
1909typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);
1910typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);
1911
1912typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
1913typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);
1914
1915typedef struct {
1916 bool available;
1917 int major_opcode;
1918 int event_base;
1919 int error_base;
1920 int major;
1921 int minor;
1922} _sapp_xi_t;
1923
1924typedef struct {
1925 int version;
1926 Window source;
1927 Atom format;
1928 Atom XdndAware;
1929 Atom XdndEnter;
1930 Atom XdndPosition;
1931 Atom XdndStatus;
1932 Atom XdndActionCopy;
1933 Atom XdndDrop;
1934 Atom XdndFinished;
1935 Atom XdndSelection;
1936 Atom XdndTypeList;
1937 Atom text_uri_list;
1938} _sapp_xdnd_t;
1939
1940typedef struct {
1941 uint8_t mouse_buttons;
1942 Display* display;
1943 int screen;
1944 Window root;
1945 Colormap colormap;
1946 Window window;
1947 Cursor hidden_cursor;
1948 int window_state;
1949 float dpi;
1950 unsigned char error_code;
1951 Atom UTF8_STRING;
1952 Atom WM_PROTOCOLS;
1953 Atom WM_DELETE_WINDOW;
1954 Atom WM_STATE;
1955 Atom NET_WM_NAME;
1956 Atom NET_WM_ICON_NAME;
1957 Atom NET_WM_STATE;
1958 Atom NET_WM_STATE_FULLSCREEN;
1959 _sapp_xi_t xi;
1960 _sapp_xdnd_t xdnd;
1961} _sapp_x11_t;
1962
1963typedef struct {
1964 void* libgl;
1965 int major;
1966 int minor;
1967 int event_base;
1968 int error_base;
1969 GLXContext ctx;
1970 GLXWindow window;
1971
1972 // GLX 1.3 functions
1973 PFNGLXGETFBCONFIGSPROC GetFBConfigs;
1974 PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
1975 PFNGLXGETCLIENTSTRINGPROC GetClientString;
1976 PFNGLXQUERYEXTENSIONPROC QueryExtension;
1977 PFNGLXQUERYVERSIONPROC QueryVersion;
1978 PFNGLXDESTROYCONTEXTPROC DestroyContext;
1979 PFNGLXMAKECURRENTPROC MakeCurrent;
1980 PFNGLXSWAPBUFFERSPROC SwapBuffers;
1981 PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString;
1982 PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig;
1983 PFNGLXCREATEWINDOWPROC CreateWindow;
1984 PFNGLXDESTROYWINDOWPROC DestroyWindow;
1985
1986 // GLX 1.4 and extension functions
1987 PFNGLXGETPROCADDRESSPROC GetProcAddress;
1988 PFNGLXGETPROCADDRESSPROC GetProcAddressARB;
1989 PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
1990 PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
1991 PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
1992
1993 // extension availability
1994 bool EXT_swap_control;
1995 bool MESA_swap_control;
1996 bool ARB_multisample;
1997 bool ARB_create_context;
1998 bool ARB_create_context_profile;
1999} _sapp_glx_t;
2000
2001#endif // _SAPP_LINUX
2002
2003/*== COMMON DECLARATIONS =====================================================*/
2004
2005/* helper macros */
2006#define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
2007#define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
2008
2009#define _SAPP_MAX_TITLE_LENGTH (128)
2010/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */
2011#define _SAPP_PIXELFORMAT_RGBA8 (23)
2012#define _SAPP_PIXELFORMAT_BGRA8 (27)
2013#define _SAPP_PIXELFORMAT_DEPTH (41)
2014#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42)
2015
2016#if defined(_SAPP_MACOS) || defined(_SAPP_IOS)
2017 // this is ARC compatible
2018 #if defined(__cplusplus)
2019 #define _SAPP_CLEAR(type, item) { item = (type) { }; }
2020 #else
2021 #define _SAPP_CLEAR(type, item) { item = (type) { 0 }; }
2022 #endif
2023#else
2024 #define _SAPP_CLEAR(type, item) { memset(&item, 0, sizeof(item)); }
2025#endif
2026
2027typedef struct {
2028 bool enabled;
2029 int buf_size;
2030 char* buffer;
2031} _sapp_clipboard_t;
2032
2033typedef struct {
2034 bool enabled;
2035 int max_files;
2036 int max_path_length;
2037 int num_files;
2038 int buf_size;
2039 char* buffer;
2040} _sapp_drop_t;
2041
2042typedef struct {
2043 float x, y;
2044 float dx, dy;
2045 bool shown;
2046 bool locked;
2047 bool pos_valid;
2048} _sapp_mouse_t;
2049
2050typedef struct {
2051 sapp_desc desc;
2052 bool valid;
2053 bool fullscreen;
2054 bool gles2_fallback;
2055 bool first_frame;
2056 bool init_called;
2057 bool cleanup_called;
2058 bool quit_requested;
2059 bool quit_ordered;
2060 bool event_consumed;
2061 bool html5_ask_leave_site;
2062 bool onscreen_keyboard_shown;
2063 int window_width;
2064 int window_height;
2065 int framebuffer_width;
2066 int framebuffer_height;
2067 int sample_count;
2068 int swap_interval;
2069 float dpi_scale;
2070 uint64_t frame_count;
2071 sapp_event event;
2072 _sapp_mouse_t mouse;
2073 _sapp_clipboard_t clipboard;
2074 _sapp_drop_t drop;
2075 #if defined(_SAPP_MACOS)
2076 _sapp_macos_t macos;
2077 #elif defined(_SAPP_IOS)
2078 _sapp_ios_t ios;
2079 #elif defined(_SAPP_EMSCRIPTEN)
2080 _sapp_emsc_t emsc;
2081 #elif defined(_SAPP_WIN32)
2082 _sapp_win32_t win32;
2083 #if defined(SOKOL_D3D11)
2084 _sapp_d3d11_t d3d11;
2085 #elif defined(SOKOL_GLCORE33)
2086 _sapp_wgl_t wgl;
2087 #endif
2088 #elif defined(_SAPP_UWP)
2089 _sapp_uwp_t uwp;
2090 #if defined(SOKOL_D3D11)
2091 _sapp_d3d11_t d3d11;
2092 #endif
2093 #elif defined(_SAPP_ANDROID)
2094 _sapp_android_t android;
2095 #elif defined(_SAPP_LINUX)
2096 _sapp_x11_t x11;
2097 _sapp_glx_t glx;
2098 #endif
2099 char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH];
2100 char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */
2101 wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */
2102 sapp_keycode keycodes[SAPP_MAX_KEYCODES];
2103
2104 /* V patches */
2105 bool __v_native_render; /* V patch to allow for native rendering */
2106} _sapp_t;
2107static _sapp_t _sapp;
2108
2109/*=== PRIVATE HELPER FUNCTIONS ===============================================*/
2110_SOKOL_PRIVATE void _sapp_fail(const char* msg) {
2111 if (_sapp.desc.fail_cb) {
2112 _sapp.desc.fail_cb(msg);
2113 }
2114 else if (_sapp.desc.fail_userdata_cb) {
2115 _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data);
2116 }
2117 else {
2118 SOKOL_LOG(msg);
2119 }
2120 SOKOL_ABORT();
2121}
2122
2123_SOKOL_PRIVATE void _sapp_call_init(void) {
2124 if (_sapp.desc.init_cb) {
2125 _sapp.desc.init_cb();
2126 }
2127 else if (_sapp.desc.init_userdata_cb) {
2128 _sapp.desc.init_userdata_cb(_sapp.desc.user_data);
2129 }
2130 _sapp.init_called = true;
2131}
2132
2133_SOKOL_PRIVATE void _sapp_call_frame(void) {
2134 if (_sapp.__v_native_render) {
2135 return;
2136 }
2137 if (_sapp.init_called && !_sapp.cleanup_called) {
2138 if (_sapp.desc.frame_cb) {
2139 _sapp.desc.frame_cb();
2140 }
2141 else if (_sapp.desc.frame_userdata_cb) {
2142 _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
2143 }
2144 }
2145}
2146
2147// __v_
2148_SOKOL_PRIVATE void _sapp_call_frame_native(void) {
2149//puts("_sapp_call_frame_native()");
2150//printf("init called=%d cleanup_called=%d\n", _sapp.init_called,_sapp.cleanup_called);
2151 if (_sapp.init_called && !_sapp.cleanup_called) {
2152 if (_sapp.desc.frame_cb) {
2153 _sapp.desc.frame_cb();
2154 }
2155 else if (_sapp.desc.frame_userdata_cb) {
2156 _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
2157 }
2158 }
2159}
2160
2161
2162_SOKOL_PRIVATE void _sapp_call_cleanup(void) {
2163 if (!_sapp.cleanup_called) {
2164 if (_sapp.desc.cleanup_cb) {
2165 _sapp.desc.cleanup_cb();
2166 }
2167 else if (_sapp.desc.cleanup_userdata_cb) {
2168 _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
2169 }
2170 _sapp.cleanup_called = true;
2171 }
2172}
2173
2174_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
2175 if (!_sapp.cleanup_called) {
2176 if (_sapp.desc.event_cb) {
2177 _sapp.desc.event_cb(e);
2178 }
2179 else if (_sapp.desc.event_userdata_cb) {
2180 _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
2181 }
2182 }
2183 if (_sapp.event_consumed) {
2184 _sapp.event_consumed = false;
2185 return true;
2186 }
2187 else {
2188 return false;
2189 }
2190}
2191
2192_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) {
2193 SOKOL_ASSERT(_sapp.drop.buffer);
2194 SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files));
2195 int offset = index * _sapp.drop.max_path_length;
2196 SOKOL_ASSERT(offset < _sapp.drop.buf_size);
2197 return &_sapp.drop.buffer[offset];
2198}
2199
2200/* Copy a string into a fixed size buffer with guaranteed zero-
2201 termination.
2202
2203 Return false if the string didn't fit into the buffer and had to be clamped.
2204
2205 FIXME: Currently UTF-8 strings might become invalid if the string
2206 is clamped, because the last zero-byte might be written into
2207 the middle of a multi-byte sequence.
2208*/
2209_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) {
2210 SOKOL_ASSERT(src && dst && (max_len > 0));
2211 char* const end = &(dst[max_len-1]);
2212 char c = 0;
2213 for (int i = 0; i < max_len; i++) {
2214 c = *src;
2215 if (c != 0) {
2216 src++;
2217 }
2218 *dst++ = c;
2219 }
2220 /* truncated? */
2221 if (c != 0) {
2222 *end = 0;
2223 return false;
2224 }
2225 else {
2226 return true;
2227 }
2228}
2229
2230_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) {
2231 sapp_desc desc = *in_desc;
2232 desc.width = _sapp_def(desc.width, 640);
2233 desc.height = _sapp_def(desc.height, 480);
2234 desc.sample_count = _sapp_def(desc.sample_count, 1);
2235 desc.swap_interval = _sapp_def(desc.swap_interval, 1);
2236 desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas");
2237 desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192);
2238 desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1);
2239 desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048);
2240 desc.window_title = _sapp_def(desc.window_title, "sokol_app");
2241 return desc;
2242}
2243
2244_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
2245 _SAPP_CLEAR(_sapp_t, _sapp);
2246 _sapp.desc = _sapp_desc_defaults(desc);
2247 _sapp.first_frame = true;
2248 _sapp.window_width = _sapp.desc.width;
2249 _sapp.window_height = _sapp.desc.height;
2250 _sapp.framebuffer_width = _sapp.window_width;
2251 _sapp.framebuffer_height = _sapp.window_height;
2252 _sapp.sample_count = _sapp.desc.sample_count;
2253 _sapp.swap_interval = _sapp.desc.swap_interval;
2254 _sapp.html5_canvas_selector[0] = '#';
2255 _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1);
2256 _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1];
2257 _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site;
2258 _sapp.clipboard.enabled = _sapp.desc.enable_clipboard;
2259 if (_sapp.clipboard.enabled) {
2260 _sapp.clipboard.buf_size = _sapp.desc.clipboard_size;
2261 _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.clipboard.buf_size);
2262 }
2263 _sapp.drop.enabled = _sapp.desc.enable_dragndrop;
2264 if (_sapp.drop.enabled) {
2265 _sapp.drop.max_files = _sapp.desc.max_dropped_files;
2266 _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length;
2267 _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length;
2268 _sapp.drop.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.drop.buf_size);
2269 }
2270 _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title));
2271 _sapp.desc.window_title = _sapp.window_title;
2272 _sapp.dpi_scale = 1.0f;
2273 _sapp.fullscreen = _sapp.desc.fullscreen;
2274 _sapp.mouse.shown = true;
2275 _sapp.__v_native_render = _sapp.desc.__v_native_render;
2276}
2277
2278_SOKOL_PRIVATE void _sapp_discard_state(void) {
2279 if (_sapp.clipboard.enabled) {
2280 SOKOL_ASSERT(_sapp.clipboard.buffer);
2281 SOKOL_FREE((void*)_sapp.clipboard.buffer);
2282 }
2283 if (_sapp.drop.enabled) {
2284 SOKOL_ASSERT(_sapp.drop.buffer);
2285 SOKOL_FREE((void*)_sapp.drop.buffer);
2286 }
2287 _SAPP_CLEAR(_sapp_t, _sapp);
2288}
2289
2290_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) {
2291 memset(&_sapp.event, 0, sizeof(_sapp.event));
2292 _sapp.event.type = type;
2293 _sapp.event.frame_count = _sapp.frame_count;
2294 _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
2295 _sapp.event.window_width = _sapp.window_width;
2296 _sapp.event.window_height = _sapp.window_height;
2297 _sapp.event.framebuffer_width = _sapp.framebuffer_width;
2298 _sapp.event.framebuffer_height = _sapp.framebuffer_height;
2299 _sapp.event.mouse_x = _sapp.mouse.x;
2300 _sapp.event.mouse_y = _sapp.mouse.y;
2301 _sapp.event.mouse_dx = _sapp.mouse.dx;
2302 _sapp.event.mouse_dy = _sapp.mouse.dy;
2303}
2304
2305_SOKOL_PRIVATE bool _sapp_events_enabled(void) {
2306 /* only send events when an event callback is set, and the init function was called */
2307 return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called;
2308}
2309
2310_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
2311 if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) {
2312 return _sapp.keycodes[scan_code];
2313 }
2314 else {
2315 return SAPP_KEYCODE_INVALID;
2316 }
2317}
2318
2319_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) {
2320 if (_sapp.drop.enabled) {
2321 SOKOL_ASSERT(_sapp.drop.buffer);
2322 memset(_sapp.drop.buffer, 0, (size_t)_sapp.drop.buf_size);
2323 }
2324}
2325
2326_SOKOL_PRIVATE void _sapp_frame(void) {
2327 if (_sapp.first_frame) {
2328 _sapp.first_frame = false;
2329 _sapp_call_init();
2330 }
2331 _sapp_call_frame();
2332 _sapp.frame_count++;
2333}
2334
2335/*== MacOS/iOS ===============================================================*/
2336#if defined(_SAPP_APPLE)
2337
2338#if __has_feature(objc_arc)
2339#define _SAPP_OBJC_RELEASE(obj) { obj = nil; }
2340#else
2341#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; }
2342#endif
2343
2344/*== MacOS ===================================================================*/
2345#if defined(_SAPP_MACOS)
2346
2347_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) {
2348 _sapp.keycodes[0x1D] = SAPP_KEYCODE_0;
2349 _sapp.keycodes[0x12] = SAPP_KEYCODE_1;
2350 _sapp.keycodes[0x13] = SAPP_KEYCODE_2;
2351 _sapp.keycodes[0x14] = SAPP_KEYCODE_3;
2352 _sapp.keycodes[0x15] = SAPP_KEYCODE_4;
2353 _sapp.keycodes[0x17] = SAPP_KEYCODE_5;
2354 _sapp.keycodes[0x16] = SAPP_KEYCODE_6;
2355 _sapp.keycodes[0x1A] = SAPP_KEYCODE_7;
2356 _sapp.keycodes[0x1C] = SAPP_KEYCODE_8;
2357 _sapp.keycodes[0x19] = SAPP_KEYCODE_9;
2358 _sapp.keycodes[0x00] = SAPP_KEYCODE_A;
2359 _sapp.keycodes[0x0B] = SAPP_KEYCODE_B;
2360 _sapp.keycodes[0x08] = SAPP_KEYCODE_C;
2361 _sapp.keycodes[0x02] = SAPP_KEYCODE_D;
2362 _sapp.keycodes[0x0E] = SAPP_KEYCODE_E;
2363 _sapp.keycodes[0x03] = SAPP_KEYCODE_F;
2364 _sapp.keycodes[0x05] = SAPP_KEYCODE_G;
2365 _sapp.keycodes[0x04] = SAPP_KEYCODE_H;
2366 _sapp.keycodes[0x22] = SAPP_KEYCODE_I;
2367 _sapp.keycodes[0x26] = SAPP_KEYCODE_J;
2368 _sapp.keycodes[0x28] = SAPP_KEYCODE_K;
2369 _sapp.keycodes[0x25] = SAPP_KEYCODE_L;
2370 _sapp.keycodes[0x2E] = SAPP_KEYCODE_M;
2371 _sapp.keycodes[0x2D] = SAPP_KEYCODE_N;
2372 _sapp.keycodes[0x1F] = SAPP_KEYCODE_O;
2373 _sapp.keycodes[0x23] = SAPP_KEYCODE_P;
2374 _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q;
2375 _sapp.keycodes[0x0F] = SAPP_KEYCODE_R;
2376 _sapp.keycodes[0x01] = SAPP_KEYCODE_S;
2377 _sapp.keycodes[0x11] = SAPP_KEYCODE_T;
2378 _sapp.keycodes[0x20] = SAPP_KEYCODE_U;
2379 _sapp.keycodes[0x09] = SAPP_KEYCODE_V;
2380 _sapp.keycodes[0x0D] = SAPP_KEYCODE_W;
2381 _sapp.keycodes[0x07] = SAPP_KEYCODE_X;
2382 _sapp.keycodes[0x10] = SAPP_KEYCODE_Y;
2383 _sapp.keycodes[0x06] = SAPP_KEYCODE_Z;
2384 _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE;
2385 _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH;
2386 _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA;
2387 _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL;
2388 _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT;
2389 _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET;
2390 _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS;
2391 _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD;
2392 _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET;
2393 _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON;
2394 _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH;
2395 _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1;
2396 _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE;
2397 _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK;
2398 _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE;
2399 _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN;
2400 _sapp.keycodes[0x77] = SAPP_KEYCODE_END;
2401 _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER;
2402 _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE;
2403 _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1;
2404 _sapp.keycodes[0x78] = SAPP_KEYCODE_F2;
2405 _sapp.keycodes[0x63] = SAPP_KEYCODE_F3;
2406 _sapp.keycodes[0x76] = SAPP_KEYCODE_F4;
2407 _sapp.keycodes[0x60] = SAPP_KEYCODE_F5;
2408 _sapp.keycodes[0x61] = SAPP_KEYCODE_F6;
2409 _sapp.keycodes[0x62] = SAPP_KEYCODE_F7;
2410 _sapp.keycodes[0x64] = SAPP_KEYCODE_F8;
2411 _sapp.keycodes[0x65] = SAPP_KEYCODE_F9;
2412 _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10;
2413 _sapp.keycodes[0x67] = SAPP_KEYCODE_F11;
2414 _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12;
2415 _sapp.keycodes[0x69] = SAPP_KEYCODE_F13;
2416 _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14;
2417 _sapp.keycodes[0x71] = SAPP_KEYCODE_F15;
2418 _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16;
2419 _sapp.keycodes[0x40] = SAPP_KEYCODE_F17;
2420 _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18;
2421 _sapp.keycodes[0x50] = SAPP_KEYCODE_F19;
2422 _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20;
2423 _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME;
2424 _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT;
2425 _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT;
2426 _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT;
2427 _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL;
2428 _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT;
2429 _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER;
2430 _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU;
2431 _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK;
2432 _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN;
2433 _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP;
2434 _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT;
2435 _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT;
2436 _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL;
2437 _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT;
2438 _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER;
2439 _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE;
2440 _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB;
2441 _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP;
2442 _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0;
2443 _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1;
2444 _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2;
2445 _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3;
2446 _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4;
2447 _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5;
2448 _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6;
2449 _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7;
2450 _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8;
2451 _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9;
2452 _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD;
2453 _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL;
2454 _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE;
2455 _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER;
2456 _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL;
2457 _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY;
2458 _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT;
2459}
2460
2461_SOKOL_PRIVATE void _sapp_macos_discard_state(void) {
2462 // NOTE: it's safe to call [release] on a nil object
2463 _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area);
2464 _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg);
2465 _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg);
2466 _SAPP_OBJC_RELEASE(_sapp.macos.view);
2467 #if defined(SOKOL_METAL)
2468 _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device);
2469 #endif
2470 _SAPP_OBJC_RELEASE(_sapp.macos.window);
2471}
2472
2473_SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) {
2474 _sapp_init_state(desc);
2475 _sapp_macos_init_keytable();
2476 [NSApplication sharedApplication];
2477 NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
2478 _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init];
2479 NSApp.delegate = _sapp.macos.app_dlg;
2480 [NSApp activateIgnoringOtherApps:YES];
2481 [NSApp run];
2482 // NOTE: [NSApp run] never returns, instead cleanup code
2483 // must be put into applicationWillTerminate
2484}
2485
2486/* MacOS entry function */
2487#if !defined(SOKOL_NO_ENTRY)
2488int main(int argc, char* argv[]) {
2489 sapp_desc desc = sokol_main(argc, argv);
2490 _sapp_macos_run(&desc);
2491 return 0;
2492}
2493#endif /* SOKOL_NO_ENTRY */
2494
2495_SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) {
2496 uint32_t m = 0;
2497 if (f & NSEventModifierFlagShift) {
2498 m |= SAPP_MODIFIER_SHIFT;
2499 }
2500 if (f & NSEventModifierFlagControl) {
2501 m |= SAPP_MODIFIER_CTRL;
2502 }
2503 if (f & NSEventModifierFlagOption) {
2504 m |= SAPP_MODIFIER_ALT;
2505 }
2506 if (f & NSEventModifierFlagCommand) {
2507 m |= SAPP_MODIFIER_SUPER;
2508 }
2509 return m;
2510}
2511
2512_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) {
2513 if (_sapp_events_enabled()) {
2514 _sapp_init_event(type);
2515 _sapp.event.mouse_button = btn;
2516 _sapp.event.modifiers = mod;
2517 _sapp_call_event(&_sapp.event);
2518 }
2519}
2520
2521_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) {
2522 if (_sapp_events_enabled()) {
2523 _sapp_init_event(type);
2524 _sapp.event.key_code = key;
2525 _sapp.event.key_repeat = repeat;
2526 _sapp.event.modifiers = mod;
2527 _sapp_call_event(&_sapp.event);
2528 }
2529}
2530
2531_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
2532 if (_sapp_events_enabled()) {
2533 _sapp_init_event(type);
2534 _sapp_call_event(&_sapp.event);
2535 }
2536}
2537
2538/* NOTE: unlike the iOS version of this function, the macOS version
2539 can dynamically update the DPI scaling factor when a window is moved
2540 between HighDPI / LowDPI screens.
2541*/
2542_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
2543 #if defined(SOKOL_METAL)
2544 const NSRect fb_rect = [_sapp.macos.view bounds];
2545 _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale;
2546 _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale;
2547 #elif defined(SOKOL_GLCORE33)
2548 const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]];
2549 _sapp.framebuffer_width = fb_rect.size.width;
2550 _sapp.framebuffer_height = fb_rect.size.height;
2551 #endif
2552 const NSRect bounds = [_sapp.macos.view bounds];
2553 _sapp.window_width = bounds.size.width;
2554 _sapp.window_height = bounds.size.height;
2555 if (_sapp.framebuffer_width == 0) {
2556 _sapp.framebuffer_width = 1;
2557 }
2558 if (_sapp.framebuffer_height == 0) {
2559 _sapp.framebuffer_height = 1;
2560 }
2561 if (_sapp.window_width == 0) {
2562 _sapp.window_width = 1;
2563 }
2564 if (_sapp.window_height == 0) {
2565 _sapp.window_height = 1;
2566 }
2567 _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
2568
2569 /* NOTE: _sapp_macos_update_dimensions() isn't called each frame, but only
2570 when the window size actually changes, so resizing the MTKView's
2571 in each call is fine even when MTKView doesn't ignore setting an
2572 identical drawableSize.
2573 */
2574 #if defined(SOKOL_METAL)
2575 CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
2576 _sapp.macos.view.drawableSize = drawable_size;
2577 #endif
2578}
2579
2580_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) {
2581 /* NOTE: the _sapp.fullscreen flag is also notified by the
2582 windowDidEnterFullscreen / windowDidExitFullscreen
2583 event handlers
2584 */
2585 _sapp.fullscreen = !_sapp.fullscreen;
2586 [_sapp.macos.window toggleFullScreen:nil];
2587}
2588
2589_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) {
2590 @autoreleasepool {
2591 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
2592 [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
2593 [pasteboard setString:@(str) forType:NSPasteboardTypeString];
2594 }
2595}
2596
2597_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) {
2598 SOKOL_ASSERT(_sapp.clipboard.buffer);
2599 @autoreleasepool {
2600 _sapp.clipboard.buffer[0] = 0;
2601 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
2602 if (![[pasteboard types] containsObject:NSPasteboardTypeString]) {
2603 return _sapp.clipboard.buffer;
2604 }
2605 NSString* str = [pasteboard stringForType:NSPasteboardTypeString];
2606 if (!str) {
2607 return _sapp.clipboard.buffer;
2608 }
2609 _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
2610 }
2611 return _sapp.clipboard.buffer;
2612}
2613
2614_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) {
2615 [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]];
2616}
2617
2618_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) {
2619 if (!_sapp.mouse.locked) {
2620 const NSPoint mouse_pos = event.locationInWindow;
2621 float new_x = mouse_pos.x * _sapp.dpi_scale;
2622 float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1;
2623 /* don't update dx/dy in the very first update */
2624 if (_sapp.mouse.pos_valid) {
2625 _sapp.mouse.dx = new_x - _sapp.mouse.x;
2626 _sapp.mouse.dy = new_y - _sapp.mouse.y;
2627 }
2628 _sapp.mouse.x = new_x;
2629 _sapp.mouse.y = new_y;
2630 _sapp.mouse.pos_valid = true;
2631 }
2632}
2633
2634_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) {
2635 /* NOTE: this function is only called when the mouse visibility actually changes */
2636 if (visible) {
2637 CGDisplayShowCursor(kCGDirectMainDisplay);
2638 }
2639 else {
2640 CGDisplayHideCursor(kCGDirectMainDisplay);
2641 }
2642}
2643
2644_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) {
2645 if (lock == _sapp.mouse.locked) {
2646 return;
2647 }
2648 _sapp.mouse.dx = 0.0f;
2649 _sapp.mouse.dy = 0.0f;
2650 _sapp.mouse.locked = lock;
2651 /*
2652 NOTE that this code doesn't warp the mouse cursor to the window
2653 center as everybody else does it. This lead to a spike in the
2654 *second* mouse-moved event after the warp happened. The
2655 mouse centering doesn't seem to be required (mouse-moved events
2656 are reported correctly even when the cursor is at an edge of the screen).
2657
2658 NOTE also that the hide/show of the mouse cursor should properly
2659 stack with calls to sapp_show_mouse()
2660 */
2661 if (_sapp.mouse.locked) {
2662 CGAssociateMouseAndMouseCursorPosition(NO);
2663 CGDisplayHideCursor(kCGDirectMainDisplay);
2664 }
2665 else {
2666 CGDisplayShowCursor(kCGDirectMainDisplay);
2667 CGAssociateMouseAndMouseCursorPosition(YES);
2668 }
2669}
2670
2671_SOKOL_PRIVATE void _sapp_macos_frame(void) {
2672 _sapp_frame();
2673 if (_sapp.quit_requested || _sapp.quit_ordered) {
2674 [_sapp.macos.window performClose:nil];
2675 }
2676}
2677
2678#include "sokol_app2.h" // __v_
2679
2680@implementation _sapp_macos_app_delegate
2681- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
2682 _SOKOL_UNUSED(aNotification);
2683 if (_sapp.fullscreen) {
2684 NSRect screen_rect = NSScreen.mainScreen.frame;
2685 _sapp.window_width = screen_rect.size.width;
2686 _sapp.window_height = screen_rect.size.height;
2687 }
2688 if (_sapp.desc.high_dpi) {
2689 _sapp.framebuffer_width = 2 * _sapp.window_width;
2690 _sapp.framebuffer_height = 2 * _sapp.window_height;
2691 }
2692 else {
2693 _sapp.framebuffer_width = _sapp.window_width;
2694 _sapp.framebuffer_height = _sapp.window_height;
2695 }
2696 _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width;
2697 const NSUInteger style = _sapp.desc.fullscreen ? NSWindowStyleMaskBorderless : // __v_
2698 NSWindowStyleMaskTitled |
2699 NSWindowStyleMaskClosable |
2700 NSWindowStyleMaskMiniaturizable |
2701 NSWindowStyleMaskResizable;
2702 NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height);
2703 _sapp.macos.window = [[_sapp_macos_window alloc]
2704 initWithContentRect:window_rect
2705 styleMask:style
2706 backing:NSBackingStoreBuffered
2707 defer:NO];
2708 _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate
2709 _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title];
2710 _sapp.macos.window.acceptsMouseMovedEvents = YES;
2711 _sapp.macos.window.restorable = YES;
2712
2713
2714
2715 // _v__
2716 _sapp.macos.window.backgroundColor = [NSColor whiteColor];
2717
2718 // Quit menu
2719 NSMenu* menu_bar = [[NSMenu alloc] init];
2720NSMenuItem* app_menu_item = [[NSMenuItem alloc] init];
2721[menu_bar addItem:app_menu_item];
2722NSApp.mainMenu = menu_bar;
2723NSMenu* app_menu = [[NSMenu alloc] init];
2724NSString* window_title_as_nsstring = [NSString stringWithUTF8String:_sapp.window_title];
2725// `quit_title` memory will be owned by the NSMenuItem, so no need to release it ourselves
2726NSString* quit_title = [@"Quit " stringByAppendingString:window_title_as_nsstring];
2727NSMenuItem* quit_menu_item = [[NSMenuItem alloc]
2728 initWithTitle:quit_title
2729 action:@selector(terminate:)
2730 keyEquivalent:@"q"];
2731[app_menu addItem:quit_menu_item];
2732app_menu_item.submenu = app_menu;
2733_SAPP_OBJC_RELEASE( window_title_as_nsstring );
2734_SAPP_OBJC_RELEASE( app_menu );
2735_SAPP_OBJC_RELEASE( app_menu_item );
2736_SAPP_OBJC_RELEASE( menu_bar );
2737
2738
2739 // _v__
2740
2741 _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init];
2742 _sapp.macos.window.delegate = _sapp.macos.win_dlg;
2743 #if defined(SOKOL_METAL)
2744 _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice();
2745 _sapp.macos.view = [[_sapp_macos_view alloc] init];
2746 [_sapp.macos.view updateTrackingAreas];
2747 _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval;
2748 _sapp.macos.view.device = _sapp.macos.mtl_device;
2749 _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
2750 _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
2751 _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count;
2752 _sapp.macos.view.autoResizeDrawable = false;
2753 _sapp.macos.window.contentView = _sapp.macos.view;
2754 [_sapp.macos.window makeFirstResponder:_sapp.macos.view];
2755 _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest;
2756 #elif defined(SOKOL_GLCORE33)
2757 NSOpenGLPixelFormatAttribute attrs[32];
2758 int i = 0;
2759 attrs[i++] = NSOpenGLPFAAccelerated;
2760 attrs[i++] = NSOpenGLPFADoubleBuffer;
2761 attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core;
2762 attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
2763 attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
2764 attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
2765 attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8;
2766 if (_sapp.sample_count > 1) {
2767 attrs[i++] = NSOpenGLPFAMultisample;
2768 attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
2769 attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count;
2770 }
2771 else {
2772 attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0;
2773 }
2774 attrs[i++] = 0;
2775 NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
2776 SOKOL_ASSERT(glpixelformat_obj != nil);
2777
2778 _sapp.macos.view = [[_sapp_macos_view alloc]
2779 initWithFrame:window_rect
2780 pixelFormat:glpixelformat_obj];
2781 _SAPP_OBJC_RELEASE(glpixelformat_obj);
2782 [_sapp.macos.view updateTrackingAreas];
2783 if (_sapp.desc.high_dpi) {
2784 [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES];
2785 }
2786 else {
2787 [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO];
2788 }
2789
2790 _sapp.macos.window.contentView = _sapp.macos.view;
2791 [_sapp.macos.window makeFirstResponder:_sapp.macos.view];
2792
2793 NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001
2794 target:_sapp.macos.view
2795 selector:@selector(timerFired:)
2796 userInfo:nil
2797 repeats:YES];
2798 [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode];
2799 timer_obj = nil;
2800 #endif
2801 _sapp.valid = true;
2802 // __v_
2803 if (!_sapp.__v_native_render) {
2804 if (_sapp.fullscreen) {
2805 // on GL, this already toggles a rendered frame, so set the valid flag before
2806 [_sapp.macos.window toggleFullScreen:self];
2807 }
2808 else {
2809 [_sapp.macos.window center];
2810 }
2811 }
2812 // __v C
2813 ///////////////////////////////////////////////////////
2814 // Create a child view for native rendering
2815 if (_sapp.__v_native_render) {
2816
2817 CGRect wRect = _sapp.macos.window.frame;
2818 NSView *contentView =_sapp.macos.window.contentView;
2819 CGRect cRect = contentView.frame;
2820
2821 CGRect rect = CGRectMake(wRect.origin.x, wRect.origin.y, cRect.size.width, cRect.size.height);
2822 NSWindow *overlayWindow = [[NSWindow alloc]initWithContentRect:rect
2823 styleMask:NSBorderlessWindowMask
2824 backing:NSBackingStoreBuffered
2825 defer:NO];
2826 //overlayWindow.backgroundColor = [NSColor whiteColor];
2827
2828 //overlayWindow.backgroundColor = [[NSColor whiteColor] colorWithAlphaComponent:0];
2829 [overlayWindow setOpaque:YES];
2830 [_sapp.macos.window setIgnoresMouseEvents:NO];
2831 g_view = [[MyView2 alloc] init];
2832 overlayWindow.contentView = g_view;
2833
2834 [ contentView addSubview:g_view];
2835//[ _sapp.macos.window addChildWindow:overlayWindow ordered:NSWindowAbove];
2836 [_sapp.macos.window center];
2837
2838}
2839 //////////////////////////////////
2840
2841 [_sapp.macos.window makeKeyAndOrderFront:nil];
2842 _sapp_macos_update_dimensions();
2843// [NSEvent setMouseCoalescingEnabled:NO];
2844}
2845
2846- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender {
2847 _SOKOL_UNUSED(sender);
2848 return YES;
2849}
2850
2851- (void)applicationWillTerminate:(NSNotification*)notification {
2852 _SOKOL_UNUSED(notification);
2853 _sapp_call_cleanup();
2854 _sapp_macos_discard_state();
2855 _sapp_discard_state();
2856}
2857@end
2858
2859@implementation _sapp_macos_window_delegate
2860- (BOOL)windowShouldClose:(id)sender {
2861 _SOKOL_UNUSED(sender);
2862 /* only give user-code a chance to intervene when sapp_quit() wasn't already called */
2863 if (!_sapp.quit_ordered) {
2864 /* if window should be closed and event handling is enabled, give user code
2865 a chance to intervene via sapp_cancel_quit()
2866 */
2867 _sapp.quit_requested = true;
2868 _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
2869 /* user code hasn't intervened, quit the app */
2870 if (_sapp.quit_requested) {
2871 _sapp.quit_ordered = true;
2872 }
2873 }
2874 if (_sapp.quit_ordered) {
2875 return YES;
2876 }
2877 else {
2878 return NO;
2879 }
2880}
2881
2882- (void)windowDidResize:(NSNotification*)notification {
2883 _SOKOL_UNUSED(notification);
2884 _sapp_macos_update_dimensions();
2885 if (!_sapp.first_frame) {
2886 _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED);
2887 }
2888}
2889
2890- (void)windowDidMiniaturize:(NSNotification*)notification {
2891 _SOKOL_UNUSED(notification);
2892 _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED);
2893}
2894
2895- (void)windowDidDeminiaturize:(NSNotification*)notification {
2896 _SOKOL_UNUSED(notification);
2897 _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED);
2898}
2899
2900- (void)windowDidEnterFullScreen:(NSNotification*)notification {
2901 _SOKOL_UNUSED(notification);
2902 _sapp.fullscreen = true;
2903}
2904
2905- (void)windowDidExitFullScreen:(NSNotification*)notification {
2906 _SOKOL_UNUSED(notification);
2907 _sapp.fullscreen = false;
2908}
2909@end
2910
2911@implementation _sapp_macos_window
2912
2913// __v_
2914- (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless
2915- (BOOL)canBecomeMainWindow { return YES; }
2916// __v_
2917
2918- (instancetype)initWithContentRect:(NSRect)contentRect
2919 styleMask:(NSWindowStyleMask)style
2920 backing:(NSBackingStoreType)backingStoreType
2921 defer:(BOOL)flag {
2922 if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) {
2923 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
2924 [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
2925 #endif
2926 }
2927 return self;
2928}
2929
2930- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
2931 return NSDragOperationCopy;
2932}
2933
2934- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
2935 return NSDragOperationCopy;
2936}
2937
2938- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
2939 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
2940 NSPasteboard *pboard = [sender draggingPasteboard];
2941 if ([pboard.types containsObject:NSPasteboardTypeFileURL]) {
2942 _sapp_clear_drop_buffer();
2943 _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count;
2944 bool drop_failed = false;
2945 for (int i = 0; i < _sapp.drop.num_files; i++) {
2946 NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]];
2947 if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) {
2948 SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n");
2949 drop_failed = true;
2950 break;
2951 }
2952 }
2953 if (!drop_failed) {
2954 if (_sapp_events_enabled()) {
2955 _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
2956 _sapp_call_event(&_sapp.event);
2957 }
2958 }
2959 else {
2960 _sapp_clear_drop_buffer();
2961 _sapp.drop.num_files = 0;
2962 }
2963 return YES;
2964 }
2965 #endif
2966 return NO;
2967}
2968@end
2969
2970@implementation _sapp_macos_view
2971#if defined(SOKOL_GLCORE33)
2972/* NOTE: this is a hack/fix when the initial window size has been clipped by
2973 macOS because it didn't fit on the screen, in that case the
2974 frame size of the window is reported wrong if low-dpi rendering
2975 was requested (instead the high-dpi dimensions are returned)
2976 until the window is resized for the first time.
2977
2978 Hooking into reshape and getting the frame dimensions seems to report
2979 the correct dimensions.
2980*/
2981- (void)reshape {
2982 _sapp_macos_update_dimensions();
2983 [super reshape];
2984}
2985- (void)timerFired:(id)sender {
2986 _SOKOL_UNUSED(sender);
2987 [self setNeedsDisplay:YES];
2988}
2989- (void)prepareOpenGL {
2990 [super prepareOpenGL];
2991 GLint swapInt = 1;
2992 NSOpenGLContext* ctx = [_sapp.macos.view openGLContext];
2993 [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval];
2994 [ctx makeCurrentContext];
2995}
2996#endif
2997
2998_SOKOL_PRIVATE void _sapp_macos_poll_input_events() {
2999 /*
3000
3001 NOTE: late event polling temporarily out-commented to check if this
3002 causes infrequent and almost impossible to reproduce probelms with the
3003 window close events, see:
3004 https://github.com/floooh/sokol/pull/483#issuecomment-805148815
3005
3006
3007 const NSEventMask mask = NSEventMaskLeftMouseDown |
3008 NSEventMaskLeftMouseUp|
3009 NSEventMaskRightMouseDown |
3010 NSEventMaskRightMouseUp |
3011 NSEventMaskMouseMoved |
3012 NSEventMaskLeftMouseDragged |
3013 NSEventMaskRightMouseDragged |
3014 NSEventMaskMouseEntered |
3015 NSEventMaskMouseExited |
3016 NSEventMaskKeyDown |
3017 NSEventMaskKeyUp |
3018 NSEventMaskCursorUpdate |
3019 NSEventMaskScrollWheel |
3020 NSEventMaskTabletPoint |
3021 NSEventMaskTabletProximity |
3022 NSEventMaskOtherMouseDown |
3023 NSEventMaskOtherMouseUp |
3024 NSEventMaskOtherMouseDragged |
3025 NSEventMaskPressure |
3026 NSEventMaskDirectTouch;
3027 @autoreleasepool {
3028 for (;;) {
3029 // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend,
3030 // see: https://github.com/floooh/sokol/issues/486
3031 NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES];
3032 if (event == nil) {
3033 break;
3034 }
3035 [NSApp sendEvent:event];
3036 }
3037 }
3038 */
3039}
3040
3041- (void)drawRect:(NSRect)rect {
3042 _SOKOL_UNUSED(rect);
3043 /* Catch any last-moment input events */
3044 _sapp_macos_poll_input_events();
3045 _sapp_macos_frame();
3046 #if !defined(SOKOL_METAL)
3047 [[_sapp.macos.view openGLContext] flushBuffer];
3048 #endif
3049}
3050
3051- (BOOL)isOpaque {
3052 return YES;
3053}
3054- (BOOL)canBecomeKeyView {
3055 return YES;
3056}
3057- (BOOL)acceptsFirstResponder {
3058 return YES;
3059}
3060- (BOOL)acceptsFirstMouse:(NSEvent *)event {
3061 return YES;
3062}
3063
3064
3065- (void)updateTrackingAreas {
3066 if (_sapp.macos.tracking_area != nil) {
3067 [self removeTrackingArea:_sapp.macos.tracking_area];
3068 _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area);
3069 }
3070 const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
3071 NSTrackingActiveInKeyWindow |
3072 NSTrackingEnabledDuringMouseDrag |
3073 NSTrackingCursorUpdate |
3074 NSTrackingInVisibleRect |
3075 NSTrackingAssumeInside;
3076 _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
3077 [self addTrackingArea:_sapp.macos.tracking_area];
3078 [super updateTrackingAreas];
3079}
3080- (void)mouseEntered:(NSEvent*)event {
3081 _sapp_macos_update_mouse(event);
3082 /* don't send mouse enter/leave while dragging (so that it behaves the same as
3083 on Windows while SetCapture is active
3084 */
3085 if (0 == _sapp.macos.mouse_buttons) {
3086 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
3087 }
3088}
3089- (void)mouseExited:(NSEvent*)event {
3090 _sapp_macos_update_mouse(event);
3091 if (0 == _sapp.macos.mouse_buttons) {
3092 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
3093 }
3094}
3095- (void)mouseDown:(NSEvent*)event {
3096 _sapp_macos_update_mouse(event);
3097 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
3098 _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT);
3099}
3100- (void)mouseUp:(NSEvent*)event {
3101 _sapp_macos_update_mouse(event);
3102 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
3103 _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT);
3104}
3105- (void)rightMouseDown:(NSEvent*)event {
3106 _sapp_macos_update_mouse(event);
3107 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
3108 _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT);
3109}
3110- (void)rightMouseUp:(NSEvent*)event {
3111 _sapp_macos_update_mouse(event);
3112 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
3113 _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT);
3114}
3115- (void)otherMouseDown:(NSEvent*)event {
3116 _sapp_macos_update_mouse(event);
3117 if (2 == event.buttonNumber) {
3118 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
3119 _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE);
3120 }
3121}
3122- (void)otherMouseUp:(NSEvent*)event {
3123 _sapp_macos_update_mouse(event);
3124 if (2 == event.buttonNumber) {
3125 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
3126 _sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE);
3127 }
3128}
3129- (void)otherMouseDragged:(NSEvent*)event {
3130 _sapp_macos_update_mouse(event);
3131 if (2 == event.buttonNumber) {
3132 if (_sapp.mouse.locked) {
3133 _sapp.mouse.dx = [event deltaX];
3134 _sapp.mouse.dy = [event deltaY];
3135 }
3136 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
3137 }
3138}
3139- (void)mouseMoved:(NSEvent*)event {
3140 _sapp_macos_update_mouse(event);
3141 if (_sapp.mouse.locked) {
3142 _sapp.mouse.dx = [event deltaX];
3143 _sapp.mouse.dy = [event deltaY];
3144 }
3145 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
3146}
3147- (void)mouseDragged:(NSEvent*)event {
3148 _sapp_macos_update_mouse(event);
3149 if (_sapp.mouse.locked) {
3150 _sapp.mouse.dx = [event deltaX];
3151 _sapp.mouse.dy = [event deltaY];
3152 }
3153 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
3154}
3155- (void)rightMouseDragged:(NSEvent*)event {
3156 _sapp_macos_update_mouse(event);
3157 if (_sapp.mouse.locked) {
3158 _sapp.mouse.dx = [event deltaX];
3159 _sapp.mouse.dy = [event deltaY];
3160 }
3161 _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
3162}
3163- (void)scrollWheel:(NSEvent*)event {
3164 _sapp_macos_update_mouse(event);
3165 if (_sapp_events_enabled()) {
3166 float dx = (float) event.scrollingDeltaX;
3167 float dy = (float) event.scrollingDeltaY;
3168 if (event.hasPreciseScrollingDeltas) {
3169 dx *= 0.1;
3170 dy *= 0.1;
3171 }
3172 if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) {
3173 _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
3174 _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags);
3175 _sapp.event.scroll_x = dx;
3176 _sapp.event.scroll_y = dy;
3177 _sapp_call_event(&_sapp.event);
3178 }
3179 }
3180}
3181- (void)keyDown:(NSEvent*)event {
3182 if (_sapp_events_enabled()) {
3183 const uint32_t mods = _sapp_macos_mod(event.modifierFlags);
3184 /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed,
3185 as a workaround, to prevent key presses from sticking we'll send
3186 a keyup event following right after the keydown if SUPER is also pressed
3187 */
3188 const sapp_keycode key_code = _sapp_translate_key(event.keyCode);
3189 _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods);
3190 if (0 != (mods & SAPP_MODIFIER_SUPER)) {
3191 _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods);
3192 }
3193 const NSString* chars = event.characters;
3194 const NSUInteger len = chars.length;
3195 if (len > 0) {
3196 _sapp_init_event(SAPP_EVENTTYPE_CHAR);
3197 _sapp.event.modifiers = mods;
3198 for (NSUInteger i = 0; i < len; i++) {
3199 const unichar codepoint = [chars characterAtIndex:i];
3200 if ((codepoint & 0xFF00) == 0xF700) {
3201 continue;
3202 }
3203 _sapp.event.char_code = codepoint;
3204 _sapp.event.key_repeat = event.isARepeat;
3205 _sapp_call_event(&_sapp.event);
3206 }
3207 }
3208 /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */
3209 if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) {
3210 _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
3211 _sapp_call_event(&_sapp.event);
3212 }
3213 }
3214}
3215- (void)keyUp:(NSEvent*)event {
3216 _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP,
3217 _sapp_translate_key(event.keyCode),
3218 event.isARepeat,
3219 _sapp_macos_mod(event.modifierFlags));
3220}
3221- (void)flagsChanged:(NSEvent*)event {
3222 const uint32_t old_f = _sapp.macos.flags_changed_store;
3223 const uint32_t new_f = event.modifierFlags;
3224 _sapp.macos.flags_changed_store = new_f;
3225 sapp_keycode key_code = SAPP_KEYCODE_INVALID;
3226 bool down = false;
3227 if ((new_f ^ old_f) & NSEventModifierFlagShift) {
3228 key_code = SAPP_KEYCODE_LEFT_SHIFT;
3229 down = 0 != (new_f & NSEventModifierFlagShift);
3230 }
3231 if ((new_f ^ old_f) & NSEventModifierFlagControl) {
3232 key_code = SAPP_KEYCODE_LEFT_CONTROL;
3233 down = 0 != (new_f & NSEventModifierFlagControl);
3234 }
3235 if ((new_f ^ old_f) & NSEventModifierFlagOption) {
3236 key_code = SAPP_KEYCODE_LEFT_ALT;
3237 down = 0 != (new_f & NSEventModifierFlagOption);
3238 }
3239 if ((new_f ^ old_f) & NSEventModifierFlagCommand) {
3240 key_code = SAPP_KEYCODE_LEFT_SUPER;
3241 down = 0 != (new_f & NSEventModifierFlagCommand);
3242 }
3243 if (key_code != SAPP_KEYCODE_INVALID) {
3244 _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP,
3245 key_code,
3246 false,
3247 _sapp_macos_mod(event.modifierFlags));
3248 }
3249}
3250- (void)cursorUpdate:(NSEvent*)event {
3251 _SOKOL_UNUSED(event);
3252 if (_sapp.desc.user_cursor) {
3253 _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR);
3254 }
3255}
3256@end
3257
3258#endif /* MacOS */
3259
3260/*== iOS =====================================================================*/
3261#if defined(_SAPP_IOS)
3262
3263_SOKOL_PRIVATE void _sapp_ios_discard_state(void) {
3264 // NOTE: it's safe to call [release] on a nil object
3265 _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg);
3266 _SAPP_OBJC_RELEASE(_sapp.ios.textfield);
3267 #if defined(SOKOL_METAL)
3268 _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
3269 _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device);
3270 #else
3271 _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
3272 _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx);
3273 #endif
3274 _SAPP_OBJC_RELEASE(_sapp.ios.view);
3275 _SAPP_OBJC_RELEASE(_sapp.ios.window);
3276}
3277
3278_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) {
3279 _sapp_init_state(desc);
3280 static int argc = 1;
3281 static char* argv[] = { (char*)"sokol_app" };
3282 UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class]));
3283}
3284
3285/* iOS entry function */
3286#if !defined(SOKOL_NO_ENTRY)
3287int main(int argc, char* argv[]) {
3288 sapp_desc desc = sokol_main(argc, argv);
3289 _sapp_ios_run(&desc);
3290 return 0;
3291}
3292#endif /* SOKOL_NO_ENTRY */
3293
3294_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) {
3295 if (_sapp_events_enabled()) {
3296 _sapp_init_event(type);
3297 _sapp_call_event(&_sapp.event);
3298 }
3299}
3300
3301_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>* touches, UIEvent* event) {
3302 if (_sapp_events_enabled()) {
3303 _sapp_init_event(type);
3304 NSEnumerator* enumerator = event.allTouches.objectEnumerator;
3305 UITouch* ios_touch;
3306 while ((ios_touch = [enumerator nextObject])) {
3307 if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) {
3308 CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view];
3309 sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++];
3310 cur_point->identifier = (uintptr_t) ios_touch;
3311 cur_point->pos_x = ios_pos.x * _sapp.dpi_scale;
3312 cur_point->pos_y = ios_pos.y * _sapp.dpi_scale;
3313 cur_point->changed = [touches containsObject:ios_touch];
3314 }
3315 }
3316 if (_sapp.event.num_touches > 0) {
3317 _sapp_call_event(&_sapp.event);
3318 }
3319 }
3320}
3321
3322_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) {
3323 CGRect screen_rect = UIScreen.mainScreen.bounds;
3324 _sapp.framebuffer_width = (int)(screen_rect.size.width * _sapp.dpi_scale);
3325 _sapp.framebuffer_height = (int)(screen_rect.size.height * _sapp.dpi_scale);
3326 _sapp.window_width = (int)screen_rect.size.width;
3327 _sapp.window_height = (int)screen_rect.size.height;
3328 int cur_fb_width, cur_fb_height;
3329 #if defined(SOKOL_METAL)
3330 const CGSize fb_size = _sapp.ios.view.drawableSize;
3331 cur_fb_width = (int) fb_size.width;
3332 cur_fb_height = (int) fb_size.height;
3333 #else
3334 cur_fb_width = (int) _sapp.ios.view.drawableWidth;
3335 cur_fb_height = (int) _sapp.ios.view.drawableHeight;
3336 #endif
3337 const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
3338 (_sapp.framebuffer_height != cur_fb_height);
3339 if (dim_changed) {
3340 #if defined(SOKOL_METAL)
3341 const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
3342 _sapp.ios.view.drawableSize = drawable_size;
3343 #else
3344 // nothing to do here, GLKView correctly respects the view's contentScaleFactor
3345 #endif
3346 if (!_sapp.first_frame) {
3347 _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED);
3348 }
3349 }
3350}
3351
3352_SOKOL_PRIVATE void _sapp_ios_frame(void) {
3353 _sapp_ios_update_dimensions();
3354 _sapp_frame();
3355}
3356
3357_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
3358 /* if not happened yet, create an invisible text field */
3359 if (nil == _sapp.ios.textfield) {
3360 _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init];
3361 _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
3362 _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault;
3363 _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault;
3364 _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
3365 _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo;
3366 _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo;
3367 _sapp.ios.textfield.hidden = YES;
3368 _sapp.ios.textfield.text = @"x";
3369 _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg;
3370 [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield];
3371
3372 [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
3373 selector:@selector(keyboardWasShown:)
3374 name:UIKeyboardDidShowNotification object:nil];
3375 [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
3376 selector:@selector(keyboardWillBeHidden:)
3377 name:UIKeyboardWillHideNotification object:nil];
3378 [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
3379 selector:@selector(keyboardDidChangeFrame:)
3380 name:UIKeyboardDidChangeFrameNotification object:nil];
3381 }
3382 if (shown) {
3383 /* setting the text field as first responder brings up the onscreen keyboard */
3384 [_sapp.ios.textfield becomeFirstResponder];
3385 }
3386 else {
3387 [_sapp.ios.textfield resignFirstResponder];
3388 }
3389}
3390
3391@implementation _sapp_app_delegate
3392- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
3393 CGRect screen_rect = UIScreen.mainScreen.bounds;
3394 _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect];
3395 _sapp.window_width = screen_rect.size.width;
3396 _sapp.window_height = screen_rect.size.height;
3397 if (_sapp.desc.high_dpi) {
3398 _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale;
3399 }
3400 else {
3401 _sapp.dpi_scale = 1.0f;
3402 }
3403 _sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale;
3404 _sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale;
3405 #if defined(SOKOL_METAL)
3406 _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice();
3407 _sapp.ios.view = [[_sapp_ios_view alloc] init];
3408 _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval;
3409 _sapp.ios.view.device = _sapp.ios.mtl_device;
3410 _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
3411 _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
3412 _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count;
3413 /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor
3414 and automatically renders at Retina resolution. We'll disable
3415 autoResize and instead do the resizing in _sapp_ios_update_dimensions()
3416 */
3417 _sapp.ios.view.autoResizeDrawable = false;
3418 _sapp.ios.view.userInteractionEnabled = YES;
3419 _sapp.ios.view.multipleTouchEnabled = YES;
3420 _sapp.ios.view_ctrl = [[UIViewController alloc] init];
3421 _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen;
3422 _sapp.ios.view_ctrl.view = _sapp.ios.view;
3423 _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
3424 #else
3425 if (_sapp.desc.gl_force_gles2) {
3426 _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
3427 _sapp.gles2_fallback = true;
3428 }
3429 else {
3430 _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
3431 if (_sapp.ios.eagl_ctx == nil) {
3432 _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
3433 _sapp.gles2_fallback = true;
3434 }
3435 }
3436 _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect];
3437 _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
3438 _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
3439 _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone;
3440 GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone;
3441 _sapp.ios.view.drawableMultisample = msaa;
3442 _sapp.ios.view.context = _sapp.ios.eagl_ctx;
3443 _sapp.ios.view.enableSetNeedsDisplay = NO;
3444 _sapp.ios.view.userInteractionEnabled = YES;
3445 _sapp.ios.view.multipleTouchEnabled = YES;
3446 // on GLKView, contentScaleFactor appears to work just fine!
3447 if (_sapp.desc.high_dpi) {
3448 _sapp.ios.view.contentScaleFactor = 2.0;
3449 }
3450 else {
3451 _sapp.ios.view.contentScaleFactor = 1.0;
3452 }
3453 _sapp.ios.view_ctrl = [[GLKViewController alloc] init];
3454 _sapp.ios.view_ctrl.view = _sapp.ios.view;
3455 _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval;
3456 _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
3457 #endif
3458 [_sapp.ios.window makeKeyAndVisible];
3459
3460 _sapp.valid = true;
3461 return YES;
3462}
3463
3464- (void)applicationWillResignActive:(UIApplication *)application {
3465 if (!_sapp.ios.suspended) {
3466 _sapp.ios.suspended = true;
3467 _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED);
3468 }
3469}
3470
3471- (void)applicationDidBecomeActive:(UIApplication *)application {
3472 if (_sapp.ios.suspended) {
3473 _sapp.ios.suspended = false;
3474 _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED);
3475 }
3476}
3477
3478/* NOTE: this method will rarely ever be called, iOS application
3479 which are terminated by the user are usually killed via signal 9
3480 by the operating system.
3481*/
3482- (void)applicationWillTerminate:(UIApplication *)application {
3483 _SOKOL_UNUSED(application);
3484 _sapp_call_cleanup();
3485 _sapp_ios_discard_state();
3486 _sapp_discard_state();
3487}
3488@end
3489
3490@implementation _sapp_textfield_dlg
3491- (void)keyboardWasShown:(NSNotification*)notif {
3492 _sapp.onscreen_keyboard_shown = true;
3493 /* query the keyboard's size, and modify the content view's size */
3494 if (_sapp.desc.ios_keyboard_resizes_canvas) {
3495 NSDictionary* info = notif.userInfo;
3496 CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
3497 CGRect view_frame = UIScreen.mainScreen.bounds;
3498 view_frame.size.height -= kbd_h;
3499 _sapp.ios.view.frame = view_frame;
3500 }
3501}
3502- (void)keyboardWillBeHidden:(NSNotification*)notif {
3503 _sapp.onscreen_keyboard_shown = false;
3504 if (_sapp.desc.ios_keyboard_resizes_canvas) {
3505 _sapp.ios.view.frame = UIScreen.mainScreen.bounds;
3506 }
3507}
3508- (void)keyboardDidChangeFrame:(NSNotification*)notif {
3509 /* this is for the case when the screen rotation changes while the keyboard is open */
3510 if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) {
3511 NSDictionary* info = notif.userInfo;
3512 CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
3513 CGRect view_frame = UIScreen.mainScreen.bounds;
3514 view_frame.size.height -= kbd_h;
3515 _sapp.ios.view.frame = view_frame;
3516 }
3517}
3518- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
3519 if (_sapp_events_enabled()) {
3520 const NSUInteger len = string.length;
3521 if (len > 0) {
3522 for (NSUInteger i = 0; i < len; i++) {
3523 unichar c = [string characterAtIndex:i];
3524 if (c >= 32) {
3525 /* ignore surrogates for now */
3526 if ((c < 0xD800) || (c > 0xDFFF)) {
3527 _sapp_init_event(SAPP_EVENTTYPE_CHAR);
3528 _sapp.event.char_code = c;
3529 _sapp_call_event(&_sapp.event);
3530 }
3531 }
3532 if (c <= 32) {
3533 sapp_keycode k = SAPP_KEYCODE_INVALID;
3534 switch (c) {
3535 case 10: k = SAPP_KEYCODE_ENTER; break;
3536 case 32: k = SAPP_KEYCODE_SPACE; break;
3537 default: break;
3538 }
3539 if (k != SAPP_KEYCODE_INVALID) {
3540 _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
3541 _sapp.event.key_code = k;
3542 _sapp_call_event(&_sapp.event);
3543 _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
3544 _sapp.event.key_code = k;
3545 _sapp_call_event(&_sapp.event);
3546 }
3547 }
3548 }
3549 }
3550 else {
3551 /* this was a backspace */
3552 _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
3553 _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
3554 _sapp_call_event(&_sapp.event);
3555 _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
3556 _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
3557 _sapp_call_event(&_sapp.event);
3558 }
3559 }
3560 return NO;
3561}
3562@end
3563
3564@implementation _sapp_ios_view
3565- (void)drawRect:(CGRect)rect {
3566 _SOKOL_UNUSED(rect);
3567 _sapp_ios_frame();
3568}
3569- (BOOL)isOpaque {
3570 return YES;
3571}
3572- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
3573 _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event);
3574}
3575- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
3576 _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event);
3577}
3578- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
3579 _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event);
3580}
3581- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
3582 _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event);
3583}
3584@end
3585#endif /* TARGET_OS_IPHONE */
3586
3587#endif /* _SAPP_APPLE */
3588
3589/*== EMSCRIPTEN ==============================================================*/
3590#if defined(_SAPP_EMSCRIPTEN)
3591
3592#ifdef __cplusplus
3593extern "C" {
3594#endif
3595
3596typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*);
3597
3598/* this function is called from a JS event handler when the user hides
3599 the onscreen keyboard pressing the 'dismiss keyboard key'
3600*/
3601EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) {
3602 _sapp.onscreen_keyboard_shown = false;
3603}
3604
3605EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) {
3606 if (_sapp.clipboard.enabled) {
3607 _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
3608 if (_sapp_events_enabled()) {
3609 _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
3610 _sapp_call_event(&_sapp.event);
3611 }
3612 }
3613}
3614
3615/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */
3616EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) {
3617 return _sapp.html5_ask_leave_site ? 1 : 0;
3618}
3619
3620EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) {
3621 if (!_sapp.drop.enabled) {
3622 return;
3623 }
3624 if (num < 0) {
3625 num = 0;
3626 }
3627 if (num > _sapp.drop.max_files) {
3628 num = _sapp.drop.max_files;
3629 }
3630 _sapp.drop.num_files = num;
3631 _sapp_clear_drop_buffer();
3632}
3633
3634EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) {
3635 /* NOTE: name is only the filename part, not a path */
3636 if (!_sapp.drop.enabled) {
3637 return;
3638 }
3639 if (0 == name) {
3640 return;
3641 }
3642 SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files);
3643 if ((i < 0) || (i >= _sapp.drop.num_files)) {
3644 return;
3645 }
3646 if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) {
3647 SOKOL_LOG("sokol_app.h: dropped file path too long!\n");
3648 _sapp.drop.num_files = 0;
3649 }
3650}
3651
3652EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) {
3653 if (!_sapp.drop.enabled) {
3654 return;
3655 }
3656 if (0 == _sapp.drop.num_files) {
3657 /* there was an error copying the filenames */
3658 _sapp_clear_drop_buffer();
3659 return;
3660
3661 }
3662 if (_sapp_events_enabled()) {
3663 _sapp.mouse.x = (float)x * _sapp.dpi_scale;
3664 _sapp.mouse.y = (float)y * _sapp.dpi_scale;
3665 _sapp.mouse.dx = 0.0f;
3666 _sapp.mouse.dy = 0.0f;
3667 _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
3668 _sapp_call_event(&_sapp.event);
3669 }
3670}
3671
3672EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) {
3673 sapp_html5_fetch_response response;
3674 memset(&response, 0, sizeof(response));
3675 response.succeeded = (0 != success);
3676 response.error_code = (sapp_html5_fetch_error) error_code;
3677 response.file_index = index;
3678 response.fetched_size = fetched_size;
3679 response.buffer_ptr = buf_ptr;
3680 response.buffer_size = buf_size;
3681 response.user_data = user_data;
3682 callback(&response);
3683}
3684
3685#ifdef __cplusplus
3686} /* extern "C" */
3687#endif
3688
3689/* Javascript helper functions for mobile virtual keyboard input */
3690EM_JS(void, sapp_js_create_textfield, (void), {
3691 var _sapp_inp = document.createElement("input");
3692 _sapp_inp.type = "text";
3693 _sapp_inp.id = "_sokol_app_input_element";
3694 _sapp_inp.autocapitalize = "none";
3695 _sapp_inp.addEventListener("focusout", function(_sapp_event) {
3696 __sapp_emsc_notify_keyboard_hidden()
3697
3698 });
3699 document.body.append(_sapp_inp);
3700});
3701
3702EM_JS(void, sapp_js_focus_textfield, (void), {
3703 document.getElementById("_sokol_app_input_element").focus();
3704});
3705
3706EM_JS(void, sapp_js_unfocus_textfield, (void), {
3707 document.getElementById("_sokol_app_input_element").blur();
3708});
3709
3710EM_JS(void, sapp_js_add_beforeunload_listener, (void), {
3711 Module.sokol_beforeunload = function(event) {
3712 if (__sapp_html5_get_ask_leave_site() != 0) {
3713 event.preventDefault();
3714 event.returnValue = ' ';
3715 }
3716 };
3717 window.addEventListener('beforeunload', Module.sokol_beforeunload);
3718});
3719
3720EM_JS(void, sapp_js_remove_beforeunload_listener, (void), {
3721 window.removeEventListener('beforeunload', Module.sokol_beforeunload);
3722});
3723
3724EM_JS(void, sapp_js_add_clipboard_listener, (void), {
3725 Module.sokol_paste = function(event) {
3726 var pasted_str = event.clipboardData.getData('text');
3727 ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]);
3728 };
3729 window.addEventListener('paste', Module.sokol_paste);
3730});
3731
3732EM_JS(void, sapp_js_remove_clipboard_listener, (void), {
3733 window.removeEventListener('paste', Module.sokol_paste);
3734});
3735
3736EM_JS(void, sapp_js_write_clipboard, (const char* c_str), {
3737 var str = UTF8ToString(c_str);
3738 var ta = document.createElement('textarea');
3739 ta.setAttribute('autocomplete', 'off');
3740 ta.setAttribute('autocorrect', 'off');
3741 ta.setAttribute('autocapitalize', 'off');
3742 ta.setAttribute('spellcheck', 'false');
3743 ta.style.left = -100 + 'px';
3744 ta.style.top = -100 + 'px';
3745 ta.style.height = 1;
3746 ta.style.width = 1;
3747 ta.value = str;
3748 document.body.appendChild(ta);
3749 ta.select();
3750 document.execCommand('copy');
3751 document.body.removeChild(ta);
3752});
3753
3754_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) {
3755 sapp_js_write_clipboard(str);
3756}
3757
3758EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), {
3759 Module.sokol_drop_files = [];
3760 var canvas_name = UTF8ToString(canvas_name_cstr);
3761 var canvas = document.getElementById(canvas_name);
3762 Module.sokol_dragenter = function(event) {
3763 event.stopPropagation();
3764 event.preventDefault();
3765 };
3766 Module.sokol_dragleave = function(event) {
3767 event.stopPropagation();
3768 event.preventDefault();
3769 };
3770 Module.sokol_dragover = function(event) {
3771 event.stopPropagation();
3772 event.preventDefault();
3773 };
3774 Module.sokol_drop = function(event) {
3775 event.stopPropagation();
3776 event.preventDefault();
3777 var files = event.dataTransfer.files;
3778 Module.sokol_dropped_files = files;
3779 __sapp_emsc_begin_drop(files.length);
3780 var i;
3781 for (i = 0; i < files.length; i++) {
3782 ccall('_sapp_emsc_drop', 'void', ['number', 'string'], [i, files[i].name]);
3783 }
3784 // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect
3785 __sapp_emsc_end_drop(event.clientX, event.clientY);
3786 };
3787 canvas.addEventListener('dragenter', Module.sokol_dragenter, false);
3788 canvas.addEventListener('dragleave', Module.sokol_dragleave, false);
3789 canvas.addEventListener('dragover', Module.sokol_dragover, false);
3790 canvas.addEventListener('drop', Module.sokol_drop, false);
3791});
3792
3793EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), {
3794 if ((index < 0) || (index >= Module.sokol_dropped_files.length)) {
3795 return 0;
3796 }
3797 else {
3798 return Module.sokol_dropped_files[index].size;
3799 }
3800});
3801
3802EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), {
3803 var reader = new FileReader();
3804 reader.onload = function(loadEvent) {
3805 var content = loadEvent.target.result;
3806 if (content.byteLength > buf_size) {
3807 // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL
3808 __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data);
3809 }
3810 else {
3811 HEAPU8.set(new Uint8Array(content), buf_ptr);
3812 __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data);
3813 }
3814 };
3815 reader.onerror = function() {
3816 // SAPP_HTML5_FETCH_ERROR_OTHER
3817 __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data);
3818 };
3819 reader.readAsArrayBuffer(Module.sokol_dropped_files[index]);
3820});
3821
3822EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), {
3823 var canvas_name = UTF8ToString(canvas_name_cstr);
3824 var canvas = document.getElementById(canvas_name);
3825 canvas.removeEventListener('dragenter', Module.sokol_dragenter);
3826 canvas.removeEventListener('dragleave', Module.sokol_dragleave);
3827 canvas.removeEventListener('dragover', Module.sokol_dragover);
3828 canvas.removeEventListener('drop', Module.sokol_drop);
3829});
3830
3831/* called from the emscripten event handler to update the keyboard visibility
3832 state, this must happen from an JS input event handler, otherwise
3833 the request will be ignored by the browser
3834*/
3835_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) {
3836 if (_sapp.emsc.wants_show_keyboard) {
3837 /* create input text field on demand */
3838 if (!_sapp.emsc.textfield_created) {
3839 _sapp.emsc.textfield_created = true;
3840 sapp_js_create_textfield();
3841 }
3842 /* focus the text input field, this will bring up the keyboard */
3843 _sapp.onscreen_keyboard_shown = true;
3844 _sapp.emsc.wants_show_keyboard = false;
3845 sapp_js_focus_textfield();
3846 }
3847 if (_sapp.emsc.wants_hide_keyboard) {
3848 /* unfocus the text input field */
3849 if (_sapp.emsc.textfield_created) {
3850 _sapp.onscreen_keyboard_shown = false;
3851 _sapp.emsc.wants_hide_keyboard = false;
3852 sapp_js_unfocus_textfield();
3853 }
3854 }
3855}
3856
3857/* actually showing the onscreen keyboard must be initiated from a JS
3858 input event handler, so we'll just keep track of the desired
3859 state, and the actual state change will happen with the next input event
3860*/
3861_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) {
3862 if (show) {
3863 _sapp.emsc.wants_show_keyboard = true;
3864 }
3865 else {
3866 _sapp.emsc.wants_hide_keyboard = true;
3867 }
3868}
3869
3870EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), {
3871 // lookup and store canvas object by name
3872 var target_str = UTF8ToString(c_str_target);
3873 Module.sapp_emsc_target = document.getElementById(target_str);
3874 if (!Module.sapp_emsc_target) {
3875 console.log("sokol_app.h: invalid target:" + target_str);
3876 }
3877 if (!Module.sapp_emsc_target.requestPointerLock) {
3878 console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str);
3879 }
3880});
3881
3882_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) {
3883 _SOKOL_UNUSED(emsc_type);
3884 _SOKOL_UNUSED(user_data);
3885 _sapp.mouse.locked = emsc_event->isActive;
3886 return EM_TRUE;
3887}
3888
3889_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) {
3890 _SOKOL_UNUSED(emsc_type);
3891 _SOKOL_UNUSED(reserved);
3892 _SOKOL_UNUSED(user_data);
3893 _sapp.mouse.locked = false;
3894 _sapp.emsc.mouse_lock_requested = false;
3895 return true;
3896}
3897
3898EM_JS(void, sapp_js_request_pointerlock, (void), {
3899 if (Module.sapp_emsc_target) {
3900 if (Module.sapp_emsc_target.requestPointerLock) {
3901 Module.sapp_emsc_target.requestPointerLock();
3902 }
3903 }
3904});
3905
3906EM_JS(void, sapp_js_exit_pointerlock, (void), {
3907 if (document.exitPointerLock) {
3908 document.exitPointerLock();
3909 }
3910});
3911
3912_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) {
3913 if (lock) {
3914 /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */
3915 _sapp.emsc.mouse_lock_requested = true;
3916 }
3917 else {
3918 /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */
3919 _sapp.emsc.mouse_lock_requested = false;
3920 sapp_js_exit_pointerlock();
3921 }
3922}
3923
3924/* called from inside event handlers to check if mouse lock had been requested,
3925 and if yes, actually enter mouse lock.
3926*/
3927_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) {
3928 if (_sapp.emsc.mouse_lock_requested) {
3929 _sapp.emsc.mouse_lock_requested = false;
3930 sapp_js_request_pointerlock();
3931 }
3932}
3933
3934#if defined(SOKOL_WGPU)
3935_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void);
3936_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void);
3937#endif
3938
3939_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
3940 _SOKOL_UNUSED(event_type);
3941 _SOKOL_UNUSED(user_data);
3942 double w, h;
3943 emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
3944 /* The above method might report zero when toggling HTML5 fullscreen,
3945 in that case use the window's inner width reported by the
3946 emscripten event. This works ok when toggling *into* fullscreen
3947 but doesn't properly restore the previous canvas size when switching
3948 back from fullscreen.
3949
3950 In general, due to the HTML5's fullscreen API's flaky nature it is
3951 recommended to use 'soft fullscreen' (stretching the WebGL canvas
3952 over the browser windows client rect) with a CSS definition like this:
3953
3954 position: absolute;
3955 top: 0px;
3956 left: 0px;
3957 margin: 0px;
3958 border: 0;
3959 width: 100%;
3960 height: 100%;
3961 overflow: hidden;
3962 display: block;
3963 */
3964 if (w < 1.0) {
3965 w = ui_event->windowInnerWidth;
3966 }
3967 else {
3968 _sapp.window_width = (int) w;
3969 }
3970 if (h < 1.0) {
3971 h = ui_event->windowInnerHeight;
3972 }
3973 else {
3974 _sapp.window_height = (int) h;
3975 }
3976 if (_sapp.desc.high_dpi) {
3977 _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
3978 }
3979 _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
3980 _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
3981 SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
3982 emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
3983 #if defined(SOKOL_WGPU)
3984 /* on WebGPU: recreate size-dependent rendering surfaces */
3985 _sapp_emsc_wgpu_surfaces_discard();
3986 _sapp_emsc_wgpu_surfaces_create();
3987 #endif
3988 if (_sapp_events_enabled()) {
3989 _sapp_init_event(SAPP_EVENTTYPE_RESIZED);
3990 _sapp_call_event(&_sapp.event);
3991 }
3992 return true;
3993}
3994
3995_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) {
3996 _SOKOL_UNUSED(user_data);
3997 if (_sapp.mouse.locked) {
3998 _sapp.mouse.dx = (float) emsc_event->movementX;
3999 _sapp.mouse.dy = (float) emsc_event->movementY;
4000 }
4001 else {
4002 float new_x = emsc_event->targetX * _sapp.dpi_scale;
4003 float new_y = emsc_event->targetY * _sapp.dpi_scale;
4004 if (_sapp.mouse.pos_valid) {
4005 _sapp.mouse.dx = new_x - _sapp.mouse.x;
4006 _sapp.mouse.dy = new_y - _sapp.mouse.y;
4007 }
4008 _sapp.mouse.x = new_x;
4009 _sapp.mouse.y = new_y;
4010 _sapp.mouse.pos_valid = true;
4011 }
4012 if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) {
4013 sapp_event_type type;
4014 bool is_button_event = false;
4015 switch (emsc_type) {
4016 case EMSCRIPTEN_EVENT_MOUSEDOWN:
4017 type = SAPP_EVENTTYPE_MOUSE_DOWN;
4018 is_button_event = true;
4019 break;
4020 case EMSCRIPTEN_EVENT_MOUSEUP:
4021 type = SAPP_EVENTTYPE_MOUSE_UP;
4022 is_button_event = true;
4023 break;
4024 case EMSCRIPTEN_EVENT_MOUSEMOVE:
4025 type = SAPP_EVENTTYPE_MOUSE_MOVE;
4026 break;
4027 case EMSCRIPTEN_EVENT_MOUSEENTER:
4028 type = SAPP_EVENTTYPE_MOUSE_ENTER;
4029 break;
4030 case EMSCRIPTEN_EVENT_MOUSELEAVE:
4031 type = SAPP_EVENTTYPE_MOUSE_LEAVE;
4032 break;
4033 default:
4034 type = SAPP_EVENTTYPE_INVALID;
4035 break;
4036 }
4037 if (type != SAPP_EVENTTYPE_INVALID) {
4038 _sapp_init_event(type);
4039 if (emsc_event->ctrlKey) {
4040 _sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
4041 }
4042 if (emsc_event->shiftKey) {
4043 _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
4044 }
4045 if (emsc_event->altKey) {
4046 _sapp.event.modifiers |= SAPP_MODIFIER_ALT;
4047 }
4048 if (emsc_event->metaKey) {
4049 _sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
4050 }
4051 if (is_button_event) {
4052 switch (emsc_event->button) {
4053 case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break;
4054 case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break;
4055 case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break;
4056 default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break;
4057 }
4058 }
4059 else {
4060 _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
4061 }
4062 _sapp_call_event(&_sapp.event);
4063 }
4064 /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */
4065 if (is_button_event) {
4066 _sapp_emsc_update_mouse_lock_state();
4067 }
4068 }
4069 _sapp_emsc_update_keyboard_state();
4070 return true;
4071}
4072
4073_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) {
4074 _SOKOL_UNUSED(emsc_type);
4075 _SOKOL_UNUSED(user_data);
4076 if (_sapp_events_enabled()) {
4077 _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
4078 if (emsc_event->mouse.ctrlKey) {
4079 _sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
4080 }
4081 if (emsc_event->mouse.shiftKey) {
4082 _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
4083 }
4084 if (emsc_event->mouse.altKey) {
4085 _sapp.event.modifiers |= SAPP_MODIFIER_ALT;
4086 }
4087 if (emsc_event->mouse.metaKey) {
4088 _sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
4089 }
4090 /* see https://github.com/floooh/sokol/issues/339 */
4091 float scale;
4092 switch (emsc_event->deltaMode) {
4093 case DOM_DELTA_PIXEL: scale = -0.04f; break;
4094 case DOM_DELTA_LINE: scale = -1.33f; break;
4095 case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess
4096 default: scale = -0.1f; break; // shouldn't happen
4097 }
4098 _sapp.event.scroll_x = scale * (float)emsc_event->deltaX;
4099 _sapp.event.scroll_y = scale * (float)emsc_event->deltaY;
4100 _sapp_call_event(&_sapp.event);
4101 }
4102 _sapp_emsc_update_keyboard_state();
4103 _sapp_emsc_update_mouse_lock_state();
4104 return true;
4105}
4106
4107_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) {
4108 _SOKOL_UNUSED(user_data);
4109 bool retval = true;
4110 if (_sapp_events_enabled()) {
4111 sapp_event_type type;
4112 switch (emsc_type) {
4113 case EMSCRIPTEN_EVENT_KEYDOWN:
4114 type = SAPP_EVENTTYPE_KEY_DOWN;
4115 break;
4116 case EMSCRIPTEN_EVENT_KEYUP:
4117 type = SAPP_EVENTTYPE_KEY_UP;
4118 break;
4119 case EMSCRIPTEN_EVENT_KEYPRESS:
4120 type = SAPP_EVENTTYPE_CHAR;
4121 break;
4122 default:
4123 type = SAPP_EVENTTYPE_INVALID;
4124 break;
4125 }
4126 if (type != SAPP_EVENTTYPE_INVALID) {
4127 bool send_keyup_followup = false;
4128 _sapp_init_event(type);
4129 _sapp.event.key_repeat = emsc_event->repeat;
4130 if (emsc_event->ctrlKey) {
4131 _sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
4132 }
4133 if (emsc_event->shiftKey) {
4134 _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
4135 }
4136 if (emsc_event->altKey) {
4137 _sapp.event.modifiers |= SAPP_MODIFIER_ALT;
4138 }
4139 if (emsc_event->metaKey) {
4140 _sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
4141 }
4142 if (type == SAPP_EVENTTYPE_CHAR) {
4143 _sapp.event.char_code = emsc_event->charCode;
4144 /* workaround to make Cmd+V work on Safari */
4145 if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) {
4146 retval = false;
4147 }
4148 }
4149 else {
4150 _sapp.event.key_code = _sapp_translate_key((int)emsc_event->keyCode);
4151 /* Special hack for macOS: if the Super key is pressed, macOS doesn't
4152 send keyUp events. As a workaround, to prevent keys from
4153 "sticking", we'll send a keyup event following a keydown
4154 when the SUPER key is pressed
4155 */
4156 if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
4157 (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
4158 (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
4159 (_sapp.event.modifiers & SAPP_MODIFIER_SUPER))
4160 {
4161 send_keyup_followup = true;
4162 }
4163 /* only forward a certain key ranges to the browser */
4164 switch (_sapp.event.key_code) {
4165 case SAPP_KEYCODE_WORLD_1:
4166 case SAPP_KEYCODE_WORLD_2:
4167 case SAPP_KEYCODE_ESCAPE:
4168 case SAPP_KEYCODE_ENTER:
4169 case SAPP_KEYCODE_TAB:
4170 case SAPP_KEYCODE_BACKSPACE:
4171 case SAPP_KEYCODE_INSERT:
4172 case SAPP_KEYCODE_DELETE:
4173 case SAPP_KEYCODE_RIGHT:
4174 case SAPP_KEYCODE_LEFT:
4175 case SAPP_KEYCODE_DOWN:
4176 case SAPP_KEYCODE_UP:
4177 case SAPP_KEYCODE_PAGE_UP:
4178 case SAPP_KEYCODE_PAGE_DOWN:
4179 case SAPP_KEYCODE_HOME:
4180 case SAPP_KEYCODE_END:
4181 case SAPP_KEYCODE_CAPS_LOCK:
4182 case SAPP_KEYCODE_SCROLL_LOCK:
4183 case SAPP_KEYCODE_NUM_LOCK:
4184 case SAPP_KEYCODE_PRINT_SCREEN:
4185 case SAPP_KEYCODE_PAUSE:
4186 case SAPP_KEYCODE_F1:
4187 case SAPP_KEYCODE_F2:
4188 case SAPP_KEYCODE_F3:
4189 case SAPP_KEYCODE_F4:
4190 case SAPP_KEYCODE_F5:
4191 case SAPP_KEYCODE_F6:
4192 case SAPP_KEYCODE_F7:
4193 case SAPP_KEYCODE_F8:
4194 case SAPP_KEYCODE_F9:
4195 case SAPP_KEYCODE_F10:
4196 case SAPP_KEYCODE_F11:
4197 case SAPP_KEYCODE_F12:
4198 case SAPP_KEYCODE_F13:
4199 case SAPP_KEYCODE_F14:
4200 case SAPP_KEYCODE_F15:
4201 case SAPP_KEYCODE_F16:
4202 case SAPP_KEYCODE_F17:
4203 case SAPP_KEYCODE_F18:
4204 case SAPP_KEYCODE_F19:
4205 case SAPP_KEYCODE_F20:
4206 case SAPP_KEYCODE_F21:
4207 case SAPP_KEYCODE_F22:
4208 case SAPP_KEYCODE_F23:
4209 case SAPP_KEYCODE_F24:
4210 case SAPP_KEYCODE_F25:
4211 case SAPP_KEYCODE_LEFT_SHIFT:
4212 case SAPP_KEYCODE_LEFT_CONTROL:
4213 case SAPP_KEYCODE_LEFT_ALT:
4214 case SAPP_KEYCODE_LEFT_SUPER:
4215 case SAPP_KEYCODE_RIGHT_SHIFT:
4216 case SAPP_KEYCODE_RIGHT_CONTROL:
4217 case SAPP_KEYCODE_RIGHT_ALT:
4218 case SAPP_KEYCODE_RIGHT_SUPER:
4219 case SAPP_KEYCODE_MENU:
4220 /* consume the event */
4221 break;
4222 default:
4223 /* forward key to browser */
4224 retval = false;
4225 break;
4226 }
4227 }
4228 if (_sapp_call_event(&_sapp.event)) {
4229 /* consume event via sapp_consume_event() */
4230 retval = true;
4231 }
4232 if (send_keyup_followup) {
4233 _sapp.event.type = SAPP_EVENTTYPE_KEY_UP;
4234 if (_sapp_call_event(&_sapp.event)) {
4235 retval = true;
4236 }
4237 }
4238 }
4239 }
4240 _sapp_emsc_update_keyboard_state();
4241 _sapp_emsc_update_mouse_lock_state();
4242 return retval;
4243}
4244
4245_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) {
4246 _SOKOL_UNUSED(user_data);
4247 bool retval = true;
4248 if (_sapp_events_enabled()) {
4249 sapp_event_type type;
4250 switch (emsc_type) {
4251 case EMSCRIPTEN_EVENT_TOUCHSTART:
4252 type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
4253 break;
4254 case EMSCRIPTEN_EVENT_TOUCHMOVE:
4255 type = SAPP_EVENTTYPE_TOUCHES_MOVED;
4256 break;
4257 case EMSCRIPTEN_EVENT_TOUCHEND:
4258 type = SAPP_EVENTTYPE_TOUCHES_ENDED;
4259 break;
4260 case EMSCRIPTEN_EVENT_TOUCHCANCEL:
4261 type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
4262 break;
4263 default:
4264 type = SAPP_EVENTTYPE_INVALID;
4265 retval = false;
4266 break;
4267 }
4268 if (type != SAPP_EVENTTYPE_INVALID) {
4269 _sapp_init_event(type);
4270 if (emsc_event->ctrlKey) {
4271 _sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
4272 }
4273 if (emsc_event->shiftKey) {
4274 _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
4275 }
4276 if (emsc_event->altKey) {
4277 _sapp.event.modifiers |= SAPP_MODIFIER_ALT;
4278 }
4279 if (emsc_event->metaKey) {
4280 _sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
4281 }
4282 _sapp.event.num_touches = emsc_event->numTouches;
4283 if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
4284 _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
4285 }
4286 for (int i = 0; i < _sapp.event.num_touches; i++) {
4287 const EmscriptenTouchPoint* src = &emsc_event->touches[i];
4288 sapp_touchpoint* dst = &_sapp.event.touches[i];
4289 dst->identifier = (uintptr_t)src->identifier;
4290 dst->pos_x = src->targetX * _sapp.dpi_scale;
4291 dst->pos_y = src->targetY * _sapp.dpi_scale;
4292 dst->changed = src->isChanged;
4293 }
4294 _sapp_call_event(&_sapp.event);
4295 }
4296 }
4297 _sapp_emsc_update_keyboard_state();
4298 return retval;
4299}
4300
4301_SOKOL_PRIVATE void _sapp_emsc_keytable_init(void) {
4302 _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE;
4303 _sapp.keycodes[9] = SAPP_KEYCODE_TAB;
4304 _sapp.keycodes[13] = SAPP_KEYCODE_ENTER;
4305 _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT;
4306 _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL;
4307 _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT;
4308 _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE;
4309 _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE;
4310 _sapp.keycodes[32] = SAPP_KEYCODE_SPACE;
4311 _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP;
4312 _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN;
4313 _sapp.keycodes[35] = SAPP_KEYCODE_END;
4314 _sapp.keycodes[36] = SAPP_KEYCODE_HOME;
4315 _sapp.keycodes[37] = SAPP_KEYCODE_LEFT;
4316 _sapp.keycodes[38] = SAPP_KEYCODE_UP;
4317 _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT;
4318 _sapp.keycodes[40] = SAPP_KEYCODE_DOWN;
4319 _sapp.keycodes[45] = SAPP_KEYCODE_INSERT;
4320 _sapp.keycodes[46] = SAPP_KEYCODE_DELETE;
4321 _sapp.keycodes[48] = SAPP_KEYCODE_0;
4322 _sapp.keycodes[49] = SAPP_KEYCODE_1;
4323 _sapp.keycodes[50] = SAPP_KEYCODE_2;
4324 _sapp.keycodes[51] = SAPP_KEYCODE_3;
4325 _sapp.keycodes[52] = SAPP_KEYCODE_4;
4326 _sapp.keycodes[53] = SAPP_KEYCODE_5;
4327 _sapp.keycodes[54] = SAPP_KEYCODE_6;
4328 _sapp.keycodes[55] = SAPP_KEYCODE_7;
4329 _sapp.keycodes[56] = SAPP_KEYCODE_8;
4330 _sapp.keycodes[57] = SAPP_KEYCODE_9;
4331 _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON;
4332 _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL;
4333 _sapp.keycodes[65] = SAPP_KEYCODE_A;
4334 _sapp.keycodes[66] = SAPP_KEYCODE_B;
4335 _sapp.keycodes[67] = SAPP_KEYCODE_C;
4336 _sapp.keycodes[68] = SAPP_KEYCODE_D;
4337 _sapp.keycodes[69] = SAPP_KEYCODE_E;
4338 _sapp.keycodes[70] = SAPP_KEYCODE_F;
4339 _sapp.keycodes[71] = SAPP_KEYCODE_G;
4340 _sapp.keycodes[72] = SAPP_KEYCODE_H;
4341 _sapp.keycodes[73] = SAPP_KEYCODE_I;
4342 _sapp.keycodes[74] = SAPP_KEYCODE_J;
4343 _sapp.keycodes[75] = SAPP_KEYCODE_K;
4344 _sapp.keycodes[76] = SAPP_KEYCODE_L;
4345 _sapp.keycodes[77] = SAPP_KEYCODE_M;
4346 _sapp.keycodes[78] = SAPP_KEYCODE_N;
4347 _sapp.keycodes[79] = SAPP_KEYCODE_O;
4348 _sapp.keycodes[80] = SAPP_KEYCODE_P;
4349 _sapp.keycodes[81] = SAPP_KEYCODE_Q;
4350 _sapp.keycodes[82] = SAPP_KEYCODE_R;
4351 _sapp.keycodes[83] = SAPP_KEYCODE_S;
4352 _sapp.keycodes[84] = SAPP_KEYCODE_T;
4353 _sapp.keycodes[85] = SAPP_KEYCODE_U;
4354 _sapp.keycodes[86] = SAPP_KEYCODE_V;
4355 _sapp.keycodes[87] = SAPP_KEYCODE_W;
4356 _sapp.keycodes[88] = SAPP_KEYCODE_X;
4357 _sapp.keycodes[89] = SAPP_KEYCODE_Y;
4358 _sapp.keycodes[90] = SAPP_KEYCODE_Z;
4359 _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER;
4360 _sapp.keycodes[93] = SAPP_KEYCODE_MENU;
4361 _sapp.keycodes[96] = SAPP_KEYCODE_KP_0;
4362 _sapp.keycodes[97] = SAPP_KEYCODE_KP_1;
4363 _sapp.keycodes[98] = SAPP_KEYCODE_KP_2;
4364 _sapp.keycodes[99] = SAPP_KEYCODE_KP_3;
4365 _sapp.keycodes[100] = SAPP_KEYCODE_KP_4;
4366 _sapp.keycodes[101] = SAPP_KEYCODE_KP_5;
4367 _sapp.keycodes[102] = SAPP_KEYCODE_KP_6;
4368 _sapp.keycodes[103] = SAPP_KEYCODE_KP_7;
4369 _sapp.keycodes[104] = SAPP_KEYCODE_KP_8;
4370 _sapp.keycodes[105] = SAPP_KEYCODE_KP_9;
4371 _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY;
4372 _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD;
4373 _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT;
4374 _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL;
4375 _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE;
4376 _sapp.keycodes[112] = SAPP_KEYCODE_F1;
4377 _sapp.keycodes[113] = SAPP_KEYCODE_F2;
4378 _sapp.keycodes[114] = SAPP_KEYCODE_F3;
4379 _sapp.keycodes[115] = SAPP_KEYCODE_F4;
4380 _sapp.keycodes[116] = SAPP_KEYCODE_F5;
4381 _sapp.keycodes[117] = SAPP_KEYCODE_F6;
4382 _sapp.keycodes[118] = SAPP_KEYCODE_F7;
4383 _sapp.keycodes[119] = SAPP_KEYCODE_F8;
4384 _sapp.keycodes[120] = SAPP_KEYCODE_F9;
4385 _sapp.keycodes[121] = SAPP_KEYCODE_F10;
4386 _sapp.keycodes[122] = SAPP_KEYCODE_F11;
4387 _sapp.keycodes[123] = SAPP_KEYCODE_F12;
4388 _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK;
4389 _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK;
4390 _sapp.keycodes[173] = SAPP_KEYCODE_MINUS;
4391 _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON;
4392 _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL;
4393 _sapp.keycodes[188] = SAPP_KEYCODE_COMMA;
4394 _sapp.keycodes[189] = SAPP_KEYCODE_MINUS;
4395 _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD;
4396 _sapp.keycodes[191] = SAPP_KEYCODE_SLASH;
4397 _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT;
4398 _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET;
4399 _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH;
4400 _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET;
4401 _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE;
4402 _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER;
4403}
4404
4405#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
4406_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) {
4407 _SOKOL_UNUSED(reserved);
4408 _SOKOL_UNUSED(user_data);
4409 sapp_event_type type;
4410 switch (emsc_type) {
4411 case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break;
4412 case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break;
4413 default: type = SAPP_EVENTTYPE_INVALID; break;
4414 }
4415 if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) {
4416 _sapp_init_event(type);
4417 _sapp_call_event(&_sapp.event);
4418 }
4419 return true;
4420}
4421
4422_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) {
4423 EmscriptenWebGLContextAttributes attrs;
4424 emscripten_webgl_init_context_attributes(&attrs);
4425 attrs.alpha = _sapp.desc.alpha;
4426 attrs.depth = true;
4427 attrs.stencil = true;
4428 attrs.antialias = _sapp.sample_count > 1;
4429 attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha;
4430 attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer;
4431 attrs.enableExtensionsByDefault = true;
4432 #if defined(SOKOL_GLES3)
4433 if (_sapp.desc.gl_force_gles2) {
4434 attrs.majorVersion = 1;
4435 _sapp.gles2_fallback = true;
4436 }
4437 else {
4438 attrs.majorVersion = 2;
4439 }
4440 #endif
4441 EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs);
4442 if (!ctx) {
4443 attrs.majorVersion = 1;
4444 ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs);
4445 _sapp.gles2_fallback = true;
4446 }
4447 emscripten_webgl_make_context_current(ctx);
4448
4449 /* some WebGL extension are not enabled automatically by emscripten */
4450 emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc");
4451}
4452#endif
4453
4454#if defined(SOKOL_WGPU)
4455#define _SAPP_EMSC_WGPU_STATE_INITIAL (0)
4456#define _SAPP_EMSC_WGPU_STATE_READY (1)
4457#define _SAPP_EMSC_WGPU_STATE_RUNNING (2)
4458
4459#if defined(__cplusplus)
4460extern "C" {
4461#endif
4462/* called when the asynchronous WebGPU device + swapchain init code in JS has finished */
4463EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) {
4464 SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device);
4465 _sapp.emsc.wgpu.device = (WGPUDevice) device_id;
4466 _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id;
4467 _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt;
4468 _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY;
4469}
4470#if defined(__cplusplus)
4471} // extern "C"
4472#endif
4473
4474/* embedded JS function to handle all the asynchronous WebGPU setup */
4475EM_JS(void, sapp_js_wgpu_init, (), {
4476 WebGPU.initManagers();
4477 // FIXME: the extension activation must be more clever here
4478 navigator.gpu.requestAdapter().then(function(adapter) {
4479 console.log("wgpu adapter extensions: " + adapter.extensions);
4480 adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) {
4481 var gpuContext = document.getElementById("canvas").getContext("gpupresent");
4482 console.log("wgpu device extensions: " + adapter.extensions);
4483 gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) {
4484 var swapChainDescriptor = { device: device, format: fmt };
4485 var swapChain = gpuContext.configureSwapChain(swapChainDescriptor);
4486 var deviceId = WebGPU.mgrDevice.create(device);
4487 var swapChainId = WebGPU.mgrSwapChain.create(swapChain);
4488 var fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; });
4489 console.log("wgpu device: " + device);
4490 console.log("wgpu swap chain: " + swapChain);
4491 console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")");
4492 __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId);
4493 });
4494 });
4495 });
4496});
4497
4498_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) {
4499 SOKOL_ASSERT(_sapp.emsc.wgpu.device);
4500 SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain);
4501 SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex);
4502 SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view);
4503 SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex);
4504 SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view);
4505
4506 WGPUTextureDescriptor ds_desc;
4507 memset(&ds_desc, 0, sizeof(ds_desc));
4508 ds_desc.usage = WGPUTextureUsage_OutputAttachment;
4509 ds_desc.dimension = WGPUTextureDimension_2D;
4510 ds_desc.size.width = (uint32_t) _sapp.framebuffer_width;
4511 ds_desc.size.height = (uint32_t) _sapp.framebuffer_height;
4512 ds_desc.size.depth = 1;
4513 ds_desc.arrayLayerCount = 1;
4514 ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8;
4515 ds_desc.mipLevelCount = 1;
4516 ds_desc.sampleCount = _sapp.sample_count;
4517 _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc);
4518 _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0);
4519
4520 if (_sapp.sample_count > 1) {
4521 WGPUTextureDescriptor msaa_desc;
4522 memset(&msaa_desc, 0, sizeof(msaa_desc));
4523 msaa_desc.usage = WGPUTextureUsage_OutputAttachment;
4524 msaa_desc.dimension = WGPUTextureDimension_2D;
4525 msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width;
4526 msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height;
4527 msaa_desc.size.depth = 1;
4528 msaa_desc.arrayLayerCount = 1;
4529 msaa_desc.format = _sapp.emsc.wgpu.render_format;
4530 msaa_desc.mipLevelCount = 1;
4531 msaa_desc.sampleCount = _sapp.sample_count;
4532 _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc);
4533 _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0);
4534 }
4535}
4536
4537_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) {
4538 if (_sapp.emsc.wgpu.msaa_tex) {
4539 wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex);
4540 _sapp.emsc.wgpu.msaa_tex = 0;
4541 }
4542 if (_sapp.emsc.wgpu.msaa_view) {
4543 wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view);
4544 _sapp.emsc.wgpu.msaa_view = 0;
4545 }
4546 if (_sapp.emsc.wgpu.depth_stencil_tex) {
4547 wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex);
4548 _sapp.emsc.wgpu.depth_stencil_tex = 0;
4549 }
4550 if (_sapp.emsc.wgpu.depth_stencil_view) {
4551 wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view);
4552 _sapp.emsc.wgpu.depth_stencil_view = 0;
4553 }
4554}
4555
4556_SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) {
4557 if (_sapp.emsc.wgpu.swapchain_view) {
4558 wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view);
4559 }
4560 _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain);
4561}
4562#endif
4563
4564_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) {
4565 emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
4566 emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
4567 emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
4568 emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
4569 emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
4570 emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb);
4571 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
4572 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
4573 emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
4574 emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
4575 emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
4576 emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
4577 emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
4578 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb);
4579 emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb);
4580 sapp_js_add_beforeunload_listener();
4581 if (_sapp.clipboard.enabled) {
4582 sapp_js_add_clipboard_listener();
4583 }
4584 if (_sapp.drop.enabled) {
4585 sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]);
4586 }
4587 #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
4588 emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
4589 emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
4590 #endif
4591}
4592
4593_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() {
4594 emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0);
4595 emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0);
4596 emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0);
4597 emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0);
4598 emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0);
4599 emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0);
4600 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
4601 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
4602 emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
4603 emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0);
4604 emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0);
4605 emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0);
4606 emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0);
4607 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
4608 emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
4609 sapp_js_remove_beforeunload_listener();
4610 if (_sapp.clipboard.enabled) {
4611 sapp_js_remove_clipboard_listener();
4612 }
4613 if (_sapp.drop.enabled) {
4614 sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]);
4615 }
4616 #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
4617 emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0);
4618 emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0);
4619 #endif
4620}
4621
4622_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) {
4623 _SOKOL_UNUSED(time);
4624 _SOKOL_UNUSED(userData);
4625
4626 #if defined(SOKOL_WGPU)
4627 /*
4628 on WebGPU, the emscripten frame callback will already be called while
4629 the asynchronous WebGPU device and swapchain initialization is still
4630 in progress
4631 */
4632 switch (_sapp.emsc.wgpu.state) {
4633 case _SAPP_EMSC_WGPU_STATE_INITIAL:
4634 /* async JS init hasn't finished yet */
4635 break;
4636 case _SAPP_EMSC_WGPU_STATE_READY:
4637 /* perform post-async init stuff */
4638 _sapp_emsc_wgpu_surfaces_create();
4639 _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING;
4640 break;
4641 case _SAPP_EMSC_WGPU_STATE_RUNNING:
4642 /* a regular frame */
4643 _sapp_emsc_wgpu_next_frame();
4644 _sapp_frame();
4645 break;
4646 }
4647 #else
4648 /* WebGL code path */
4649 _sapp_frame();
4650 #endif
4651
4652 /* quit-handling */
4653 if (_sapp.quit_requested) {
4654 _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
4655 _sapp_call_event(&_sapp.event);
4656 if (_sapp.quit_requested) {
4657 _sapp.quit_ordered = true;
4658 }
4659 }
4660 if (_sapp.quit_ordered) {
4661 _sapp_emsc_unregister_eventhandlers();
4662 _sapp_call_cleanup();
4663 _sapp_discard_state();
4664 return EM_FALSE;
4665 }
4666 return EM_TRUE;
4667}
4668
4669_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) {
4670 _sapp_init_state(desc);
4671 sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]);
4672 _sapp_emsc_keytable_init();
4673 double w, h;
4674 if (_sapp.desc.html5_canvas_resize) {
4675 w = (double) _sapp.desc.width;
4676 h = (double) _sapp.desc.height;
4677 }
4678 else {
4679 emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
4680 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed);
4681 }
4682 if (_sapp.desc.high_dpi) {
4683 _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
4684 }
4685 _sapp.window_width = (int) w;
4686 _sapp.window_height = (int) h;
4687 _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
4688 _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
4689 emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
4690 #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
4691 _sapp_emsc_webgl_init();
4692 #elif defined(SOKOL_WGPU)
4693 sapp_js_wgpu_init();
4694 #endif
4695 _sapp.valid = true;
4696 _sapp_emsc_register_eventhandlers();
4697
4698 /* start the frame loop */
4699 emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0);
4700
4701 /* NOT A BUG: do not call _sapp_discard_state() here, instead this is
4702 called in _sapp_emsc_frame() when the application is ordered to quit
4703 */
4704}
4705
4706#if !defined(SOKOL_NO_ENTRY)
4707int main(int argc, char* argv[]) {
4708 sapp_desc desc = sokol_main(argc, argv);
4709 _sapp_emsc_run(&desc);
4710 return 0;
4711}
4712#endif /* SOKOL_NO_ENTRY */
4713#endif /* _SAPP_EMSCRIPTEN */
4714
4715/*== MISC GL SUPPORT FUNCTIONS ================================================*/
4716#if defined(SOKOL_GLCORE33)
4717typedef struct {
4718 int red_bits;
4719 int green_bits;
4720 int blue_bits;
4721 int alpha_bits;
4722 int depth_bits;
4723 int stencil_bits;
4724 int samples;
4725 bool doublebuffer;
4726 uintptr_t handle;
4727} _sapp_gl_fbconfig;
4728
4729_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) {
4730 memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig));
4731 /* -1 means "don't care" */
4732 fbconfig->red_bits = -1;
4733 fbconfig->green_bits = -1;
4734 fbconfig->blue_bits = -1;
4735 fbconfig->alpha_bits = -1;
4736 fbconfig->depth_bits = -1;
4737 fbconfig->stencil_bits = -1;
4738 fbconfig->samples = -1;
4739}
4740
4741_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) {
4742 int missing, least_missing = 1000000;
4743 int color_diff, least_color_diff = 10000000;
4744 int extra_diff, least_extra_diff = 10000000;
4745 const _sapp_gl_fbconfig* current;
4746 const _sapp_gl_fbconfig* closest = 0;
4747 for (int i = 0; i < count; i++) {
4748 current = alternatives + i;
4749 if (desired->doublebuffer != current->doublebuffer) {
4750 continue;
4751 }
4752 missing = 0;
4753 if (desired->alpha_bits > 0 && current->alpha_bits == 0) {
4754 missing++;
4755 }
4756 if (desired->depth_bits > 0 && current->depth_bits == 0) {
4757 missing++;
4758 }
4759 if (desired->stencil_bits > 0 && current->stencil_bits == 0) {
4760 missing++;
4761 }
4762 if (desired->samples > 0 && current->samples == 0) {
4763 /* Technically, several multisampling buffers could be
4764 involved, but that's a lower level implementation detail and
4765 not important to us here, so we count them as one
4766 */
4767 missing++;
4768 }
4769
4770 /* These polynomials make many small channel size differences matter
4771 less than one large channel size difference
4772 Calculate color channel size difference value
4773 */
4774 color_diff = 0;
4775 if (desired->red_bits != -1) {
4776 color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
4777 }
4778 if (desired->green_bits != -1) {
4779 color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
4780 }
4781 if (desired->blue_bits != -1) {
4782 color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
4783 }
4784
4785 /* Calculate non-color channel size difference value */
4786 extra_diff = 0;
4787 if (desired->alpha_bits != -1) {
4788 extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
4789 }
4790 if (desired->depth_bits != -1) {
4791 extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
4792 }
4793 if (desired->stencil_bits != -1) {
4794 extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
4795 }
4796 if (desired->samples != -1) {
4797 extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
4798 }
4799
4800 /* Figure out if the current one is better than the best one found so far
4801 Least number of missing buffers is the most important heuristic,
4802 then color buffer size match and lastly size match for other buffers
4803 */
4804 if (missing < least_missing) {
4805 closest = current;
4806 }
4807 else if (missing == least_missing) {
4808 if ((color_diff < least_color_diff) ||
4809 (color_diff == least_color_diff && extra_diff < least_extra_diff))
4810 {
4811 closest = current;
4812 }
4813 }
4814 if (current == closest) {
4815 least_missing = missing;
4816 least_color_diff = color_diff;
4817 least_extra_diff = extra_diff;
4818 }
4819 }
4820 return closest;
4821}
4822#endif
4823
4824/*== WINDOWS DESKTOP and UWP====================================================*/
4825#if defined(_SAPP_WIN32) || defined(_SAPP_UWP)
4826_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) {
4827 SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
4828 memset(dst, 0, (size_t)dst_num_bytes);
4829 const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t);
4830 const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0);
4831 if ((dst_needed > 0) && (dst_needed < dst_chars)) {
4832 MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars);
4833 return true;
4834 }
4835 else {
4836 /* input string doesn't fit into destination buffer */
4837 return false;
4838 }
4839}
4840
4841_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) {
4842 if (_sapp_events_enabled()) {
4843 _sapp_init_event(type);
4844 _sapp_call_event(&_sapp.event);
4845 }
4846}
4847
4848_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) {
4849 /* same as GLFW */
4850 _sapp.keycodes[0x00B] = SAPP_KEYCODE_0;
4851 _sapp.keycodes[0x002] = SAPP_KEYCODE_1;
4852 _sapp.keycodes[0x003] = SAPP_KEYCODE_2;
4853 _sapp.keycodes[0x004] = SAPP_KEYCODE_3;
4854 _sapp.keycodes[0x005] = SAPP_KEYCODE_4;
4855 _sapp.keycodes[0x006] = SAPP_KEYCODE_5;
4856 _sapp.keycodes[0x007] = SAPP_KEYCODE_6;
4857 _sapp.keycodes[0x008] = SAPP_KEYCODE_7;
4858 _sapp.keycodes[0x009] = SAPP_KEYCODE_8;
4859 _sapp.keycodes[0x00A] = SAPP_KEYCODE_9;
4860 _sapp.keycodes[0x01E] = SAPP_KEYCODE_A;
4861 _sapp.keycodes[0x030] = SAPP_KEYCODE_B;
4862 _sapp.keycodes[0x02E] = SAPP_KEYCODE_C;
4863 _sapp.keycodes[0x020] = SAPP_KEYCODE_D;
4864 _sapp.keycodes[0x012] = SAPP_KEYCODE_E;
4865 _sapp.keycodes[0x021] = SAPP_KEYCODE_F;
4866 _sapp.keycodes[0x022] = SAPP_KEYCODE_G;
4867 _sapp.keycodes[0x023] = SAPP_KEYCODE_H;
4868 _sapp.keycodes[0x017] = SAPP_KEYCODE_I;
4869 _sapp.keycodes[0x024] = SAPP_KEYCODE_J;
4870 _sapp.keycodes[0x025] = SAPP_KEYCODE_K;
4871 _sapp.keycodes[0x026] = SAPP_KEYCODE_L;
4872 _sapp.keycodes[0x032] = SAPP_KEYCODE_M;
4873 _sapp.keycodes[0x031] = SAPP_KEYCODE_N;
4874 _sapp.keycodes[0x018] = SAPP_KEYCODE_O;
4875 _sapp.keycodes[0x019] = SAPP_KEYCODE_P;
4876 _sapp.keycodes[0x010] = SAPP_KEYCODE_Q;
4877 _sapp.keycodes[0x013] = SAPP_KEYCODE_R;
4878 _sapp.keycodes[0x01F] = SAPP_KEYCODE_S;
4879 _sapp.keycodes[0x014] = SAPP_KEYCODE_T;
4880 _sapp.keycodes[0x016] = SAPP_KEYCODE_U;
4881 _sapp.keycodes[0x02F] = SAPP_KEYCODE_V;
4882 _sapp.keycodes[0x011] = SAPP_KEYCODE_W;
4883 _sapp.keycodes[0x02D] = SAPP_KEYCODE_X;
4884 _sapp.keycodes[0x015] = SAPP_KEYCODE_Y;
4885 _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z;
4886 _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE;
4887 _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH;
4888 _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA;
4889 _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL;
4890 _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT;
4891 _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET;
4892 _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS;
4893 _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD;
4894 _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET;
4895 _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON;
4896 _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH;
4897 _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2;
4898 _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE;
4899 _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE;
4900 _sapp.keycodes[0x14F] = SAPP_KEYCODE_END;
4901 _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER;
4902 _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE;
4903 _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME;
4904 _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT;
4905 _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU;
4906 _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN;
4907 _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP;
4908 _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE;
4909 _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE;
4910 _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE;
4911 _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB;
4912 _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK;
4913 _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK;
4914 _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK;
4915 _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1;
4916 _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2;
4917 _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3;
4918 _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4;
4919 _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5;
4920 _sapp.keycodes[0x040] = SAPP_KEYCODE_F6;
4921 _sapp.keycodes[0x041] = SAPP_KEYCODE_F7;
4922 _sapp.keycodes[0x042] = SAPP_KEYCODE_F8;
4923 _sapp.keycodes[0x043] = SAPP_KEYCODE_F9;
4924 _sapp.keycodes[0x044] = SAPP_KEYCODE_F10;
4925 _sapp.keycodes[0x057] = SAPP_KEYCODE_F11;
4926 _sapp.keycodes[0x058] = SAPP_KEYCODE_F12;
4927 _sapp.keycodes[0x064] = SAPP_KEYCODE_F13;
4928 _sapp.keycodes[0x065] = SAPP_KEYCODE_F14;
4929 _sapp.keycodes[0x066] = SAPP_KEYCODE_F15;
4930 _sapp.keycodes[0x067] = SAPP_KEYCODE_F16;
4931 _sapp.keycodes[0x068] = SAPP_KEYCODE_F17;
4932 _sapp.keycodes[0x069] = SAPP_KEYCODE_F18;
4933 _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19;
4934 _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20;
4935 _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21;
4936 _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22;
4937 _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23;
4938 _sapp.keycodes[0x076] = SAPP_KEYCODE_F24;
4939 _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT;
4940 _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL;
4941 _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT;
4942 _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER;
4943 _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN;
4944 _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT;
4945 _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL;
4946 _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT;
4947 _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER;
4948 _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN;
4949 _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT;
4950 _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT;
4951 _sapp.keycodes[0x148] = SAPP_KEYCODE_UP;
4952 _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0;
4953 _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1;
4954 _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2;
4955 _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3;
4956 _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4;
4957 _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5;
4958 _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6;
4959 _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7;
4960 _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8;
4961 _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9;
4962 _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD;
4963 _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL;
4964 _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE;
4965 _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER;
4966 _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY;
4967 _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT;
4968}
4969#endif // _SAPP_WIN32 || _SAPP_UWP
4970
4971/*== WINDOWS DESKTOP===========================================================*/
4972#if defined(_SAPP_WIN32)
4973
4974#if defined(SOKOL_D3D11)
4975
4976#if defined(__cplusplus)
4977#define _sapp_d3d11_Release(self) (self)->Release()
4978#else
4979#define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self)
4980#endif
4981
4982#define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; }
4983
4984static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) {
4985 #if defined(__cplusplus)
4986 return self->GetBuffer(Buffer, riid, ppSurface);
4987 #else
4988 return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface);
4989 #endif
4990}
4991
4992static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) {
4993 #if defined(__cplusplus)
4994 return self->CreateRenderTargetView(pResource, pDesc, ppRTView);
4995 #else
4996 return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView);
4997 #endif
4998}
4999
5000static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) {
5001 #if defined(__cplusplus)
5002 return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D);
5003 #else
5004 return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D);
5005 #endif
5006}
5007
5008static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) {
5009 #if defined(__cplusplus)
5010 return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView);
5011 #else
5012 return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView);
5013 #endif
5014}
5015
5016static inline void _sapp_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) {
5017 #if defined(__cplusplus)
5018 self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
5019 #else
5020 self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
5021 #endif
5022}
5023
5024static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) {
5025 #if defined(__cplusplus)
5026 return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
5027 #else
5028 return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags);
5029 #endif
5030}
5031
5032static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) {
5033 #if defined(__cplusplus)
5034 return self->Present(SyncInterval, Flags);
5035 #else
5036 return self->lpVtbl->Present(self, SyncInterval, Flags);
5037 #endif
5038}
5039
5040_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) {
5041 DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc;
5042 sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width;
5043 sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height;
5044 sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
5045 sc_desc->BufferDesc.RefreshRate.Numerator = 60;
5046 sc_desc->BufferDesc.RefreshRate.Denominator = 1;
5047 sc_desc->OutputWindow = _sapp.win32.hwnd;
5048 sc_desc->Windowed = true;
5049 if (_sapp.win32.is_win10_or_greater) {
5050 sc_desc->BufferCount = 2;
5051 sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD;
5052 }
5053 else {
5054 sc_desc->BufferCount = 1;
5055 sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
5056 }
5057 sc_desc->SampleDesc.Count = 1;
5058 sc_desc->SampleDesc.Quality = 0;
5059 sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
5060 UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
5061 #if defined(SOKOL_DEBUG)
5062 create_flags |= D3D11_CREATE_DEVICE_DEBUG;
5063 #endif
5064 D3D_FEATURE_LEVEL feature_level;
5065 HRESULT hr = D3D11CreateDeviceAndSwapChain(
5066 NULL, /* pAdapter (use default) */
5067 D3D_DRIVER_TYPE_HARDWARE, /* DriverType */
5068 NULL, /* Software */
5069 create_flags, /* Flags */
5070 NULL, /* pFeatureLevels */
5071 0, /* FeatureLevels */
5072 D3D11_SDK_VERSION, /* SDKVersion */
5073 sc_desc, /* pSwapChainDesc */
5074 &_sapp.d3d11.swap_chain, /* ppSwapChain */
5075 &_sapp.d3d11.device, /* ppDevice */
5076 &feature_level, /* pFeatureLevel */
5077 &_sapp.d3d11.device_context); /* ppImmediateContext */
5078 _SOKOL_UNUSED(hr);
5079 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context);
5080}
5081
5082_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) {
5083 _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain);
5084 _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context);
5085 _SAPP_SAFE_RELEASE(_sapp.d3d11.device);
5086}
5087
5088_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) {
5089 SOKOL_ASSERT(0 == _sapp.d3d11.rt);
5090 SOKOL_ASSERT(0 == _sapp.d3d11.rtv);
5091 SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt);
5092 SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv);
5093 SOKOL_ASSERT(0 == _sapp.d3d11.ds);
5094 SOKOL_ASSERT(0 == _sapp.d3d11.dsv);
5095
5096 HRESULT hr;
5097
5098 /* view for the swapchain-created framebuffer */
5099 #ifdef __cplusplus
5100 hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt);
5101 #else
5102 hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt);
5103 #endif
5104 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt);
5105 hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv);
5106 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv);
5107
5108 /* common desc for MSAA and depth-stencil texture */
5109 D3D11_TEXTURE2D_DESC tex_desc;
5110 memset(&tex_desc, 0, sizeof(tex_desc));
5111 tex_desc.Width = (UINT)_sapp.framebuffer_width;
5112 tex_desc.Height = (UINT)_sapp.framebuffer_height;
5113 tex_desc.MipLevels = 1;
5114 tex_desc.ArraySize = 1;
5115 tex_desc.Usage = D3D11_USAGE_DEFAULT;
5116 tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET;
5117 tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count;
5118 tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0);
5119
5120 /* create MSAA texture and view if antialiasing requested */
5121 if (_sapp.sample_count > 1) {
5122 tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
5123 hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt);
5124 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt);
5125 hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv);
5126 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv);
5127 }
5128
5129 /* texture and view for the depth-stencil-surface */
5130 tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
5131 tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
5132 hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds);
5133 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds);
5134 hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv);
5135 SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv);
5136}
5137
5138_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) {
5139 _SAPP_SAFE_RELEASE(_sapp.d3d11.rt);
5140 _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv);
5141 _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt);
5142 _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv);
5143 _SAPP_SAFE_RELEASE(_sapp.d3d11.ds);
5144 _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv);
5145}
5146
5147_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) {
5148 if (_sapp.d3d11.swap_chain) {
5149 _sapp_d3d11_destroy_default_render_target();
5150 _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
5151 _sapp_d3d11_create_default_render_target();
5152 }
5153}
5154
5155_SOKOL_PRIVATE void _sapp_d3d11_present(void) {
5156 /* do MSAA resolve if needed */
5157 if (_sapp.sample_count > 1) {
5158 SOKOL_ASSERT(_sapp.d3d11.rt);
5159 SOKOL_ASSERT(_sapp.d3d11.msaa_rt);
5160 _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM);
5161 }
5162 _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, 0);
5163}
5164
5165#endif /* SOKOL_D3D11 */
5166
5167#if defined(SOKOL_GLCORE33)
5168_SOKOL_PRIVATE void _sapp_wgl_init(void) {
5169 _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll");
5170 if (!_sapp.wgl.opengl32) {
5171 _sapp_fail("Failed to load opengl32.dll\n");
5172 }
5173 SOKOL_ASSERT(_sapp.wgl.opengl32);
5174 _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext");
5175 SOKOL_ASSERT(_sapp.wgl.CreateContext);
5176 _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext");
5177 SOKOL_ASSERT(_sapp.wgl.DeleteContext);
5178 _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress");
5179 SOKOL_ASSERT(_sapp.wgl.GetProcAddress);
5180 _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC");
5181 SOKOL_ASSERT(_sapp.wgl.GetCurrentDC);
5182 _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent");
5183 SOKOL_ASSERT(_sapp.wgl.MakeCurrent);
5184
5185 _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW,
5186 L"SOKOLAPP",
5187 L"sokol-app message window",
5188 WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
5189 0, 0, 1, 1,
5190 NULL, NULL,
5191 GetModuleHandleW(NULL),
5192 NULL);
5193 if (!_sapp.wgl.msg_hwnd) {
5194 _sapp_fail("Win32: failed to create helper window!\n");
5195 }
5196 SOKOL_ASSERT(_sapp.wgl.msg_hwnd);
5197 ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE);
5198 MSG msg;
5199 while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) {
5200 TranslateMessage(&msg);
5201 DispatchMessageW(&msg);
5202 }
5203 _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd);
5204 if (!_sapp.wgl.msg_dc) {
5205 _sapp_fail("Win32: failed to obtain helper window DC!\n");
5206 }
5207}
5208
5209_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) {
5210 SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd);
5211 DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0;
5212 FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0;
5213}
5214
5215_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) {
5216 SOKOL_ASSERT(ext && extensions);
5217 const char* start = extensions;
5218 while (true) {
5219 const char* where = strstr(start, ext);
5220 if (!where) {
5221 return false;
5222 }
5223 const char* terminator = where + strlen(ext);
5224 if ((where == start) || (*(where - 1) == ' ')) {
5225 if (*terminator == ' ' || *terminator == '\0') {
5226 break;
5227 }
5228 }
5229 start = terminator;
5230 }
5231 return true;
5232}
5233
5234_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) {
5235 SOKOL_ASSERT(ext);
5236 if (_sapp.wgl.GetExtensionsStringEXT) {
5237 const char* extensions = _sapp.wgl.GetExtensionsStringEXT();
5238 if (extensions) {
5239 if (_sapp_wgl_has_ext(ext, extensions)) {
5240 return true;
5241 }
5242 }
5243 }
5244 if (_sapp.wgl.GetExtensionsStringARB) {
5245 const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC());
5246 if (extensions) {
5247 if (_sapp_wgl_has_ext(ext, extensions)) {
5248 return true;
5249 }
5250 }
5251 }
5252 return false;
5253}
5254
5255_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) {
5256 SOKOL_ASSERT(_sapp.wgl.msg_dc);
5257 PIXELFORMATDESCRIPTOR pfd;
5258 memset(&pfd, 0, sizeof(pfd));
5259 pfd.nSize = sizeof(pfd);
5260 pfd.nVersion = 1;
5261 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
5262 pfd.iPixelType = PFD_TYPE_RGBA;
5263 pfd.cColorBits = 24;
5264 if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) {
5265 _sapp_fail("WGL: failed to set pixel format for dummy context\n");
5266 }
5267 HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc);
5268 if (!rc) {
5269 _sapp_fail("WGL: Failed to create dummy context\n");
5270 }
5271 if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) {
5272 _sapp_fail("WGL: Failed to make context current\n");
5273 }
5274 _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT");
5275 _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB");
5276 _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB");
5277 _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT");
5278 _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB");
5279 _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample");
5280 _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context");
5281 _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile");
5282 _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control");
5283 _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format");
5284 _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0);
5285 _sapp.wgl.DeleteContext(rc);
5286}
5287
5288_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
5289 SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
5290 int value = 0;
5291 if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) {
5292 _sapp_fail("WGL: Failed to retrieve pixel format attribute\n");
5293 }
5294 return value;
5295}
5296
5297_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
5298 SOKOL_ASSERT(_sapp.win32.dc);
5299 SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
5300 const _sapp_gl_fbconfig* closest;
5301
5302 int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
5303 _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig));
5304 SOKOL_ASSERT(usable_configs);
5305 int usable_count = 0;
5306 for (int i = 0; i < native_count; i++) {
5307 const int n = i + 1;
5308 _sapp_gl_fbconfig* u = usable_configs + usable_count;
5309 _sapp_gl_init_fbconfig(u);
5310 if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) {
5311 continue;
5312 }
5313 if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) {
5314 continue;
5315 }
5316 if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) {
5317 continue;
5318 }
5319 u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB);
5320 u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB);
5321 u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB);
5322 u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB);
5323 u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB);
5324 u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB);
5325 if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) {
5326 u->doublebuffer = true;
5327 }
5328 if (_sapp.wgl.arb_multisample) {
5329 u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB);
5330 }
5331 u->handle = (uintptr_t)n;
5332 usable_count++;
5333 }
5334 SOKOL_ASSERT(usable_count > 0);
5335 _sapp_gl_fbconfig desired;
5336 _sapp_gl_init_fbconfig(&desired);
5337 desired.red_bits = 8;
5338 desired.green_bits = 8;
5339 desired.blue_bits = 8;
5340 desired.alpha_bits = 8;
5341 desired.depth_bits = 24;
5342 desired.stencil_bits = 8;
5343 desired.doublebuffer = true;
5344 desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
5345 closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
5346 int pixel_format = 0;
5347 if (closest) {
5348 pixel_format = (int) closest->handle;
5349 }
5350 SOKOL_FREE(usable_configs);
5351 return pixel_format;
5352}
5353
5354_SOKOL_PRIVATE void _sapp_wgl_create_context(void) {
5355 int pixel_format = _sapp_wgl_find_pixel_format();
5356 if (0 == pixel_format) {
5357 _sapp_fail("WGL: Didn't find matching pixel format.\n");
5358 }
5359 PIXELFORMATDESCRIPTOR pfd;
5360 if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) {
5361 _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n");
5362 }
5363 if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) {
5364 _sapp_fail("WGL: Failed to set selected pixel format!\n");
5365 }
5366 if (!_sapp.wgl.arb_create_context) {
5367 _sapp_fail("WGL: ARB_create_context required!\n");
5368 }
5369 if (!_sapp.wgl.arb_create_context_profile) {
5370 _sapp_fail("WGL: ARB_create_context_profile required!\n");
5371 }
5372 const int attrs[] = {
5373 WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
5374 WGL_CONTEXT_MINOR_VERSION_ARB, 3,
5375 WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
5376 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
5377 0, 0
5378 };
5379 _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs);
5380 if (!_sapp.wgl.gl_ctx) {
5381 const DWORD err = GetLastError();
5382 if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) {
5383 _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n");
5384 }
5385 else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) {
5386 _sapp_fail("WGL: Driver does not support the requested OpenGL profile");
5387 }
5388 else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) {
5389 _sapp_fail("WGL: The share context is not compatible with the requested context");
5390 }
5391 else {
5392 _sapp_fail("WGL: Failed to create OpenGL context");
5393 }
5394 }
5395 _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx);
5396 if (_sapp.wgl.ext_swap_control) {
5397 /* FIXME: DwmIsCompositionEnabled() (see GLFW) */
5398 _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval);
5399 }
5400}
5401
5402_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) {
5403 SOKOL_ASSERT(_sapp.wgl.gl_ctx);
5404 _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx);
5405 _sapp.wgl.gl_ctx = 0;
5406}
5407
5408_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) {
5409 SOKOL_ASSERT(_sapp.win32.dc);
5410 /* FIXME: DwmIsCompositionEnabled? (see GLFW) */
5411 SwapBuffers(_sapp.win32.dc);
5412}
5413#endif /* SOKOL_GLCORE33 */
5414
5415_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) {
5416 SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
5417 memset(dst, 0, (size_t)dst_num_bytes);
5418 const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
5419 if (bytes_needed <= dst_num_bytes) {
5420 WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL);
5421 return true;
5422 }
5423 else {
5424 return false;
5425 }
5426}
5427
5428_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) {
5429 HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST);
5430 MONITORINFO minfo;
5431 memset(&minfo, 0, sizeof(minfo));
5432 minfo.cbSize = sizeof(MONITORINFO);
5433 GetMonitorInfo(monitor, &minfo);
5434 const RECT mr = minfo.rcMonitor;
5435 const int monitor_w = mr.right - mr.left;
5436 const int monitor_h = mr.bottom - mr.top;
5437
5438 const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
5439 DWORD win_style;
5440 RECT rect = { 0, 0, 0, 0 };
5441
5442 _sapp.fullscreen = !_sapp.fullscreen;
5443 if (!_sapp.fullscreen) {
5444 win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
5445 rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale);
5446 rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale);
5447 }
5448 else {
5449 win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
5450 rect.right = monitor_w;
5451 rect.bottom = monitor_h;
5452 }
5453 AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
5454 int win_width = rect.right - rect.left;
5455 int win_height = rect.bottom - rect.top;
5456 if (!_sapp.fullscreen) {
5457 rect.left = (monitor_w - win_width) / 2;
5458 rect.top = (monitor_h - win_height) / 2;
5459 }
5460
5461 SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style);
5462 SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED);
5463}
5464
5465_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) {
5466 /* NOTE: this function is only called when the mouse visibility actually changes */
5467 ShowCursor((BOOL)visible);
5468}
5469
5470_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) {
5471 if (0 == _sapp.win32.mouse_capture_mask) {
5472 SetCapture(_sapp.win32.hwnd);
5473 }
5474 _sapp.win32.mouse_capture_mask |= btn_mask;
5475}
5476
5477_SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) {
5478 if (0 != _sapp.win32.mouse_capture_mask) {
5479 _sapp.win32.mouse_capture_mask &= ~btn_mask;
5480 if (0 == _sapp.win32.mouse_capture_mask) {
5481 ReleaseCapture();
5482 }
5483 }
5484}
5485
5486_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) {
5487 if (lock == _sapp.mouse.locked) {
5488 return;
5489 }
5490 _sapp.mouse.dx = 0.0f;
5491 _sapp.mouse.dy = 0.0f;
5492 _sapp.mouse.locked = lock;
5493 _sapp_win32_release_mouse(0xFF);
5494 if (_sapp.mouse.locked) {
5495 /* store the current mouse position, so it can be restored when unlocked */
5496 POINT pos;
5497 BOOL res = GetCursorPos(&pos);
5498 SOKOL_ASSERT(res); _SOKOL_UNUSED(res);
5499 _sapp.win32.mouse_locked_x = pos.x;
5500 _sapp.win32.mouse_locked_y = pos.y;
5501
5502 /* while the mouse is locked, make the mouse cursor invisible and
5503 confine the mouse movement to a small rectangle inside our window
5504 (so that we dont miss any mouse up events)
5505 */
5506 RECT client_rect = {
5507 _sapp.win32.mouse_locked_x,
5508 _sapp.win32.mouse_locked_y,
5509 _sapp.win32.mouse_locked_x,
5510 _sapp.win32.mouse_locked_y
5511 };
5512 ClipCursor(&client_rect);
5513
5514 /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */
5515 ShowCursor(FALSE);
5516
5517 /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */
5518 const RAWINPUTDEVICE rid = {
5519 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC
5520 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE
5521 0, // dwFlags
5522 _sapp.win32.hwnd // hwndTarget
5523 };
5524 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
5525 SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n");
5526 }
5527 /* in case the raw mouse device only supports absolute position reporting,
5528 we need to skip the dx/dy compution for the first WM_INPUT event
5529 */
5530 _sapp.win32.raw_input_mousepos_valid = false;
5531 }
5532 else {
5533 /* disable raw input for mouse */
5534 const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
5535 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
5536 SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n");
5537 }
5538
5539 /* let the mouse roam freely again */
5540 ClipCursor(NULL);
5541 ShowCursor(TRUE);
5542
5543 /* restore the 'pre-locked' mouse position */
5544 BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y);
5545 SOKOL_ASSERT(res); _SOKOL_UNUSED(res);
5546 }
5547}
5548
5549/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */
5550_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) {
5551 RECT rect;
5552 if (GetClientRect(_sapp.win32.hwnd, &rect)) {
5553 _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale);
5554 _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale);
5555 const int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale);
5556 const int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale);
5557 if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) {
5558 _sapp.framebuffer_width = fb_width;
5559 _sapp.framebuffer_height = fb_height;
5560 /* prevent a framebuffer size of 0 when window is minimized */
5561 if (_sapp.framebuffer_width == 0) {
5562 _sapp.framebuffer_width = 1;
5563 }
5564 if (_sapp.framebuffer_height == 0) {
5565 _sapp.framebuffer_height = 1;
5566 }
5567 return true;
5568 }
5569 }
5570 else {
5571 _sapp.window_width = _sapp.window_height = 1;
5572 _sapp.framebuffer_width = _sapp.framebuffer_height = 1;
5573 }
5574 return false;
5575}
5576
5577_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) {
5578 uint32_t mods = 0;
5579 if (GetKeyState(VK_SHIFT) & (1<<15)) {
5580 mods |= SAPP_MODIFIER_SHIFT;
5581 }
5582 if (GetKeyState(VK_CONTROL) & (1<<15)) {
5583 mods |= SAPP_MODIFIER_CTRL;
5584 }
5585 if (GetKeyState(VK_MENU) & (1<<15)) {
5586 mods |= SAPP_MODIFIER_ALT;
5587 }
5588 if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) {
5589 mods |= SAPP_MODIFIER_SUPER;
5590 }
5591 return mods;
5592}
5593
5594_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) {
5595 if (_sapp_events_enabled()) {
5596 _sapp_init_event(type);
5597 _sapp.event.modifiers = _sapp_win32_mods();
5598 _sapp.event.mouse_button = btn;
5599 _sapp_call_event(&_sapp.event);
5600 }
5601}
5602
5603_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) {
5604 if (_sapp_events_enabled()) {
5605 _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
5606 _sapp.event.modifiers = _sapp_win32_mods();
5607 _sapp.event.scroll_x = -x / 30.0f;
5608 _sapp.event.scroll_y = y / 30.0f;
5609 _sapp_call_event(&_sapp.event);
5610 }
5611}
5612
5613_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) {
5614 if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) {
5615 _sapp_init_event(type);
5616 _sapp.event.modifiers = _sapp_win32_mods();
5617 _sapp.event.key_code = _sapp.keycodes[vk];
5618 _sapp.event.key_repeat = repeat;
5619 _sapp_call_event(&_sapp.event);
5620 /* check if a CLIPBOARD_PASTED event must be sent too */
5621 if (_sapp.clipboard.enabled &&
5622 (type == SAPP_EVENTTYPE_KEY_DOWN) &&
5623 (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
5624 (_sapp.event.key_code == SAPP_KEYCODE_V))
5625 {
5626 _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
5627 _sapp_call_event(&_sapp.event);
5628 }
5629 }
5630}
5631
5632_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) {
5633 if (_sapp_events_enabled() && (c >= 32)) {
5634 _sapp_init_event(SAPP_EVENTTYPE_CHAR);
5635 _sapp.event.modifiers = _sapp_win32_mods();
5636 _sapp.event.char_code = c;
5637 _sapp.event.key_repeat = repeat;
5638 _sapp_call_event(&_sapp.event);
5639 }
5640}
5641
5642_SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) {
5643 if (!_sapp.drop.enabled) {
5644 return;
5645 }
5646 _sapp_clear_drop_buffer();
5647 bool drop_failed = false;
5648 const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0);
5649 _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count;
5650 for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) {
5651 const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1;
5652 WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR));
5653 DragQueryFileW(hdrop, i, buffer, num_chars);
5654 if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) {
5655 SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n");
5656 drop_failed = true;
5657 }
5658 SOKOL_FREE(buffer);
5659 }
5660 DragFinish(hdrop);
5661 if (!drop_failed) {
5662 if (_sapp_events_enabled()) {
5663 _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
5664 _sapp_call_event(&_sapp.event);
5665 }
5666 }
5667 else {
5668 _sapp_clear_drop_buffer();
5669 _sapp.drop.num_files = 0;
5670 }
5671}
5672
5673_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
5674 if (!_sapp.win32.in_create_window) {
5675 switch (uMsg) {
5676 case WM_CLOSE:
5677 /* only give user a chance to intervene when sapp_quit() wasn't already called */
5678 if (!_sapp.quit_ordered) {
5679 /* if window should be closed and event handling is enabled, give user code
5680 a change to intervene via sapp_cancel_quit()
5681 */
5682 _sapp.quit_requested = true;
5683 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
5684 /* if user code hasn't intervened, quit the app */
5685 if (_sapp.quit_requested) {
5686 _sapp.quit_ordered = true;
5687 }
5688 }
5689 if (_sapp.quit_ordered) {
5690 PostQuitMessage(0);
5691 }
5692 return 0;
5693 case WM_SYSCOMMAND:
5694 switch (wParam & 0xFFF0) {
5695 case SC_SCREENSAVE:
5696 case SC_MONITORPOWER:
5697 if (_sapp.fullscreen) {
5698 /* disable screen saver and blanking in fullscreen mode */
5699 return 0;
5700 }
5701 break;
5702 case SC_KEYMENU:
5703 /* user trying to access menu via ALT */
5704 return 0;
5705 }
5706 break;
5707 case WM_ERASEBKGND:
5708 return 1;
5709 case WM_SIZE:
5710 {
5711 const bool iconified = wParam == SIZE_MINIMIZED;
5712 if (iconified != _sapp.win32.iconified) {
5713 _sapp.win32.iconified = iconified;
5714 if (iconified) {
5715 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED);
5716 }
5717 else {
5718 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED);
5719 }
5720 }
5721 }
5722 break;
5723 case WM_SETCURSOR:
5724 if (_sapp.desc.user_cursor) {
5725 if (LOWORD(lParam) == HTCLIENT) {
5726 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR);
5727 return 1;
5728 }
5729 }
5730 break;
5731 case WM_LBUTTONDOWN:
5732 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT);
5733 _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
5734 break;
5735 case WM_RBUTTONDOWN:
5736 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT);
5737 _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
5738 break;
5739 case WM_MBUTTONDOWN:
5740 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE);
5741 _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
5742 break;
5743 case WM_LBUTTONUP:
5744 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT);
5745 _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
5746 break;
5747 case WM_RBUTTONUP:
5748 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT);
5749 _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
5750 break;
5751 case WM_MBUTTONUP:
5752 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE);
5753 _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
5754 break;
5755 case WM_MOUSEMOVE:
5756 if (!_sapp.mouse.locked) {
5757 const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
5758 const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
5759 /* don't update dx/dy in the very first event */
5760 if (_sapp.mouse.pos_valid) {
5761 _sapp.mouse.dx = new_x - _sapp.mouse.x;
5762 _sapp.mouse.dy = new_y - _sapp.mouse.y;
5763 }
5764 _sapp.mouse.x = new_x;
5765 _sapp.mouse.y = new_y;
5766 _sapp.mouse.pos_valid = true;
5767 if (!_sapp.win32.mouse_tracked) {
5768 _sapp.win32.mouse_tracked = true;
5769 TRACKMOUSEEVENT tme;
5770 memset(&tme, 0, sizeof(tme));
5771 tme.cbSize = sizeof(tme);
5772 tme.dwFlags = TME_LEAVE;
5773 tme.hwndTrack = _sapp.win32.hwnd;
5774 TrackMouseEvent(&tme);
5775 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID);
5776 }
5777 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
5778 }
5779 break;
5780 case WM_INPUT:
5781 /* raw mouse input during mouse-lock */
5782 if (_sapp.mouse.locked) {
5783 HRAWINPUT ri = (HRAWINPUT) lParam;
5784 UINT size = sizeof(_sapp.win32.raw_input_data);
5785 // see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdata
5786 if ((UINT)-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) {
5787 SOKOL_LOG("GetRawInputData() failed\n");
5788 break;
5789 }
5790 const RAWINPUT* raw_mouse_data = (const RAWINPUT*) &_sapp.win32.raw_input_data;
5791 if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
5792 /* mouse only reports absolute position
5793 NOTE: THIS IS UNTESTED, it's unclear from reading the
5794 Win32 RawInput docs under which circumstances absolute
5795 positions are sent.
5796 */
5797 if (_sapp.win32.raw_input_mousepos_valid) {
5798 LONG new_x = raw_mouse_data->data.mouse.lLastX;
5799 LONG new_y = raw_mouse_data->data.mouse.lLastY;
5800 _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x);
5801 _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y);
5802 _sapp.win32.raw_input_mousepos_x = new_x;
5803 _sapp.win32.raw_input_mousepos_y = new_y;
5804 _sapp.win32.raw_input_mousepos_valid = true;
5805 }
5806 }
5807 else {
5808 /* mouse reports movement delta (this seems to be the common case) */
5809 _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX;
5810 _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY;
5811 }
5812 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
5813 }
5814 break;
5815
5816 case WM_MOUSELEAVE:
5817 if (!_sapp.mouse.locked) {
5818 _sapp.win32.mouse_tracked = false;
5819 _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID);
5820 }
5821 break;
5822 case WM_MOUSEWHEEL:
5823 _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam)));
5824 break;
5825 case WM_MOUSEHWHEEL:
5826 _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f);
5827 break;
5828 case WM_CHAR:
5829 _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000));
5830 break;
5831 case WM_KEYDOWN:
5832 case WM_SYSKEYDOWN:
5833 _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000));
5834 break;
5835 case WM_KEYUP:
5836 case WM_SYSKEYUP:
5837 _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false);
5838 break;
5839 case WM_ENTERSIZEMOVE:
5840 SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL);
5841 break;
5842 case WM_EXITSIZEMOVE:
5843 KillTimer(_sapp.win32.hwnd, 1);
5844 break;
5845 case WM_TIMER:
5846 _sapp_frame();
5847 #if defined(SOKOL_D3D11)
5848 _sapp_d3d11_present();
5849 #endif
5850 #if defined(SOKOL_GLCORE33)
5851 _sapp_wgl_swap_buffers();
5852 #endif
5853 /* NOTE: resizing the swap-chain during resize leads to a substantial
5854 memory spike (hundreds of megabytes for a few seconds).
5855
5856 if (_sapp_win32_update_dimensions()) {
5857 #if defined(SOKOL_D3D11)
5858 _sapp_d3d11_resize_default_render_target();
5859 #endif
5860 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
5861 }
5862 */
5863 break;
5864 case WM_DROPFILES:
5865 _sapp_win32_files_dropped((HDROP)wParam);
5866 break;
5867 default:
5868 break;
5869 }
5870 }
5871 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
5872}
5873
5874_SOKOL_PRIVATE void _sapp_win32_create_window(void) {
5875 WNDCLASSW wndclassw;
5876 memset(&wndclassw, 0, sizeof(wndclassw));
5877 wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
5878 wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc;
5879 wndclassw.hInstance = GetModuleHandleW(NULL);
5880 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5881 wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO);
5882 wndclassw.lpszClassName = L"SOKOLAPP";
5883 RegisterClassW(&wndclassw);
5884
5885 DWORD win_style;
5886 const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
5887 RECT rect = { 0, 0, 0, 0 };
5888 if (_sapp.fullscreen) {
5889 win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
5890 rect.right = GetSystemMetrics(SM_CXSCREEN);
5891 rect.bottom = GetSystemMetrics(SM_CYSCREEN);
5892 }
5893 else {
5894 win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
5895 rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale);
5896 rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale);
5897 }
5898 AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
5899 const int win_width = rect.right - rect.left;
5900 const int win_height = rect.bottom - rect.top;
5901 _sapp.win32.in_create_window = true;
5902 _sapp.win32.hwnd = CreateWindowExW(
5903 win_ex_style, /* dwExStyle */
5904 L"SOKOLAPP", /* lpClassName */
5905 _sapp.window_title_wide, /* lpWindowName */
5906 win_style, /* dwStyle */
5907 CW_USEDEFAULT, /* X */
5908 CW_USEDEFAULT, /* Y */
5909 win_width, /* nWidth */
5910 win_height, /* nHeight */
5911 NULL, /* hWndParent */
5912 NULL, /* hMenu */
5913 GetModuleHandle(NULL), /* hInstance */
5914 NULL); /* lParam */
5915 ShowWindow(_sapp.win32.hwnd, SW_SHOW);
5916 _sapp.win32.in_create_window = false;
5917 _sapp.win32.dc = GetDC(_sapp.win32.hwnd);
5918 SOKOL_ASSERT(_sapp.win32.dc);
5919 _sapp_win32_update_dimensions();
5920
5921 DragAcceptFiles(_sapp.win32.hwnd, 1);
5922}
5923
5924_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) {
5925 DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0;
5926 UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL));
5927}
5928
5929_SOKOL_PRIVATE void _sapp_win32_init_console(void) {
5930 if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) {
5931 BOOL con_valid = FALSE;
5932 if (_sapp.desc.win32_console_create) {
5933 con_valid = AllocConsole();
5934 }
5935 else if (_sapp.desc.win32_console_attach) {
5936 con_valid = AttachConsole(ATTACH_PARENT_PROCESS);
5937 }
5938 if (con_valid) {
5939 FILE* res_fp = 0;
5940 errno_t err;
5941 err = freopen_s(&res_fp, "CON", "w", stdout);
5942 err = freopen_s(&res_fp, "CON", "w", stderr);
5943 (void)err;
5944 }
5945 }
5946 if (_sapp.desc.win32_console_utf8) {
5947 _sapp.win32.orig_codepage = GetConsoleOutputCP();
5948 SetConsoleOutputCP(CP_UTF8);
5949 }
5950}
5951
5952_SOKOL_PRIVATE void _sapp_win32_restore_console(void) {
5953 if (_sapp.desc.win32_console_utf8) {
5954 SetConsoleOutputCP(_sapp.win32.orig_codepage);
5955 }
5956}
5957
5958_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) {
5959
5960 typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void);
5961 typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS);
5962 typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
5963
5964 SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0;
5965 SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0;
5966 GETDPIFORMONITOR_T fn_getdpiformonitor = 0;
5967 HINSTANCE user32 = LoadLibraryA("user32.dll");
5968 if (user32) {
5969 fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware");
5970 }
5971 HINSTANCE shcore = LoadLibraryA("shcore.dll");
5972 if (shcore) {
5973 fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness");
5974 fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor");
5975 }
5976 if (fn_setprocessdpiawareness) {
5977 /* if the app didn't request HighDPI rendering, let Windows do the upscaling */
5978 PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE;
5979 _sapp.win32.dpi.aware = true;
5980 if (!_sapp.desc.high_dpi) {
5981 process_dpi_awareness = PROCESS_DPI_UNAWARE;
5982 _sapp.win32.dpi.aware = false;
5983 }
5984 fn_setprocessdpiawareness(process_dpi_awareness);
5985 }
5986 else if (fn_setprocessdpiaware) {
5987 fn_setprocessdpiaware();
5988 _sapp.win32.dpi.aware = true;
5989 }
5990 /* get dpi scale factor for main monitor */
5991 if (fn_getdpiformonitor && _sapp.win32.dpi.aware) {
5992 POINT pt = { 1, 1 };
5993 HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
5994 UINT dpix, dpiy;
5995 HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
5996 _SOKOL_UNUSED(hr);
5997 SOKOL_ASSERT(SUCCEEDED(hr));
5998 /* clamp window scale to an integer factor */
5999 _sapp.win32.dpi.window_scale = (float)dpix / 96.0f;
6000 }
6001 else {
6002 _sapp.win32.dpi.window_scale = 1.0f;
6003 }
6004 if (_sapp.desc.high_dpi) {
6005 _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale;
6006 _sapp.win32.dpi.mouse_scale = 1.0f;
6007 }
6008 else {
6009 _sapp.win32.dpi.content_scale = 1.0f;
6010 _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale;
6011 }
6012 _sapp.dpi_scale = _sapp.win32.dpi.content_scale;
6013 if (user32) {
6014 FreeLibrary(user32);
6015 }
6016 if (shcore) {
6017 FreeLibrary(shcore);
6018 }
6019}
6020
6021_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) {
6022 SOKOL_ASSERT(str);
6023 SOKOL_ASSERT(_sapp.win32.hwnd);
6024 SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0));
6025
6026 wchar_t* wchar_buf = 0;
6027 const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t);
6028 HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size);
6029 if (!object) {
6030 goto error;
6031 }
6032 wchar_buf = (wchar_t*) GlobalLock(object);
6033 if (!wchar_buf) {
6034 goto error;
6035 }
6036 if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) {
6037 goto error;
6038 }
6039 GlobalUnlock(wchar_buf);
6040 wchar_buf = 0;
6041 if (!OpenClipboard(_sapp.win32.hwnd)) {
6042 goto error;
6043 }
6044 EmptyClipboard();
6045 SetClipboardData(CF_UNICODETEXT, object);
6046 CloseClipboard();
6047 return true;
6048
6049error:
6050 if (wchar_buf) {
6051 GlobalUnlock(object);
6052 }
6053 if (object) {
6054 GlobalFree(object);
6055 }
6056 return false;
6057}
6058
6059_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) {
6060 SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
6061 SOKOL_ASSERT(_sapp.win32.hwnd);
6062 if (!OpenClipboard(_sapp.win32.hwnd)) {
6063 /* silently ignore any errors and just return the current
6064 content of the local clipboard buffer
6065 */
6066 return _sapp.clipboard.buffer;
6067 }
6068 HANDLE object = GetClipboardData(CF_UNICODETEXT);
6069 if (!object) {
6070 CloseClipboard();
6071 return _sapp.clipboard.buffer;
6072 }
6073 const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object);
6074 if (!wchar_buf) {
6075 CloseClipboard();
6076 return _sapp.clipboard.buffer;
6077 }
6078 if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) {
6079 SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n");
6080 }
6081 GlobalUnlock(object);
6082 CloseClipboard();
6083 return _sapp.clipboard.buffer;
6084}
6085
6086_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) {
6087 _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
6088 SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide);
6089}
6090
6091/* don't laugh, but this seems to be the easiest and most robust
6092 way to check if we're running on Win10
6093
6094 From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263
6095*/
6096_SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) {
6097 HMODULE h = GetModuleHandleW(L"kernel32.dll");
6098 if (NULL != h) {
6099 return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation"));
6100 }
6101 else {
6102 return false;
6103 }
6104}
6105
6106_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
6107 _sapp_init_state(desc);
6108 _sapp_win32_init_console();
6109 _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater();
6110 _sapp_win32_uwp_init_keytable();
6111 _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
6112 _sapp_win32_init_dpi();
6113 _sapp_win32_create_window();
6114 #if defined(SOKOL_D3D11)
6115 _sapp_d3d11_create_device_and_swapchain();
6116 _sapp_d3d11_create_default_render_target();
6117 #endif
6118 #if defined(SOKOL_GLCORE33)
6119 _sapp_wgl_init();
6120 _sapp_wgl_load_extensions();
6121 _sapp_wgl_create_context();
6122 #endif
6123 _sapp.valid = true;
6124
6125 bool done = false;
6126 while (!(done || _sapp.quit_ordered)) {
6127 MSG msg;
6128 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
6129 if (WM_QUIT == msg.message) {
6130 done = true;
6131 continue;
6132 }
6133 else {
6134 TranslateMessage(&msg);
6135 DispatchMessage(&msg);
6136 }
6137 }
6138 _sapp_frame();
6139 #if defined(SOKOL_D3D11)
6140 _sapp_d3d11_present();
6141 if (IsIconic(_sapp.win32.hwnd)) {
6142 Sleep((DWORD)(16 * _sapp.swap_interval));
6143 }
6144 #endif
6145 #if defined(SOKOL_GLCORE33)
6146 _sapp_wgl_swap_buffers();
6147 #endif
6148 /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */
6149 if (_sapp_win32_update_dimensions()) {
6150 #if defined(SOKOL_D3D11)
6151 _sapp_d3d11_resize_default_render_target();
6152 #endif
6153 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
6154 }
6155 if (_sapp.quit_requested) {
6156 PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0);
6157 }
6158 }
6159 _sapp_call_cleanup();
6160
6161 #if defined(SOKOL_D3D11)
6162 _sapp_d3d11_destroy_default_render_target();
6163 _sapp_d3d11_destroy_device_and_swapchain();
6164 #else
6165 _sapp_wgl_destroy_context();
6166 _sapp_wgl_shutdown();
6167 #endif
6168 _sapp_win32_destroy_window();
6169 _sapp_win32_restore_console();
6170 _sapp_discard_state();
6171}
6172
6173_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) {
6174 int argc = 0;
6175 char** argv = 0;
6176 char* args;
6177
6178 LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc);
6179 if (w_argv == NULL) {
6180 _sapp_fail("Win32: failed to parse command line");
6181 } else {
6182 size_t size = wcslen(w_command_line) * 4;
6183 argv = (char**) SOKOL_CALLOC(1, ((size_t)argc + 1) * sizeof(char*) + size);
6184 SOKOL_ASSERT(argv);
6185 args = (char*) &argv[argc + 1];
6186 int n;
6187 for (int i = 0; i < argc; ++i) {
6188 n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL);
6189 if (n == 0) {
6190 _sapp_fail("Win32: failed to convert all arguments to utf8");
6191 break;
6192 }
6193 argv[i] = args;
6194 size -= (size_t)n;
6195 args += n;
6196 }
6197 LocalFree(w_argv);
6198 }
6199 *o_argc = argc;
6200 return argv;
6201}
6202
6203#if !defined(SOKOL_NO_ENTRY)
6204#if defined(SOKOL_WIN32_FORCE_MAIN)
6205int main(int argc, char* argv[]) {
6206 sapp_desc desc = sokol_main(argc, argv);
6207 _sapp_win32_run(&desc);
6208 return 0;
6209}
6210#else
6211int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
6212 _SOKOL_UNUSED(hInstance);
6213 _SOKOL_UNUSED(hPrevInstance);
6214 _SOKOL_UNUSED(lpCmdLine);
6215 _SOKOL_UNUSED(nCmdShow);
6216 int argc_utf8 = 0;
6217 char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8);
6218 sapp_desc desc = sokol_main(argc_utf8, argv_utf8);
6219 _sapp_win32_run(&desc);
6220 SOKOL_FREE(argv_utf8);
6221 return 0;
6222}
6223#endif /* SOKOL_WIN32_FORCE_MAIN */
6224#endif /* SOKOL_NO_ENTRY */
6225
6226#ifdef _MSC_VER
6227 #pragma warning(pop)
6228#endif
6229
6230#endif /* _SAPP_WIN32 */
6231
6232/*== UWP ================================================================*/
6233#if defined(_SAPP_UWP)
6234
6235// Helper functions
6236_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) {
6237 _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f;
6238 if (_sapp.desc.high_dpi) {
6239 _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale;
6240 _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale;
6241 }
6242 else {
6243 _sapp.uwp.dpi.content_scale = 1.0f;
6244 _sapp.uwp.dpi.mouse_scale = 1.0f;
6245 }
6246 _sapp.dpi_scale = _sapp.uwp.dpi.content_scale;
6247}
6248
6249_SOKOL_PRIVATE void _sapp_uwp_show_mouse(bool visible) {
6250 using namespace winrt::Windows::UI::Core;
6251
6252 /* NOTE: this function is only called when the mouse visibility actually changes */
6253 CoreWindow::GetForCurrentThread().PointerCursor(visible ?
6254 CoreCursor(CoreCursorType::Arrow, 0) :
6255 CoreCursor(nullptr));
6256}
6257
6258_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) {
6259 using namespace winrt::Windows::System;
6260 using namespace winrt::Windows::UI::Core;
6261
6262 uint32_t mods = 0;
6263 if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
6264 mods |= SAPP_MODIFIER_SHIFT;
6265 }
6266 if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
6267 mods |= SAPP_MODIFIER_CTRL;
6268 }
6269 if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
6270 mods |= SAPP_MODIFIER_ALT;
6271 }
6272 if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) ||
6273 ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down))
6274 {
6275 mods |= SAPP_MODIFIER_SUPER;
6276 }
6277 return mods;
6278}
6279
6280_SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton btn, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
6281 if (_sapp_events_enabled()) {
6282 _sapp_init_event(type);
6283 _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
6284 _sapp.event.mouse_button = btn;
6285 _sapp_call_event(&_sapp.event);
6286 }
6287}
6288
6289_SOKOL_PRIVATE void _sapp_uwp_scroll_event(float delta, bool horizontal, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
6290 if (_sapp_events_enabled()) {
6291 _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
6292 _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
6293 _sapp.event.scroll_x = horizontal ? (-delta / 30.0f) : 0.0f;
6294 _sapp.event.scroll_y = horizontal ? 0.0f : (delta / 30.0f);
6295 _sapp_call_event(&_sapp.event);
6296 }
6297}
6298
6299_SOKOL_PRIVATE void _sapp_uwp_extract_mouse_button_events(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
6300
6301 // we need to figure out ourselves what mouse buttons have been pressed and released,
6302 // because UWP doesn't properly send down/up mouse button events when multiple buttons
6303 // are pressed down, so we also need to check the mouse button state in other mouse events
6304 // to track what buttons have been pressed down and released
6305 //
6306 auto properties = args.CurrentPoint().Properties();
6307 const uint8_t lmb_bit = (1 << SAPP_MOUSEBUTTON_LEFT);
6308 const uint8_t rmb_bit = (1 << SAPP_MOUSEBUTTON_RIGHT);
6309 const uint8_t mmb_bit = (1 << SAPP_MOUSEBUTTON_MIDDLE);
6310 uint8_t new_btns = 0;
6311 if (properties.IsLeftButtonPressed()) {
6312 new_btns |= lmb_bit;
6313 }
6314 if (properties.IsRightButtonPressed()) {
6315 new_btns |= rmb_bit;
6316 }
6317 if (properties.IsMiddleButtonPressed()) {
6318 new_btns |= mmb_bit;
6319 }
6320 const uint8_t old_btns = _sapp.uwp.mouse_buttons;
6321 const uint8_t chg_btns = new_btns ^ old_btns;
6322
6323 _sapp.uwp.mouse_buttons = new_btns;
6324
6325 sapp_event_type type = SAPP_EVENTTYPE_INVALID;
6326 sapp_mousebutton btn = SAPP_MOUSEBUTTON_INVALID;
6327 if (chg_btns & lmb_bit) {
6328 btn = SAPP_MOUSEBUTTON_LEFT;
6329 type = (new_btns & lmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
6330 }
6331 if (chg_btns & rmb_bit) {
6332 btn = SAPP_MOUSEBUTTON_RIGHT;
6333 type = (new_btns & rmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
6334 }
6335 if (chg_btns & mmb_bit) {
6336 btn = SAPP_MOUSEBUTTON_MIDDLE;
6337 type = (new_btns & mmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
6338 }
6339 if (type != SAPP_EVENTTYPE_INVALID) {
6340 _sapp_uwp_mouse_event(type, btn, sender);
6341 }
6342}
6343
6344_SOKOL_PRIVATE void _sapp_uwp_key_event(sapp_event_type type, winrt::Windows::UI::Core::CoreWindow const& sender_window, winrt::Windows::UI::Core::KeyEventArgs const& args) {
6345 auto key_status = args.KeyStatus();
6346 uint32_t ext_scan_code = key_status.ScanCode | (key_status.IsExtendedKey ? 0x100 : 0);
6347 if (_sapp_events_enabled() && (ext_scan_code < SAPP_MAX_KEYCODES)) {
6348 _sapp_init_event(type);
6349 _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
6350 _sapp.event.key_code = _sapp.keycodes[ext_scan_code];
6351 _sapp.event.key_repeat = type == SAPP_EVENTTYPE_KEY_UP ? false : key_status.WasKeyDown;
6352 _sapp_call_event(&_sapp.event);
6353 /* check if a CLIPBOARD_PASTED event must be sent too */
6354 if (_sapp.clipboard.enabled &&
6355 (type == SAPP_EVENTTYPE_KEY_DOWN) &&
6356 (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
6357 (_sapp.event.key_code == SAPP_KEYCODE_V))
6358 {
6359 _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
6360 _sapp_call_event(&_sapp.event);
6361 }
6362 }
6363}
6364
6365_SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
6366 if (_sapp_events_enabled() && (c >= 32)) {
6367 _sapp_init_event(SAPP_EVENTTYPE_CHAR);
6368 _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
6369 _sapp.event.char_code = c;
6370 _sapp.event.key_repeat = repeat;
6371 _sapp_call_event(&_sapp.event);
6372 }
6373}
6374
6375_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) {
6376 auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
6377 _sapp.fullscreen = appView.IsFullScreenMode();
6378 if (!_sapp.fullscreen) {
6379 appView.TryEnterFullScreenMode();
6380 }
6381 else {
6382 appView.ExitFullScreenMode();
6383 }
6384 _sapp.fullscreen = appView.IsFullScreenMode();
6385}
6386
6387namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */
6388
6389// Controls all the DirectX device resources.
6390class DeviceResources {
6391public:
6392 // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created.
6393 interface IDeviceNotify {
6394 virtual void OnDeviceLost() = 0;
6395 virtual void OnDeviceRestored() = 0;
6396 };
6397
6398 DeviceResources();
6399 ~DeviceResources();
6400 void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window);
6401 void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize);
6402 void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation);
6403 void SetDpi(float dpi);
6404 void ValidateDevice();
6405 void HandleDeviceLost();
6406 void RegisterDeviceNotify(IDeviceNotify* deviceNotify);
6407 void Trim();
6408 void Present();
6409
6410private:
6411
6412 // Swapchain Rotation Matrices (Z-rotation)
6413 static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = {
6414 1.0f, 0.0f, 0.0f, 0.0f,
6415 0.0f, 1.0f, 0.0f, 0.0f,
6416 0.0f, 0.0f, 1.0f, 0.0f,
6417 0.0f, 0.0f, 0.0f, 1.0f
6418 };
6419 static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = {
6420 0.0f, 1.0f, 0.0f, 0.0f,
6421 -1.0f, 0.0f, 0.0f, 0.0f,
6422 0.0f, 0.0f, 1.0f, 0.0f,
6423 0.0f, 0.0f, 0.0f, 1.0f
6424 };
6425 static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = {
6426 -1.0f, 0.0f, 0.0f, 0.0f,
6427 0.0f, -1.0f, 0.0f, 0.0f,
6428 0.0f, 0.0f, 1.0f, 0.0f,
6429 0.0f, 0.0f, 0.0f, 1.0f
6430 };
6431 static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = {
6432 0.0f, -1.0f, 0.0f, 0.0f,
6433 1.0f, 0.0f, 0.0f, 0.0f,
6434 0.0f, 0.0f, 1.0f, 0.0f,
6435 0.0f, 0.0f, 0.0f, 1.0f
6436 };
6437
6438 void CreateDeviceResources();
6439 void CreateWindowSizeDependentResources();
6440 void UpdateRenderTargetSize();
6441 DXGI_MODE_ROTATION ComputeDisplayRotation();
6442 bool SdkLayersAvailable();
6443
6444 // Direct3D objects.
6445 winrt::com_ptr<ID3D11Device3> m_d3dDevice;
6446 winrt::com_ptr<ID3D11DeviceContext3> m_d3dContext;
6447 winrt::com_ptr<IDXGISwapChain3> m_swapChain;
6448
6449 // Direct3D rendering objects. Required for 3D.
6450 winrt::com_ptr<ID3D11Texture2D1> m_d3dRenderTarget;
6451 winrt::com_ptr<ID3D11RenderTargetView1> m_d3dRenderTargetView;
6452 winrt::com_ptr<ID3D11Texture2D1> m_d3dMSAARenderTarget;
6453 winrt::com_ptr<ID3D11RenderTargetView1> m_d3dMSAARenderTargetView;
6454 winrt::com_ptr<ID3D11Texture2D1> m_d3dDepthStencil;
6455 winrt::com_ptr<ID3D11DepthStencilView> m_d3dDepthStencilView;
6456 D3D11_VIEWPORT m_screenViewport = { };
6457
6458 // Cached reference to the Window.
6459 winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window;
6460
6461 // Cached device properties.
6462 D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1;
6463 winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { };
6464 winrt::Windows::Foundation::Size m_outputSize = { };
6465 winrt::Windows::Foundation::Size m_logicalSize = { };
6466 winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None;
6467 winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None;
6468 float m_dpi = -1.0f;
6469
6470 // Transforms used for display orientation.
6471 DirectX::XMFLOAT4X4 m_orientationTransform3D;
6472
6473 // The IDeviceNotify can be held directly as it owns the DeviceResources.
6474 IDeviceNotify* m_deviceNotify = nullptr;
6475};
6476
6477// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events.
6478struct App : winrt::implements<App, winrt::Windows::ApplicationModel::Core::IFrameworkViewSource, winrt::Windows::ApplicationModel::Core::IFrameworkView> {
6479public:
6480 // IFrameworkViewSource Methods
6481 winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; }
6482
6483 // IFrameworkView Methods.
6484 virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView);
6485 virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window);
6486 virtual void Load(winrt::hstring const& entryPoint);
6487 virtual void Run();
6488 virtual void Uninitialize();
6489
6490protected:
6491 // Application lifecycle event handlers
6492 void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args);
6493 void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args);
6494 void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args);
6495
6496 // Window event handlers
6497 void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args);
6498 void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args);
6499
6500 // Navigation event handlers
6501 void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args);
6502
6503 // Input event handlers
6504 void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args);
6505 void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args);
6506 void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args);
6507
6508 // Pointer event handlers
6509 void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6510 void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6511 void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6512 void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6513 void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6514 void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
6515
6516 // DisplayInformation event handlers.
6517 void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
6518 void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
6519 void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
6520
6521private:
6522 std::unique_ptr<DeviceResources> m_deviceResources;
6523 bool m_windowVisible = true;
6524};
6525
6526DeviceResources::DeviceResources() {
6527 CreateDeviceResources();
6528}
6529
6530DeviceResources::~DeviceResources() {
6531 // Cleanup Sokol Context
6532 _sapp.d3d11.device = nullptr;
6533 _sapp.d3d11.device_context = nullptr;
6534}
6535
6536void DeviceResources::CreateDeviceResources() {
6537 // This flag adds support for surfaces with a different color channel ordering
6538 // than the API default. It is required for compatibility with Direct2D.
6539 UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
6540
6541 #if defined(_DEBUG)
6542 if (SdkLayersAvailable()) {
6543 // If the project is in a debug build, enable debugging via SDK Layers with this flag.
6544 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
6545 }
6546 #endif
6547
6548 // This array defines the set of DirectX hardware feature levels this app will support.
6549 // Note the ordering should be preserved.
6550 // Don't forget to declare your application's minimum required feature level in its
6551 // description. All applications are assumed to support 9.1 unless otherwise stated.
6552 D3D_FEATURE_LEVEL featureLevels[] = {
6553 D3D_FEATURE_LEVEL_12_1,
6554 D3D_FEATURE_LEVEL_12_0,
6555 D3D_FEATURE_LEVEL_11_1,
6556 D3D_FEATURE_LEVEL_11_0,
6557 D3D_FEATURE_LEVEL_10_1,
6558 D3D_FEATURE_LEVEL_10_0,
6559 D3D_FEATURE_LEVEL_9_3,
6560 D3D_FEATURE_LEVEL_9_2,
6561 D3D_FEATURE_LEVEL_9_1
6562 };
6563
6564 // Create the Direct3D 11 API device object and a corresponding context.
6565 winrt::com_ptr<ID3D11Device> device;
6566 winrt::com_ptr<ID3D11DeviceContext> context;
6567
6568 HRESULT hr = D3D11CreateDevice(
6569 nullptr, // Specify nullptr to use the default adapter.
6570 D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver.
6571 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
6572 creationFlags, // Set debug and Direct2D compatibility flags.
6573 featureLevels, // List of feature levels this app can support.
6574 ARRAYSIZE(featureLevels), // Size of the list above.
6575 D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps.
6576 device.put(), // Returns the Direct3D device created.
6577 &m_d3dFeatureLevel, // Returns feature level of device created.
6578 context.put() // Returns the device immediate context.
6579 );
6580
6581 if (FAILED(hr)) {
6582 // If the initialization fails, fall back to the WARP device.
6583 // For more information on WARP, see:
6584 // https://go.microsoft.com/fwlink/?LinkId=286690
6585 winrt::check_hresult(
6586 D3D11CreateDevice(
6587 nullptr,
6588 D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
6589 0,
6590 creationFlags,
6591 featureLevels,
6592 ARRAYSIZE(featureLevels),
6593 D3D11_SDK_VERSION,
6594 device.put(),
6595 &m_d3dFeatureLevel,
6596 context.put()
6597 )
6598 );
6599 }
6600
6601 // Store pointers to the Direct3D 11.3 API device and immediate context.
6602 m_d3dDevice = device.as<ID3D11Device3>();
6603 m_d3dContext = context.as<ID3D11DeviceContext3>();
6604
6605 // Setup Sokol Context
6606 _sapp.d3d11.device = m_d3dDevice.get();
6607 _sapp.d3d11.device_context = m_d3dContext.get();
6608}
6609
6610void DeviceResources::CreateWindowSizeDependentResources() {
6611 // Cleanup Sokol Context (these are non-owning raw pointers)
6612 _sapp.d3d11.rt = nullptr;
6613 _sapp.d3d11.rtv = nullptr;
6614 _sapp.d3d11.msaa_rt = nullptr;
6615 _sapp.d3d11.msaa_rtv = nullptr;
6616 _sapp.d3d11.ds = nullptr;
6617 _sapp.d3d11.dsv = nullptr;
6618
6619 // Clear the previous window size specific context.
6620 ID3D11RenderTargetView* nullViews[] = { nullptr };
6621 m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
6622 // these are smart pointers, setting to nullptr will delete the objects
6623 m_d3dRenderTarget = nullptr;
6624 m_d3dRenderTargetView = nullptr;
6625 m_d3dMSAARenderTarget = nullptr;
6626 m_d3dMSAARenderTargetView = nullptr;
6627 m_d3dDepthStencilView = nullptr;
6628 m_d3dDepthStencil = nullptr;
6629 m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
6630
6631 UpdateRenderTargetSize();
6632
6633 // The width and height of the swap chain must be based on the window's
6634 // natively-oriented width and height. If the window is not in the native
6635 // orientation, the dimensions must be reversed.
6636 DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();
6637
6638 bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
6639 m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
6640 m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
6641
6642 if (m_swapChain != nullptr) {
6643 // If the swap chain already exists, resize it.
6644 HRESULT hr = m_swapChain->ResizeBuffers(
6645 2, // Double-buffered swap chain.
6646 lround(m_d3dRenderTargetSize.Width),
6647 lround(m_d3dRenderTargetSize.Height),
6648 DXGI_FORMAT_B8G8R8A8_UNORM,
6649 0
6650 );
6651
6652 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
6653 // If the device was removed for any reason, a new device and swap chain will need to be created.
6654 HandleDeviceLost();
6655
6656 // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
6657 // and correctly set up the new device.
6658 return;
6659 }
6660 else {
6661 winrt::check_hresult(hr);
6662 }
6663 }
6664 else {
6665 // Otherwise, create a new one using the same adapter as the existing Direct3D device.
6666 DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
6667 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
6668
6669 swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
6670 swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
6671 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
6672 swapChainDesc.Stereo = false;
6673 swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
6674 swapChainDesc.SampleDesc.Quality = 0;
6675 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6676 swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
6677 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect.
6678 swapChainDesc.Flags = 0;
6679 swapChainDesc.Scaling = scaling;
6680 swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
6681
6682 // This sequence obtains the DXGI factory that was used to create the Direct3D device above.
6683 winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as<IDXGIDevice3>();
6684 winrt::com_ptr<IDXGIAdapter> dxgiAdapter;
6685 winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put()));
6686 winrt::com_ptr<IDXGIFactory4> dxgiFactory;
6687 winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void()));
6688 winrt::com_ptr<IDXGISwapChain1> swapChain;
6689 winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put()));
6690 m_swapChain = swapChain.as<IDXGISwapChain3>();
6691
6692 // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
6693 // ensures that the application will only render after each VSync, minimizing power consumption.
6694 winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1));
6695
6696 // Setup Sokol Context
6697 winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc));
6698 _sapp.d3d11.swap_chain = m_swapChain.as<IDXGISwapChain3>().detach();
6699 }
6700
6701 // Set the proper orientation for the swap chain, and generate 2D and
6702 // 3D matrix transformations for rendering to the rotated swap chain.
6703 // Note the rotation angle for the 2D and 3D transforms are different.
6704 // This is due to the difference in coordinate spaces. Additionally,
6705 // the 3D matrix is specified explicitly to avoid rounding errors.
6706 switch (displayRotation) {
6707 case DXGI_MODE_ROTATION_IDENTITY:
6708 m_orientationTransform3D = m_rotation0;
6709 break;
6710
6711 case DXGI_MODE_ROTATION_ROTATE90:
6712 m_orientationTransform3D = m_rotation270;
6713 break;
6714
6715 case DXGI_MODE_ROTATION_ROTATE180:
6716 m_orientationTransform3D = m_rotation180;
6717 break;
6718
6719 case DXGI_MODE_ROTATION_ROTATE270:
6720 m_orientationTransform3D = m_rotation90;
6721 break;
6722 }
6723 winrt::check_hresult(m_swapChain->SetRotation(displayRotation));
6724
6725 // Create a render target view of the swap chain back buffer.
6726 winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget)));
6727 winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put()));
6728
6729 // Create MSAA texture and view if needed
6730 if (_sapp.sample_count > 1) {
6731 CD3D11_TEXTURE2D_DESC1 msaaTexDesc(
6732 DXGI_FORMAT_B8G8R8A8_UNORM,
6733 lround(m_d3dRenderTargetSize.Width),
6734 lround(m_d3dRenderTargetSize.Height),
6735 1, // arraySize
6736 1, // mipLevels
6737 D3D11_BIND_RENDER_TARGET,
6738 D3D11_USAGE_DEFAULT,
6739 0, // cpuAccessFlags
6740 _sapp.sample_count,
6741 _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0
6742 );
6743 winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put()));
6744 winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put()));
6745 }
6746
6747 // Create a depth stencil view for use with 3D rendering if needed.
6748 CD3D11_TEXTURE2D_DESC1 depthStencilDesc(
6749 DXGI_FORMAT_D24_UNORM_S8_UINT,
6750 lround(m_d3dRenderTargetSize.Width),
6751 lround(m_d3dRenderTargetSize.Height),
6752 1, // This depth stencil view has only one texture.
6753 1, // Use a single mipmap level.
6754 D3D11_BIND_DEPTH_STENCIL,
6755 D3D11_USAGE_DEFAULT,
6756 0, // cpuAccessFlag
6757 _sapp.sample_count,
6758 _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0
6759 );
6760 winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put()));
6761
6762 CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
6763 winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put()));
6764
6765 // Set sokol window and framebuffer sizes
6766 _sapp.window_width = (int) m_logicalSize.Width;
6767 _sapp.window_height = (int) m_logicalSize.Height;
6768 _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width);
6769 _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height);
6770
6771 // Setup Sokol Context
6772 _sapp.d3d11.rt = m_d3dRenderTarget.as<ID3D11Texture2D>().get();
6773 _sapp.d3d11.rtv = m_d3dRenderTargetView.as<ID3D11RenderTargetView>().get();
6774 _sapp.d3d11.ds = m_d3dDepthStencil.as<ID3D11Texture2D>().get();
6775 _sapp.d3d11.dsv = m_d3dDepthStencilView.get();
6776 if (_sapp.sample_count > 1) {
6777 _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as<ID3D11Texture2D>().get();
6778 _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as<ID3D11RenderTargetView>().get();
6779 }
6780
6781 // Sokol app is now valid
6782 _sapp.valid = true;
6783}
6784
6785// Determine the dimensions of the render target and whether it will be scaled down.
6786void DeviceResources::UpdateRenderTargetSize() {
6787 // Calculate the necessary render target size in pixels.
6788 m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale;
6789 m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale;
6790
6791 // Prevent zero size DirectX content from being created.
6792 m_outputSize.Width = std::max(m_outputSize.Width, 1.0f);
6793 m_outputSize.Height = std::max(m_outputSize.Height, 1.0f);
6794}
6795
6796// This method is called when the CoreWindow is created (or re-created).
6797void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) {
6798 auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView();
6799 m_window = window;
6800 m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height);
6801 m_nativeOrientation = currentDisplayInformation.NativeOrientation();
6802 m_currentOrientation = currentDisplayInformation.CurrentOrientation();
6803 m_dpi = currentDisplayInformation.LogicalDpi();
6804 _sapp_uwp_configure_dpi(m_dpi);
6805 CreateWindowSizeDependentResources();
6806}
6807
6808// This method is called in the event handler for the SizeChanged event.
6809void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) {
6810 if (m_logicalSize != logicalSize) {
6811 m_logicalSize = logicalSize;
6812 CreateWindowSizeDependentResources();
6813 }
6814}
6815
6816// This method is called in the event handler for the DpiChanged event.
6817void DeviceResources::SetDpi(float dpi) {
6818 if (dpi != m_dpi) {
6819 m_dpi = dpi;
6820 _sapp_uwp_configure_dpi(m_dpi);
6821 // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated.
6822 auto window = m_window.get();
6823 m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height);
6824 CreateWindowSizeDependentResources();
6825 }
6826}
6827
6828// This method is called in the event handler for the OrientationChanged event.
6829void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) {
6830 if (m_currentOrientation != currentOrientation) {
6831 m_currentOrientation = currentOrientation;
6832 CreateWindowSizeDependentResources();
6833 }
6834}
6835
6836// This method is called in the event handler for the DisplayContentsInvalidated event.
6837void DeviceResources::ValidateDevice() {
6838 // The D3D Device is no longer valid if the default adapter changed since the device
6839 // was created or if the device has been removed.
6840
6841 // First, get the information for the default adapter from when the device was created.
6842 winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as< IDXGIDevice3>();
6843 winrt::com_ptr<IDXGIAdapter> deviceAdapter;
6844 winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put()));
6845 winrt::com_ptr<IDXGIFactory4> deviceFactory;
6846 winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
6847 winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
6848 winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
6849 DXGI_ADAPTER_DESC1 previousDesc;
6850 winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc));
6851
6852 // Next, get the information for the current default adapter.
6853 winrt::com_ptr<IDXGIFactory4> currentFactory;
6854 winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory)));
6855 winrt::com_ptr<IDXGIAdapter1> currentDefaultAdapter;
6856 winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put()));
6857 DXGI_ADAPTER_DESC1 currentDesc;
6858 winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc));
6859
6860 // If the adapter LUIDs don't match, or if the device reports that it has been removed,
6861 // a new D3D device must be created.
6862 if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
6863 previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
6864 FAILED(m_d3dDevice->GetDeviceRemovedReason()))
6865 {
6866 // Release references to resources related to the old device.
6867 dxgiDevice = nullptr;
6868 deviceAdapter = nullptr;
6869 deviceFactory = nullptr;
6870 previousDefaultAdapter = nullptr;
6871
6872 // Create a new device and swap chain.
6873 HandleDeviceLost();
6874 }
6875}
6876
6877// Recreate all device resources and set them back to the current state.
6878void DeviceResources::HandleDeviceLost() {
6879 m_swapChain = nullptr;
6880 if (m_deviceNotify != nullptr) {
6881 m_deviceNotify->OnDeviceLost();
6882 }
6883 CreateDeviceResources();
6884 CreateWindowSizeDependentResources();
6885 if (m_deviceNotify != nullptr) {
6886 m_deviceNotify->OnDeviceRestored();
6887 }
6888}
6889
6890// Register our DeviceNotify to be informed on device lost and creation.
6891void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) {
6892 m_deviceNotify = deviceNotify;
6893}
6894
6895// Call this method when the app suspends. It provides a hint to the driver that the app
6896// is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
6897void DeviceResources::Trim() {
6898 m_d3dDevice.as<IDXGIDevice3>()->Trim();
6899}
6900
6901// Present the contents of the swap chain to the screen.
6902void DeviceResources::Present() {
6903
6904 // MSAA resolve if needed
6905 if (_sapp.sample_count > 1) {
6906 m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM);
6907 m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0);
6908 }
6909
6910 // The first argument instructs DXGI to block until VSync, putting the application
6911 // to sleep until the next VSync. This ensures we don't waste any cycles rendering
6912 // frames that will never be displayed to the screen.
6913 DXGI_PRESENT_PARAMETERS parameters = { 0 };
6914 HRESULT hr = m_swapChain->Present1(1, 0, ¶meters);
6915
6916 // Discard the contents of the render target.
6917 // This is a valid operation only when the existing contents will be entirely
6918 // overwritten. If dirty or scroll rects are used, this call should be removed.
6919 m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0);
6920
6921 // Discard the contents of the depth stencil.
6922 m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0);
6923
6924 // If the device was removed either by a disconnection or a driver upgrade, we
6925 // must recreate all device resources.
6926 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
6927 HandleDeviceLost();
6928 }
6929 else {
6930 winrt::check_hresult(hr);
6931 }
6932}
6933
6934// This method determines the rotation between the display device's native orientation and the
6935// current display orientation.
6936DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() {
6937 DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
6938
6939 // Note: NativeOrientation can only be Landscape or Portrait even though
6940 // the DisplayOrientations enum has other values.
6941 switch (m_nativeOrientation) {
6942 case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
6943 switch (m_currentOrientation) {
6944 case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
6945 rotation = DXGI_MODE_ROTATION_IDENTITY;
6946 break;
6947
6948 case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
6949 rotation = DXGI_MODE_ROTATION_ROTATE270;
6950 break;
6951
6952 case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
6953 rotation = DXGI_MODE_ROTATION_ROTATE180;
6954 break;
6955
6956 case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
6957 rotation = DXGI_MODE_ROTATION_ROTATE90;
6958 break;
6959 }
6960 break;
6961
6962 case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
6963 switch (m_currentOrientation) {
6964 case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
6965 rotation = DXGI_MODE_ROTATION_ROTATE90;
6966 break;
6967
6968 case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
6969 rotation = DXGI_MODE_ROTATION_IDENTITY;
6970 break;
6971
6972 case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
6973 rotation = DXGI_MODE_ROTATION_ROTATE270;
6974 break;
6975
6976 case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
6977 rotation = DXGI_MODE_ROTATION_ROTATE180;
6978 break;
6979 }
6980 break;
6981 }
6982 return rotation;
6983}
6984
6985// Check for SDK Layer support.
6986bool DeviceResources::SdkLayersAvailable() {
6987 #if defined(_DEBUG)
6988 HRESULT hr = D3D11CreateDevice(
6989 nullptr,
6990 D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
6991 0,
6992 D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
6993 nullptr, // Any feature level will do.
6994 0,
6995 D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps.
6996 nullptr, // No need to keep the D3D device reference.
6997 nullptr, // No need to know the feature level.
6998 nullptr // No need to keep the D3D device context reference.
6999 );
7000 return SUCCEEDED(hr);
7001 #else
7002 return false;
7003 #endif
7004}
7005
7006// The first method called when the IFrameworkView is being created.
7007void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) {
7008 // Register event handlers for app lifecycle. This example includes Activated, so that we
7009 // can make the CoreWindow active and start rendering on the window.
7010 applicationView.Activated({ this, &App::OnActivated });
7011
7012 winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending });
7013 winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming });
7014
7015 // At this point we have access to the device.
7016 // We can create the device-dependent resources.
7017 m_deviceResources = std::make_unique<DeviceResources>();
7018}
7019
7020// Called when the CoreWindow object is created (or re-created).
7021void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) {
7022 window.SizeChanged({ this, &App::OnWindowSizeChanged });
7023 window.VisibilityChanged({ this, &App::OnVisibilityChanged });
7024
7025 window.KeyDown({ this, &App::OnKeyDown });
7026 window.KeyUp({ this, &App::OnKeyUp });
7027 window.CharacterReceived({ this, &App::OnCharacterReceived });
7028
7029 window.PointerEntered({ this, &App::OnPointerEntered });
7030 window.PointerExited({ this, &App::OnPointerExited });
7031 window.PointerPressed({ this, &App::OnPointerPressed });
7032 window.PointerReleased({ this, &App::OnPointerReleased });
7033 window.PointerMoved({ this, &App::OnPointerMoved });
7034 window.PointerWheelChanged({ this, &App::OnPointerWheelChanged });
7035
7036 auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView();
7037
7038 currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged });
7039 currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged });
7040 winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated });
7041
7042 winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested });
7043
7044 m_deviceResources->SetWindow(window);
7045}
7046
7047// Initializes scene resources, or loads a previously saved app state.
7048void App::Load(winrt::hstring const& entryPoint) {
7049 _SOKOL_UNUSED(entryPoint);
7050}
7051
7052// This method is called after the window becomes active.
7053void App::Run() {
7054 // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed
7055 while (true) {
7056 if (m_windowVisible) {
7057 winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
7058 _sapp_frame();
7059 m_deviceResources->Present();
7060 }
7061 else {
7062 winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending);
7063 }
7064 }
7065}
7066
7067// Required for IFrameworkView.
7068// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView
7069// class is torn down while the app is in the foreground.
7070void App::Uninitialize() {
7071 // empty
7072}
7073
7074// Application lifecycle event handlers.
7075void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) {
7076 _SOKOL_UNUSED(args);
7077 _SOKOL_UNUSED(applicationView);
7078 auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
7079 auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height);
7080 appView.SetPreferredMinSize(targetSize);
7081 appView.TryResizeView(targetSize);
7082
7083 // Disabling this since it can only append the title to the app name (Title - Appname).
7084 // There's no way of just setting a string to be the window title.
7085 //appView.Title(_sapp.window_title_wide);
7086
7087 // Run() won't start until the CoreWindow is activated.
7088 winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate();
7089 if (_sapp.desc.fullscreen) {
7090 appView.TryEnterFullScreenMode();
7091 }
7092 _sapp.fullscreen = appView.IsFullScreenMode();
7093}
7094
7095void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) {
7096 _SOKOL_UNUSED(sender);
7097 _SOKOL_UNUSED(args);
7098 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED);
7099}
7100
7101void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) {
7102 _SOKOL_UNUSED(args);
7103 _SOKOL_UNUSED(sender);
7104 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED);
7105}
7106
7107void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) {
7108 _SOKOL_UNUSED(args);
7109 m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height));
7110 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
7111}
7112
7113void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) {
7114 _SOKOL_UNUSED(sender);
7115 m_windowVisible = args.Visible();
7116 _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED);
7117}
7118
7119void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) {
7120 _SOKOL_UNUSED(sender);
7121 args.Handled(true);
7122}
7123
7124void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) {
7125 auto status = args.KeyStatus();
7126 _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args);
7127}
7128
7129void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) {
7130 auto status = args.KeyStatus();
7131 _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args);
7132}
7133
7134void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) {
7135 _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender);
7136}
7137
7138void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7139 _SOKOL_UNUSED(args);
7140 _sapp.uwp.mouse_tracked = true;
7141 _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender);
7142}
7143
7144void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7145 _SOKOL_UNUSED(args);
7146 _sapp.uwp.mouse_tracked = false;
7147 _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender);
7148}
7149
7150void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7151 _sapp_uwp_extract_mouse_button_events(sender, args);
7152}
7153
7154// NOTE: for some reason this event handler is never called??
7155void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7156 _sapp_uwp_extract_mouse_button_events(sender, args);
7157}
7158
7159void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7160 auto position = args.CurrentPoint().Position();
7161 const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f);
7162 const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f);
7163 // don't update dx/dy in the very first event
7164 if (_sapp.mouse.pos_valid) {
7165 _sapp.mouse.dx = new_x - _sapp.mouse.x;
7166 _sapp.mouse.dy = new_y - _sapp.mouse.y;
7167 }
7168 _sapp.mouse.x = new_x;
7169 _sapp.mouse.y = new_y;
7170 _sapp.mouse.pos_valid = true;
7171 if (!_sapp.uwp.mouse_tracked) {
7172 _sapp.uwp.mouse_tracked = true;
7173 _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender);
7174 }
7175 _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender);
7176
7177 // HACK for detecting multiple mouse button presses
7178 _sapp_uwp_extract_mouse_button_events(sender, args);
7179}
7180
7181void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
7182 auto properties = args.CurrentPoint().Properties();
7183 _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender);
7184}
7185
7186void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
7187 // NOTE: UNTESTED
7188 _SOKOL_UNUSED(args);
7189 m_deviceResources->SetDpi(sender.LogicalDpi());
7190 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
7191}
7192
7193void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
7194 // NOTE: UNTESTED
7195 _SOKOL_UNUSED(args);
7196 m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation());
7197 _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
7198}
7199
7200void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
7201 // NOTE: UNTESTED
7202 _SOKOL_UNUSED(args);
7203 _SOKOL_UNUSED(sender);
7204 m_deviceResources->ValidateDevice();
7205}
7206
7207} /* End empty namespace */
7208
7209_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) {
7210 _sapp_init_state(desc);
7211 _sapp_win32_uwp_init_keytable();
7212 _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
7213 winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make<App>());
7214}
7215
7216#if !defined(SOKOL_NO_ENTRY)
7217#if defined(UNICODE)
7218int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
7219#else
7220int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
7221#endif
7222 _SOKOL_UNUSED(hInstance);
7223 _SOKOL_UNUSED(hPrevInstance);
7224 _SOKOL_UNUSED(lpCmdLine);
7225 _SOKOL_UNUSED(nCmdShow);
7226 sapp_desc desc = sokol_main(0, nullptr);
7227 _sapp_uwp_run(&desc);
7228 return 0;
7229}
7230#endif /* SOKOL_NO_ENTRY */
7231#endif /* _SAPP_UWP */
7232
7233/*== Android ================================================================*/
7234#if defined(_SAPP_ANDROID)
7235
7236/* android loop thread */
7237_SOKOL_PRIVATE bool _sapp_android_init_egl(void) {
7238 SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY);
7239 SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT);
7240
7241 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
7242 if (display == EGL_NO_DISPLAY) {
7243 return false;
7244 }
7245 if (eglInitialize(display, NULL, NULL) == EGL_FALSE) {
7246 return false;
7247 }
7248
7249 EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
7250 const EGLint cfg_attributes[] = {
7251 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
7252 EGL_RED_SIZE, 8,
7253 EGL_GREEN_SIZE, 8,
7254 EGL_BLUE_SIZE, 8,
7255 EGL_ALPHA_SIZE, alpha_size,
7256 EGL_DEPTH_SIZE, 16,
7257 EGL_STENCIL_SIZE, 0,
7258 EGL_NONE,
7259 };
7260 EGLConfig available_cfgs[32];
7261 EGLint cfg_count;
7262 eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count);
7263 SOKOL_ASSERT(cfg_count > 0);
7264 SOKOL_ASSERT(cfg_count <= 32);
7265
7266 /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */
7267 EGLConfig config;
7268 bool exact_cfg_found = false;
7269 for (int i = 0; i < cfg_count; ++i) {
7270 EGLConfig c = available_cfgs[i];
7271 EGLint r, g, b, a, d;
7272 if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE &&
7273 eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE &&
7274 eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE &&
7275 eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE &&
7276 eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE &&
7277 r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) {
7278 exact_cfg_found = true;
7279 config = c;
7280 break;
7281 }
7282 }
7283 if (!exact_cfg_found) {
7284 config = available_cfgs[0];
7285 }
7286
7287 EGLint ctx_attributes[] = {
7288 #if defined(SOKOL_GLES3)
7289 EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3,
7290 #else
7291 EGL_CONTEXT_CLIENT_VERSION, 2,
7292 #endif
7293 EGL_NONE,
7294 };
7295 EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes);
7296 if (context == EGL_NO_CONTEXT) {
7297 return false;
7298 }
7299
7300 _sapp.android.config = config;
7301 _sapp.android.display = display;
7302 _sapp.android.context = context;
7303 return true;
7304}
7305
7306_SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) {
7307 if (_sapp.android.display != EGL_NO_DISPLAY) {
7308 eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
7309 if (_sapp.android.surface != EGL_NO_SURFACE) {
7310 SOKOL_LOG("Destroying egl surface");
7311 eglDestroySurface(_sapp.android.display, _sapp.android.surface);
7312 _sapp.android.surface = EGL_NO_SURFACE;
7313 }
7314 if (_sapp.android.context != EGL_NO_CONTEXT) {
7315 SOKOL_LOG("Destroying egl context");
7316 eglDestroyContext(_sapp.android.display, _sapp.android.context);
7317 _sapp.android.context = EGL_NO_CONTEXT;
7318 }
7319 SOKOL_LOG("Terminating egl display");
7320 eglTerminate(_sapp.android.display);
7321 _sapp.android.display = EGL_NO_DISPLAY;
7322 }
7323}
7324
7325_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) {
7326 SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
7327 SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
7328 SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE);
7329 SOKOL_ASSERT(window);
7330
7331 /* TODO: set window flags */
7332 /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */
7333
7334 /* create egl surface and make it current */
7335 EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL);
7336 if (surface == EGL_NO_SURFACE) {
7337 return false;
7338 }
7339 if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) {
7340 return false;
7341 }
7342 _sapp.android.surface = surface;
7343 return true;
7344}
7345
7346_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) {
7347 if (_sapp.android.display == EGL_NO_DISPLAY) {
7348 return;
7349 }
7350 eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
7351 if (_sapp.android.surface != EGL_NO_SURFACE) {
7352 eglDestroySurface(_sapp.android.display, _sapp.android.surface);
7353 _sapp.android.surface = EGL_NO_SURFACE;
7354 }
7355}
7356
7357_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) {
7358 if (_sapp_events_enabled()) {
7359 _sapp_init_event(type);
7360 SOKOL_LOG("event_cb()");
7361 _sapp_call_event(&_sapp.event);
7362 }
7363}
7364
7365_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) {
7366 SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
7367 SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
7368 SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
7369 SOKOL_ASSERT(window);
7370
7371 const int32_t win_w = ANativeWindow_getWidth(window);
7372 const int32_t win_h = ANativeWindow_getHeight(window);
7373 SOKOL_ASSERT(win_w >= 0 && win_h >= 0);
7374 const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height);
7375 _sapp.window_width = win_w;
7376 _sapp.window_height = win_h;
7377 if (win_changed || force_update) {
7378 if (!_sapp.desc.high_dpi) {
7379 const int32_t buf_w = win_w / 2;
7380 const int32_t buf_h = win_h / 2;
7381 EGLint format;
7382 EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format);
7383 SOKOL_ASSERT(egl_result == EGL_TRUE);
7384 /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions
7385 as the ANativeWindow size results in weird display artefacts, that's
7386 why it's only called when the buffer geometry is different from
7387 the window size
7388 */
7389 int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format);
7390 SOKOL_ASSERT(result == 0);
7391 }
7392 }
7393
7394 /* query surface size */
7395 EGLint fb_w, fb_h;
7396 EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w);
7397 EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h);
7398 SOKOL_ASSERT(egl_result_w == EGL_TRUE);
7399 SOKOL_ASSERT(egl_result_h == EGL_TRUE);
7400 const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height);
7401 _sapp.framebuffer_width = fb_w;
7402 _sapp.framebuffer_height = fb_h;
7403 _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
7404 if (win_changed || fb_changed || force_update) {
7405 if (!_sapp.first_frame) {
7406 SOKOL_LOG("SAPP_EVENTTYPE_RESIZED");
7407 _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED);
7408 }
7409 }
7410}
7411
7412_SOKOL_PRIVATE void _sapp_android_cleanup(void) {
7413 SOKOL_LOG("Cleaning up");
7414 if (_sapp.android.surface != EGL_NO_SURFACE) {
7415 /* egl context is bound, cleanup gracefully */
7416 if (_sapp.init_called && !_sapp.cleanup_called) {
7417 SOKOL_LOG("cleanup_cb()");
7418 _sapp_call_cleanup();
7419 }
7420 }
7421 /* always try to cleanup by destroying egl context */
7422 _sapp_android_cleanup_egl();
7423}
7424
7425_SOKOL_PRIVATE void _sapp_android_shutdown(void) {
7426 /* try to cleanup while we still have a surface and can call cleanup_cb() */
7427 _sapp_android_cleanup();
7428 /* request exit */
7429 ANativeActivity_finish(_sapp.android.activity);
7430}
7431
7432_SOKOL_PRIVATE void _sapp_android_frame(void) {
7433 SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
7434 SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
7435 SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
7436 _sapp_android_update_dimensions(_sapp.android.current.window, false);
7437 _sapp_frame();
7438 eglSwapBuffers(_sapp.android.display, _sapp.android.surface);
7439}
7440
7441_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
7442 if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) {
7443 return false;
7444 }
7445 if (!_sapp_events_enabled()) {
7446 return false;
7447 }
7448 int32_t action_idx = AMotionEvent_getAction(e);
7449 int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK;
7450 sapp_event_type type = SAPP_EVENTTYPE_INVALID;
7451 switch (action) {
7452 case AMOTION_EVENT_ACTION_DOWN:
7453 SOKOL_LOG("Touch: down");
7454 case AMOTION_EVENT_ACTION_POINTER_DOWN:
7455 SOKOL_LOG("Touch: ptr down");
7456 type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
7457 break;
7458 case AMOTION_EVENT_ACTION_MOVE:
7459 type = SAPP_EVENTTYPE_TOUCHES_MOVED;
7460 break;
7461 case AMOTION_EVENT_ACTION_UP:
7462 SOKOL_LOG("Touch: up");
7463 case AMOTION_EVENT_ACTION_POINTER_UP:
7464 SOKOL_LOG("Touch: ptr up");
7465 type = SAPP_EVENTTYPE_TOUCHES_ENDED;
7466 break;
7467 case AMOTION_EVENT_ACTION_CANCEL:
7468 SOKOL_LOG("Touch: cancel");
7469 type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
7470 break;
7471 default:
7472 break;
7473 }
7474 if (type == SAPP_EVENTTYPE_INVALID) {
7475 return false;
7476 }
7477 int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
7478 _sapp_init_event(type);
7479 _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e);
7480 if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
7481 _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
7482 }
7483 for (int32_t i = 0; i < _sapp.event.num_touches; i++) {
7484 sapp_touchpoint* dst = &_sapp.event.touches[i];
7485 dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i);
7486 dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width;
7487 dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height;
7488
7489 if (action == AMOTION_EVENT_ACTION_POINTER_DOWN ||
7490 action == AMOTION_EVENT_ACTION_POINTER_UP) {
7491 dst->changed = (i == idx);
7492 } else {
7493 dst->changed = true;
7494 }
7495 }
7496 _sapp_call_event(&_sapp.event);
7497 return true;
7498}
7499
7500_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) {
7501 if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) {
7502 return false;
7503 }
7504 if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) {
7505 /* FIXME: this should be hooked into a "really quit?" mechanism
7506 so the app can ask the user for confirmation, this is currently
7507 generally missing in sokol_app.h
7508 */
7509 _sapp_android_shutdown();
7510 return true;
7511 }
7512 return false;
7513}
7514
7515_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) {
7516 if ((events & ALOOPER_EVENT_INPUT) == 0) {
7517 SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event");
7518 return 1;
7519 }
7520 SOKOL_ASSERT(_sapp.android.current.input);
7521 AInputEvent* event = NULL;
7522 while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) {
7523 if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) {
7524 continue;
7525 }
7526 int32_t handled = 0;
7527 if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) {
7528 handled = 1;
7529 }
7530 AInputQueue_finishEvent(_sapp.android.current.input, event, handled);
7531 }
7532 return 1;
7533}
7534
7535_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) {
7536 if ((events & ALOOPER_EVENT_INPUT) == 0) {
7537 SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event");
7538 return 1;
7539 }
7540
7541 _sapp_android_msg_t msg;
7542 if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) {
7543 SOKOL_LOG("Could not write to read_from_main_fd");
7544 return 1;
7545 }
7546
7547 pthread_mutex_lock(&_sapp.android.pt.mutex);
7548 switch (msg) {
7549 case _SOKOL_ANDROID_MSG_CREATE:
7550 {
7551 SOKOL_LOG("MSG_CREATE");
7552 SOKOL_ASSERT(!_sapp.valid);
7553 bool result = _sapp_android_init_egl();
7554 SOKOL_ASSERT(result);
7555 _sapp.valid = true;
7556 _sapp.android.has_created = true;
7557 }
7558 break;
7559 case _SOKOL_ANDROID_MSG_RESUME:
7560 SOKOL_LOG("MSG_RESUME");
7561 _sapp.android.has_resumed = true;
7562 _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED);
7563 break;
7564 case _SOKOL_ANDROID_MSG_PAUSE:
7565 SOKOL_LOG("MSG_PAUSE");
7566 _sapp.android.has_resumed = false;
7567 _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED);
7568 break;
7569 case _SOKOL_ANDROID_MSG_FOCUS:
7570 SOKOL_LOG("MSG_FOCUS");
7571 _sapp.android.has_focus = true;
7572 break;
7573 case _SOKOL_ANDROID_MSG_NO_FOCUS:
7574 SOKOL_LOG("MSG_NO_FOCUS");
7575 _sapp.android.has_focus = false;
7576 break;
7577 case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW:
7578 SOKOL_LOG("MSG_SET_NATIVE_WINDOW");
7579 if (_sapp.android.current.window != _sapp.android.pending.window) {
7580 if (_sapp.android.current.window != NULL) {
7581 _sapp_android_cleanup_egl_surface();
7582 }
7583 if (_sapp.android.pending.window != NULL) {
7584 SOKOL_LOG("Creating egl surface ...");
7585 if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) {
7586 SOKOL_LOG("... ok!");
7587 _sapp_android_update_dimensions(_sapp.android.pending.window, true);
7588 } else {
7589 SOKOL_LOG("... failed!");
7590 _sapp_android_shutdown();
7591 }
7592 }
7593 }
7594 _sapp.android.current.window = _sapp.android.pending.window;
7595 break;
7596 case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE:
7597 SOKOL_LOG("MSG_SET_INPUT_QUEUE");
7598 if (_sapp.android.current.input != _sapp.android.pending.input) {
7599 if (_sapp.android.current.input != NULL) {
7600 AInputQueue_detachLooper(_sapp.android.current.input);
7601 }
7602 if (_sapp.android.pending.input != NULL) {
7603 AInputQueue_attachLooper(
7604 _sapp.android.pending.input,
7605 _sapp.android.looper,
7606 ALOOPER_POLL_CALLBACK,
7607 _sapp_android_input_cb,
7608 NULL); /* data */
7609 }
7610 }
7611 _sapp.android.current.input = _sapp.android.pending.input;
7612 break;
7613 case _SOKOL_ANDROID_MSG_DESTROY:
7614 SOKOL_LOG("MSG_DESTROY");
7615 _sapp_android_cleanup();
7616 _sapp.valid = false;
7617 _sapp.android.is_thread_stopping = true;
7618 break;
7619 default:
7620 SOKOL_LOG("Unknown msg type received");
7621 break;
7622 }
7623 pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */
7624 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7625 return 1;
7626}
7627
7628_SOKOL_PRIVATE bool _sapp_android_should_update(void) {
7629 bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus;
7630 bool has_surface = _sapp.android.surface != EGL_NO_SURFACE;
7631 return is_in_front && has_surface;
7632}
7633
7634_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) {
7635 SOKOL_ASSERT(_sapp.valid);
7636 /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */
7637 if (shown) {
7638 SOKOL_LOG("Showing keyboard");
7639 ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED);
7640 } else {
7641 SOKOL_LOG("Hiding keyboard");
7642 ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
7643 }
7644}
7645
7646_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
7647 _SOKOL_UNUSED(arg);
7648 SOKOL_LOG("Loop thread started");
7649
7650 _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/);
7651 ALooper_addFd(_sapp.android.looper,
7652 _sapp.android.pt.read_from_main_fd,
7653 ALOOPER_POLL_CALLBACK,
7654 ALOOPER_EVENT_INPUT,
7655 _sapp_android_main_cb,
7656 NULL); /* data */
7657
7658 /* signal start to main thread */
7659 pthread_mutex_lock(&_sapp.android.pt.mutex);
7660 _sapp.android.is_thread_started = true;
7661 pthread_cond_broadcast(&_sapp.android.pt.cond);
7662 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7663
7664 /* main loop */
7665 while (!_sapp.android.is_thread_stopping) {
7666 /* sokol frame */
7667 if (_sapp_android_should_update()) {
7668 _sapp_android_frame();
7669 }
7670
7671 /* process all events (or stop early if app is requested to quit) */
7672 bool process_events = true;
7673 while (process_events && !_sapp.android.is_thread_stopping) {
7674 bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update();
7675 process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK;
7676 }
7677 }
7678
7679 /* cleanup thread */
7680 if (_sapp.android.current.input != NULL) {
7681 AInputQueue_detachLooper(_sapp.android.current.input);
7682 }
7683
7684 /* the following causes heap corruption on exit, why??
7685 ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd);
7686 ALooper_release(_sapp.android.looper);*/
7687
7688 /* signal "destroyed" */
7689 pthread_mutex_lock(&_sapp.android.pt.mutex);
7690 _sapp.android.is_thread_stopped = true;
7691 pthread_cond_broadcast(&_sapp.android.pt.cond);
7692 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7693 SOKOL_LOG("Loop thread done");
7694 return NULL;
7695}
7696
7697/* android main/ui thread */
7698_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) {
7699 if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) {
7700 SOKOL_LOG("Could not write to write_from_main_fd");
7701 }
7702}
7703
7704_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) {
7705 SOKOL_LOG("NativeActivity onStart()");
7706}
7707
7708_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) {
7709 SOKOL_LOG("NativeActivity onResume()");
7710 _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME);
7711}
7712
7713_SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) {
7714 SOKOL_LOG("NativeActivity onSaveInstanceState()");
7715 *out_size = 0;
7716 return NULL;
7717}
7718
7719_SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) {
7720 SOKOL_LOG("NativeActivity onWindowFocusChanged()");
7721 if (has_focus) {
7722 _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS);
7723 } else {
7724 _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS);
7725 }
7726}
7727
7728_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) {
7729 SOKOL_LOG("NativeActivity onPause()");
7730 _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE);
7731}
7732
7733_SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) {
7734 SOKOL_LOG("NativeActivity onStop()");
7735}
7736
7737_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) {
7738 pthread_mutex_lock(&_sapp.android.pt.mutex);
7739 _sapp.android.pending.window = window;
7740 _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW);
7741 while (_sapp.android.current.window != window) {
7742 pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
7743 }
7744 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7745}
7746
7747_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) {
7748 SOKOL_LOG("NativeActivity onNativeWindowCreated()");
7749 _sapp_android_msg_set_native_window(window);
7750}
7751
7752_SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) {
7753 SOKOL_LOG("NativeActivity onNativeWindowDestroyed()");
7754 _sapp_android_msg_set_native_window(NULL);
7755}
7756
7757_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) {
7758 pthread_mutex_lock(&_sapp.android.pt.mutex);
7759 _sapp.android.pending.input = input;
7760 _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE);
7761 while (_sapp.android.current.input != input) {
7762 pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
7763 }
7764 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7765}
7766
7767_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) {
7768 SOKOL_LOG("NativeActivity onInputQueueCreated()");
7769 _sapp_android_msg_set_input_queue(queue);
7770}
7771
7772_SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) {
7773 SOKOL_LOG("NativeActivity onInputQueueDestroyed()");
7774 _sapp_android_msg_set_input_queue(NULL);
7775}
7776
7777_SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) {
7778 SOKOL_LOG("NativeActivity onConfigurationChanged()");
7779 /* see android:configChanges in manifest */
7780}
7781
7782_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) {
7783 SOKOL_LOG("NativeActivity onLowMemory()");
7784}
7785
7786_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) {
7787 /*
7788 * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH)
7789 * on my device (Moto X 2nd gen) when the app is removed from the task view
7790 * (TaskStackView: onTaskViewDismissed).
7791 *
7792 * However, if ANativeActivity_finish() is explicitly called from for example
7793 * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity?
7794 */
7795 SOKOL_LOG("NativeActivity onDestroy()");
7796
7797 /* send destroy msg */
7798 pthread_mutex_lock(&_sapp.android.pt.mutex);
7799 _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY);
7800 while (!_sapp.android.is_thread_stopped) {
7801 pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
7802 }
7803 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7804
7805 /* clean up main thread */
7806 pthread_cond_destroy(&_sapp.android.pt.cond);
7807 pthread_mutex_destroy(&_sapp.android.pt.mutex);
7808
7809 close(_sapp.android.pt.read_from_main_fd);
7810 close(_sapp.android.pt.write_from_main_fd);
7811
7812 SOKOL_LOG("NativeActivity done");
7813
7814 /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */
7815 exit(0);
7816}
7817
7818JNIEXPORT
7819void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) {
7820 SOKOL_LOG("NativeActivity onCreate()");
7821
7822 sapp_desc desc = sokol_main(0, NULL);
7823 _sapp_init_state(&desc);
7824
7825 /* start loop thread */
7826 _sapp.android.activity = activity;
7827
7828 int pipe_fd[2];
7829 if (pipe(pipe_fd) != 0) {
7830 SOKOL_LOG("Could not create thread pipe");
7831 return;
7832 }
7833 _sapp.android.pt.read_from_main_fd = pipe_fd[0];
7834 _sapp.android.pt.write_from_main_fd = pipe_fd[1];
7835
7836 pthread_mutex_init(&_sapp.android.pt.mutex, NULL);
7837 pthread_cond_init(&_sapp.android.pt.cond, NULL);
7838
7839 pthread_attr_t attr;
7840 pthread_attr_init(&attr);
7841 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
7842 pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0);
7843 pthread_attr_destroy(&attr);
7844
7845 /* wait until main loop has started */
7846 pthread_mutex_lock(&_sapp.android.pt.mutex);
7847 while (!_sapp.android.is_thread_started) {
7848 pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
7849 }
7850 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7851
7852 /* send create msg */
7853 pthread_mutex_lock(&_sapp.android.pt.mutex);
7854 _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE);
7855 while (!_sapp.android.has_created) {
7856 pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
7857 }
7858 pthread_mutex_unlock(&_sapp.android.pt.mutex);
7859
7860 /* register for callbacks */
7861 activity->callbacks->onStart = _sapp_android_on_start;
7862 activity->callbacks->onResume = _sapp_android_on_resume;
7863 activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state;
7864 activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed;
7865 activity->callbacks->onPause = _sapp_android_on_pause;
7866 activity->callbacks->onStop = _sapp_android_on_stop;
7867 activity->callbacks->onDestroy = _sapp_android_on_destroy;
7868 activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created;
7869 /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */
7870 /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */
7871 activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed;
7872 activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created;
7873 activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed;
7874 /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */
7875 activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed;
7876 activity->callbacks->onLowMemory = _sapp_android_on_low_memory;
7877
7878 SOKOL_LOG("NativeActivity successfully created");
7879
7880 /* NOT A BUG: do NOT call sapp_discard_state() */
7881}
7882
7883#endif /* _SAPP_ANDROID */
7884
7885/*== LINUX ==================================================================*/
7886#if defined(_SAPP_LINUX)
7887
7888/* see GLFW's xkb_unicode.c */
7889static const struct _sapp_x11_codepair {
7890 uint16_t keysym;
7891 uint16_t ucs;
7892} _sapp_x11_keysymtab[] = {
7893 { 0x01a1, 0x0104 },
7894 { 0x01a2, 0x02d8 },
7895 { 0x01a3, 0x0141 },
7896 { 0x01a5, 0x013d },
7897 { 0x01a6, 0x015a },
7898 { 0x01a9, 0x0160 },
7899 { 0x01aa, 0x015e },
7900 { 0x01ab, 0x0164 },
7901 { 0x01ac, 0x0179 },
7902 { 0x01ae, 0x017d },
7903 { 0x01af, 0x017b },
7904 { 0x01b1, 0x0105 },
7905 { 0x01b2, 0x02db },
7906 { 0x01b3, 0x0142 },
7907 { 0x01b5, 0x013e },
7908 { 0x01b6, 0x015b },
7909 { 0x01b7, 0x02c7 },
7910 { 0x01b9, 0x0161 },
7911 { 0x01ba, 0x015f },
7912 { 0x01bb, 0x0165 },
7913 { 0x01bc, 0x017a },
7914 { 0x01bd, 0x02dd },
7915 { 0x01be, 0x017e },
7916 { 0x01bf, 0x017c },
7917 { 0x01c0, 0x0154 },
7918 { 0x01c3, 0x0102 },
7919 { 0x01c5, 0x0139 },
7920 { 0x01c6, 0x0106 },
7921 { 0x01c8, 0x010c },
7922 { 0x01ca, 0x0118 },
7923 { 0x01cc, 0x011a },
7924 { 0x01cf, 0x010e },
7925 { 0x01d0, 0x0110 },
7926 { 0x01d1, 0x0143 },
7927 { 0x01d2, 0x0147 },
7928 { 0x01d5, 0x0150 },
7929 { 0x01d8, 0x0158 },
7930 { 0x01d9, 0x016e },
7931 { 0x01db, 0x0170 },
7932 { 0x01de, 0x0162 },
7933 { 0x01e0, 0x0155 },
7934 { 0x01e3, 0x0103 },
7935 { 0x01e5, 0x013a },
7936 { 0x01e6, 0x0107 },
7937 { 0x01e8, 0x010d },
7938 { 0x01ea, 0x0119 },
7939 { 0x01ec, 0x011b },
7940 { 0x01ef, 0x010f },
7941 { 0x01f0, 0x0111 },
7942 { 0x01f1, 0x0144 },
7943 { 0x01f2, 0x0148 },
7944 { 0x01f5, 0x0151 },
7945 { 0x01f8, 0x0159 },
7946 { 0x01f9, 0x016f },
7947 { 0x01fb, 0x0171 },
7948 { 0x01fe, 0x0163 },
7949 { 0x01ff, 0x02d9 },
7950 { 0x02a1, 0x0126 },
7951 { 0x02a6, 0x0124 },
7952 { 0x02a9, 0x0130 },
7953 { 0x02ab, 0x011e },
7954 { 0x02ac, 0x0134 },
7955 { 0x02b1, 0x0127 },
7956 { 0x02b6, 0x0125 },
7957 { 0x02b9, 0x0131 },
7958 { 0x02bb, 0x011f },
7959 { 0x02bc, 0x0135 },
7960 { 0x02c5, 0x010a },
7961 { 0x02c6, 0x0108 },
7962 { 0x02d5, 0x0120 },
7963 { 0x02d8, 0x011c },
7964 { 0x02dd, 0x016c },
7965 { 0x02de, 0x015c },
7966 { 0x02e5, 0x010b },
7967 { 0x02e6, 0x0109 },
7968 { 0x02f5, 0x0121 },
7969 { 0x02f8, 0x011d },
7970 { 0x02fd, 0x016d },
7971 { 0x02fe, 0x015d },
7972 { 0x03a2, 0x0138 },
7973 { 0x03a3, 0x0156 },
7974 { 0x03a5, 0x0128 },
7975 { 0x03a6, 0x013b },
7976 { 0x03aa, 0x0112 },
7977 { 0x03ab, 0x0122 },
7978 { 0x03ac, 0x0166 },
7979 { 0x03b3, 0x0157 },
7980 { 0x03b5, 0x0129 },
7981 { 0x03b6, 0x013c },
7982 { 0x03ba, 0x0113 },
7983 { 0x03bb, 0x0123 },
7984 { 0x03bc, 0x0167 },
7985 { 0x03bd, 0x014a },
7986 { 0x03bf, 0x014b },
7987 { 0x03c0, 0x0100 },
7988 { 0x03c7, 0x012e },
7989 { 0x03cc, 0x0116 },
7990 { 0x03cf, 0x012a },
7991 { 0x03d1, 0x0145 },
7992 { 0x03d2, 0x014c },
7993 { 0x03d3, 0x0136 },
7994 { 0x03d9, 0x0172 },
7995 { 0x03dd, 0x0168 },
7996 { 0x03de, 0x016a },
7997 { 0x03e0, 0x0101 },
7998 { 0x03e7, 0x012f },
7999 { 0x03ec, 0x0117 },
8000 { 0x03ef, 0x012b },
8001 { 0x03f1, 0x0146 },
8002 { 0x03f2, 0x014d },
8003 { 0x03f3, 0x0137 },
8004 { 0x03f9, 0x0173 },
8005 { 0x03fd, 0x0169 },
8006 { 0x03fe, 0x016b },
8007 { 0x047e, 0x203e },
8008 { 0x04a1, 0x3002 },
8009 { 0x04a2, 0x300c },
8010 { 0x04a3, 0x300d },
8011 { 0x04a4, 0x3001 },
8012 { 0x04a5, 0x30fb },
8013 { 0x04a6, 0x30f2 },
8014 { 0x04a7, 0x30a1 },
8015 { 0x04a8, 0x30a3 },
8016 { 0x04a9, 0x30a5 },
8017 { 0x04aa, 0x30a7 },
8018 { 0x04ab, 0x30a9 },
8019 { 0x04ac, 0x30e3 },
8020 { 0x04ad, 0x30e5 },
8021 { 0x04ae, 0x30e7 },
8022 { 0x04af, 0x30c3 },
8023 { 0x04b0, 0x30fc },
8024 { 0x04b1, 0x30a2 },
8025 { 0x04b2, 0x30a4 },
8026 { 0x04b3, 0x30a6 },
8027 { 0x04b4, 0x30a8 },
8028 { 0x04b5, 0x30aa },
8029 { 0x04b6, 0x30ab },
8030 { 0x04b7, 0x30ad },
8031 { 0x04b8, 0x30af },
8032 { 0x04b9, 0x30b1 },
8033 { 0x04ba, 0x30b3 },
8034 { 0x04bb, 0x30b5 },
8035 { 0x04bc, 0x30b7 },
8036 { 0x04bd, 0x30b9 },
8037 { 0x04be, 0x30bb },
8038 { 0x04bf, 0x30bd },
8039 { 0x04c0, 0x30bf },
8040 { 0x04c1, 0x30c1 },
8041 { 0x04c2, 0x30c4 },
8042 { 0x04c3, 0x30c6 },
8043 { 0x04c4, 0x30c8 },
8044 { 0x04c5, 0x30ca },
8045 { 0x04c6, 0x30cb },
8046 { 0x04c7, 0x30cc },
8047 { 0x04c8, 0x30cd },
8048 { 0x04c9, 0x30ce },
8049 { 0x04ca, 0x30cf },
8050 { 0x04cb, 0x30d2 },
8051 { 0x04cc, 0x30d5 },
8052 { 0x04cd, 0x30d8 },
8053 { 0x04ce, 0x30db },
8054 { 0x04cf, 0x30de },
8055 { 0x04d0, 0x30df },
8056 { 0x04d1, 0x30e0 },
8057 { 0x04d2, 0x30e1 },
8058 { 0x04d3, 0x30e2 },
8059 { 0x04d4, 0x30e4 },
8060 { 0x04d5, 0x30e6 },
8061 { 0x04d6, 0x30e8 },
8062 { 0x04d7, 0x30e9 },
8063 { 0x04d8, 0x30ea },
8064 { 0x04d9, 0x30eb },
8065 { 0x04da, 0x30ec },
8066 { 0x04db, 0x30ed },
8067 { 0x04dc, 0x30ef },
8068 { 0x04dd, 0x30f3 },
8069 { 0x04de, 0x309b },
8070 { 0x04df, 0x309c },
8071 { 0x05ac, 0x060c },
8072 { 0x05bb, 0x061b },
8073 { 0x05bf, 0x061f },
8074 { 0x05c1, 0x0621 },
8075 { 0x05c2, 0x0622 },
8076 { 0x05c3, 0x0623 },
8077 { 0x05c4, 0x0624 },
8078 { 0x05c5, 0x0625 },
8079 { 0x05c6, 0x0626 },
8080 { 0x05c7, 0x0627 },
8081 { 0x05c8, 0x0628 },
8082 { 0x05c9, 0x0629 },
8083 { 0x05ca, 0x062a },
8084 { 0x05cb, 0x062b },
8085 { 0x05cc, 0x062c },
8086 { 0x05cd, 0x062d },
8087 { 0x05ce, 0x062e },
8088 { 0x05cf, 0x062f },
8089 { 0x05d0, 0x0630 },
8090 { 0x05d1, 0x0631 },
8091 { 0x05d2, 0x0632 },
8092 { 0x05d3, 0x0633 },
8093 { 0x05d4, 0x0634 },
8094 { 0x05d5, 0x0635 },
8095 { 0x05d6, 0x0636 },
8096 { 0x05d7, 0x0637 },
8097 { 0x05d8, 0x0638 },
8098 { 0x05d9, 0x0639 },
8099 { 0x05da, 0x063a },
8100 { 0x05e0, 0x0640 },
8101 { 0x05e1, 0x0641 },
8102 { 0x05e2, 0x0642 },
8103 { 0x05e3, 0x0643 },
8104 { 0x05e4, 0x0644 },
8105 { 0x05e5, 0x0645 },
8106 { 0x05e6, 0x0646 },
8107 { 0x05e7, 0x0647 },
8108 { 0x05e8, 0x0648 },
8109 { 0x05e9, 0x0649 },
8110 { 0x05ea, 0x064a },
8111 { 0x05eb, 0x064b },
8112 { 0x05ec, 0x064c },
8113 { 0x05ed, 0x064d },
8114 { 0x05ee, 0x064e },
8115 { 0x05ef, 0x064f },
8116 { 0x05f0, 0x0650 },
8117 { 0x05f1, 0x0651 },
8118 { 0x05f2, 0x0652 },
8119 { 0x06a1, 0x0452 },
8120 { 0x06a2, 0x0453 },
8121 { 0x06a3, 0x0451 },
8122 { 0x06a4, 0x0454 },
8123 { 0x06a5, 0x0455 },
8124 { 0x06a6, 0x0456 },
8125 { 0x06a7, 0x0457 },
8126 { 0x06a8, 0x0458 },
8127 { 0x06a9, 0x0459 },
8128 { 0x06aa, 0x045a },
8129 { 0x06ab, 0x045b },
8130 { 0x06ac, 0x045c },
8131 { 0x06ae, 0x045e },
8132 { 0x06af, 0x045f },
8133 { 0x06b0, 0x2116 },
8134 { 0x06b1, 0x0402 },
8135 { 0x06b2, 0x0403 },
8136 { 0x06b3, 0x0401 },
8137 { 0x06b4, 0x0404 },
8138 { 0x06b5, 0x0405 },
8139 { 0x06b6, 0x0406 },
8140 { 0x06b7, 0x0407 },
8141 { 0x06b8, 0x0408 },
8142 { 0x06b9, 0x0409 },
8143 { 0x06ba, 0x040a },
8144 { 0x06bb, 0x040b },
8145 { 0x06bc, 0x040c },
8146 { 0x06be, 0x040e },
8147 { 0x06bf, 0x040f },
8148 { 0x06c0, 0x044e },
8149 { 0x06c1, 0x0430 },
8150 { 0x06c2, 0x0431 },
8151 { 0x06c3, 0x0446 },
8152 { 0x06c4, 0x0434 },
8153 { 0x06c5, 0x0435 },
8154 { 0x06c6, 0x0444 },
8155 { 0x06c7, 0x0433 },
8156 { 0x06c8, 0x0445 },
8157 { 0x06c9, 0x0438 },
8158 { 0x06ca, 0x0439 },
8159 { 0x06cb, 0x043a },
8160 { 0x06cc, 0x043b },
8161 { 0x06cd, 0x043c },
8162 { 0x06ce, 0x043d },
8163 { 0x06cf, 0x043e },
8164 { 0x06d0, 0x043f },
8165 { 0x06d1, 0x044f },
8166 { 0x06d2, 0x0440 },
8167 { 0x06d3, 0x0441 },
8168 { 0x06d4, 0x0442 },
8169 { 0x06d5, 0x0443 },
8170 { 0x06d6, 0x0436 },
8171 { 0x06d7, 0x0432 },
8172 { 0x06d8, 0x044c },
8173 { 0x06d9, 0x044b },
8174 { 0x06da, 0x0437 },
8175 { 0x06db, 0x0448 },
8176 { 0x06dc, 0x044d },
8177 { 0x06dd, 0x0449 },
8178 { 0x06de, 0x0447 },
8179 { 0x06df, 0x044a },
8180 { 0x06e0, 0x042e },
8181 { 0x06e1, 0x0410 },
8182 { 0x06e2, 0x0411 },
8183 { 0x06e3, 0x0426 },
8184 { 0x06e4, 0x0414 },
8185 { 0x06e5, 0x0415 },
8186 { 0x06e6, 0x0424 },
8187 { 0x06e7, 0x0413 },
8188 { 0x06e8, 0x0425 },
8189 { 0x06e9, 0x0418 },
8190 { 0x06ea, 0x0419 },
8191 { 0x06eb, 0x041a },
8192 { 0x06ec, 0x041b },
8193 { 0x06ed, 0x041c },
8194 { 0x06ee, 0x041d },
8195 { 0x06ef, 0x041e },
8196 { 0x06f0, 0x041f },
8197 { 0x06f1, 0x042f },
8198 { 0x06f2, 0x0420 },
8199 { 0x06f3, 0x0421 },
8200 { 0x06f4, 0x0422 },
8201 { 0x06f5, 0x0423 },
8202 { 0x06f6, 0x0416 },
8203 { 0x06f7, 0x0412 },
8204 { 0x06f8, 0x042c },
8205 { 0x06f9, 0x042b },
8206 { 0x06fa, 0x0417 },
8207 { 0x06fb, 0x0428 },
8208 { 0x06fc, 0x042d },
8209 { 0x06fd, 0x0429 },
8210 { 0x06fe, 0x0427 },
8211 { 0x06ff, 0x042a },
8212 { 0x07a1, 0x0386 },
8213 { 0x07a2, 0x0388 },
8214 { 0x07a3, 0x0389 },
8215 { 0x07a4, 0x038a },
8216 { 0x07a5, 0x03aa },
8217 { 0x07a7, 0x038c },
8218 { 0x07a8, 0x038e },
8219 { 0x07a9, 0x03ab },
8220 { 0x07ab, 0x038f },
8221 { 0x07ae, 0x0385 },
8222 { 0x07af, 0x2015 },
8223 { 0x07b1, 0x03ac },
8224 { 0x07b2, 0x03ad },
8225 { 0x07b3, 0x03ae },
8226 { 0x07b4, 0x03af },
8227 { 0x07b5, 0x03ca },
8228 { 0x07b6, 0x0390 },
8229 { 0x07b7, 0x03cc },
8230 { 0x07b8, 0x03cd },
8231 { 0x07b9, 0x03cb },
8232 { 0x07ba, 0x03b0 },
8233 { 0x07bb, 0x03ce },
8234 { 0x07c1, 0x0391 },
8235 { 0x07c2, 0x0392 },
8236 { 0x07c3, 0x0393 },
8237 { 0x07c4, 0x0394 },
8238 { 0x07c5, 0x0395 },
8239 { 0x07c6, 0x0396 },
8240 { 0x07c7, 0x0397 },
8241 { 0x07c8, 0x0398 },
8242 { 0x07c9, 0x0399 },
8243 { 0x07ca, 0x039a },
8244 { 0x07cb, 0x039b },
8245 { 0x07cc, 0x039c },
8246 { 0x07cd, 0x039d },
8247 { 0x07ce, 0x039e },
8248 { 0x07cf, 0x039f },
8249 { 0x07d0, 0x03a0 },
8250 { 0x07d1, 0x03a1 },
8251 { 0x07d2, 0x03a3 },
8252 { 0x07d4, 0x03a4 },
8253 { 0x07d5, 0x03a5 },
8254 { 0x07d6, 0x03a6 },
8255 { 0x07d7, 0x03a7 },
8256 { 0x07d8, 0x03a8 },
8257 { 0x07d9, 0x03a9 },
8258 { 0x07e1, 0x03b1 },
8259 { 0x07e2, 0x03b2 },
8260 { 0x07e3, 0x03b3 },
8261 { 0x07e4, 0x03b4 },
8262 { 0x07e5, 0x03b5 },
8263 { 0x07e6, 0x03b6 },
8264 { 0x07e7, 0x03b7 },
8265 { 0x07e8, 0x03b8 },
8266 { 0x07e9, 0x03b9 },
8267 { 0x07ea, 0x03ba },
8268 { 0x07eb, 0x03bb },
8269 { 0x07ec, 0x03bc },
8270 { 0x07ed, 0x03bd },
8271 { 0x07ee, 0x03be },
8272 { 0x07ef, 0x03bf },
8273 { 0x07f0, 0x03c0 },
8274 { 0x07f1, 0x03c1 },
8275 { 0x07f2, 0x03c3 },
8276 { 0x07f3, 0x03c2 },
8277 { 0x07f4, 0x03c4 },
8278 { 0x07f5, 0x03c5 },
8279 { 0x07f6, 0x03c6 },
8280 { 0x07f7, 0x03c7 },
8281 { 0x07f8, 0x03c8 },
8282 { 0x07f9, 0x03c9 },
8283 { 0x08a1, 0x23b7 },
8284 { 0x08a2, 0x250c },
8285 { 0x08a3, 0x2500 },
8286 { 0x08a4, 0x2320 },
8287 { 0x08a5, 0x2321 },
8288 { 0x08a6, 0x2502 },
8289 { 0x08a7, 0x23a1 },
8290 { 0x08a8, 0x23a3 },
8291 { 0x08a9, 0x23a4 },
8292 { 0x08aa, 0x23a6 },
8293 { 0x08ab, 0x239b },
8294 { 0x08ac, 0x239d },
8295 { 0x08ad, 0x239e },
8296 { 0x08ae, 0x23a0 },
8297 { 0x08af, 0x23a8 },
8298 { 0x08b0, 0x23ac },
8299 { 0x08bc, 0x2264 },
8300 { 0x08bd, 0x2260 },
8301 { 0x08be, 0x2265 },
8302 { 0x08bf, 0x222b },
8303 { 0x08c0, 0x2234 },
8304 { 0x08c1, 0x221d },
8305 { 0x08c2, 0x221e },
8306 { 0x08c5, 0x2207 },
8307 { 0x08c8, 0x223c },
8308 { 0x08c9, 0x2243 },
8309 { 0x08cd, 0x21d4 },
8310 { 0x08ce, 0x21d2 },
8311 { 0x08cf, 0x2261 },
8312 { 0x08d6, 0x221a },
8313 { 0x08da, 0x2282 },
8314 { 0x08db, 0x2283 },
8315 { 0x08dc, 0x2229 },
8316 { 0x08dd, 0x222a },
8317 { 0x08de, 0x2227 },
8318 { 0x08df, 0x2228 },
8319 { 0x08ef, 0x2202 },
8320 { 0x08f6, 0x0192 },
8321 { 0x08fb, 0x2190 },
8322 { 0x08fc, 0x2191 },
8323 { 0x08fd, 0x2192 },
8324 { 0x08fe, 0x2193 },
8325 { 0x09e0, 0x25c6 },
8326 { 0x09e1, 0x2592 },
8327 { 0x09e2, 0x2409 },
8328 { 0x09e3, 0x240c },
8329 { 0x09e4, 0x240d },
8330 { 0x09e5, 0x240a },
8331 { 0x09e8, 0x2424 },
8332 { 0x09e9, 0x240b },
8333 { 0x09ea, 0x2518 },
8334 { 0x09eb, 0x2510 },
8335 { 0x09ec, 0x250c },
8336 { 0x09ed, 0x2514 },
8337 { 0x09ee, 0x253c },
8338 { 0x09ef, 0x23ba },
8339 { 0x09f0, 0x23bb },
8340 { 0x09f1, 0x2500 },
8341 { 0x09f2, 0x23bc },
8342 { 0x09f3, 0x23bd },
8343 { 0x09f4, 0x251c },
8344 { 0x09f5, 0x2524 },
8345 { 0x09f6, 0x2534 },
8346 { 0x09f7, 0x252c },
8347 { 0x09f8, 0x2502 },
8348 { 0x0aa1, 0x2003 },
8349 { 0x0aa2, 0x2002 },
8350 { 0x0aa3, 0x2004 },
8351 { 0x0aa4, 0x2005 },
8352 { 0x0aa5, 0x2007 },
8353 { 0x0aa6, 0x2008 },
8354 { 0x0aa7, 0x2009 },
8355 { 0x0aa8, 0x200a },
8356 { 0x0aa9, 0x2014 },
8357 { 0x0aaa, 0x2013 },
8358 { 0x0aae, 0x2026 },
8359 { 0x0aaf, 0x2025 },
8360 { 0x0ab0, 0x2153 },
8361 { 0x0ab1, 0x2154 },
8362 { 0x0ab2, 0x2155 },
8363 { 0x0ab3, 0x2156 },
8364 { 0x0ab4, 0x2157 },
8365 { 0x0ab5, 0x2158 },
8366 { 0x0ab6, 0x2159 },
8367 { 0x0ab7, 0x215a },
8368 { 0x0ab8, 0x2105 },
8369 { 0x0abb, 0x2012 },
8370 { 0x0abc, 0x2329 },
8371 { 0x0abe, 0x232a },
8372 { 0x0ac3, 0x215b },
8373 { 0x0ac4, 0x215c },
8374 { 0x0ac5, 0x215d },
8375 { 0x0ac6, 0x215e },
8376 { 0x0ac9, 0x2122 },
8377 { 0x0aca, 0x2613 },
8378 { 0x0acc, 0x25c1 },
8379 { 0x0acd, 0x25b7 },
8380 { 0x0ace, 0x25cb },
8381 { 0x0acf, 0x25af },
8382 { 0x0ad0, 0x2018 },
8383 { 0x0ad1, 0x2019 },
8384 { 0x0ad2, 0x201c },
8385 { 0x0ad3, 0x201d },
8386 { 0x0ad4, 0x211e },
8387 { 0x0ad6, 0x2032 },
8388 { 0x0ad7, 0x2033 },
8389 { 0x0ad9, 0x271d },
8390 { 0x0adb, 0x25ac },
8391 { 0x0adc, 0x25c0 },
8392 { 0x0add, 0x25b6 },
8393 { 0x0ade, 0x25cf },
8394 { 0x0adf, 0x25ae },
8395 { 0x0ae0, 0x25e6 },
8396 { 0x0ae1, 0x25ab },
8397 { 0x0ae2, 0x25ad },
8398 { 0x0ae3, 0x25b3 },
8399 { 0x0ae4, 0x25bd },
8400 { 0x0ae5, 0x2606 },
8401 { 0x0ae6, 0x2022 },
8402 { 0x0ae7, 0x25aa },
8403 { 0x0ae8, 0x25b2 },
8404 { 0x0ae9, 0x25bc },
8405 { 0x0aea, 0x261c },
8406 { 0x0aeb, 0x261e },
8407 { 0x0aec, 0x2663 },
8408 { 0x0aed, 0x2666 },
8409 { 0x0aee, 0x2665 },
8410 { 0x0af0, 0x2720 },
8411 { 0x0af1, 0x2020 },
8412 { 0x0af2, 0x2021 },
8413 { 0x0af3, 0x2713 },
8414 { 0x0af4, 0x2717 },
8415 { 0x0af5, 0x266f },
8416 { 0x0af6, 0x266d },
8417 { 0x0af7, 0x2642 },
8418 { 0x0af8, 0x2640 },
8419 { 0x0af9, 0x260e },
8420 { 0x0afa, 0x2315 },
8421 { 0x0afb, 0x2117 },
8422 { 0x0afc, 0x2038 },
8423 { 0x0afd, 0x201a },
8424 { 0x0afe, 0x201e },
8425 { 0x0ba3, 0x003c },
8426 { 0x0ba6, 0x003e },
8427 { 0x0ba8, 0x2228 },
8428 { 0x0ba9, 0x2227 },
8429 { 0x0bc0, 0x00af },
8430 { 0x0bc2, 0x22a5 },
8431 { 0x0bc3, 0x2229 },
8432 { 0x0bc4, 0x230a },
8433 { 0x0bc6, 0x005f },
8434 { 0x0bca, 0x2218 },
8435 { 0x0bcc, 0x2395 },
8436 { 0x0bce, 0x22a4 },
8437 { 0x0bcf, 0x25cb },
8438 { 0x0bd3, 0x2308 },
8439 { 0x0bd6, 0x222a },
8440 { 0x0bd8, 0x2283 },
8441 { 0x0bda, 0x2282 },
8442 { 0x0bdc, 0x22a2 },
8443 { 0x0bfc, 0x22a3 },
8444 { 0x0cdf, 0x2017 },
8445 { 0x0ce0, 0x05d0 },
8446 { 0x0ce1, 0x05d1 },
8447 { 0x0ce2, 0x05d2 },
8448 { 0x0ce3, 0x05d3 },
8449 { 0x0ce4, 0x05d4 },
8450 { 0x0ce5, 0x05d5 },
8451 { 0x0ce6, 0x05d6 },
8452 { 0x0ce7, 0x05d7 },
8453 { 0x0ce8, 0x05d8 },
8454 { 0x0ce9, 0x05d9 },
8455 { 0x0cea, 0x05da },
8456 { 0x0ceb, 0x05db },
8457 { 0x0cec, 0x05dc },
8458 { 0x0ced, 0x05dd },
8459 { 0x0cee, 0x05de },
8460 { 0x0cef, 0x05df },
8461 { 0x0cf0, 0x05e0 },
8462 { 0x0cf1, 0x05e1 },
8463 { 0x0cf2, 0x05e2 },
8464 { 0x0cf3, 0x05e3 },
8465 { 0x0cf4, 0x05e4 },
8466 { 0x0cf5, 0x05e5 },
8467 { 0x0cf6, 0x05e6 },
8468 { 0x0cf7, 0x05e7 },
8469 { 0x0cf8, 0x05e8 },
8470 { 0x0cf9, 0x05e9 },
8471 { 0x0cfa, 0x05ea },
8472 { 0x0da1, 0x0e01 },
8473 { 0x0da2, 0x0e02 },
8474 { 0x0da3, 0x0e03 },
8475 { 0x0da4, 0x0e04 },
8476 { 0x0da5, 0x0e05 },
8477 { 0x0da6, 0x0e06 },
8478 { 0x0da7, 0x0e07 },
8479 { 0x0da8, 0x0e08 },
8480 { 0x0da9, 0x0e09 },
8481 { 0x0daa, 0x0e0a },
8482 { 0x0dab, 0x0e0b },
8483 { 0x0dac, 0x0e0c },
8484 { 0x0dad, 0x0e0d },
8485 { 0x0dae, 0x0e0e },
8486 { 0x0daf, 0x0e0f },
8487 { 0x0db0, 0x0e10 },
8488 { 0x0db1, 0x0e11 },
8489 { 0x0db2, 0x0e12 },
8490 { 0x0db3, 0x0e13 },
8491 { 0x0db4, 0x0e14 },
8492 { 0x0db5, 0x0e15 },
8493 { 0x0db6, 0x0e16 },
8494 { 0x0db7, 0x0e17 },
8495 { 0x0db8, 0x0e18 },
8496 { 0x0db9, 0x0e19 },
8497 { 0x0dba, 0x0e1a },
8498 { 0x0dbb, 0x0e1b },
8499 { 0x0dbc, 0x0e1c },
8500 { 0x0dbd, 0x0e1d },
8501 { 0x0dbe, 0x0e1e },
8502 { 0x0dbf, 0x0e1f },
8503 { 0x0dc0, 0x0e20 },
8504 { 0x0dc1, 0x0e21 },
8505 { 0x0dc2, 0x0e22 },
8506 { 0x0dc3, 0x0e23 },
8507 { 0x0dc4, 0x0e24 },
8508 { 0x0dc5, 0x0e25 },
8509 { 0x0dc6, 0x0e26 },
8510 { 0x0dc7, 0x0e27 },
8511 { 0x0dc8, 0x0e28 },
8512 { 0x0dc9, 0x0e29 },
8513 { 0x0dca, 0x0e2a },
8514 { 0x0dcb, 0x0e2b },
8515 { 0x0dcc, 0x0e2c },
8516 { 0x0dcd, 0x0e2d },
8517 { 0x0dce, 0x0e2e },
8518 { 0x0dcf, 0x0e2f },
8519 { 0x0dd0, 0x0e30 },
8520 { 0x0dd1, 0x0e31 },
8521 { 0x0dd2, 0x0e32 },
8522 { 0x0dd3, 0x0e33 },
8523 { 0x0dd4, 0x0e34 },
8524 { 0x0dd5, 0x0e35 },
8525 { 0x0dd6, 0x0e36 },
8526 { 0x0dd7, 0x0e37 },
8527 { 0x0dd8, 0x0e38 },
8528 { 0x0dd9, 0x0e39 },
8529 { 0x0dda, 0x0e3a },
8530 { 0x0ddf, 0x0e3f },
8531 { 0x0de0, 0x0e40 },
8532 { 0x0de1, 0x0e41 },
8533 { 0x0de2, 0x0e42 },
8534 { 0x0de3, 0x0e43 },
8535 { 0x0de4, 0x0e44 },
8536 { 0x0de5, 0x0e45 },
8537 { 0x0de6, 0x0e46 },
8538 { 0x0de7, 0x0e47 },
8539 { 0x0de8, 0x0e48 },
8540 { 0x0de9, 0x0e49 },
8541 { 0x0dea, 0x0e4a },
8542 { 0x0deb, 0x0e4b },
8543 { 0x0dec, 0x0e4c },
8544 { 0x0ded, 0x0e4d },
8545 { 0x0df0, 0x0e50 },
8546 { 0x0df1, 0x0e51 },
8547 { 0x0df2, 0x0e52 },
8548 { 0x0df3, 0x0e53 },
8549 { 0x0df4, 0x0e54 },
8550 { 0x0df5, 0x0e55 },
8551 { 0x0df6, 0x0e56 },
8552 { 0x0df7, 0x0e57 },
8553 { 0x0df8, 0x0e58 },
8554 { 0x0df9, 0x0e59 },
8555 { 0x0ea1, 0x3131 },
8556 { 0x0ea2, 0x3132 },
8557 { 0x0ea3, 0x3133 },
8558 { 0x0ea4, 0x3134 },
8559 { 0x0ea5, 0x3135 },
8560 { 0x0ea6, 0x3136 },
8561 { 0x0ea7, 0x3137 },
8562 { 0x0ea8, 0x3138 },
8563 { 0x0ea9, 0x3139 },
8564 { 0x0eaa, 0x313a },
8565 { 0x0eab, 0x313b },
8566 { 0x0eac, 0x313c },
8567 { 0x0ead, 0x313d },
8568 { 0x0eae, 0x313e },
8569 { 0x0eaf, 0x313f },
8570 { 0x0eb0, 0x3140 },
8571 { 0x0eb1, 0x3141 },
8572 { 0x0eb2, 0x3142 },
8573 { 0x0eb3, 0x3143 },
8574 { 0x0eb4, 0x3144 },
8575 { 0x0eb5, 0x3145 },
8576 { 0x0eb6, 0x3146 },
8577 { 0x0eb7, 0x3147 },
8578 { 0x0eb8, 0x3148 },
8579 { 0x0eb9, 0x3149 },
8580 { 0x0eba, 0x314a },
8581 { 0x0ebb, 0x314b },
8582 { 0x0ebc, 0x314c },
8583 { 0x0eb