1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL) |
2 | #define SOKOL_APP_IMPL |
3 | #endif |
4 | #ifndef SOKOL_APP_INCLUDED |
5 | |
6 | /* |
7 | V language IMPORTANT NOTE: all the V patches in this code, are marked with: |
8 | // __v_ start |
9 | // __v_ end |
10 | */ |
11 | |
12 | /* |
13 | sokol_app.h -- cross-platform application wrapper |
14 | |
15 | Project URL: https://github.com/floooh/sokol |
16 | |
17 | Do this: |
18 | #define SOKOL_IMPL or |
19 | #define SOKOL_APP_IMPL |
20 | before you include this file in *one* C or C++ file to create the |
21 | implementation. |
22 | |
23 | In the same place define one of the following to select the 3D-API |
24 | which should be initialized by sokol_app.h (this must also match |
25 | the backend selected for sokol_gfx.h if both are used in the same |
26 | project): |
27 | |
28 | #define SOKOL_GLCORE33 |
29 | #define SOKOL_GLES2 |
30 | #define SOKOL_GLES3 |
31 | #define SOKOL_D3D11 |
32 | #define SOKOL_METAL |
33 | #define SOKOL_WGPU |
34 | |
35 | Optionally provide the following defines with your own implementations: |
36 | |
37 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) |
38 | SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) |
39 | SOKOL_ABORT() - called after an unrecoverable error (default: abort()) |
40 | SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain |
41 | SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function |
42 | SOKOL_APP_API_DECL - public function declaration prefix (default: extern) |
43 | SOKOL_API_DECL - same as SOKOL_APP_API_DECL |
44 | SOKOL_API_IMPL - public function implementation prefix (default: -) |
45 | |
46 | Optionally define the following to force debug checks and validations |
47 | even in release mode: |
48 | |
49 | SOKOL_DEBUG - by default this is defined if _DEBUG is defined |
50 | |
51 | If sokol_app.h is compiled as a DLL, define the following before |
52 | including the declaration or implementation: |
53 | |
54 | SOKOL_DLL |
55 | |
56 | On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) |
57 | or __declspec(dllimport) as needed. |
58 | |
59 | On Linux, SOKOL_GLCORE33 can use either GLX or EGL. |
60 | GLX is default, set SOKOL_FORCE_EGL to override. |
61 | |
62 | For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp |
63 | |
64 | Portions of the Windows and Linux GL initialization, event-, icon- etc... code |
65 | have been taken from GLFW (http://www.glfw.org/) |
66 | |
67 | iOS onscreen keyboard support 'inspired' by libgdx. |
68 | |
69 | Link with the following system libraries: |
70 | |
71 | - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit |
72 | - on macOS with GL: Cocoa, QuartzCore, OpenGL |
73 | - on iOS with Metal: Foundation, UIKit, Metal, MetalKit |
74 | - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit |
75 | - on Linux with EGL: X11, Xi, Xcursor, EGL, GL (or GLESv2), dl, pthread, m(?) |
76 | - on Linux with GLX: X11, Xi, Xcursor, GL, dl, pthread, m(?) |
77 | - on Android: GLESv3, EGL, log, android |
78 | - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib |
79 | - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined |
80 | - link with the following libs: -lkernel32 -luser32 -lshell32 |
81 | - additionally with the GL backend: -lgdi32 |
82 | - additionally with the D3D11 backend: -ld3d11 -ldxgi |
83 | |
84 | On Linux, you also need to use the -pthread compiler and linker option, otherwise weird |
85 | things will happen, see here for details: https://github.com/floooh/sokol/issues/376 |
86 | |
87 | Building for UWP requires a recent Visual Studio toolchain and Windows SDK |
88 | (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is |
89 | selected, the sokol_app.h implementation must be compiled as C++17. |
90 | |
91 | On macOS and iOS, the implementation must be compiled as Objective-C. |
92 | |
93 | FEATURE OVERVIEW |
94 | ================ |
95 | sokol_app.h provides a minimalistic cross-platform API which |
96 | implements the 'application-wrapper' parts of a 3D application: |
97 | |
98 | - a common application entry function |
99 | - creates a window and 3D-API context/device with a 'default framebuffer' |
100 | - makes the rendered frame visible |
101 | - provides keyboard-, mouse- and low-level touch-events |
102 | - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android |
103 | - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 |
104 | |
105 | FEATURE/PLATFORM MATRIX |
106 | ======================= |
107 | | Windows | macOS | Linux | iOS | Android | UWP | HTML5 |
108 | --------------------+---------+-------+-------+-------+---------+------+------- |
109 | gl 3.x | YES | YES | YES | --- | --- | --- | --- |
110 | gles2/webgl | --- | --- | YES(2)| YES | YES | --- | YES |
111 | gles3/webgl2 | --- | --- | YES(2)| YES | YES | --- | YES |
112 | metal | --- | YES | --- | YES | --- | --- | --- |
113 | d3d11 | YES | --- | --- | --- | --- | YES | --- |
114 | KEY_DOWN | YES | YES | YES | SOME | TODO | YES | YES |
115 | KEY_UP | YES | YES | YES | SOME | TODO | YES | YES |
116 | CHAR | YES | YES | YES | YES | TODO | YES | YES |
117 | MOUSE_DOWN | YES | YES | YES | --- | --- | YES | YES |
118 | MOUSE_UP | YES | YES | YES | --- | --- | YES | YES |
119 | MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | YES |
120 | MOUSE_MOVE | YES | YES | YES | --- | --- | YES | YES |
121 | MOUSE_ENTER | YES | YES | YES | --- | --- | YES | YES |
122 | MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | YES |
123 | TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | YES |
124 | TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | YES |
125 | TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | YES |
126 | TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | YES |
127 | RESIZED | YES | YES | YES | YES | YES | YES | YES |
128 | ICONIFIED | YES | YES | YES | --- | --- | YES | --- |
129 | RESTORED | YES | YES | YES | --- | --- | YES | --- |
130 | FOCUSED | YES | YES | YES | --- | --- | --- | YES |
131 | UNFOCUSED | YES | YES | YES | --- | --- | --- | YES |
132 | SUSPENDED | --- | --- | --- | YES | YES | YES | TODO |
133 | RESUMED | --- | --- | --- | YES | YES | YES | TODO |
134 | QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | YES |
135 | IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? |
136 | key repeat flag | YES | YES | YES | --- | --- | YES | YES |
137 | windowed | YES | YES | YES | --- | --- | YES | YES |
138 | fullscreen | YES | YES | YES | YES | YES | YES | --- |
139 | mouse hide | YES | YES | YES | --- | --- | YES | YES |
140 | mouse lock | YES | YES | YES | --- | --- | TODO | YES |
141 | set cursor type | YES | YES | YES | --- | --- | YES | YES |
142 | screen keyboard | --- | --- | --- | YES | TODO | TODO | YES |
143 | swap interval | YES | YES | YES | YES | TODO | --- | YES |
144 | high-dpi | YES | YES | TODO | YES | YES | YES | YES |
145 | clipboard | YES | YES | TODO | --- | --- | TODO | YES |
146 | MSAA | YES | YES | YES | YES | YES | TODO | YES |
147 | drag'n'drop | YES | YES | YES | --- | --- | TODO | YES |
148 | window icon | YES | YES(1)| YES | --- | --- | TODO | YES |
149 | |
150 | (1) macOS has no regular window icons, instead the dock icon is changed |
151 | (2) supported with EGL only (not GLX) |
152 | |
153 | STEP BY STEP |
154 | ============ |
155 | --- Add a sokol_main() function to your code which returns a sapp_desc structure |
156 | with initialization parameters and callback function pointers. This |
157 | function is called very early, usually at the start of the |
158 | platform's entry function (e.g. main or WinMain). You should do as |
159 | little as possible here, since the rest of your code might be called |
160 | from another thread (this depends on the platform): |
161 | |
162 | sapp_desc sokol_main(int argc, char* argv[]) { |
163 | return (sapp_desc) { |
164 | .width = 640, |
165 | .height = 480, |
166 | .init_cb = my_init_func, |
167 | .frame_cb = my_frame_func, |
168 | .cleanup_cb = my_cleanup_func, |
169 | .event_cb = my_event_func, |
170 | ... |
171 | }; |
172 | } |
173 | |
174 | There are many more setup parameters, but these are the most important. |
175 | For a complete list search for the sapp_desc structure declaration |
176 | below. |
177 | |
178 | DO NOT call any sokol-app function from inside sokol_main(), since |
179 | sokol-app will not be initialized at this point. |
180 | |
181 | The .width and .height parameters are the preferred size of the 3D |
182 | rendering canvas. The actual size may differ from this depending on |
183 | platform and other circumstances. Also the canvas size may change at |
184 | any time (for instance when the user resizes the application window, |
185 | or rotates the mobile device). You can just keep .width and .height |
186 | zero-initialized to open a default-sized window (what "default-size" |
187 | exactly means is platform-specific, but usually it's a size that covers |
188 | most of, but not all, of the display). |
189 | |
190 | All provided function callbacks will be called from the same thread, |
191 | but this may be different from the thread where sokol_main() was called. |
192 | |
193 | .init_cb (void (*)(void)) |
194 | This function is called once after the application window, |
195 | 3D rendering context and swap chain have been created. The |
196 | function takes no arguments and has no return value. |
197 | .frame_cb (void (*)(void)) |
198 | This is the per-frame callback, which is usually called 60 |
199 | times per second. This is where your application would update |
200 | most of its state and perform all rendering. |
201 | .cleanup_cb (void (*)(void)) |
202 | The cleanup callback is called once right before the application |
203 | quits. |
204 | .event_cb (void (*)(const sapp_event* event)) |
205 | The event callback is mainly for input handling, but is also |
206 | used to communicate other types of events to the application. Keep the |
207 | event_cb struct member zero-initialized if your application doesn't require |
208 | event handling. |
209 | .fail_cb (void (*)(const char* msg)) |
210 | The fail callback is called when a fatal error is encountered |
211 | during start which doesn't allow the program to continue. |
212 | Providing a callback here gives you a chance to show an error message |
213 | to the user. The default behaviour is SAPP_LOG(msg) |
214 | |
215 | As you can see, those 'standard callbacks' don't have a user_data |
216 | argument, so any data that needs to be preserved between callbacks |
217 | must live in global variables. If keeping state in global variables |
218 | is not an option, there's an alternative set of callbacks with |
219 | an additional user_data pointer argument: |
220 | |
221 | .user_data (void*) |
222 | The user-data argument for the callbacks below |
223 | .init_userdata_cb (void (*)(void* user_data)) |
224 | .frame_userdata_cb (void (*)(void* user_data)) |
225 | .cleanup_userdata_cb (void (*)(void* user_data)) |
226 | .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) |
227 | .fail_userdata_cb (void(*)(const char* msg, void* user_data)) |
228 | These are the user-data versions of the callback functions. You |
229 | can mix those with the standard callbacks that don't have the |
230 | user_data argument. |
231 | |
232 | The function sapp_userdata() can be used to query the user_data |
233 | pointer provided in the sapp_desc struct. |
234 | |
235 | You can also call sapp_query_desc() to get a copy of the |
236 | original sapp_desc structure. |
237 | |
238 | NOTE that there's also an alternative compile mode where sokol_app.h |
239 | doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. |
240 | |
241 | --- Implement the initialization callback function (init_cb), this is called |
242 | once after the rendering surface, 3D API and swap chain have been |
243 | initialized by sokol_app. All sokol-app functions can be called |
244 | from inside the initialization callback, the most useful functions |
245 | at this point are: |
246 | |
247 | int sapp_width(void) |
248 | int sapp_height(void) |
249 | Returns the current width and height of the default framebuffer in pixels, |
250 | this may change from one frame to the next, and it may be different |
251 | from the initial size provided in the sapp_desc struct. |
252 | |
253 | float sapp_widthf(void) |
254 | float sapp_heightf(void) |
255 | These are alternatives to sapp_width() and sapp_height() which return |
256 | the default framebuffer size as float values instead of integer. This |
257 | may help to prevent casting back and forth between int and float |
258 | in more strongly typed languages than C and C++. |
259 | |
260 | double sapp_frame_duration(void) |
261 | Returns the frame duration in seconds averaged over a number of |
262 | frames to smooth out any jittering spikes. |
263 | |
264 | int sapp_color_format(void) |
265 | int sapp_depth_format(void) |
266 | The color and depth-stencil pixelformats of the default framebuffer, |
267 | as integer values which are compatible with sokol-gfx's |
268 | sg_pixel_format enum (so that they can be plugged directly in places |
269 | where sg_pixel_format is expected). Possible values are: |
270 | |
271 | 23 == SG_PIXELFORMAT_RGBA8 |
272 | 27 == SG_PIXELFORMAT_BGRA8 |
273 | 41 == SG_PIXELFORMAT_DEPTH |
274 | 42 == SG_PIXELFORMAT_DEPTH_STENCIL |
275 | |
276 | int sapp_sample_count(void) |
277 | Return the MSAA sample count of the default framebuffer. |
278 | |
279 | bool sapp_gles2(void) |
280 | Returns true if a GLES2 or WebGL context has been created. This |
281 | is useful when a GLES3/WebGL2 context was requested but is not |
282 | available so that sokol_app.h had to fallback to GLES2/WebGL. |
283 | |
284 | const void* sapp_metal_get_device(void) |
285 | const void* sapp_metal_get_renderpass_descriptor(void) |
286 | const void* sapp_metal_get_drawable(void) |
287 | If the Metal backend has been selected, these functions return pointers |
288 | to various Metal API objects required for rendering, otherwise |
289 | they return a null pointer. These void pointers are actually |
290 | Objective-C ids converted with a (ARC) __bridge cast so that |
291 | the ids can be tunnel through C code. Also note that the returned |
292 | pointers to the renderpass-descriptor and drawable may change from one |
293 | frame to the next, only the Metal device object is guaranteed to |
294 | stay the same. |
295 | |
296 | const void* sapp_macos_get_window(void) |
297 | On macOS, get the NSWindow object pointer, otherwise a null pointer. |
298 | Before being used as Objective-C object, the void* must be converted |
299 | back with a (ARC) __bridge cast. |
300 | |
301 | const void* sapp_ios_get_window(void) |
302 | On iOS, get the UIWindow object pointer, otherwise a null pointer. |
303 | Before being used as Objective-C object, the void* must be converted |
304 | back with a (ARC) __bridge cast. |
305 | |
306 | const void* sapp_win32_get_hwnd(void) |
307 | On Windows, get the window's HWND, otherwise a null pointer. The |
308 | HWND has been cast to a void pointer in order to be tunneled |
309 | through code which doesn't include Windows.h. |
310 | |
311 | const void* sapp_d3d11_get_device(void) |
312 | const void* sapp_d3d11_get_device_context(void) |
313 | const void* sapp_d3d11_get_render_target_view(void) |
314 | const void* sapp_d3d11_get_depth_stencil_view(void) |
315 | Similar to the sapp_metal_* functions, the sapp_d3d11_* functions |
316 | return pointers to D3D11 API objects required for rendering, |
317 | only if the D3D11 backend has been selected. Otherwise they |
318 | return a null pointer. Note that the returned pointers to the |
319 | render-target-view and depth-stencil-view may change from one |
320 | frame to the next! |
321 | |
322 | const void* sapp_wgpu_get_device(void) |
323 | const void* sapp_wgpu_get_render_view(void) |
324 | const void* sapp_wgpu_get_resolve_view(void) |
325 | const void* sapp_wgpu_get_depth_stencil_view(void) |
326 | These are the WebGPU-specific functions to get the WebGPU |
327 | objects and values required for rendering. If sokol_app.h |
328 | is not compiled with SOKOL_WGPU, these functions return null. |
329 | |
330 | const void* sapp_android_get_native_activity(void); |
331 | On Android, get the native activity ANativeActivity pointer, otherwise |
332 | a null pointer. |
333 | |
334 | --- Implement the frame-callback function, this function will be called |
335 | on the same thread as the init callback, but might be on a different |
336 | thread than the sokol_main() function. Note that the size of |
337 | the rendering framebuffer might have changed since the frame callback |
338 | was called last. Call the functions sapp_width() and sapp_height() |
339 | each frame to get the current size. |
340 | |
341 | --- Optionally implement the event-callback to handle input events. |
342 | sokol-app provides the following type of input events: |
343 | - a 'virtual key' was pressed down or released |
344 | - a single text character was entered (provided as UTF-32 code point) |
345 | - a mouse button was pressed down or released (left, right, middle) |
346 | - mouse-wheel or 2D scrolling events |
347 | - the mouse was moved |
348 | - the mouse has entered or left the application window boundaries |
349 | - low-level, portable multi-touch events (began, moved, ended, cancelled) |
350 | - the application window was resized, iconified or restored |
351 | - the application was suspended or restored (on mobile platforms) |
352 | - the user or application code has asked to quit the application |
353 | - a string was pasted to the system clipboard |
354 | - one or more files have been dropped onto the application window |
355 | |
356 | To explicitly 'consume' an event and prevent that the event is |
357 | forwarded for further handling to the operating system, call |
358 | sapp_consume_event() from inside the event handler (NOTE that |
359 | this behaviour is currently only implemented for some HTML5 |
360 | events, support for other platforms and event types will |
361 | be added as needed, please open a github ticket and/or provide |
362 | a PR if needed). |
363 | |
364 | NOTE: Do *not* call any 3D API rendering functions in the event |
365 | callback function, since the 3D API context may not be active when the |
366 | event callback is called (it may work on some platforms and 3D APIs, |
367 | but not others, and the exact behaviour may change between |
368 | sokol-app versions). |
369 | |
370 | --- Implement the cleanup-callback function, this is called once |
371 | after the user quits the application (see the section |
372 | "APPLICATION QUIT" for detailed information on quitting |
373 | behaviour, and how to intercept a pending quit - for instance to show a |
374 | "Really Quit?" dialog box). Note that the cleanup-callback isn't |
375 | guaranteed to be called on the web and mobile platforms. |
376 | |
377 | MOUSE CURSOR TYPE AND VISIBILITY |
378 | ================================ |
379 | You can show and hide the mouse cursor with |
380 | |
381 | void sapp_show_mouse(bool show) |
382 | |
383 | And to get the current shown status: |
384 | |
385 | bool sapp_mouse_shown(void) |
386 | |
387 | NOTE that hiding the mouse cursor is different and independent from |
388 | the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when |
389 | active (MOUSE LOCK is described below). |
390 | |
391 | To change the mouse cursor to one of several predefined types, call |
392 | the function: |
393 | |
394 | void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) |
395 | |
396 | Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore |
397 | the standard look. |
398 | |
399 | To get the currently active mouse cursor type, call: |
400 | |
401 | sapp_mouse_cursor sapp_get_mouse_cursor(void) |
402 | |
403 | MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) |
404 | ================================================ |
405 | In normal mouse mode, no mouse movement events are reported when the |
406 | mouse leaves the windows client area or hits the screen border (whether |
407 | it's one or the other depends on the platform), and the mouse move events |
408 | (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in |
409 | framebuffer pixels in the sapp_event items mouse_x and mouse_y, and |
410 | relative movement in framebuffer pixels in the sapp_event items mouse_dx |
411 | and mouse_dy. |
412 | |
413 | To get continuous mouse movement (also when the mouse leaves the window |
414 | client area or hits the screen border), activate mouse-lock mode |
415 | by calling: |
416 | |
417 | sapp_lock_mouse(true) |
418 | |
419 | When mouse lock is activated, the mouse pointer is hidden, the |
420 | reported absolute mouse position (sapp_event.mouse_x/y) appears |
421 | frozen, and the relative mouse movement in sapp_event.mouse_dx/dy |
422 | no longer has a direct relation to framebuffer pixels but instead |
423 | uses "raw mouse input" (what "raw mouse input" exactly means also |
424 | differs by platform). |
425 | |
426 | To deactivate mouse lock and return to normal mouse mode, call |
427 | |
428 | sapp_lock_mouse(false) |
429 | |
430 | And finally, to check if mouse lock is currently active, call |
431 | |
432 | if (sapp_mouse_locked()) { ... } |
433 | |
434 | On native platforms, the sapp_lock_mouse() and sapp_mouse_locked() |
435 | functions work as expected (mouse lock is activated or deactivated |
436 | immediately when sapp_lock_mouse() is called, and sapp_mouse_locked() |
437 | also immediately returns the new state after sapp_lock_mouse() |
438 | is called. |
439 | |
440 | On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave |
441 | differently, as dictated by the limitations of the HTML5 Pointer Lock API: |
442 | |
443 | - sapp_lock_mouse(true) can be called at any time, but it will |
444 | only take effect in a 'short-lived input event handler of a specific |
445 | type', meaning when one of the following events happens: |
446 | - SAPP_EVENTTYPE_MOUSE_DOWN |
447 | - SAPP_EVENTTYPE_MOUSE_UP |
448 | - SAPP_EVENTTYPE_MOUSE_SCROLL |
449 | - SAPP_EVENTTYPE_KEY_UP |
450 | - SAPP_EVENTTYPE_KEY_DOWN |
451 | - The mouse lock/unlock action on the web platform is asynchronous, |
452 | this means that sapp_mouse_locked() won't immediately return |
453 | the new status after calling sapp_lock_mouse(), instead the |
454 | reported status will only change when the pointer lock has actually |
455 | been activated or deactivated in the browser. |
456 | - On the web, mouse lock can be deactivated by the user at any time |
457 | by pressing the Esc key. When this happens, sokol_app.h behaves |
458 | the same as if sapp_lock_mouse(false) is called. |
459 | |
460 | For things like camera manipulation it's most straightforward to lock |
461 | and unlock the mouse right from the sokol_app.h event handler, for |
462 | instance the following code enters and leaves mouse lock when the |
463 | left mouse button is pressed and released, and then uses the relative |
464 | movement information to manipulate a camera (taken from the |
465 | cgltf-sapp.c sample in the sokol-samples repository |
466 | at https://github.com/floooh/sokol-samples): |
467 | |
468 | static void input(const sapp_event* ev) { |
469 | switch (ev->type) { |
470 | case SAPP_EVENTTYPE_MOUSE_DOWN: |
471 | if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { |
472 | sapp_lock_mouse(true); |
473 | } |
474 | break; |
475 | |
476 | case SAPP_EVENTTYPE_MOUSE_UP: |
477 | if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { |
478 | sapp_lock_mouse(false); |
479 | } |
480 | break; |
481 | |
482 | case SAPP_EVENTTYPE_MOUSE_MOVE: |
483 | if (sapp_mouse_locked()) { |
484 | cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); |
485 | } |
486 | break; |
487 | |
488 | default: |
489 | break; |
490 | } |
491 | } |
492 | |
493 | CLIPBOARD SUPPORT |
494 | ================= |
495 | Applications can send and receive UTF-8 encoded text data from and to the |
496 | system clipboard. By default, clipboard support is disabled and |
497 | must be enabled at startup via the following sapp_desc struct |
498 | members: |
499 | |
500 | sapp_desc.enable_clipboard - set to true to enable clipboard support |
501 | sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes |
502 | |
503 | Enabling the clipboard will dynamically allocate a clipboard buffer |
504 | for UTF-8 encoded text data of the requested size in bytes, the default |
505 | size is 8 KBytes. Strings that don't fit into the clipboard buffer |
506 | (including the terminating zero) will be silently clipped, so it's |
507 | important that you provide a big enough clipboard size for your |
508 | use case. |
509 | |
510 | To send data to the clipboard, call sapp_set_clipboard_string() with |
511 | a pointer to an UTF-8 encoded, null-terminated C-string. |
512 | |
513 | NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be |
514 | called from inside a 'short-lived event handler', and there are a few |
515 | other HTML5-specific caveats to workaround. You'll basically have to |
516 | tinker until it works in all browsers :/ (maybe the situation will |
517 | improve when all browsers agree on and implement the new |
518 | HTML5 navigator.clipboard API). |
519 | |
520 | To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED |
521 | event in your event handler function, and then call sapp_get_clipboard_string() |
522 | to obtain the pasted UTF-8 encoded text. |
523 | |
524 | NOTE that behaviour of sapp_get_clipboard_string() is slightly different |
525 | depending on platform: |
526 | |
527 | - on the HTML5 platform, the internal clipboard buffer will only be updated |
528 | right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, |
529 | and sapp_get_clipboard_string() will simply return the current content |
530 | of the clipboard buffer |
531 | - on 'native' platforms, the call to sapp_get_clipboard_string() will |
532 | update the internal clipboard buffer with the most recent data |
533 | from the system clipboard |
534 | |
535 | Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event, |
536 | and then call sapp_get_clipboard_string() right in the event handler. |
537 | |
538 | The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app |
539 | as follows: |
540 | |
541 | - on macOS: when the Cmd+V key is pressed down |
542 | - on HTML5: when the browser sends a 'paste' event to the global 'window' object |
543 | - on all other platforms: when the Ctrl+V key is pressed down |
544 | |
545 | DRAG AND DROP SUPPORT |
546 | ===================== |
547 | PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5 |
548 | and on the native desktop platforms (Win32, Linux and macOS) because |
549 | of security-related restrictions in the HTML5 drag'n'drop API. The |
550 | WASM/HTML5 specifics are described at the end of this documentation |
551 | section: |
552 | |
553 | Like clipboard support, drag'n'drop support must be explicitly enabled |
554 | at startup in the sapp_desc struct. |
555 | |
556 | sapp_desc sokol_main() { |
557 | return (sapp_desc) { |
558 | .enable_dragndrop = true, // default is false |
559 | ... |
560 | }; |
561 | } |
562 | |
563 | You can also adjust the maximum number of files that are accepted |
564 | in a drop operation, and the maximum path length in bytes if needed: |
565 | |
566 | sapp_desc sokol_main() { |
567 | return (sapp_desc) { |
568 | .enable_dragndrop = true, // default is false |
569 | .max_dropped_files = 8, // default is 1 |
570 | .max_dropped_file_path_length = 8192, // in bytes, default is 2048 |
571 | ... |
572 | }; |
573 | } |
574 | |
575 | When drag'n'drop is enabled, the event callback will be invoked with an |
576 | event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on |
577 | the application window. |
578 | |
579 | After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the |
580 | number of dropped files, and their absolute paths by calling separate |
581 | functions: |
582 | |
583 | void on_event(const sapp_event* ev) { |
584 | if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) { |
585 | |
586 | // the mouse position where the drop happened |
587 | float x = ev->mouse_x; |
588 | float y = ev->mouse_y; |
589 | |
590 | // get the number of files and their paths like this: |
591 | const int num_dropped_files = sapp_get_num_dropped_files(); |
592 | for (int i = 0; i < num_dropped_files; i++) { |
593 | const char* path = sapp_get_dropped_file_path(i); |
594 | ... |
595 | } |
596 | } |
597 | } |
598 | |
599 | The returned file paths are UTF-8 encoded strings. |
600 | |
601 | You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path() |
602 | anywhere, also outside the event handler callback, but be aware that the |
603 | file path strings will be overwritten with the next drop operation. |
604 | |
605 | In any case, sapp_get_dropped_file_path() will never return a null pointer, |
606 | instead an empty string "" will be returned if the drag'n'drop feature |
607 | hasn't been enabled, the last drop-operation failed, or the file path index |
608 | is out of range. |
609 | |
610 | Drag'n'drop caveats: |
611 | |
612 | - if more files are dropped in a single drop-action |
613 | than sapp_desc.max_dropped_files, the additional |
614 | files will be silently ignored |
615 | - if any of the file paths is longer than |
616 | sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8 |
617 | encoding) the entire drop operation will be silently ignored (this |
618 | needs some sort of error feedback in the future) |
619 | - no mouse positions are reported while the drag is in |
620 | process, this may change in the future |
621 | |
622 | Drag'n'drop on HTML5/WASM: |
623 | |
624 | The HTML5 drag'n'drop API doesn't return file paths, but instead |
625 | black-box 'file objects' which must be used to load the content |
626 | of dropped files. This is the reason why sokol_app.h adds two |
627 | HTML5-specific functions to the drag'n'drop API: |
628 | |
629 | uint32_t sapp_html5_get_dropped_file_size(int index) |
630 | Returns the size in bytes of a dropped file. |
631 | |
632 | void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) |
633 | Asynchronously loads the content of a dropped file into a |
634 | provided memory buffer (which must be big enough to hold |
635 | the file content) |
636 | |
637 | To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED |
638 | event is received: |
639 | |
640 | sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ |
641 | .dropped_file_index = 0, |
642 | .callback = fetch_cb |
643 | .buffer = { |
644 | .ptr = buf, |
645 | .size = sizeof(buf) |
646 | }, |
647 | .user_data = ... |
648 | }); |
649 | |
650 | Make sure that the memory pointed to by 'buf' stays valid until the |
651 | callback function is called! |
652 | |
653 | As result of the asynchronous loading operation (no matter if succeeded or |
654 | failed) the 'fetch_cb' function will be called: |
655 | |
656 | void fetch_cb(const sapp_html5_fetch_response* response) { |
657 | // IMPORTANT: check if the loading operation actually succeeded: |
658 | if (response->succeeded) { |
659 | // the size of the loaded file: |
660 | const size_t num_bytes = response->data.size; |
661 | // and the pointer to the data (same as 'buf' in the fetch-call): |
662 | const void* ptr = response->data.ptr; |
663 | } |
664 | else { |
665 | // on error check the error code: |
666 | switch (response->error_code) { |
667 | case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL: |
668 | ... |
669 | break; |
670 | case SAPP_HTML5_FETCH_ERROR_OTHER: |
671 | ... |
672 | break; |
673 | } |
674 | } |
675 | } |
676 | |
677 | Check the droptest-sapp example for a real-world example which works |
678 | both on native platforms and the web: |
679 | |
680 | https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c |
681 | |
682 | HIGH-DPI RENDERING |
683 | ================== |
684 | You can set the sapp_desc.high_dpi flag during initialization to request |
685 | a full-resolution framebuffer on HighDPI displays. The default behaviour |
686 | is sapp_desc.high_dpi=false, this means that the application will |
687 | render to a lower-resolution framebuffer on HighDPI displays and the |
688 | rendered content will be upscaled by the window system composer. |
689 | |
690 | In a HighDPI scenario, you still request the same window size during |
691 | sokol_main(), but the framebuffer sizes returned by sapp_width() |
692 | and sapp_height() will be scaled up according to the DPI scaling |
693 | ratio. |
694 | |
695 | Note that on some platforms the DPI scaling factor may change at any |
696 | time (for instance when a window is moved from a high-dpi display |
697 | to a low-dpi display). |
698 | |
699 | To query the current DPI scaling factor, call the function: |
700 | |
701 | float sapp_dpi_scale(void); |
702 | |
703 | For instance on a Retina Mac, returning the following sapp_desc |
704 | struct from sokol_main(): |
705 | |
706 | sapp_desc sokol_main() { |
707 | return (sapp_desc) { |
708 | .width = 640, |
709 | .height = 480, |
710 | .high_dpi = true, |
711 | ... |
712 | }; |
713 | } |
714 | |
715 | ...the functions the functions sapp_width(), sapp_height() |
716 | and sapp_dpi_scale() will return the following values: |
717 | |
718 | sapp_width: 1280 |
719 | sapp_height: 960 |
720 | sapp_dpi_scale: 2.0 |
721 | |
722 | If the high_dpi flag is false, or you're not running on a Retina display, |
723 | the values would be: |
724 | |
725 | sapp_width: 640 |
726 | sapp_height: 480 |
727 | sapp_dpi_scale: 1.0 |
728 | |
729 | If the window is moved from the Retina display to a low-dpi external display, |
730 | the values would change as follows: |
731 | |
732 | sapp_width: 1280 => 640 |
733 | sapp_height: 960 => 480 |
734 | sapp_dpi_scale: 2.0 => 1.0 |
735 | |
736 | Currently there is no event associated with a DPI change, but an |
737 | SAPP_EVENTTYPE_RESIZED will be sent as a side effect of the |
738 | framebuffer size changing. |
739 | |
740 | Per-monitor DPI is currently supported on macOS and Windows. |
741 | |
742 | APPLICATION QUIT |
743 | ================ |
744 | Without special quit handling, a sokol_app.h application will quit |
745 | 'gracefully' when the user clicks the window close-button unless a |
746 | platform's application model prevents this (e.g. on web or mobile). |
747 | 'Graceful exit' means that the application-provided cleanup callback will |
748 | be called before the application quits. |
749 | |
750 | On native desktop platforms sokol_app.h provides more control over the |
751 | application-quit-process. It's possible to initiate a 'programmatic quit' |
752 | from the application code, and a quit initiated by the application user can |
753 | be intercepted (for instance to show a custom dialog box). |
754 | |
755 | This 'programmatic quit protocol' is implemented through 3 functions |
756 | and 1 event: |
757 | |
758 | - sapp_quit(): This function simply quits the application without |
759 | giving the user a chance to intervene. Usually this might |
760 | be called when the user clicks the 'Ok' button in a 'Really Quit?' |
761 | dialog box |
762 | - sapp_request_quit(): Calling sapp_request_quit() will send the |
763 | event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler |
764 | callback, giving the user code a chance to intervene and cancel the |
765 | pending quit process (for instance to show a 'Really Quit?' dialog |
766 | box). If the event handler callback does nothing, the application |
767 | will be quit as usual. To prevent this, call the function |
768 | sapp_cancel_quit() from inside the event handler. |
769 | - sapp_cancel_quit(): Cancels a pending quit request, either initiated |
770 | by the user clicking the window close button, or programmatically |
771 | by calling sapp_request_quit(). The only place where calling this |
772 | function makes sense is from inside the event handler callback when |
773 | the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received. |
774 | - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user |
775 | clicks the window's close button or application code calls the |
776 | sapp_request_quit() function. The event handler callback code can handle |
777 | this event by calling sapp_cancel_quit() to cancel the quit. |
778 | If the event is ignored, the application will quit as usual. |
779 | |
780 | On the web platform, the quit behaviour differs from native platforms, |
781 | because of web-specific restrictions: |
782 | |
783 | A `programmatic quit` initiated by calling sapp_quit() or |
784 | sapp_request_quit() will work as described above: the cleanup callback is |
785 | called, platform-specific cleanup is performed (on the web |
786 | this means that JS event handlers are unregisters), and then |
787 | the request-animation-loop will be exited. However that's all. The |
788 | web page itself will continue to exist (e.g. it's not possible to |
789 | programmatically close the browser tab). |
790 | |
791 | On the web it's also not possible to run custom code when the user |
792 | closes a brower tab, so it's not possible to prevent this with a |
793 | fancy custom dialog box. |
794 | |
795 | Instead the standard "Leave Site?" dialog box can be activated (or |
796 | deactivated) with the following function: |
797 | |
798 | sapp_html5_ask_leave_site(bool ask); |
799 | |
800 | The initial state of the associated internal flag can be provided |
801 | at startup via sapp_desc.html5_ask_leave_site. |
802 | |
803 | This feature should only be used sparingly in critical situations - for |
804 | instance when the user would loose data - since popping up modal dialog |
805 | boxes is considered quite rude in the web world. Note that there's no way |
806 | to customize the content of this dialog box or run any code as a result |
807 | of the user's decision. Also note that the user must have interacted with |
808 | the site before the dialog box will appear. These are all security measures |
809 | to prevent fishing. |
810 | |
811 | The Dear ImGui HighDPI sample contains example code of how to |
812 | implement a 'Really Quit?' dialog box with Dear ImGui (native desktop |
813 | platforms only), and for showing the hardwired "Leave Site?" dialog box |
814 | when running on the web platform: |
815 | |
816 | https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html |
817 | |
818 | FULLSCREEN |
819 | ========== |
820 | If the sapp_desc.fullscreen flag is true, sokol-app will try to create |
821 | a fullscreen window on platforms with a 'proper' window system |
822 | (mobile devices will always use fullscreen). The implementation details |
823 | depend on the target platform, in general sokol-app will use a |
824 | 'soft approach' which doesn't interfere too much with the platform's |
825 | window system (for instance borderless fullscreen window instead of |
826 | a 'real' fullscreen mode). Such details might change over time |
827 | as sokol-app is adapted for different needs. |
828 | |
829 | The most important effect of fullscreen mode to keep in mind is that |
830 | the requested canvas width and height will be ignored for the initial |
831 | window size, calling sapp_width() and sapp_height() will instead return |
832 | the resolution of the fullscreen canvas (however the provided size |
833 | might still be used for the non-fullscreen window, in case the user can |
834 | switch back from fullscreen- to windowed-mode). |
835 | |
836 | To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). |
837 | |
838 | To check if the application window is currently in fullscreen mode, |
839 | call sapp_is_fullscreen(). |
840 | |
841 | WINDOW ICON SUPPORT |
842 | =================== |
843 | Some sokol_app.h backends allow to change the window icon programmatically: |
844 | |
845 | - on Win32: the small icon in the window's title bar, and the |
846 | bigger icon in the task bar |
847 | - on Linux: highly dependent on the used window manager, but usually |
848 | the window's title bar icon and/or the task bar icon |
849 | - on HTML5: the favicon shown in the page's browser tab |
850 | |
851 | NOTE that it is not possible to set the actual application icon which is |
852 | displayed by the operating system on the desktop or 'home screen'. Those |
853 | icons must be provided 'traditionally' through operating-system-specific |
854 | resources which are associated with the application (sokol_app.h might |
855 | later support setting the window icon from platform specific resource data |
856 | though). |
857 | |
858 | There are two ways to set the window icon: |
859 | |
860 | - at application start in the sokol_main() function by initializing |
861 | the sapp_desc.icon nested struct |
862 | - or later by calling the function sapp_set_icon() |
863 | |
864 | As a convenient shortcut, sokol_app.h comes with a builtin default-icon |
865 | (a rainbow-colored 'S', which at least looks a bit better than the Windows |
866 | default icon for applications), which can be activated like this: |
867 | |
868 | At startup in sokol_main(): |
869 | |
870 | sapp_desc sokol_main(...) { |
871 | return (sapp_desc){ |
872 | ... |
873 | icon.sokol_default = true |
874 | }; |
875 | } |
876 | |
877 | Or later by calling: |
878 | |
879 | sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true }); |
880 | |
881 | NOTE that a completely zero-initialized sapp_icon_desc struct will not |
882 | update the window icon in any way. This is an 'escape hatch' so that you |
883 | can handle the window icon update yourself (or if you do this already, |
884 | sokol_app.h won't get in your way, in this case just leave the |
885 | sapp_desc.icon struct zero-initialized). |
886 | |
887 | Providing your own icon images works exactly like in GLFW (down to the |
888 | data format): |
889 | |
890 | You provide one or more 'candidate images' in different sizes, and the |
891 | sokol_app.h platform backends pick the best match for the specific backend |
892 | and icon type. |
893 | |
894 | For each candidate image, you need to provide: |
895 | |
896 | - the width in pixels |
897 | - the height in pixels |
898 | - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844 |
899 | on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44) |
900 | |
901 | For instance, if you have 3 candidate images (small, medium, big) of |
902 | sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup |
903 | like this: |
904 | |
905 | // the actual pixel data (RGBA8, origin top-left) |
906 | const uint32_t small[16][16] = { ... }; |
907 | const uint32_t medium[32][32] = { ... }; |
908 | const uint32_t big[64][64] = { ... }; |
909 | |
910 | const sapp_icon_desc icon_desc = { |
911 | .images = { |
912 | { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) }, |
913 | { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) }, |
914 | // ...or without the SAPP_RANGE helper macro: |
915 | { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } } |
916 | } |
917 | }; |
918 | |
919 | An sapp_icon_desc struct initialized like this can then either be applied |
920 | at application start in sokol_main: |
921 | |
922 | sapp_desc sokol_main(...) { |
923 | return (sapp_desc){ |
924 | ... |
925 | icon = icon_desc |
926 | }; |
927 | } |
928 | |
929 | ...or later by calling sapp_set_icon(): |
930 | |
931 | sapp_set_icon(&icon_desc); |
932 | |
933 | Some window icon caveats: |
934 | |
935 | - once the window icon has been updated, there's no way to go back to |
936 | the platform's default icon, this is because some platforms (Linux |
937 | and HTML5) don't switch the icon visual back to the default even if |
938 | the custom icon is deleted or removed |
939 | - on HTML5, if the sokol_app.h icon doesn't show up in the browser |
940 | tab, check that there's no traditional favicon 'link' element |
941 | is defined in the page's index.html, sokol_app.h will only |
942 | append a new favicon link element, but not delete any manually |
943 | defined favicon in the page |
944 | |
945 | For an example and test of the window icon feature, check out the the |
946 | 'icon-sapp' sample on the sokol-samples git repository. |
947 | |
948 | ONSCREEN KEYBOARD |
949 | ================= |
950 | On some platforms which don't provide a physical keyboard, sokol-app |
951 | can display the platform's integrated onscreen keyboard for text |
952 | input. To request that the onscreen keyboard is shown, call |
953 | |
954 | sapp_show_keyboard(true); |
955 | |
956 | Likewise, to hide the keyboard call: |
957 | |
958 | sapp_show_keyboard(false); |
959 | |
960 | Note that on the web platform, the keyboard can only be shown from |
961 | inside an input handler. On such platforms, sapp_show_keyboard() |
962 | will only work as expected when it is called from inside the |
963 | sokol-app event callback function. When called from other places, |
964 | an internal flag will be set, and the onscreen keyboard will be |
965 | called at the next 'legal' opportunity (when the next input event |
966 | is handled). |
967 | |
968 | OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) |
969 | ====================================================== |
970 | In its default configuration, sokol_app.h "hijacks" the platform's |
971 | standard main() function. This was done because different platforms |
972 | have different main functions which are not compatible with |
973 | C's main() (for instance WinMain on Windows has completely different |
974 | arguments). However, this "main hijacking" posed a problem for |
975 | usage scenarios like integrating sokol_app.h with other languages than |
976 | C or C++, so an alternative SOKOL_NO_ENTRY mode has been added |
977 | in which the user code provides the platform's main function: |
978 | |
979 | - define SOKOL_NO_ENTRY before including the sokol_app.h implementation |
980 | - do *not* provide a sokol_main() function |
981 | - instead provide the standard main() function of the platform |
982 | - from the main function, call the function ```sapp_run()``` which |
983 | takes a pointer to an ```sapp_desc``` structure. |
984 | - ```sapp_run()``` takes over control and calls the provided init-, frame-, |
985 | shutdown- and event-callbacks just like in the default model, it |
986 | will only return when the application quits (or not at all on some |
987 | platforms, like emscripten) |
988 | |
989 | NOTE: SOKOL_NO_ENTRY is currently not supported on Android. |
990 | |
991 | WINDOWS CONSOLE OUTPUT |
992 | ====================== |
993 | On Windows, regular windowed applications don't show any stdout/stderr text |
994 | output, which can be a bit of a hassle for printf() debugging or generally |
995 | logging text to the console. Also, console output by default uses a local |
996 | codepage setting and thus international UTF-8 encoded text is printed |
997 | as garbage. |
998 | |
999 | To help with these issues, sokol_app.h can be configured at startup |
1000 | via the following Windows-specific sapp_desc flags: |
1001 | |
1002 | sapp_desc.win32_console_utf8 (default: false) |
1003 | When set to true, the output console codepage will be switched |
1004 | to UTF-8 (and restored to the original codepage on exit) |
1005 | |
1006 | sapp_desc.win32_console_attach (default: false) |
1007 | When set to true, stdout and stderr will be attached to the |
1008 | console of the parent process (if the parent process actually |
1009 | has a console). This means that if the application was started |
1010 | in a command line window, stdout and stderr output will be printed |
1011 | to the terminal, just like a regular command line program. But if |
1012 | the application is started via double-click, it will behave like |
1013 | a regular UI application, and stdout/stderr will not be visible. |
1014 | |
1015 | sapp_desc.win32_console_create (default: false) |
1016 | When set to true, a new console window will be created and |
1017 | stdout/stderr will be redirected to that console window. It |
1018 | doesn't matter if the application is started from the command |
1019 | line or via double-click. |
1020 | |
1021 | MEMORY ALLOCATION OVERRIDE |
1022 | ========================== |
1023 | You can override the memory allocation functions at initialization time |
1024 | like this: |
1025 | |
1026 | void* my_alloc(size_t size, void* user_data) { |
1027 | return malloc(size); |
1028 | } |
1029 | |
1030 | void my_free(void* ptr, void* user_data) { |
1031 | free(ptr); |
1032 | } |
1033 | |
1034 | sapp_desc sokol_main(int argc, char* argv[]) { |
1035 | return (sapp_desc){ |
1036 | // ... |
1037 | .allocator = { |
1038 | .alloc = my_alloc, |
1039 | .free = my_free, |
1040 | .user_data = ..., |
1041 | } |
1042 | }; |
1043 | } |
1044 | |
1045 | If no overrides are provided, malloc and free will be used. |
1046 | |
1047 | This only affects memory allocation calls done by sokol_app.h |
1048 | itself though, not any allocations in OS libraries. |
1049 | |
1050 | |
1051 | LOG FUNCTION OVERRIDE |
1052 | ===================== |
1053 | You can override the log function at initialization time like this: |
1054 | |
1055 | void my_log(const char* message, void* user_data) { |
1056 | printf("sapp says: \s\n", message); |
1057 | } |
1058 | |
1059 | sapp_desc sokol_main(int argc, char* argv[]) { |
1060 | return (sapp_desc){ |
1061 | // ... |
1062 | .logger = { |
1063 | .log_cb = my_log, |
1064 | .user_data = ..., |
1065 | } |
1066 | }; |
1067 | } |
1068 | |
1069 | If no overrides are provided, puts will be used on most platforms. |
1070 | On Android, __android_log_write will be used instead. |
1071 | |
1072 | |
1073 | TEMP NOTE DUMP |
1074 | ============== |
1075 | - onscreen keyboard support on Android requires Java :(, should we even bother? |
1076 | - sapp_desc needs a bool whether to initialize depth-stencil surface |
1077 | - GL context initialization needs more control (at least what GL version to initialize) |
1078 | - application icon |
1079 | - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy |
1080 | at the latest but should do it earlier, in onStop, as an app is "killable" after onStop |
1081 | on Android Honeycomb and later (it can't be done at the moment as the app may be started |
1082 | again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) |
1083 | |
1084 | |
1085 | LICENSE |
1086 | ======= |
1087 | zlib/libpng license |
1088 | |
1089 | Copyright (c) 2018 Andre Weissflog |
1090 | |
1091 | This software is provided 'as-is', without any express or implied warranty. |
1092 | In no event will the authors be held liable for any damages arising from the |
1093 | use of this software. |
1094 | |
1095 | Permission is granted to anyone to use this software for any purpose, |
1096 | including commercial applications, and to alter it and redistribute it |
1097 | freely, subject to the following restrictions: |
1098 | |
1099 | 1. The origin of this software must not be misrepresented; you must not |
1100 | claim that you wrote the original software. If you use this software in a |
1101 | product, an acknowledgment in the product documentation would be |
1102 | appreciated but is not required. |
1103 | |
1104 | 2. Altered source versions must be plainly marked as such, and must not |
1105 | be misrepresented as being the original software. |
1106 | |
1107 | 3. This notice may not be removed or altered from any source |
1108 | distribution. |
1109 | */ |
1110 | #define SOKOL_APP_INCLUDED (1) |
1111 | #include <stddef.h> // size_t |
1112 | #include <stdint.h> |
1113 | #include <stdbool.h> |
1114 | |
1115 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL) |
1116 | #define SOKOL_APP_API_DECL SOKOL_API_DECL |
1117 | #endif |
1118 | #ifndef SOKOL_APP_API_DECL |
1119 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL) |
1120 | #define SOKOL_APP_API_DECL __declspec(dllexport) |
1121 | #elif defined(_WIN32) && defined(SOKOL_DLL) |
1122 | #define SOKOL_APP_API_DECL __declspec(dllimport) |
1123 | #else |
1124 | #define SOKOL_APP_API_DECL extern |
1125 | #endif |
1126 | #endif |
1127 | |
1128 | #ifdef __cplusplus |
1129 | extern "C" { |
1130 | #endif |
1131 | |
1132 | /* misc constants */ |
1133 | enum { |
1134 | SAPP_MAX_TOUCHPOINTS = 8, |
1135 | SAPP_MAX_MOUSEBUTTONS = 3, |
1136 | SAPP_MAX_KEYCODES = 512, |
1137 | SAPP_MAX_ICONIMAGES = 8, |
1138 | }; |
1139 | |
1140 | /* |
1141 | sapp_event_type |
1142 | |
1143 | The type of event that's passed to the event handler callback |
1144 | in the sapp_event.type field. These are not just "traditional" |
1145 | input events, but also notify the application about state changes |
1146 | or other user-invoked actions. |
1147 | */ |
1148 | typedef enum sapp_event_type { |
1149 | SAPP_EVENTTYPE_INVALID, |
1150 | SAPP_EVENTTYPE_KEY_DOWN, |
1151 | SAPP_EVENTTYPE_KEY_UP, |
1152 | SAPP_EVENTTYPE_CHAR, |
1153 | SAPP_EVENTTYPE_MOUSE_DOWN, |
1154 | SAPP_EVENTTYPE_MOUSE_UP, |
1155 | SAPP_EVENTTYPE_MOUSE_SCROLL, |
1156 | SAPP_EVENTTYPE_MOUSE_MOVE, |
1157 | SAPP_EVENTTYPE_MOUSE_ENTER, |
1158 | SAPP_EVENTTYPE_MOUSE_LEAVE, |
1159 | SAPP_EVENTTYPE_TOUCHES_BEGAN, |
1160 | SAPP_EVENTTYPE_TOUCHES_MOVED, |
1161 | SAPP_EVENTTYPE_TOUCHES_ENDED, |
1162 | SAPP_EVENTTYPE_TOUCHES_CANCELLED, |
1163 | SAPP_EVENTTYPE_RESIZED, |
1164 | SAPP_EVENTTYPE_ICONIFIED, |
1165 | SAPP_EVENTTYPE_RESTORED, |
1166 | SAPP_EVENTTYPE_FOCUSED, |
1167 | SAPP_EVENTTYPE_UNFOCUSED, |
1168 | SAPP_EVENTTYPE_SUSPENDED, |
1169 | SAPP_EVENTTYPE_RESUMED, |
1170 | SAPP_EVENTTYPE_QUIT_REQUESTED, |
1171 | SAPP_EVENTTYPE_CLIPBOARD_PASTED, |
1172 | SAPP_EVENTTYPE_FILES_DROPPED, |
1173 | _SAPP_EVENTTYPE_NUM, |
1174 | _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF |
1175 | } sapp_event_type; |
1176 | |
1177 | /* |
1178 | sapp_keycode |
1179 | |
1180 | The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the |
1181 | struct field sapp_event.key_code. |
1182 | |
1183 | Note that the keycode values are identical with GLFW. |
1184 | */ |
1185 | typedef enum sapp_keycode { |
1186 | SAPP_KEYCODE_INVALID = 0, |
1187 | SAPP_KEYCODE_SPACE = 32, |
1188 | SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ |
1189 | SAPP_KEYCODE_COMMA = 44, /* , */ |
1190 | SAPP_KEYCODE_MINUS = 45, /* - */ |
1191 | SAPP_KEYCODE_PERIOD = 46, /* . */ |
1192 | SAPP_KEYCODE_SLASH = 47, /* / */ |
1193 | SAPP_KEYCODE_0 = 48, |
1194 | SAPP_KEYCODE_1 = 49, |
1195 | SAPP_KEYCODE_2 = 50, |
1196 | SAPP_KEYCODE_3 = 51, |
1197 | SAPP_KEYCODE_4 = 52, |
1198 | SAPP_KEYCODE_5 = 53, |
1199 | SAPP_KEYCODE_6 = 54, |
1200 | SAPP_KEYCODE_7 = 55, |
1201 | SAPP_KEYCODE_8 = 56, |
1202 | SAPP_KEYCODE_9 = 57, |
1203 | SAPP_KEYCODE_SEMICOLON = 59, /* ; */ |
1204 | SAPP_KEYCODE_EQUAL = 61, /* = */ |
1205 | SAPP_KEYCODE_A = 65, |
1206 | SAPP_KEYCODE_B = 66, |
1207 | SAPP_KEYCODE_C = 67, |
1208 | SAPP_KEYCODE_D = 68, |
1209 | SAPP_KEYCODE_E = 69, |
1210 | SAPP_KEYCODE_F = 70, |
1211 | SAPP_KEYCODE_G = 71, |
1212 | SAPP_KEYCODE_H = 72, |
1213 | SAPP_KEYCODE_I = 73, |
1214 | SAPP_KEYCODE_J = 74, |
1215 | SAPP_KEYCODE_K = 75, |
1216 | SAPP_KEYCODE_L = 76, |
1217 | SAPP_KEYCODE_M = 77, |
1218 | SAPP_KEYCODE_N = 78, |
1219 | SAPP_KEYCODE_O = 79, |
1220 | SAPP_KEYCODE_P = 80, |
1221 | SAPP_KEYCODE_Q = 81, |
1222 | SAPP_KEYCODE_R = 82, |
1223 | SAPP_KEYCODE_S = 83, |
1224 | SAPP_KEYCODE_T = 84, |
1225 | SAPP_KEYCODE_U = 85, |
1226 | SAPP_KEYCODE_V = 86, |
1227 | SAPP_KEYCODE_W = 87, |
1228 | SAPP_KEYCODE_X = 88, |
1229 | SAPP_KEYCODE_Y = 89, |
1230 | SAPP_KEYCODE_Z = 90, |
1231 | SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ |
1232 | SAPP_KEYCODE_BACKSLASH = 92, /* \ */ |
1233 | SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ |
1234 | SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ |
1235 | SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ |
1236 | SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ |
1237 | SAPP_KEYCODE_ESCAPE = 256, |
1238 | SAPP_KEYCODE_ENTER = 257, |
1239 | SAPP_KEYCODE_TAB = 258, |
1240 | SAPP_KEYCODE_BACKSPACE = 259, |
1241 | SAPP_KEYCODE_INSERT = 260, |
1242 | SAPP_KEYCODE_DELETE = 261, |
1243 | SAPP_KEYCODE_RIGHT = 262, |
1244 | SAPP_KEYCODE_LEFT = 263, |
1245 | SAPP_KEYCODE_DOWN = 264, |
1246 | SAPP_KEYCODE_UP = 265, |
1247 | SAPP_KEYCODE_PAGE_UP = 266, |
1248 | SAPP_KEYCODE_PAGE_DOWN = 267, |
1249 | SAPP_KEYCODE_HOME = 268, |
1250 | SAPP_KEYCODE_END = 269, |
1251 | SAPP_KEYCODE_CAPS_LOCK = 280, |
1252 | SAPP_KEYCODE_SCROLL_LOCK = 281, |
1253 | SAPP_KEYCODE_NUM_LOCK = 282, |
1254 | SAPP_KEYCODE_PRINT_SCREEN = 283, |
1255 | SAPP_KEYCODE_PAUSE = 284, |
1256 | SAPP_KEYCODE_F1 = 290, |
1257 | SAPP_KEYCODE_F2 = 291, |
1258 | SAPP_KEYCODE_F3 = 292, |
1259 | SAPP_KEYCODE_F4 = 293, |
1260 | SAPP_KEYCODE_F5 = 294, |
1261 | SAPP_KEYCODE_F6 = 295, |
1262 | SAPP_KEYCODE_F7 = 296, |
1263 | SAPP_KEYCODE_F8 = 297, |
1264 | SAPP_KEYCODE_F9 = 298, |
1265 | SAPP_KEYCODE_F10 = 299, |
1266 | SAPP_KEYCODE_F11 = 300, |
1267 | SAPP_KEYCODE_F12 = 301, |
1268 | SAPP_KEYCODE_F13 = 302, |
1269 | SAPP_KEYCODE_F14 = 303, |
1270 | SAPP_KEYCODE_F15 = 304, |
1271 | SAPP_KEYCODE_F16 = 305, |
1272 | SAPP_KEYCODE_F17 = 306, |
1273 | SAPP_KEYCODE_F18 = 307, |
1274 | SAPP_KEYCODE_F19 = 308, |
1275 | SAPP_KEYCODE_F20 = 309, |
1276 | SAPP_KEYCODE_F21 = 310, |
1277 | SAPP_KEYCODE_F22 = 311, |
1278 | SAPP_KEYCODE_F23 = 312, |
1279 | SAPP_KEYCODE_F24 = 313, |
1280 | SAPP_KEYCODE_F25 = 314, |
1281 | SAPP_KEYCODE_KP_0 = 320, |
1282 | SAPP_KEYCODE_KP_1 = 321, |
1283 | SAPP_KEYCODE_KP_2 = 322, |
1284 | SAPP_KEYCODE_KP_3 = 323, |
1285 | SAPP_KEYCODE_KP_4 = 324, |
1286 | SAPP_KEYCODE_KP_5 = 325, |
1287 | SAPP_KEYCODE_KP_6 = 326, |
1288 | SAPP_KEYCODE_KP_7 = 327, |
1289 | SAPP_KEYCODE_KP_8 = 328, |
1290 | SAPP_KEYCODE_KP_9 = 329, |
1291 | SAPP_KEYCODE_KP_DECIMAL = 330, |
1292 | SAPP_KEYCODE_KP_DIVIDE = 331, |
1293 | SAPP_KEYCODE_KP_MULTIPLY = 332, |
1294 | SAPP_KEYCODE_KP_SUBTRACT = 333, |
1295 | SAPP_KEYCODE_KP_ADD = 334, |
1296 | SAPP_KEYCODE_KP_ENTER = 335, |
1297 | SAPP_KEYCODE_KP_EQUAL = 336, |
1298 | SAPP_KEYCODE_LEFT_SHIFT = 340, |
1299 | SAPP_KEYCODE_LEFT_CONTROL = 341, |
1300 | SAPP_KEYCODE_LEFT_ALT = 342, |
1301 | SAPP_KEYCODE_LEFT_SUPER = 343, |
1302 | SAPP_KEYCODE_RIGHT_SHIFT = 344, |
1303 | SAPP_KEYCODE_RIGHT_CONTROL = 345, |
1304 | SAPP_KEYCODE_RIGHT_ALT = 346, |
1305 | SAPP_KEYCODE_RIGHT_SUPER = 347, |
1306 | SAPP_KEYCODE_MENU = 348, |
1307 | } sapp_keycode; |
1308 | |
1309 | /* |
1310 | Android specific 'tool type' enum for touch events. This lets the |
1311 | application check what type of input device was used for |
1312 | touch events. |
1313 | |
1314 | NOTE: the values must remain in sync with the corresponding |
1315 | Android SDK type, so don't change those. |
1316 | |
1317 | See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN |
1318 | */ |
1319 | typedef enum sapp_android_tooltype { |
1320 | SAPP_ANDROIDTOOLTYPE_UNKNOWN = 0, // TOOL_TYPE_UNKNOWN |
1321 | SAPP_ANDROIDTOOLTYPE_FINGER = 1, // TOOL_TYPE_FINGER |
1322 | SAPP_ANDROIDTOOLTYPE_STYLUS = 2, // TOOL_TYPE_STYLUS |
1323 | SAPP_ANDROIDTOOLTYPE_MOUSE = 3, // TOOL_TYPE_MOUSE |
1324 | } sapp_android_tooltype; |
1325 | |
1326 | /* |
1327 | sapp_touchpoint |
1328 | |
1329 | Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN, |
1330 | TOUCHES_MOVED, TOUCHES_ENDED). |
1331 | |
1332 | Touch points are stored in the nested array sapp_event.touches[], |
1333 | and the number of touches is stored in sapp_event.num_touches. |
1334 | */ |
1335 | typedef struct sapp_touchpoint { |
1336 | uintptr_t identifier; |
1337 | float pos_x; |
1338 | float pos_y; |
1339 | sapp_android_tooltype android_tooltype; // only valid on Android |
1340 | bool changed; |
1341 | } sapp_touchpoint; |
1342 | |
1343 | /* |
1344 | sapp_mousebutton |
1345 | |
1346 | The currently pressed mouse button in the events MOUSE_DOWN |
1347 | and MOUSE_UP, stored in the struct field sapp_event.mouse_button. |
1348 | */ |
1349 | typedef enum sapp_mousebutton { |
1350 | SAPP_MOUSEBUTTON_LEFT = 0x0, |
1351 | SAPP_MOUSEBUTTON_RIGHT = 0x1, |
1352 | SAPP_MOUSEBUTTON_MIDDLE = 0x2, |
1353 | SAPP_MOUSEBUTTON_INVALID = 0x100, |
1354 | } sapp_mousebutton; |
1355 | |
1356 | /* |
1357 | These are currently pressed modifier keys (and mouse buttons) which are |
1358 | passed in the event struct field sapp_event.modifiers. |
1359 | */ |
1360 | enum { |
1361 | SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key |
1362 | SAPP_MODIFIER_CTRL = 0x2, // left or right control key |
1363 | SAPP_MODIFIER_ALT = 0x4, // left or right alt key |
1364 | SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key |
1365 | SAPP_MODIFIER_LMB = 0x100, // left mouse button |
1366 | SAPP_MODIFIER_RMB = 0x200, // right mouse button |
1367 | SAPP_MODIFIER_MMB = 0x400, // middle mouse button |
1368 | }; |
1369 | |
1370 | /* |
1371 | sapp_event |
1372 | |
1373 | This is an all-in-one event struct passed to the event handler |
1374 | user callback function. Note that it depends on the event |
1375 | type what struct fields actually contain useful values, so you |
1376 | should first check the event type before reading other struct |
1377 | fields. |
1378 | */ |
1379 | typedef struct sapp_event { |
1380 | uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame |
1381 | sapp_event_type type; // the event type, always valid |
1382 | sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN |
1383 | uint32_t char_code; // the UTF-32 character code, only valid in CHAR events |
1384 | bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR |
1385 | uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events |
1386 | sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP |
1387 | float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock |
1388 | float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock |
1389 | float mouse_dx; // relative horizontal mouse movement since last frame, always valid |
1390 | float mouse_dy; // relative vertical mouse movement since last frame, always valid |
1391 | float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events |
1392 | float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events |
1393 | int num_touches; // number of valid items in the touches[] array |
1394 | sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED |
1395 | int window_width; // current window- and framebuffer sizes in pixels, always valid |
1396 | int window_height; |
1397 | int framebuffer_width; // = window_width * dpi_scale |
1398 | int framebuffer_height; // = window_height * dpi_scale |
1399 | } sapp_event; |
1400 | |
1401 | /* |
1402 | sg_range |
1403 | |
1404 | A general pointer/size-pair struct and constructor macros for passing binary blobs |
1405 | into sokol_app.h. |
1406 | */ |
1407 | typedef struct sapp_range { |
1408 | const void* ptr; |
1409 | size_t size; |
1410 | } sapp_range; |
1411 | // disabling this for every includer isn't great, but the warnings are also quite pointless |
1412 | #if defined(_MSC_VER) |
1413 | #pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ |
1414 | #pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ |
1415 | #endif |
1416 | #if defined(__cplusplus) |
1417 | #define SAPP_RANGE(x) sapp_range{ &x, sizeof(x) } |
1418 | #else |
1419 | #define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) } |
1420 | #endif |
1421 | |
1422 | /* |
1423 | sapp_image_desc |
1424 | |
1425 | This is used to describe image data to sokol_app.h (at first, window |
1426 | icons, later maybe cursor images). |
1427 | |
1428 | Note that the actual image pixel format depends on the use case: |
1429 | |
1430 | - window icon pixels are RGBA8 |
1431 | - cursor images are ??? (FIXME) |
1432 | */ |
1433 | typedef struct sapp_image_desc { |
1434 | int width; |
1435 | int height; |
1436 | sapp_range pixels; |
1437 | } sapp_image_desc; |
1438 | |
1439 | /* |
1440 | sapp_icon_desc |
1441 | |
1442 | An icon description structure for use in sapp_desc.icon and |
1443 | sapp_set_icon(). |
1444 | |
1445 | When setting a custom image, the application can provide a number of |
1446 | candidates differing in size, and sokol_app.h will pick the image(s) |
1447 | closest to the size expected by the platform's window system. |
1448 | |
1449 | To set sokol-app's default icon, set .sokol_default to true. |
1450 | |
1451 | Otherwise provide candidate images of different sizes in the |
1452 | images[] array. |
1453 | |
1454 | If both the sokol_default flag is set to true, any image candidates |
1455 | will be ignored and the sokol_app.h default icon will be set. |
1456 | */ |
1457 | typedef struct sapp_icon_desc { |
1458 | bool sokol_default; |
1459 | sapp_image_desc images[SAPP_MAX_ICONIMAGES]; |
1460 | } sapp_icon_desc; |
1461 | |
1462 | /* |
1463 | sapp_allocator |
1464 | |
1465 | Used in sapp_desc to provide custom memory-alloc and -free functions |
1466 | to sokol_app.h. If memory management should be overridden, both the |
1467 | alloc and free function must be provided (e.g. it's not valid to |
1468 | override one function but not the other). |
1469 | */ |
1470 | typedef struct sapp_allocator { |
1471 | void* (*alloc)(size_t size, void* user_data); |
1472 | void (*free)(void* ptr, void* user_data); |
1473 | void* user_data; |
1474 | } sapp_allocator; |
1475 | |
1476 | /* |
1477 | sapp_logger |
1478 | |
1479 | Used in sapp_desc to provide custom log callbacks to sokol_app.h. |
1480 | Default behavior is SOKOL_LOG(message). |
1481 | */ |
1482 | typedef struct sapp_logger { |
1483 | void (*log_cb)(const char* message, void* user_data); |
1484 | void* user_data; |
1485 | } sapp_logger; |
1486 | |
1487 | typedef struct sapp_desc { |
1488 | void (*init_cb)(void); // these are the user-provided callbacks without user data |
1489 | void (*frame_cb)(void); |
1490 | void (*cleanup_cb)(void); |
1491 | void (*event_cb)(const sapp_event*); |
1492 | void (*fail_cb)(const char*); |
1493 | |
1494 | void* user_data; // these are the user-provided callbacks with user data |
1495 | void (*init_userdata_cb)(void*); |
1496 | void (*frame_userdata_cb)(void*); |
1497 | void (*cleanup_userdata_cb)(void*); |
1498 | void (*event_userdata_cb)(const sapp_event*, void*); |
1499 | void (*fail_userdata_cb)(const char*, void*); |
1500 | |
1501 | int width; // the preferred width of the window / canvas |
1502 | int height; // the preferred height of the window / canvas |
1503 | int sample_count; // MSAA sample count |
1504 | int swap_interval; // the preferred swap interval (ignored on some platforms) |
1505 | bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays |
1506 | bool fullscreen; // whether the window should be created in fullscreen mode |
1507 | bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms) |
1508 | const char* window_title; // the window title as UTF-8 encoded string |
1509 | bool enable_clipboard; // enable clipboard access, default is false |
1510 | int clipboard_size; // max size of clipboard content in bytes |
1511 | bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false |
1512 | int max_dropped_files; // max number of dropped files to process (default: 1) |
1513 | int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) |
1514 | sapp_icon_desc icon; // the initial window icon to set |
1515 | sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) |
1516 | sapp_logger logger; // optional log callback overrides (default: SAPP_LOG(message)) |
1517 | |
1518 | /* backend-specific options */ |
1519 | bool gl_force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available |
1520 | int gl_major_version; // override GL major and minor version (the default GL version is 3.2) |
1521 | int gl_minor_version; |
1522 | bool win32_console_utf8; // if true, set the output console codepage to UTF-8 |
1523 | bool win32_console_create; // if true, attach stdout/stderr to a new console window |
1524 | bool win32_console_attach; // if true, attach stdout/stderr to parent process |
1525 | const char* html5_canvas_name; // the name (id) of the HTML5 canvas element, default is "canvas" |
1526 | bool html5_canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked |
1527 | bool html5_preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames |
1528 | bool html5_premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention |
1529 | bool html5_ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) |
1530 | bool ios_keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas |
1531 | // __v_ start |
1532 | bool __v_native_render; // V patch to allow for native rendering |
1533 | // __v_ end |
1534 | } sapp_desc; |
1535 | |
1536 | /* HTML5 specific: request and response structs for |
1537 | asynchronously loading dropped-file content. |
1538 | */ |
1539 | typedef enum sapp_html5_fetch_error { |
1540 | SAPP_HTML5_FETCH_ERROR_NO_ERROR, |
1541 | SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL, |
1542 | SAPP_HTML5_FETCH_ERROR_OTHER, |
1543 | } sapp_html5_fetch_error; |
1544 | |
1545 | typedef struct sapp_html5_fetch_response { |
1546 | bool succeeded; // true if the loading operation has succeeded |
1547 | sapp_html5_fetch_error error_code; |
1548 | int file_index; // index of the dropped file (0..sapp_get_num_dropped_filed()-1) |
1549 | sapp_range data; // pointer and size of the fetched data (data.ptr == buffer.ptr, data.size <= buffer.size) |
1550 | sapp_range buffer; // the user-provided buffer ptr/size pair (buffer.ptr == data.ptr, buffer.size >= data.size) |
1551 | void* user_data; // user-provided user data pointer |
1552 | } sapp_html5_fetch_response; |
1553 | |
1554 | typedef struct sapp_html5_fetch_request { |
1555 | int dropped_file_index; // 0..sapp_get_num_dropped_files()-1 |
1556 | void (*callback)(const sapp_html5_fetch_response*); // response callback function pointer (required) |
1557 | sapp_range buffer; // ptr/size of a memory buffer to load the data into |
1558 | void* user_data; // optional userdata pointer |
1559 | } sapp_html5_fetch_request; |
1560 | |
1561 | /* |
1562 | sapp_mouse_cursor |
1563 | |
1564 | Predefined cursor image definitions, set with sapp_set_mouse_cursor(sapp_mouse_cursor cursor) |
1565 | */ |
1566 | typedef enum sapp_mouse_cursor { |
1567 | SAPP_MOUSECURSOR_DEFAULT = 0, // equivalent with system default cursor |
1568 | SAPP_MOUSECURSOR_ARROW, |
1569 | SAPP_MOUSECURSOR_IBEAM, |
1570 | SAPP_MOUSECURSOR_CROSSHAIR, |
1571 | SAPP_MOUSECURSOR_POINTING_HAND, |
1572 | SAPP_MOUSECURSOR_RESIZE_EW, |
1573 | SAPP_MOUSECURSOR_RESIZE_NS, |
1574 | SAPP_MOUSECURSOR_RESIZE_NWSE, |
1575 | SAPP_MOUSECURSOR_RESIZE_NESW, |
1576 | SAPP_MOUSECURSOR_RESIZE_ALL, |
1577 | SAPP_MOUSECURSOR_NOT_ALLOWED, |
1578 | _SAPP_MOUSECURSOR_NUM, |
1579 | } sapp_mouse_cursor; |
1580 | |
1581 | /* user-provided functions */ |
1582 | extern sapp_desc sokol_main(int argc, char* argv[]); |
1583 | |
1584 | /* returns true after sokol-app has been initialized */ |
1585 | SOKOL_APP_API_DECL bool sapp_isvalid(void); |
1586 | /* returns the current framebuffer width in pixels */ |
1587 | SOKOL_APP_API_DECL int sapp_width(void); |
1588 | /* same as sapp_width(), but returns float */ |
1589 | SOKOL_APP_API_DECL float sapp_widthf(void); |
1590 | /* returns the current framebuffer height in pixels */ |
1591 | SOKOL_APP_API_DECL int sapp_height(void); |
1592 | /* same as sapp_height(), but returns float */ |
1593 | SOKOL_APP_API_DECL float sapp_heightf(void); |
1594 | /* get default framebuffer color pixel format */ |
1595 | SOKOL_APP_API_DECL int sapp_color_format(void); |
1596 | /* get default framebuffer depth pixel format */ |
1597 | SOKOL_APP_API_DECL int sapp_depth_format(void); |
1598 | /* get default framebuffer sample count */ |
1599 | SOKOL_APP_API_DECL int sapp_sample_count(void); |
1600 | /* returns true when high_dpi was requested and actually running in a high-dpi scenario */ |
1601 | SOKOL_APP_API_DECL bool sapp_high_dpi(void); |
1602 | /* returns the dpi scaling factor (window pixels to framebuffer pixels) */ |
1603 | SOKOL_APP_API_DECL float sapp_dpi_scale(void); |
1604 | /* show or hide the mobile device onscreen keyboard */ |
1605 | SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); |
1606 | /* return true if the mobile device onscreen keyboard is currently shown */ |
1607 | SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); |
1608 | /* query fullscreen mode */ |
1609 | SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); |
1610 | /* toggle fullscreen mode */ |
1611 | SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); |
1612 | /* show or hide the mouse cursor */ |
1613 | SOKOL_APP_API_DECL void sapp_show_mouse(bool show); |
1614 | /* show or hide the mouse cursor */ |
1615 | SOKOL_APP_API_DECL bool sapp_mouse_shown(void); |
1616 | /* enable/disable mouse-pointer-lock mode */ |
1617 | SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); |
1618 | /* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ |
1619 | SOKOL_APP_API_DECL bool sapp_mouse_locked(void); |
1620 | /* set mouse cursor type */ |
1621 | SOKOL_APP_API_DECL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor); |
1622 | /* get current mouse cursor type */ |
1623 | SOKOL_APP_API_DECL sapp_mouse_cursor sapp_get_mouse_cursor(void); |
1624 | /* return the userdata pointer optionally provided in sapp_desc */ |
1625 | SOKOL_APP_API_DECL void* sapp_userdata(void); |
1626 | /* return a copy of the sapp_desc structure */ |
1627 | SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); |
1628 | /* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */ |
1629 | SOKOL_APP_API_DECL void sapp_request_quit(void); |
1630 | /* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ |
1631 | SOKOL_APP_API_DECL void sapp_cancel_quit(void); |
1632 | /* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ |
1633 | SOKOL_APP_API_DECL void sapp_quit(void); |
1634 | /* call from inside event callback to consume the current event (don't forward to platform) */ |
1635 | SOKOL_APP_API_DECL void sapp_consume_event(void); |
1636 | /* get the current frame counter (for comparison with sapp_event.frame_count) */ |
1637 | SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); |
1638 | /* get an averaged/smoothed frame duration in seconds */ |
1639 | SOKOL_APP_API_DECL double sapp_frame_duration(void); |
1640 | /* write string into clipboard */ |
1641 | SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); |
1642 | /* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ |
1643 | SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); |
1644 | /* set the window title (only on desktop platforms) */ |
1645 | SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); |
1646 | /* set the window icon (only on Windows and Linux) */ |
1647 | SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc); |
1648 | /* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ |
1649 | SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); |
1650 | /* gets the dropped file paths */ |
1651 | SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); |
1652 | |
1653 | /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ |
1654 | SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc); |
1655 | |
1656 | /* EGL: get EGLDisplay object */ |
1657 | SOKOL_APP_API_DECL const void* sapp_egl_get_display(void); |
1658 | /* EGL: get EGLContext object */ |
1659 | SOKOL_APP_API_DECL const void* sapp_egl_get_context(void); |
1660 | |
1661 | /* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ |
1662 | SOKOL_APP_API_DECL bool sapp_gles2(void); |
1663 | |
1664 | /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ |
1665 | SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); |
1666 | /* HTML5: get byte size of a dropped file */ |
1667 | SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index); |
1668 | /* HTML5: asynchronously load the content of a dropped file */ |
1669 | SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request); |
1670 | |
1671 | /* Metal: get bridged pointer to Metal device object */ |
1672 | SOKOL_APP_API_DECL const void* sapp_metal_get_device(void); |
1673 | /* Metal: get bridged pointer to this frame's renderpass descriptor */ |
1674 | SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); |
1675 | /* Metal: get bridged pointer to current drawable */ |
1676 | SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void); |
1677 | /* macOS: get bridged pointer to macOS NSWindow */ |
1678 | SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); |
1679 | /* iOS: get bridged pointer to iOS UIWindow */ |
1680 | SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); |
1681 | |
1682 | /* D3D11: get pointer to ID3D11Device object */ |
1683 | SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void); |
1684 | /* D3D11: get pointer to ID3D11DeviceContext object */ |
1685 | SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void); |
1686 | /* D3D11: get pointer to IDXGISwapChain object */ |
1687 | SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void); |
1688 | /* D3D11: get pointer to ID3D11RenderTargetView object */ |
1689 | SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void); |
1690 | /* D3D11: get pointer to ID3D11DepthStencilView */ |
1691 | SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); |
1692 | /* Win32: get the HWND window handle */ |
1693 | SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); |
1694 | |
1695 | /* WebGPU: get WGPUDevice handle */ |
1696 | SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void); |
1697 | /* WebGPU: get swapchain's WGPUTextureView handle for rendering */ |
1698 | SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void); |
1699 | /* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */ |
1700 | SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void); |
1701 | /* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */ |
1702 | SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); |
1703 | |
1704 | /* Android: get native activity handle */ |
1705 | SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); |
1706 | |
1707 | #ifdef __cplusplus |
1708 | } /* extern "C" */ |
1709 | |
1710 | /* reference-based equivalents for C++ */ |
1711 | inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } |
1712 | |
1713 | #endif |
1714 | |
1715 | // this WinRT specific hack is required when wWinMain is in a static library |
1716 | #if defined(_MSC_VER) && defined(UNICODE) |
1717 | #include <winapifamily.h> |
1718 | #if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) |
1719 | #pragma comment(linker, "/include:wWinMain") |
1720 | #endif |
1721 | #endif |
1722 | |
1723 | #endif // SOKOL_APP_INCLUDED |
1724 | |
1725 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ |
1726 | #ifdef SOKOL_APP_IMPL |
1727 | #define SOKOL_APP_IMPL_INCLUDED (1) |
1728 | |
1729 | #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) |
1730 | #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sapp_desc.allocator to override memory allocation functions" |
1731 | #endif |
1732 | |
1733 | #include <stdlib.h> // malloc, free |
1734 | #include <string.h> // memset |
1735 | #include <stddef.h> // size_t |
1736 | #include <math.h> /* roundf() */ |
1737 | |
1738 | /* check if the config defines are alright */ |
1739 | #if defined(__APPLE__) |
1740 | // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting |
1741 | #if !defined(__cplusplus) |
1742 | #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) |
1743 | #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" |
1744 | #endif |
1745 | #endif |
1746 | #define _SAPP_APPLE (1) |
1747 | #include <TargetConditionals.h> |
1748 | #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE |
1749 | /* MacOS */ |
1750 | #define _SAPP_MACOS (1) |
1751 | #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33) |
1752 | #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") |
1753 | #endif |
1754 | #else |
1755 | /* iOS or iOS Simulator */ |
1756 | #define _SAPP_IOS (1) |
1757 | #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) |
1758 | #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") |
1759 | #endif |
1760 | #endif |
1761 | #elif defined(__EMSCRIPTEN__) |
1762 | /* emscripten (asm.js or wasm) */ |
1763 | #define _SAPP_EMSCRIPTEN (1) |
1764 | #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU) |
1765 | #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU") |
1766 | #endif |
1767 | #elif defined(_WIN32) |
1768 | /* Windows (D3D11 or GL) */ |
1769 | #include <winapifamily.h> |
1770 | #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) |
1771 | #define _SAPP_UWP (1) |
1772 | #if !defined(SOKOL_D3D11) |
1773 | #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") |
1774 | #endif |
1775 | #if !defined(__cplusplus) |
1776 | #error("sokol_app.h: UWP bindings require C++/17") |
1777 | #endif |
1778 | #else |
1779 | #define _SAPP_WIN32 (1) |
1780 | #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) |
1781 | #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") |
1782 | #endif |
1783 | #endif |
1784 | #elif defined(__ANDROID__) |
1785 | /* Android */ |
1786 | #define _SAPP_ANDROID (1) |
1787 | #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) |
1788 | #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") |
1789 | #endif |
1790 | #if defined(SOKOL_NO_ENTRY) |
1791 | #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") |
1792 | #endif |
1793 | #elif defined(__linux__) || defined(__unix__) |
1794 | /* Linux */ |
1795 | #define _SAPP_LINUX (1) |
1796 | #if defined(SOKOL_GLCORE33) |
1797 | #if !defined(SOKOL_FORCE_EGL) |
1798 | #define _SAPP_GLX (1) |
1799 | #endif |
1800 | #elif !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) |
1801 | #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3 or SOKOL_GLES2") |
1802 | #endif |
1803 | #else |
1804 | #error "sokol_app.h: Unknown platform" |
1805 | #endif |
1806 | |
1807 | #ifndef SOKOL_API_IMPL |
1808 | #define SOKOL_API_IMPL |
1809 | #endif |
1810 | #ifndef SOKOL_DEBUG |
1811 | #ifndef NDEBUG |
1812 | #define SOKOL_DEBUG |
1813 | #endif |
1814 | #endif |
1815 | #ifndef SOKOL_ASSERT |
1816 | #include <assert.h> |
1817 | #define SOKOL_ASSERT(c) assert(c) |
1818 | #endif |
1819 | #ifndef SOKOL_UNREACHABLE |
1820 | #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) |
1821 | #endif |
1822 | |
1823 | #if !defined(SOKOL_DEBUG) |
1824 | #define SAPP_LOG(s) |
1825 | #else |
1826 | #define SAPP_LOG(s) _sapp_log(s) |
1827 | #ifndef SOKOL_LOG |
1828 | #if defined(__ANDROID__) |
1829 | #include <android/log.h> |
1830 | #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s) |
1831 | #else |
1832 | #include <stdio.h> |
1833 | #define SOKOL_LOG(s) puts(s) |
1834 | #endif |
1835 | #endif |
1836 | #endif |
1837 | |
1838 | #ifndef SOKOL_ABORT |
1839 | #define SOKOL_ABORT() abort() |
1840 | #endif |
1841 | #ifndef _SOKOL_PRIVATE |
1842 | #if defined(__GNUC__) || defined(__clang__) |
1843 | #define _SOKOL_PRIVATE __attribute__((unused)) static |
1844 | #else |
1845 | #define _SOKOL_PRIVATE static |
1846 | #endif |
1847 | #endif |
1848 | #ifndef _SOKOL_UNUSED |
1849 | #define _SOKOL_UNUSED(x) (void)(x) |
1850 | #endif |
1851 | |
1852 | /*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ |
1853 | #if defined(_SAPP_APPLE) |
1854 | #if defined(SOKOL_METAL) |
1855 | #import <Metal/Metal.h> |
1856 | #import <MetalKit/MetalKit.h> |
1857 | #endif |
1858 | #if defined(_SAPP_MACOS) |
1859 | #if !defined(SOKOL_METAL) |
1860 | #ifndef GL_SILENCE_DEPRECATION |
1861 | #define GL_SILENCE_DEPRECATION |
1862 | #endif |
1863 | #include <Cocoa/Cocoa.h> |
1864 | #endif |
1865 | #elif defined(_SAPP_IOS) |
1866 | #import <UIKit/UIKit.h> |
1867 | #if !defined(SOKOL_METAL) |
1868 | #import <GLKit/GLKit.h> |
1869 | #endif |
1870 | #endif |
1871 | #include <AvailabilityMacros.h> |
1872 | #include <mach/mach_time.h> |
1873 | #elif defined(_SAPP_EMSCRIPTEN) |
1874 | #if defined(SOKOL_WGPU) |
1875 | #include <webgpu/webgpu.h> |
1876 | #endif |
1877 | #include <emscripten/emscripten.h> |
1878 | #include <emscripten/html5.h> |
1879 | #elif defined(_SAPP_WIN32) |
1880 | #ifdef _MSC_VER |
1881 | #pragma warning(push) |
1882 | #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ |
1883 | #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ |
1884 | #pragma warning(disable:4054) /* 'type cast': from function pointer */ |
1885 | #pragma warning(disable:4055) /* 'type cast': from data pointer */ |
1886 | #pragma warning(disable:4505) /* unreferenced local function has been removed */ |
1887 | #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ |
1888 | #endif |
1889 | #ifndef WIN32_LEAN_AND_MEAN |
1890 | #define WIN32_LEAN_AND_MEAN |
1891 | #endif |
1892 | #ifndef NOMINMAX |
1893 | #define NOMINMAX |
1894 | #endif |
1895 | #include <windows.h> |
1896 | #include <windowsx.h> |
1897 | #include <shellapi.h> |
1898 | #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem |
1899 | #if defined(SOKOL_WIN32_FORCE_MAIN) |
1900 | #pragma comment (linker, "/subsystem:console") |
1901 | #else |
1902 | #pragma comment (linker, "/subsystem:windows") |
1903 | #endif |
1904 | #endif |
1905 | #include <stdio.h> /* freopen_s() */ |
1906 | #include <wchar.h> /* wcslen() */ |
1907 | |
1908 | #pragma comment (lib, "kernel32") |
1909 | #pragma comment (lib, "user32") |
1910 | #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */ |
1911 | #pragma comment (lib, "gdi32") |
1912 | #if defined(SOKOL_D3D11) |
1913 | #pragma comment (lib, "dxgi") |
1914 | #pragma comment (lib, "d3d11") |
1915 | #endif |
1916 | |
1917 | #if defined(SOKOL_D3D11) |
1918 | #ifndef D3D11_NO_HELPERS |
1919 | #define D3D11_NO_HELPERS |
1920 | #endif |
1921 | #include <d3d11.h> |
1922 | #include <dxgi.h> |
1923 | // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it |
1924 | #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4) |
1925 | #endif |
1926 | #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ |
1927 | #define WM_MOUSEHWHEEL (0x020E) |
1928 | #endif |
1929 | #ifndef WM_DPICHANGED |
1930 | #define WM_DPICHANGED (0x02E0) |
1931 | #endif |
1932 | #elif defined(_SAPP_UWP) |
1933 | #ifndef NOMINMAX |
1934 | #define NOMINMAX |
1935 | #endif |
1936 | #ifdef _MSC_VER |
1937 | #pragma warning(push) |
1938 | #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ |
1939 | #pragma warning(disable:4054) /* 'type cast': from function pointer */ |
1940 | #pragma warning(disable:4055) /* 'type cast': from data pointer */ |
1941 | #pragma warning(disable:4505) /* unreferenced local function has been removed */ |
1942 | #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ |
1943 | #endif |
1944 | #include <windows.h> |
1945 | #include <winrt/Windows.ApplicationModel.Core.h> |
1946 | #include <winrt/Windows.Foundation.h> |
1947 | #include <winrt/Windows.Foundation.Collections.h> |
1948 | #include <winrt/Windows.Graphics.Display.h> |
1949 | #include <winrt/Windows.UI.Core.h> |
1950 | #include <winrt/Windows.UI.Composition.h> |
1951 | #include <winrt/Windows.UI.Input.h> |
1952 | #include <winrt/Windows.UI.ViewManagement.h> |
1953 | #include <winrt/Windows.System.h> |
1954 | #include <ppltasks.h> |
1955 | |
1956 | #include <dxgi1_4.h> |
1957 | #include <d3d11_3.h> |
1958 | #include <DirectXMath.h> |
1959 | |
1960 | #pragma comment (lib, "WindowsApp") |
1961 | #pragma comment (lib, "dxguid") |
1962 | #elif defined(_SAPP_ANDROID) |
1963 | #include <pthread.h> |
1964 | #include <unistd.h> |
1965 | #include <time.h> |
1966 | #include <android/native_activity.h> |
1967 | #include <android/looper.h> |
1968 | #include <EGL/egl.h> |
1969 | #elif defined(_SAPP_LINUX) |
1970 | #define GL_GLEXT_PROTOTYPES |
1971 | #include <X11/Xlib.h> |
1972 | #include <X11/Xutil.h> |
1973 | #include <X11/XKBlib.h> |
1974 | #include <X11/keysym.h> |
1975 | #include <X11/Xresource.h> |
1976 | #include <X11/Xatom.h> |
1977 | #include <X11/extensions/XInput2.h> |
1978 | #include <X11/Xcursor/Xcursor.h> |
1979 | #include <X11/cursorfont.h> /* XC_* font cursors */ |
1980 | #include <X11/Xmd.h> /* CARD32 */ |
1981 | #if !defined(_SAPP_GLX) |
1982 | #include <EGL/egl.h> |
1983 | #endif |
1984 | #include <dlfcn.h> /* dlopen, dlsym, dlclose */ |
1985 | #include <limits.h> /* LONG_MAX */ |
1986 | #include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */ |
1987 | #include <time.h> |
1988 | #endif |
1989 | |
1990 | /*== frame timing helpers ===================================================*/ |
1991 | #define _SAPP_RING_NUM_SLOTS (256) |
1992 | typedef struct { |
1993 | int head; |
1994 | int tail; |
1995 | double buf[_SAPP_RING_NUM_SLOTS]; |
1996 | } _sapp_ring_t; |
1997 | |
1998 | _SOKOL_PRIVATE int _sapp_ring_idx(int i) { |
1999 | return i % _SAPP_RING_NUM_SLOTS; |
2000 | } |
2001 | |
2002 | _SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t* ring) { |
2003 | ring->head = 0; |
2004 | ring->tail = 0; |
2005 | } |
2006 | |
2007 | _SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t* ring) { |
2008 | return _sapp_ring_idx(ring->head + 1) == ring->tail; |
2009 | } |
2010 | |
2011 | _SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t* ring) { |
2012 | return ring->head == ring->tail; |
2013 | } |
2014 | |
2015 | _SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t* ring) { |
2016 | int count; |
2017 | if (ring->head >= ring->tail) { |
2018 | count = ring->head - ring->tail; |
2019 | } |
2020 | else { |
2021 | count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail; |
2022 | } |
2023 | SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS)); |
2024 | return count; |
2025 | } |
2026 | |
2027 | _SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t* ring, double val) { |
2028 | SOKOL_ASSERT(!_sapp_ring_full(ring)); |
2029 | ring->buf[ring->head] = val; |
2030 | ring->head = _sapp_ring_idx(ring->head + 1); |
2031 | } |
2032 | |
2033 | _SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t* ring) { |
2034 | SOKOL_ASSERT(!_sapp_ring_empty(ring)); |
2035 | double val = ring->buf[ring->tail]; |
2036 | ring->tail = _sapp_ring_idx(ring->tail + 1); |
2037 | return val; |
2038 | } |
2039 | |
2040 | /* |
2041 | NOTE: |
2042 | |
2043 | Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS? |
2044 | A: The value appears to be highly unstable during the first few |
2045 | seconds, sometimes several frames are dropped in sequence, or |
2046 | switch between 120 and 60 Hz for a few frames. Simply measuring |
2047 | and averaging the frame time yielded a more stable frame duration. |
2048 | Maybe switching to CVDisplayLink would yield better results. |
2049 | Until then just measure the time. |
2050 | */ |
2051 | typedef struct { |
2052 | #if defined(_SAPP_APPLE) |
2053 | struct { |
2054 | mach_timebase_info_data_t timebase; |
2055 | uint64_t start; |
2056 | } mach; |
2057 | #elif defined(_SAPP_EMSCRIPTEN) |
2058 | // empty |
2059 | #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) |
2060 | struct { |
2061 | LARGE_INTEGER freq; |
2062 | LARGE_INTEGER start; |
2063 | } win; |
2064 | #else // Linux, Android, ... |
2065 | #ifdef CLOCK_MONOTONIC |
2066 | #define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC |
2067 | #else |
2068 | // on some embedded platforms, CLOCK_MONOTONIC isn't defined |
2069 | #define _SAPP_CLOCK_MONOTONIC (1) |
2070 | #endif |
2071 | struct { |
2072 | uint64_t start; |
2073 | } posix; |
2074 | #endif |
2075 | } _sapp_timestamp_t; |
2076 | |
2077 | _SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { |
2078 | int64_t q = value / denom; |
2079 | int64_t r = value % denom; |
2080 | return q * numer + r * numer / denom; |
2081 | } |
2082 | |
2083 | _SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) { |
2084 | #if defined(_SAPP_APPLE) |
2085 | mach_timebase_info(&ts->mach.timebase); |
2086 | ts->mach.start = mach_absolute_time(); |
2087 | #elif defined(_SAPP_EMSCRIPTEN) |
2088 | (void)ts; |
2089 | #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) |
2090 | QueryPerformanceFrequency(&ts->win.freq); |
2091 | QueryPerformanceCounter(&ts->win.start); |
2092 | #else |
2093 | struct timespec tspec; |
2094 | clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); |
2095 | ts->posix.start = (uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec; |
2096 | #endif |
2097 | } |
2098 | |
2099 | _SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) { |
2100 | #if defined(_SAPP_APPLE) |
2101 | const uint64_t traw = mach_absolute_time() - ts->mach.start; |
2102 | const uint64_t now = (uint64_t) _sapp_int64_muldiv((int64_t)traw, (int64_t)ts->mach.timebase.numer, (int64_t)ts->mach.timebase.denom); |
2103 | return (double)now / 1000000000.0; |
2104 | #elif defined(_SAPP_EMSCRIPTEN) |
2105 | (void)ts; |
2106 | SOKOL_ASSERT(false); |
2107 | return 0.0; |
2108 | #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) |
2109 | LARGE_INTEGER qpc; |
2110 | QueryPerformanceCounter(&qpc); |
2111 | const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart); |
2112 | return (double)now / 1000000000.0; |
2113 | #else |
2114 | struct timespec tspec; |
2115 | clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); |
2116 | const uint64_t now = ((uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec) - ts->posix.start; |
2117 | return (double)now / 1000000000.0; |
2118 | #endif |
2119 | } |
2120 | |
2121 | typedef struct { |
2122 | double last; |
2123 | double accum; |
2124 | double avg; |
2125 | int spike_count; |
2126 | int num; |
2127 | _sapp_timestamp_t timestamp; |
2128 | _sapp_ring_t ring; |
2129 | } _sapp_timing_t; |
2130 | |
2131 | _SOKOL_PRIVATE void _sapp_timing_reset(_sapp_timing_t* t) { |
2132 | t->last = 0.0; |
2133 | t->accum = 0.0; |
2134 | t->spike_count = 0; |
2135 | t->num = 0; |
2136 | _sapp_ring_init(&t->ring); |
2137 | } |
2138 | |
2139 | _SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t* t) { |
2140 | t->avg = 1.0 / 60.0; // dummy value until first actual value is available |
2141 | _sapp_timing_reset(t); |
2142 | _sapp_timestamp_init(&t->timestamp); |
2143 | } |
2144 | |
2145 | _SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t* t, double dur) { |
2146 | // arbitrary upper limit to ignore outliers (e.g. during window resizing, or debugging) |
2147 | double min_dur = 0.0; |
2148 | double max_dur = 0.1; |
2149 | // if we have enough samples for a useful average, use a much tighter 'valid window' |
2150 | if (_sapp_ring_full(&t->ring)) { |
2151 | min_dur = t->avg * 0.8; |
2152 | max_dur = t->avg * 1.2; |
2153 | } |
2154 | if ((dur < min_dur) || (dur > max_dur)) { |
2155 | t->spike_count++; |
2156 | // if there have been many spikes in a row, the display refresh rate |
2157 | // might have changed, so a timing reset is needed |
2158 | if (t->spike_count > 20) { |
2159 | _sapp_timing_reset(t); |
2160 | } |
2161 | return; |
2162 | } |
2163 | if (_sapp_ring_full(&t->ring)) { |
2164 | double old_val = _sapp_ring_dequeue(&t->ring); |
2165 | t->accum -= old_val; |
2166 | t->num -= 1; |
2167 | } |
2168 | _sapp_ring_enqueue(&t->ring, dur); |
2169 | t->accum += dur; |
2170 | t->num += 1; |
2171 | SOKOL_ASSERT(t->num > 0); |
2172 | t->avg = t->accum / t->num; |
2173 | t->spike_count = 0; |
2174 | } |
2175 | |
2176 | _SOKOL_PRIVATE void _sapp_timing_discontinuity(_sapp_timing_t* t) { |
2177 | t->last = 0.0; |
2178 | } |
2179 | |
2180 | _SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t* t) { |
2181 | const double now = _sapp_timestamp_now(&t->timestamp); |
2182 | if (t->last > 0.0) { |
2183 | double dur = now - t->last; |
2184 | _sapp_timing_put(t, dur); |
2185 | } |
2186 | t->last = now; |
2187 | } |
2188 | |
2189 | _SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t* t, double now) { |
2190 | if (t->last > 0.0) { |
2191 | double dur = now - t->last; |
2192 | _sapp_timing_put(t, dur); |
2193 | } |
2194 | t->last = now; |
2195 | } |
2196 | |
2197 | _SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) { |
2198 | return t->avg; |
2199 | } |
2200 | |
2201 | /*== MACOS DECLARATIONS ======================================================*/ |
2202 | #if defined(_SAPP_MACOS) |
2203 | |
2204 | // __v_ start |
2205 | @interface SokolWindow : NSWindow { |
2206 | } |
2207 | @end |
2208 | @interface MyView2 : NSView |
2209 | @end |
2210 | |
2211 | MyView2* g_view; |
2212 | |
2213 | // A custom NSWindow interface to handle events in borderless windows. |
2214 | @implementation SokolWindow |
2215 | - (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless |
2216 | - (BOOL)canBecomeMainWindow { return YES; } |
2217 | @end |
2218 | // __v_ end |
2219 | |
2220 | |
2221 | @interface _sapp_macos_app_delegate : NSObject<NSApplicationDelegate> |
2222 | @end |
2223 | @interface _sapp_macos_window : NSWindow |
2224 | @end |
2225 | @interface _sapp_macos_window_delegate : NSObject<NSWindowDelegate> |
2226 | @end |
2227 | #if defined(SOKOL_METAL) |
2228 | @interface _sapp_macos_view : MTKView |
2229 | @end |
2230 | #elif defined(SOKOL_GLCORE33) |
2231 | @interface _sapp_macos_view : NSOpenGLView |
2232 | - (void)timerFired:(id)sender; |
2233 | @end |
2234 | #endif // SOKOL_GLCORE33 |
2235 | |
2236 | typedef struct { |
2237 | uint32_t flags_changed_store; |
2238 | uint8_t mouse_buttons; |
2239 | // __v_ start |
2240 | //NSWindow* window; // __v_ removed |
2241 | _sapp_macos_window* window; // __v_ added |
2242 | // __v_ end |
2243 | NSTrackingArea* tracking_area; |
2244 | _sapp_macos_app_delegate* app_dlg; |
2245 | _sapp_macos_window_delegate* win_dlg; |
2246 | _sapp_macos_view* view; |
2247 | NSCursor* cursors[_SAPP_MOUSECURSOR_NUM]; |
2248 | #if defined(SOKOL_METAL) |
2249 | id<MTLDevice> mtl_device; |
2250 | #endif |
2251 | } _sapp_macos_t; |
2252 | |
2253 | #endif // _SAPP_MACOS |
2254 | |
2255 | /*== IOS DECLARATIONS ========================================================*/ |
2256 | #if defined(_SAPP_IOS) |
2257 | |
2258 | @interface _sapp_app_delegate : NSObject<UIApplicationDelegate> |
2259 | @end |
2260 | @interface _sapp_textfield_dlg : NSObject<UITextFieldDelegate> |
2261 | - (void)keyboardWasShown:(NSNotification*)notif; |
2262 | - (void)keyboardWillBeHidden:(NSNotification*)notif; |
2263 | - (void)keyboardDidChangeFrame:(NSNotification*)notif; |
2264 | @end |
2265 | #if defined(SOKOL_METAL) |
2266 | @interface _sapp_ios_view : MTKView; |
2267 | @end |
2268 | #else |
2269 | @interface _sapp_ios_view : GLKView |
2270 | @end |
2271 | #endif |
2272 | |
2273 | typedef struct { |
2274 | UIWindow* window; |
2275 | _sapp_ios_view* view; |
2276 | UITextField* textfield; |
2277 | _sapp_textfield_dlg* textfield_dlg; |
2278 | #if defined(SOKOL_METAL) |
2279 | UIViewController* view_ctrl; |
2280 | id<MTLDevice> mtl_device; |
2281 | #else |
2282 | GLKViewController* view_ctrl; |
2283 | EAGLContext* eagl_ctx; |
2284 | #endif |
2285 | bool suspended; |
2286 | } _sapp_ios_t; |
2287 | |
2288 | #endif // _SAPP_IOS |
2289 | |
2290 | /*== EMSCRIPTEN DECLARATIONS =================================================*/ |
2291 | #if defined(_SAPP_EMSCRIPTEN) |
2292 | |
2293 | #if defined(SOKOL_WGPU) |
2294 | typedef struct { |
2295 | int state; |
2296 | WGPUDevice device; |
2297 | WGPUSwapChain swapchain; |
2298 | WGPUTextureFormat render_format; |
2299 | WGPUTexture msaa_tex; |
2300 | WGPUTexture depth_stencil_tex; |
2301 | WGPUTextureView swapchain_view; |
2302 | WGPUTextureView msaa_view; |
2303 | WGPUTextureView depth_stencil_view; |
2304 | } _sapp_wgpu_t; |
2305 | #endif |
2306 | |
2307 | typedef struct { |
2308 | bool textfield_created; |
2309 | bool wants_show_keyboard; |
2310 | bool wants_hide_keyboard; |
2311 | bool mouse_lock_requested; |
2312 | uint16_t mouse_buttons; |
2313 | #if defined(SOKOL_WGPU) |
2314 | _sapp_wgpu_t wgpu; |
2315 | #endif |
2316 | } _sapp_emsc_t; |
2317 | #endif // _SAPP_EMSCRIPTEN |
2318 | |
2319 | /*== WIN32 DECLARATIONS ======================================================*/ |
2320 | #if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) |
2321 | typedef struct { |
2322 | ID3D11Device* device; |
2323 | ID3D11DeviceContext* device_context; |
2324 | ID3D11Texture2D* rt; |
2325 | ID3D11RenderTargetView* rtv; |
2326 | ID3D11Texture2D* msaa_rt; |
2327 | ID3D11RenderTargetView* msaa_rtv; |
2328 | ID3D11Texture2D* ds; |
2329 | ID3D11DepthStencilView* dsv; |
2330 | DXGI_SWAP_CHAIN_DESC swap_chain_desc; |
2331 | IDXGISwapChain* swap_chain; |
2332 | IDXGIDevice1* dxgi_device; |
2333 | bool use_dxgi_frame_stats; |
2334 | UINT sync_refresh_count; |
2335 | } _sapp_d3d11_t; |
2336 | #endif |
2337 | |
2338 | /*== WIN32 DECLARATIONS ======================================================*/ |
2339 | #if defined(_SAPP_WIN32) |
2340 | |
2341 | #ifndef DPI_ENUMS_DECLARED |
2342 | typedef enum PROCESS_DPI_AWARENESS |
2343 | { |
2344 | PROCESS_DPI_UNAWARE = 0, |
2345 | PROCESS_SYSTEM_DPI_AWARE = 1, |
2346 | PROCESS_PER_MONITOR_DPI_AWARE = 2 |
2347 | } PROCESS_DPI_AWARENESS; |
2348 | typedef enum MONITOR_DPI_TYPE { |
2349 | MDT_EFFECTIVE_DPI = 0, |
2350 | MDT_ANGULAR_DPI = 1, |
2351 | MDT_RAW_DPI = 2, |
2352 | MDT_DEFAULT = MDT_EFFECTIVE_DPI |
2353 | } MONITOR_DPI_TYPE; |
2354 | #endif /*DPI_ENUMS_DECLARED*/ |
2355 | |
2356 | typedef struct { |
2357 | bool aware; |
2358 | float content_scale; |
2359 | float window_scale; |
2360 | float mouse_scale; |
2361 | } _sapp_win32_dpi_t; |
2362 | |
2363 | typedef struct { |
2364 | HWND hwnd; |
2365 | HMONITOR hmonitor; |
2366 | HDC dc; |
2367 | HICON big_icon; |
2368 | HICON small_icon; |
2369 | HCURSOR cursors[_SAPP_MOUSECURSOR_NUM]; |
2370 | UINT orig_codepage; |
2371 | LONG mouse_locked_x, mouse_locked_y; |
2372 | RECT stored_window_rect; // used to restore window pos/size when toggling fullscreen => windowed |
2373 | bool is_win10_or_greater; |
2374 | bool in_create_window; |
2375 | bool iconified; |
2376 | bool mouse_tracked; |
2377 | uint8_t mouse_capture_mask; |
2378 | _sapp_win32_dpi_t dpi; |
2379 | bool raw_input_mousepos_valid; |
2380 | LONG raw_input_mousepos_x; |
2381 | LONG raw_input_mousepos_y; |
2382 | uint8_t raw_input_data[256]; |
2383 | } _sapp_win32_t; |
2384 | |
2385 | #if defined(SOKOL_GLCORE33) |
2386 | #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 |
2387 | #define WGL_SUPPORT_OPENGL_ARB 0x2010 |
2388 | #define WGL_DRAW_TO_WINDOW_ARB 0x2001 |
2389 | #define WGL_PIXEL_TYPE_ARB 0x2013 |
2390 | #define WGL_TYPE_RGBA_ARB 0x202b |
2391 | #define WGL_ACCELERATION_ARB 0x2003 |
2392 | #define WGL_NO_ACCELERATION_ARB 0x2025 |
2393 | #define WGL_RED_BITS_ARB 0x2015 |
2394 | #define WGL_GREEN_BITS_ARB 0x2017 |
2395 | #define WGL_BLUE_BITS_ARB 0x2019 |
2396 | #define WGL_ALPHA_BITS_ARB 0x201b |
2397 | #define WGL_DEPTH_BITS_ARB 0x2022 |
2398 | #define WGL_STENCIL_BITS_ARB 0x2023 |
2399 | #define WGL_DOUBLE_BUFFER_ARB 0x2011 |
2400 | #define WGL_SAMPLES_ARB 0x2042 |
2401 | #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 |
2402 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 |
2403 | #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 |
2404 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 |
2405 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 |
2406 | #define WGL_CONTEXT_FLAGS_ARB 0x2094 |
2407 | #define ERROR_INVALID_VERSION_ARB 0x2095 |
2408 | #define ERROR_INVALID_PROFILE_ARB 0x2096 |
2409 | #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 |
2410 | typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); |
2411 | typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); |
2412 | typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); |
2413 | typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); |
2414 | typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); |
2415 | typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); |
2416 | typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); |
2417 | typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); |
2418 | typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); |
2419 | typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); |
2420 | |
2421 | typedef struct { |
2422 | HINSTANCE opengl32; |
2423 | HGLRC gl_ctx; |
2424 | PFN_wglCreateContext CreateContext; |
2425 | PFN_wglDeleteContext DeleteContext; |
2426 | PFN_wglGetProcAddress GetProcAddress; |
2427 | PFN_wglGetCurrentDC GetCurrentDC; |
2428 | PFN_wglMakeCurrent MakeCurrent; |
2429 | PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; |
2430 | PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; |
2431 | PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; |
2432 | PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; |
2433 | PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; |
2434 | bool ext_swap_control; |
2435 | bool arb_multisample; |
2436 | bool arb_pixel_format; |
2437 | bool arb_create_context; |
2438 | bool arb_create_context_profile; |
2439 | HWND msg_hwnd; |
2440 | HDC msg_dc; |
2441 | } _sapp_wgl_t; |
2442 | #endif // SOKOL_GLCORE33 |
2443 | |
2444 | #endif // _SAPP_WIN32 |
2445 | |
2446 | /*== UWP DECLARATIONS ======================================================*/ |
2447 | #if defined(_SAPP_UWP) |
2448 | |
2449 | typedef struct { |
2450 | float content_scale; |
2451 | float window_scale; |
2452 | float mouse_scale; |
2453 | } _sapp_uwp_dpi_t; |
2454 | |
2455 | typedef struct { |
2456 | bool mouse_tracked; |
2457 | uint8_t mouse_buttons; |
2458 | _sapp_uwp_dpi_t dpi; |
2459 | } _sapp_uwp_t; |
2460 | |
2461 | #endif // _SAPP_UWP |
2462 | |
2463 | /*== ANDROID DECLARATIONS ====================================================*/ |
2464 | |
2465 | #if defined(_SAPP_ANDROID) |
2466 | typedef enum { |
2467 | _SOKOL_ANDROID_MSG_CREATE, |
2468 | _SOKOL_ANDROID_MSG_RESUME, |
2469 | _SOKOL_ANDROID_MSG_PAUSE, |
2470 | _SOKOL_ANDROID_MSG_FOCUS, |
2471 | _SOKOL_ANDROID_MSG_NO_FOCUS, |
2472 | _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, |
2473 | _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, |
2474 | _SOKOL_ANDROID_MSG_DESTROY, |
2475 | } _sapp_android_msg_t; |
2476 | |
2477 | typedef struct { |
2478 | pthread_t thread; |
2479 | pthread_mutex_t mutex; |
2480 | pthread_cond_t cond; |
2481 | int read_from_main_fd; |
2482 | int write_from_main_fd; |
2483 | } _sapp_android_pt_t; |
2484 | |
2485 | typedef struct { |
2486 | ANativeWindow* window; |
2487 | AInputQueue* input; |
2488 | } _sapp_android_resources_t; |
2489 | |
2490 | typedef struct { |
2491 | ANativeActivity* activity; |
2492 | _sapp_android_pt_t pt; |
2493 | _sapp_android_resources_t pending; |
2494 | _sapp_android_resources_t current; |
2495 | ALooper* looper; |
2496 | bool is_thread_started; |
2497 | bool is_thread_stopping; |
2498 | bool is_thread_stopped; |
2499 | bool has_created; |
2500 | bool has_resumed; |
2501 | bool has_focus; |
2502 | EGLConfig config; |
2503 | EGLDisplay display; |
2504 | EGLContext context; |
2505 | EGLSurface surface; |
2506 | } _sapp_android_t; |
2507 | |
2508 | #endif // _SAPP_ANDROID |
2509 | |
2510 | /*== LINUX DECLARATIONS ======================================================*/ |
2511 | #if defined(_SAPP_LINUX) |
2512 | |
2513 | #define _SAPP_X11_XDND_VERSION (5) |
2514 | |
2515 | #define GLX_VENDOR 1 |
2516 | #define GLX_RGBA_BIT 0x00000001 |
2517 | #define GLX_WINDOW_BIT 0x00000001 |
2518 | #define GLX_DRAWABLE_TYPE 0x8010 |
2519 | #define GLX_RENDER_TYPE 0x8011 |
2520 | #define GLX_DOUBLEBUFFER 5 |
2521 | #define GLX_RED_SIZE 8 |
2522 | #define GLX_GREEN_SIZE 9 |
2523 | #define GLX_BLUE_SIZE 10 |
2524 | #define GLX_ALPHA_SIZE 11 |
2525 | #define GLX_DEPTH_SIZE 12 |
2526 | #define GLX_STENCIL_SIZE 13 |
2527 | #define GLX_SAMPLES 0x186a1 |
2528 | #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 |
2529 | #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 |
2530 | #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 |
2531 | #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 |
2532 | #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 |
2533 | #define GLX_CONTEXT_FLAGS_ARB 0x2094 |
2534 | |
2535 | typedef XID GLXWindow; |
2536 | typedef XID GLXDrawable; |
2537 | typedef struct __GLXFBConfig* GLXFBConfig; |
2538 | typedef struct __GLXcontext* GLXContext; |
2539 | typedef void (*__GLXextproc)(void); |
2540 | |
2541 | typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); |
2542 | typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); |
2543 | typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); |
2544 | typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); |
2545 | typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); |
2546 | typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); |
2547 | typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); |
2548 | typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); |
2549 | typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); |
2550 | typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName); |
2551 | typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); |
2552 | typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); |
2553 | typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); |
2554 | typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); |
2555 | |
2556 | typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); |
2557 | typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); |
2558 | |
2559 | typedef struct { |
2560 | bool available; |
2561 | int major_opcode; |
2562 | int event_base; |
2563 | int error_base; |
2564 | int major; |
2565 | int minor; |
2566 | } _sapp_xi_t; |
2567 | |
2568 | typedef struct { |
2569 | int version; |
2570 | Window source; |
2571 | Atom format; |
2572 | Atom XdndAware; |
2573 | Atom XdndEnter; |
2574 | Atom XdndPosition; |
2575 | Atom XdndStatus; |
2576 | Atom XdndActionCopy; |
2577 | Atom XdndDrop; |
2578 | Atom XdndFinished; |
2579 | Atom XdndSelection; |
2580 | Atom XdndTypeList; |
2581 | Atom text_uri_list; |
2582 | } _sapp_xdnd_t; |
2583 | |
2584 | typedef struct { |
2585 | uint8_t mouse_buttons; |
2586 | Display* display; |
2587 | int screen; |
2588 | Window root; |
2589 | Colormap colormap; |
2590 | Window window; |
2591 | Cursor hidden_cursor; |
2592 | Cursor cursors[_SAPP_MOUSECURSOR_NUM]; |
2593 | int window_state; |
2594 | float dpi; |
2595 | unsigned char error_code; |
2596 | Atom UTF8_STRING; |
2597 | Atom WM_PROTOCOLS; |
2598 | Atom WM_DELETE_WINDOW; |
2599 | Atom WM_STATE; |
2600 | Atom NET_WM_NAME; |
2601 | Atom NET_WM_ICON_NAME; |
2602 | Atom NET_WM_ICON; |
2603 | Atom NET_WM_STATE; |
2604 | Atom NET_WM_STATE_FULLSCREEN; |
2605 | _sapp_xi_t xi; |
2606 | _sapp_xdnd_t xdnd; |
2607 | } _sapp_x11_t; |
2608 | |
2609 | #if defined(_SAPP_GLX) |
2610 | |
2611 | typedef struct { |
2612 | void* libgl; |
2613 | int major; |
2614 | int minor; |
2615 | int event_base; |
2616 | int error_base; |
2617 | GLXContext ctx; |
2618 | GLXWindow window; |
2619 | |
2620 | // GLX 1.3 functions |
2621 | PFNGLXGETFBCONFIGSPROC GetFBConfigs; |
2622 | PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; |
2623 | PFNGLXGETCLIENTSTRINGPROC GetClientString; |
2624 | PFNGLXQUERYEXTENSIONPROC QueryExtension; |
2625 | PFNGLXQUERYVERSIONPROC QueryVersion; |
2626 | PFNGLXDESTROYCONTEXTPROC DestroyContext; |
2627 | PFNGLXMAKECURRENTPROC MakeCurrent; |
2628 | PFNGLXSWAPBUFFERSPROC SwapBuffers; |
2629 | PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; |
2630 | PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; |
2631 | PFNGLXCREATEWINDOWPROC CreateWindow; |
2632 | PFNGLXDESTROYWINDOWPROC DestroyWindow; |
2633 | |
2634 | // GLX 1.4 and extension functions |
2635 | PFNGLXGETPROCADDRESSPROC GetProcAddress; |
2636 | PFNGLXGETPROCADDRESSPROC GetProcAddressARB; |
2637 | PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; |
2638 | PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; |
2639 | PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; |
2640 | |
2641 | // extension availability |
2642 | bool EXT_swap_control; |
2643 | bool MESA_swap_control; |
2644 | bool ARB_multisample; |
2645 | bool ARB_create_context; |
2646 | bool ARB_create_context_profile; |
2647 | } _sapp_glx_t; |
2648 | |
2649 | #else |
2650 | |
2651 | typedef struct { |
2652 | EGLDisplay display; |
2653 | EGLContext context; |
2654 | EGLSurface surface; |
2655 | } _sapp_egl_t; |
2656 | |
2657 | #endif // _SAPP_GLX |
2658 | |
2659 | #endif // _SAPP_LINUX |
2660 | |
2661 | /*== COMMON DECLARATIONS =====================================================*/ |
2662 | |
2663 | /* helper macros */ |
2664 | #define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) |
2665 | #define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) |
2666 | |
2667 | #define _SAPP_MAX_TITLE_LENGTH (128) |
2668 | #define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) |
2669 | #define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) |
2670 | /* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ |
2671 | #define _SAPP_PIXELFORMAT_RGBA8 (23) |
2672 | #define _SAPP_PIXELFORMAT_BGRA8 (27) |
2673 | #define _SAPP_PIXELFORMAT_DEPTH (41) |
2674 | #define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) |
2675 | |
2676 | #if defined(_SAPP_MACOS) || defined(_SAPP_IOS) |
2677 | // this is ARC compatible |
2678 | #if defined(__cplusplus) |
2679 | #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } |
2680 | #else |
2681 | #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } |
2682 | #endif |
2683 | #else |
2684 | #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } |
2685 | #endif |
2686 | |
2687 | typedef struct { |
2688 | bool enabled; |
2689 | int buf_size; |
2690 | char* buffer; |
2691 | } _sapp_clipboard_t; |
2692 | |
2693 | typedef struct { |
2694 | bool enabled; |
2695 | int max_files; |
2696 | int max_path_length; |
2697 | int num_files; |
2698 | int buf_size; |
2699 | char* buffer; |
2700 | } _sapp_drop_t; |
2701 | |
2702 | typedef struct { |
2703 | float x, y; |
2704 | float dx, dy; |
2705 | bool shown; |
2706 | bool locked; |
2707 | bool pos_valid; |
2708 | sapp_mouse_cursor current_cursor; |
2709 | } _sapp_mouse_t; |
2710 | |
2711 | typedef struct { |
2712 | sapp_desc desc; |
2713 | bool valid; |
2714 | bool fullscreen; |
2715 | bool gles2_fallback; |
2716 | bool first_frame; |
2717 | bool init_called; |
2718 | bool cleanup_called; |
2719 | bool quit_requested; |
2720 | bool quit_ordered; |
2721 | bool event_consumed; |
2722 | bool html5_ask_leave_site; |
2723 | bool onscreen_keyboard_shown; |
2724 | int window_width; |
2725 | int window_height; |
2726 | int framebuffer_width; |
2727 | int framebuffer_height; |
2728 | int sample_count; |
2729 | int swap_interval; |
2730 | float dpi_scale; |
2731 | uint64_t frame_count; |
2732 | _sapp_timing_t timing; |
2733 | sapp_event event; |
2734 | _sapp_mouse_t mouse; |
2735 | _sapp_clipboard_t clipboard; |
2736 | _sapp_drop_t drop; |
2737 | sapp_icon_desc default_icon_desc; |
2738 | uint32_t* default_icon_pixels; |
2739 | #if defined(_SAPP_MACOS) |
2740 | _sapp_macos_t macos; |
2741 | #elif defined(_SAPP_IOS) |
2742 | _sapp_ios_t ios; |
2743 | #elif defined(_SAPP_EMSCRIPTEN) |
2744 | _sapp_emsc_t emsc; |
2745 | #elif defined(_SAPP_WIN32) |
2746 | _sapp_win32_t win32; |
2747 | #if defined(SOKOL_D3D11) |
2748 | _sapp_d3d11_t d3d11; |
2749 | #elif defined(SOKOL_GLCORE33) |
2750 | _sapp_wgl_t wgl; |
2751 | #endif |
2752 | #elif defined(_SAPP_UWP) |
2753 | _sapp_uwp_t uwp; |
2754 | #if defined(SOKOL_D3D11) |
2755 | _sapp_d3d11_t d3d11; |
2756 | #endif |
2757 | #elif defined(_SAPP_ANDROID) |
2758 | _sapp_android_t android; |
2759 | #elif defined(_SAPP_LINUX) |
2760 | _sapp_x11_t x11; |
2761 | #if defined(_SAPP_GLX) |
2762 | _sapp_glx_t glx; |
2763 | #else |
2764 | _sapp_egl_t egl; |
2765 | #endif |
2766 | #endif |
2767 | char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; |
2768 | char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ |
2769 | wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ |
2770 | sapp_keycode keycodes[SAPP_MAX_KEYCODES]; |
2771 | // __v_ start |
2772 | bool __v_native_render; /* V patch to allow for native rendering */ |
2773 | // __v_ end |
2774 | } _sapp_t; |
2775 | static _sapp_t _sapp; |
2776 | |
2777 | /*=== PRIVATE HELPER FUNCTIONS ===============================================*/ |
2778 | _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { |
2779 | SOKOL_ASSERT(ptr && (size > 0)); |
2780 | memset(ptr, 0, size); |
2781 | } |
2782 | |
2783 | _SOKOL_PRIVATE void* _sapp_malloc(size_t size) { |
2784 | SOKOL_ASSERT(size > 0); |
2785 | void* ptr; |
2786 | if (_sapp.desc.allocator.alloc) { |
2787 | ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data); |
2788 | } |
2789 | else { |
2790 | ptr = malloc(size); |
2791 | } |
2792 | SOKOL_ASSERT(ptr); |
2793 | return ptr; |
2794 | } |
2795 | |
2796 | _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) { |
2797 | void* ptr = _sapp_malloc(size); |
2798 | _sapp_clear(ptr, size); |
2799 | return ptr; |
2800 | } |
2801 | |
2802 | _SOKOL_PRIVATE void _sapp_free(void* ptr) { |
2803 | if (_sapp.desc.allocator.free) { |
2804 | _sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data); |
2805 | } |
2806 | else { |
2807 | free(ptr); |
2808 | } |
2809 | } |
2810 | |
2811 | #if defined(SOKOL_DEBUG) |
2812 | _SOKOL_PRIVATE void _sapp_log(const char* msg) { |
2813 | SOKOL_ASSERT(msg); |
2814 | if (_sapp.desc.logger.log_cb) { |
2815 | _sapp.desc.logger.log_cb(msg, _sapp.desc.logger.user_data); |
2816 | } |
2817 | else { |
2818 | SOKOL_LOG(msg); |
2819 | } |
2820 | } |
2821 | #endif |
2822 | |
2823 | _SOKOL_PRIVATE void _sapp_fail(const char* msg) { |
2824 | if (_sapp.desc.fail_cb) { |
2825 | _sapp.desc.fail_cb(msg); |
2826 | } |
2827 | else if (_sapp.desc.fail_userdata_cb) { |
2828 | _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); |
2829 | } |
2830 | else { |
2831 | SAPP_LOG(msg); |
2832 | } |
2833 | SOKOL_ABORT(); |
2834 | } |
2835 | |
2836 | _SOKOL_PRIVATE void _sapp_call_init(void) { |
2837 | if (_sapp.desc.init_cb) { |
2838 | _sapp.desc.init_cb(); |
2839 | } |
2840 | else if (_sapp.desc.init_userdata_cb) { |
2841 | _sapp.desc.init_userdata_cb(_sapp.desc.user_data); |
2842 | } |
2843 | _sapp.init_called = true; |
2844 | } |
2845 | |
2846 | _SOKOL_PRIVATE void _sapp_call_frame(void) { |
2847 | // __v_ start |
2848 | if (_sapp.__v_native_render) { |
2849 | return; |
2850 | } |
2851 | // __v_ end |
2852 | if (_sapp.init_called && !_sapp.cleanup_called) { |
2853 | if (_sapp.desc.frame_cb) { |
2854 | _sapp.desc.frame_cb(); |
2855 | } |
2856 | else if (_sapp.desc.frame_userdata_cb) { |
2857 | _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); |
2858 | } |
2859 | } |
2860 | } |
2861 | |
2862 | // __v_ start |
2863 | _SOKOL_PRIVATE void _sapp_call_frame_native(void) { |
2864 | //puts("_sapp_call_frame_native()"); |
2865 | //printf("init called=%d cleanup_called=%d\n", _sapp.init_called,_sapp.cleanup_called); |
2866 | if (_sapp.init_called && !_sapp.cleanup_called) { |
2867 | if (_sapp.desc.frame_cb) { |
2868 | _sapp.desc.frame_cb(); |
2869 | } |
2870 | else if (_sapp.desc.frame_userdata_cb) { |
2871 | _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); |
2872 | } |
2873 | } |
2874 | } |
2875 | // __v_ end |
2876 | |
2877 | _SOKOL_PRIVATE void _sapp_call_cleanup(void) { |
2878 | if (!_sapp.cleanup_called) { |
2879 | if (_sapp.desc.cleanup_cb) { |
2880 | _sapp.desc.cleanup_cb(); |
2881 | } |
2882 | else if (_sapp.desc.cleanup_userdata_cb) { |
2883 | _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); |
2884 | } |
2885 | _sapp.cleanup_called = true; |
2886 | } |
2887 | } |
2888 | |
2889 | _SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { |
2890 | if (!_sapp.cleanup_called) { |
2891 | if (_sapp.desc.event_cb) { |
2892 | _sapp.desc.event_cb(e); |
2893 | } |
2894 | else if (_sapp.desc.event_userdata_cb) { |
2895 | _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); |
2896 | } |
2897 | } |
2898 | if (_sapp.event_consumed) { |
2899 | _sapp.event_consumed = false; |
2900 | return true; |
2901 | } |
2902 | else { |
2903 | return false; |
2904 | } |
2905 | } |
2906 | |
2907 | _SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { |
2908 | SOKOL_ASSERT(_sapp.drop.buffer); |
2909 | SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); |
2910 | int offset = index * _sapp.drop.max_path_length; |
2911 | SOKOL_ASSERT(offset < _sapp.drop.buf_size); |
2912 | return &_sapp.drop.buffer[offset]; |
2913 | } |
2914 | |
2915 | /* Copy a string into a fixed size buffer with guaranteed zero- |
2916 | termination. |
2917 | |
2918 | Return false if the string didn't fit into the buffer and had to be clamped. |
2919 | |
2920 | FIXME: Currently UTF-8 strings might become invalid if the string |
2921 | is clamped, because the last zero-byte might be written into |
2922 | the middle of a multi-byte sequence. |
2923 | */ |
2924 | _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { |
2925 | SOKOL_ASSERT(src && dst && (max_len > 0)); |
2926 | char* const end = &(dst[max_len-1]); |
2927 | char c = 0; |
2928 | for (int i = 0; i < max_len; i++) { |
2929 | c = *src; |
2930 | if (c != 0) { |
2931 | src++; |
2932 | } |
2933 | *dst++ = c; |
2934 | } |
2935 | /* truncated? */ |
2936 | if (c != 0) { |
2937 | *end = 0; |
2938 | return false; |
2939 | } |
2940 | else { |
2941 | return true; |
2942 | } |
2943 | } |
2944 | |
2945 | _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { |
2946 | SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); |
2947 | sapp_desc res = *desc; |
2948 | res.sample_count = _sapp_def(res.sample_count, 1); |
2949 | res.swap_interval = _sapp_def(res.swap_interval, 1); |
2950 | // NOTE: can't patch the default for gl_major_version and gl_minor_version |
2951 | // independently, because a desired version 4.0 would be patched to 4.2 |
2952 | // (or expressed differently: zero is a valid value for gl_minor_version |
2953 | // and can't be used to indicate 'default') |
2954 | if (0 == res.gl_major_version) { |
2955 | res.gl_major_version = 3; |
2956 | res.gl_minor_version = 2; |
2957 | } |
2958 | res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas"); |
2959 | res.clipboard_size = _sapp_def(res.clipboard_size, 8192); |
2960 | res.max_dropped_files = _sapp_def(res.max_dropped_files, 1); |
2961 | res.max_dropped_file_path_length = _sapp_def(res.max_dropped_file_path_length, 2048); |
2962 | res.window_title = _sapp_def(res.window_title, "sokol_app"); |
2963 | return res; |
2964 | } |
2965 | |
2966 | _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { |
2967 | SOKOL_ASSERT(desc); |
2968 | SOKOL_ASSERT(desc->width >= 0); |
2969 | SOKOL_ASSERT(desc->height >= 0); |
2970 | SOKOL_ASSERT(desc->sample_count >= 0); |
2971 | SOKOL_ASSERT(desc->swap_interval >= 0); |
2972 | SOKOL_ASSERT(desc->clipboard_size >= 0); |
2973 | SOKOL_ASSERT(desc->max_dropped_files >= 0); |
2974 | SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0); |
2975 | _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); |
2976 | _sapp.desc = _sapp_desc_defaults(desc); |
2977 | _sapp.first_frame = true; |
2978 | // NOTE: _sapp.desc.width/height may be 0! Platform backends need to deal with this |
2979 | _sapp.window_width = _sapp.desc.width; |
2980 | _sapp.window_height = _sapp.desc.height; |
2981 | _sapp.framebuffer_width = _sapp.window_width; |
2982 | _sapp.framebuffer_height = _sapp.window_height; |
2983 | _sapp.sample_count = _sapp.desc.sample_count; |
2984 | _sapp.swap_interval = _sapp.desc.swap_interval; |
2985 | _sapp.html5_canvas_selector[0] = '#'; |
2986 | _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); |
2987 | _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; |
2988 | _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; |
2989 | _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; |
2990 | if (_sapp.clipboard.enabled) { |
2991 | _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; |
2992 | _sapp.clipboard.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.clipboard.buf_size); |
2993 | } |
2994 | _sapp.drop.enabled = _sapp.desc.enable_dragndrop; |
2995 | if (_sapp.drop.enabled) { |
2996 | _sapp.drop.max_files = _sapp.desc.max_dropped_files; |
2997 | _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; |
2998 | _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; |
2999 | _sapp.drop.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.drop.buf_size); |
3000 | } |
3001 | _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); |
3002 | _sapp.desc.window_title = _sapp.window_title; |
3003 | _sapp.dpi_scale = 1.0f; |
3004 | _sapp.fullscreen = _sapp.desc.fullscreen; |
3005 | _sapp.mouse.shown = true; |
3006 | _sapp_timing_init(&_sapp.timing); |
3007 | // __v_ start |
3008 | _sapp.__v_native_render = _sapp.desc.__v_native_render; |
3009 | // __v_end |
3010 | } |
3011 | |
3012 | _SOKOL_PRIVATE void _sapp_discard_state(void) { |
3013 | if (_sapp.clipboard.enabled) { |
3014 | SOKOL_ASSERT(_sapp.clipboard.buffer); |
3015 | _sapp_free((void*)_sapp.clipboard.buffer); |
3016 | } |
3017 | if (_sapp.drop.enabled) { |
3018 | SOKOL_ASSERT(_sapp.drop.buffer); |
3019 | _sapp_free((void*)_sapp.drop.buffer); |
3020 | } |
3021 | if (_sapp.default_icon_pixels) { |
3022 | _sapp_free((void*)_sapp.default_icon_pixels); |
3023 | } |
3024 | _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); |
3025 | } |
3026 | |
3027 | _SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { |
3028 | _sapp_clear(&_sapp.event, sizeof(_sapp.event)); |
3029 | _sapp.event.type = type; |
3030 | _sapp.event.frame_count = _sapp.frame_count; |
3031 | _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; |
3032 | _sapp.event.window_width = _sapp.window_width; |
3033 | _sapp.event.window_height = _sapp.window_height; |
3034 | _sapp.event.framebuffer_width = _sapp.framebuffer_width; |
3035 | _sapp.event.framebuffer_height = _sapp.framebuffer_height; |
3036 | _sapp.event.mouse_x = _sapp.mouse.x; |
3037 | _sapp.event.mouse_y = _sapp.mouse.y; |
3038 | _sapp.event.mouse_dx = _sapp.mouse.dx; |
3039 | _sapp.event.mouse_dy = _sapp.mouse.dy; |
3040 | } |
3041 | |
3042 | _SOKOL_PRIVATE bool _sapp_events_enabled(void) { |
3043 | /* only send events when an event callback is set, and the init function was called */ |
3044 | return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; |
3045 | } |
3046 | |
3047 | _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { |
3048 | if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { |
3049 | return _sapp.keycodes[scan_code]; |
3050 | } |
3051 | else { |
3052 | return SAPP_KEYCODE_INVALID; |
3053 | } |
3054 | } |
3055 | |
3056 | _SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { |
3057 | if (_sapp.drop.enabled) { |
3058 | SOKOL_ASSERT(_sapp.drop.buffer); |
3059 | _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size); |
3060 | } |
3061 | } |
3062 | |
3063 | _SOKOL_PRIVATE void _sapp_frame(void) { |
3064 | if (_sapp.first_frame) { |
3065 | _sapp.first_frame = false; |
3066 | _sapp_call_init(); |
3067 | } |
3068 | _sapp_call_frame(); |
3069 | _sapp.frame_count++; |
3070 | } |
3071 | |
3072 | _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { |
3073 | SOKOL_ASSERT(desc->width > 0); |
3074 | SOKOL_ASSERT(desc->height > 0); |
3075 | SOKOL_ASSERT(desc->pixels.ptr != 0); |
3076 | SOKOL_ASSERT(desc->pixels.size > 0); |
3077 | const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); |
3078 | if (wh_size != desc->pixels.size) { |
3079 | SAPP_LOG("Image data size mismatch (must be width*height*4 bytes)\n"); |
3080 | return false; |
3081 | } |
3082 | return true; |
3083 | } |
3084 | |
3085 | _SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) { |
3086 | int least_diff = 0x7FFFFFFF; |
3087 | int least_index = 0; |
3088 | for (int i = 0; i < num_images; i++) { |
3089 | int diff = (image_descs[i].width * image_descs[i].height) - (width * height); |
3090 | if (diff < 0) { |
3091 | diff = -diff; |
3092 | } |
3093 | if (diff < least_diff) { |
3094 | least_diff = diff; |
3095 | least_index = i; |
3096 | } |
3097 | } |
3098 | return least_index; |
3099 | } |
3100 | |
3101 | _SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) { |
3102 | int index = 0; |
3103 | for (; index < SAPP_MAX_ICONIMAGES; index++) { |
3104 | if (0 == desc->images[index].pixels.ptr) { |
3105 | break; |
3106 | } |
3107 | } |
3108 | return index; |
3109 | } |
3110 | |
3111 | _SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) { |
3112 | SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES); |
3113 | for (int i = 0; i < num_images; i++) { |
3114 | const sapp_image_desc* img_desc = &desc->images[i]; |
3115 | if (!_sapp_image_validate(img_desc)) { |
3116 | return false; |
3117 | } |
3118 | } |
3119 | return true; |
3120 | } |
3121 | |
3122 | _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { |
3123 | SOKOL_ASSERT(0 == _sapp.default_icon_pixels); |
3124 | |
3125 | const int num_icons = 3; |
3126 | const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8! |
3127 | |
3128 | // allocate a pixel buffer for all icon pixels |
3129 | int all_num_pixels = 0; |
3130 | for (int i = 0; i < num_icons; i++) { |
3131 | all_num_pixels += icon_sizes[i] * icon_sizes[i]; |
3132 | } |
3133 | _sapp.default_icon_pixels = (uint32_t*) _sapp_malloc_clear((size_t)all_num_pixels * sizeof(uint32_t)); |
3134 | |
3135 | // initialize default_icon_desc struct |
3136 | uint32_t* dst = _sapp.default_icon_pixels; |
3137 | const uint32_t* dst_end = dst + all_num_pixels; |
3138 | (void)dst_end; // silence unused warning in release mode |
3139 | for (int i = 0; i < num_icons; i++) { |
3140 | const int dim = (int) icon_sizes[i]; |
3141 | const int num_pixels = dim * dim; |
3142 | sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i]; |
3143 | img_desc->width = dim; |
3144 | img_desc->height = dim; |
3145 | img_desc->pixels.ptr = dst; |
3146 | img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t); |
3147 | dst += num_pixels; |
3148 | } |
3149 | SOKOL_ASSERT(dst == dst_end); |
3150 | |
3151 | // Amstrad CPC font 'S' |
3152 | const uint8_t tile[8] = { |
3153 | 0x3C, |
3154 | 0x66, |
3155 | 0x60, |
3156 | 0x3C, |
3157 | 0x06, |
3158 | 0x66, |
3159 | 0x3C, |
3160 | 0x00, |
3161 | }; |
3162 | // rainbow colors |
3163 | const uint32_t colors[8] = { |
3164 | 0xFF4370FF, |
3165 | 0xFF26A7FF, |
3166 | 0xFF58EEFF, |
3167 | 0xFF57E1D4, |
3168 | 0xFF65CC9C, |
3169 | 0xFF6ABB66, |
3170 | 0xFFF5A542, |
3171 | 0xFFC2577E, |
3172 | }; |
3173 | dst = _sapp.default_icon_pixels; |
3174 | const uint32_t blank = 0x00FFFFFF; |
3175 | const uint32_t shadow = 0xFF000000; |
3176 | for (int i = 0; i < num_icons; i++) { |
3177 | const int dim = icon_sizes[i]; |
3178 | SOKOL_ASSERT((dim % 8) == 0); |
3179 | const int scale = dim / 8; |
3180 | for (int ty = 0, y = 0; ty < 8; ty++) { |
3181 | const uint32_t color = colors[ty]; |
3182 | for (int sy = 0; sy < scale; sy++, y++) { |
3183 | uint8_t bits = tile[ty]; |
3184 | for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) { |
3185 | uint32_t pixel = (0 == (bits & 0x80)) ? blank : color; |
3186 | for (int sx = 0; sx < scale; sx++, x++) { |
3187 | SOKOL_ASSERT(dst < dst_end); |
3188 | *dst++ = pixel; |
3189 | } |
3190 | } |
3191 | } |
3192 | } |
3193 | } |
3194 | SOKOL_ASSERT(dst == dst_end); |
3195 | |
3196 | // right shadow |
3197 | dst = _sapp.default_icon_pixels; |
3198 | for (int i = 0; i < num_icons; i++) { |
3199 | const int dim = icon_sizes[i]; |
3200 | for (int y = 0; y < dim; y++) { |
3201 | uint32_t prev_color = blank; |
3202 | for (int x = 0; x < dim; x++) { |
3203 | const int dst_index = y * dim + x; |
3204 | const uint32_t cur_color = dst[dst_index]; |
3205 | if ((cur_color == blank) && (prev_color != blank)) { |
3206 | dst[dst_index] = shadow; |
3207 | } |
3208 | prev_color = cur_color; |
3209 | } |
3210 | } |
3211 | dst += dim * dim; |
3212 | } |
3213 | SOKOL_ASSERT(dst == dst_end); |
3214 | |
3215 | // bottom shadow |
3216 | dst = _sapp.default_icon_pixels; |
3217 | for (int i = 0; i < num_icons; i++) { |
3218 | const int dim = icon_sizes[i]; |
3219 | for (int x = 0; x < dim; x++) { |
3220 | uint32_t prev_color = blank; |
3221 | for (int y = 0; y < dim; y++) { |
3222 | const int dst_index = y * dim + x; |
3223 | const uint32_t cur_color = dst[dst_index]; |
3224 | if ((cur_color == blank) && (prev_color != blank)) { |
3225 | dst[dst_index] = shadow; |
3226 | } |
3227 | prev_color = cur_color; |
3228 | } |
3229 | } |
3230 | dst += dim * dim; |
3231 | } |
3232 | SOKOL_ASSERT(dst == dst_end); |
3233 | } |
3234 | |
3235 | /*== MacOS/iOS ===============================================================*/ |
3236 | #if defined(_SAPP_APPLE) |
3237 | |
3238 | #if __has_feature(objc_arc) |
3239 | #define _SAPP_OBJC_RELEASE(obj) { obj = nil; } |
3240 | #else |
3241 | #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } |
3242 | #endif |
3243 | |
3244 | /*== MacOS ===================================================================*/ |
3245 | #if defined(_SAPP_MACOS) |
3246 | |
3247 | _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { |
3248 | _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; |
3249 | _sapp.keycodes[0x12] = SAPP_KEYCODE_1; |
3250 | _sapp.keycodes[0x13] = SAPP_KEYCODE_2; |
3251 | _sapp.keycodes[0x14] = SAPP_KEYCODE_3; |
3252 | _sapp.keycodes[0x15] = SAPP_KEYCODE_4; |
3253 | _sapp.keycodes[0x17] = SAPP_KEYCODE_5; |
3254 | _sapp.keycodes[0x16] = SAPP_KEYCODE_6; |
3255 | _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; |
3256 | _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; |
3257 | _sapp.keycodes[0x19] = SAPP_KEYCODE_9; |
3258 | _sapp.keycodes[0x00] = SAPP_KEYCODE_A; |
3259 | _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; |
3260 | _sapp.keycodes[0x08] = SAPP_KEYCODE_C; |
3261 | _sapp.keycodes[0x02] = SAPP_KEYCODE_D; |
3262 | _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; |
3263 | _sapp.keycodes[0x03] = SAPP_KEYCODE_F; |
3264 | _sapp.keycodes[0x05] = SAPP_KEYCODE_G; |
3265 | _sapp.keycodes[0x04] = SAPP_KEYCODE_H; |
3266 | _sapp.keycodes[0x22] = SAPP_KEYCODE_I; |
3267 | _sapp.keycodes[0x26] = SAPP_KEYCODE_J; |
3268 | _sapp.keycodes[0x28] = SAPP_KEYCODE_K; |
3269 | _sapp.keycodes[0x25] = SAPP_KEYCODE_L; |
3270 | _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; |
3271 | _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; |
3272 | _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; |
3273 | _sapp.keycodes[0x23] = SAPP_KEYCODE_P; |
3274 | _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; |
3275 | _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; |
3276 | _sapp.keycodes[0x01] = SAPP_KEYCODE_S; |
3277 | _sapp.keycodes[0x11] = SAPP_KEYCODE_T; |
3278 | _sapp.keycodes[0x20] = SAPP_KEYCODE_U; |
3279 | _sapp.keycodes[0x09] = SAPP_KEYCODE_V; |
3280 | _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; |
3281 | _sapp.keycodes[0x07] = SAPP_KEYCODE_X; |
3282 | _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; |
3283 | _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; |
3284 | _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; |
3285 | _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; |
3286 | _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; |
3287 | _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; |
3288 | _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; |
3289 | _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; |
3290 | _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; |
3291 | _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; |
3292 | _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; |
3293 | _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; |
3294 | _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; |
3295 | _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; |
3296 | _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; |
3297 | _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; |
3298 | _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; |
3299 | _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; |
3300 | _sapp.keycodes[0x77] = SAPP_KEYCODE_END; |
3301 | _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; |
3302 | _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; |
3303 | _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; |
3304 | _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; |
3305 | _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; |
3306 | _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; |
3307 | _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; |
3308 | _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; |
3309 | _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; |
3310 | _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; |
3311 | _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; |
3312 | _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; |
3313 | _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; |
3314 | _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; |
3315 | _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; |
3316 | _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; |
3317 | _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; |
3318 | _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; |
3319 | _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; |
3320 | _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; |
3321 | _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; |
3322 | _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; |
3323 | _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; |
3324 | _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; |
3325 | _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; |
3326 | _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; |
3327 | _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; |
3328 | _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; |
3329 | _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; |
3330 | _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; |
3331 | _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; |
3332 | _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; |
3333 | _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; |
3334 | _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; |
3335 | _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; |
3336 | _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; |
3337 | _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; |
3338 | _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; |
3339 | _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; |
3340 | _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; |
3341 | _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; |
3342 | _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; |
3343 | _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; |
3344 | _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; |
3345 | _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; |
3346 | _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; |
3347 | _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; |
3348 | _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; |
3349 | _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; |
3350 | _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; |
3351 | _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; |
3352 | _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; |
3353 | _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; |
3354 | _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; |
3355 | _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; |
3356 | _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; |
3357 | _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; |
3358 | _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; |
3359 | } |
3360 | |
3361 | _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { |
3362 | // NOTE: it's safe to call [release] on a nil object |
3363 | _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); |
3364 | _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); |
3365 | _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); |
3366 | _SAPP_OBJC_RELEASE(_sapp.macos.view); |
3367 | #if defined(SOKOL_METAL) |
3368 | _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); |
3369 | #endif |
3370 | _SAPP_OBJC_RELEASE(_sapp.macos.window); |
3371 | } |
3372 | |
3373 | // undocumented methods for creating cursors (see GLFW 3.4 and imgui_impl_osx.mm) |
3374 | @interface NSCursor() |
3375 | + (id)_windowResizeNorthWestSouthEastCursor; |
3376 | + (id)_windowResizeNorthEastSouthWestCursor; |
3377 | + (id)_windowResizeNorthSouthCursor; |
3378 | + (id)_windowResizeEastWestCursor; |
3379 | @end |
3380 | |
3381 | _SOKOL_PRIVATE void _sapp_macos_init_cursors(void) { |
3382 | _sapp.macos.cursors[SAPP_MOUSECURSOR_DEFAULT] = nil; // not a bug |
3383 | _sapp.macos.cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor]; |
3384 | _sapp.macos.cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor]; |
3385 | _sapp.macos.cursors[SAPP_MOUSECURSOR_CROSSHAIR] = [NSCursor crosshairCursor]; |
3386 | _sapp.macos.cursors[SAPP_MOUSECURSOR_POINTING_HAND] = [NSCursor pointingHandCursor]; |
3387 | _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_EW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; |
3388 | _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; |
3389 | _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; |
3390 | _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; |
3391 | _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_ALL] = [NSCursor closedHandCursor]; |
3392 | _sapp.macos.cursors[SAPP_MOUSECURSOR_NOT_ALLOWED] = [NSCursor operationNotAllowedCursor]; |
3393 | } |
3394 | |
3395 | _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { |
3396 | _sapp_init_state(desc); |
3397 | _sapp_macos_init_keytable(); |
3398 | [NSApplication sharedApplication]; |
3399 | // set the application dock icon as early as possible, otherwise |
3400 | // the dummy icon will be visible for a short time |
3401 | sapp_set_icon(&_sapp.desc.icon); |
3402 | _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; |
3403 | NSApp.delegate = _sapp.macos.app_dlg; |
3404 | [NSApp run]; |
3405 | // NOTE: [NSApp run] never returns, instead cleanup code |
3406 | // must be put into applicationWillTerminate |
3407 | } |
3408 | |
3409 | /* MacOS entry function */ |
3410 | #if !defined(SOKOL_NO_ENTRY) |
3411 | int main(int argc, char* argv[]) { |
3412 | sapp_desc desc = sokol_main(argc, argv); |
3413 | _sapp_macos_run(&desc); |
3414 | return 0; |
3415 | } |
3416 | #endif /* SOKOL_NO_ENTRY */ |
3417 | |
3418 | _SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { |
3419 | const NSEventModifierFlags f = ev.modifierFlags; |
3420 | const NSUInteger b = NSEvent.pressedMouseButtons; |
3421 | uint32_t m = 0; |
3422 | if (f & NSEventModifierFlagShift) { |
3423 | m |= SAPP_MODIFIER_SHIFT; |
3424 | } |
3425 | if (f & NSEventModifierFlagControl) { |
3426 | m |= SAPP_MODIFIER_CTRL; |
3427 | } |
3428 | if (f & NSEventModifierFlagOption) { |
3429 | m |= SAPP_MODIFIER_ALT; |
3430 | } |
3431 | if (f & NSEventModifierFlagCommand) { |
3432 | m |= SAPP_MODIFIER_SUPER; |
3433 | } |
3434 | if (0 != (b & (1<<0))) { |
3435 | m |= SAPP_MODIFIER_LMB; |
3436 | } |
3437 | if (0 != (b & (1<<1))) { |
3438 | m |= SAPP_MODIFIER_RMB; |
3439 | } |
3440 | if (0 != (b & (1<<2))) { |
3441 | m |= SAPP_MODIFIER_MMB; |
3442 | } |
3443 | return m; |
3444 | } |
3445 | |
3446 | _SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { |
3447 | if (_sapp_events_enabled()) { |
3448 | _sapp_init_event(type); |
3449 | _sapp.event.mouse_button = btn; |
3450 | _sapp.event.modifiers = mod; |
3451 | _sapp_call_event(&_sapp.event); |
3452 | } |
3453 | } |
3454 | |
3455 | _SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { |
3456 | if (_sapp_events_enabled()) { |
3457 | _sapp_init_event(type); |
3458 | _sapp.event.key_code = key; |
3459 | _sapp.event.key_repeat = repeat; |
3460 | _sapp.event.modifiers = mod; |
3461 | _sapp_call_event(&_sapp.event); |
3462 | } |
3463 | } |
3464 | |
3465 | _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { |
3466 | if (_sapp_events_enabled()) { |
3467 | _sapp_init_event(type); |
3468 | _sapp_call_event(&_sapp.event); |
3469 | } |
3470 | } |
3471 | |
3472 | /* NOTE: unlike the iOS version of this function, the macOS version |
3473 | can dynamically update the DPI scaling factor when a window is moved |
3474 | between HighDPI / LowDPI screens. |
3475 | */ |
3476 | _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { |
3477 | if (_sapp.desc.high_dpi) { |
3478 | _sapp.dpi_scale = [_sapp.macos.window screen].backingScaleFactor; |
3479 | } |
3480 | else { |
3481 | _sapp.dpi_scale = 1.0f; |
3482 | } |
3483 | const NSRect bounds = [_sapp.macos.view bounds]; |
3484 | _sapp.window_width = (int)roundf(bounds.size.width); |
3485 | _sapp.window_height = (int)roundf(bounds.size.height); |
3486 | #if defined(SOKOL_METAL) |
3487 | _sapp.framebuffer_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); |
3488 | _sapp.framebuffer_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); |
3489 | const CGSize fb_size = _sapp.macos.view.drawableSize; |
3490 | const int cur_fb_width = (int)roundf(fb_size.width); |
3491 | const int cur_fb_height = (int)roundf(fb_size.height); |
3492 | const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || |
3493 | (_sapp.framebuffer_height != cur_fb_height); |
3494 | #elif defined(SOKOL_GLCORE33) |
3495 | const int cur_fb_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); |
3496 | const int cur_fb_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); |
3497 | const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || |
3498 | (_sapp.framebuffer_height != cur_fb_height); |
3499 | _sapp.framebuffer_width = cur_fb_width; |
3500 | _sapp.framebuffer_height = cur_fb_height; |
3501 | #endif |
3502 | if (_sapp.framebuffer_width == 0) { |
3503 | _sapp.framebuffer_width = 1; |
3504 | } |
3505 | if (_sapp.framebuffer_height == 0) { |
3506 | _sapp.framebuffer_height = 1; |
3507 | } |
3508 | if (_sapp.window_width == 0) { |
3509 | _sapp.window_width = 1; |
3510 | } |
3511 | if (_sapp.window_height == 0) { |
3512 | _sapp.window_height = 1; |
3513 | } |
3514 | if (dim_changed) { |
3515 | #if defined(SOKOL_METAL) |
3516 | CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; |
3517 | _sapp.macos.view.drawableSize = drawable_size; |
3518 | #else |
3519 | // nothing to do for GL? |
3520 | #endif |
3521 | if (!_sapp.first_frame) { |
3522 | _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); |
3523 | } |
3524 | } |
3525 | } |
3526 | |
3527 | _SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { |
3528 | /* NOTE: the _sapp.fullscreen flag is also notified by the |
3529 | windowDidEnterFullscreen / windowDidExitFullscreen |
3530 | event handlers |
3531 | */ |
3532 | _sapp.fullscreen = !_sapp.fullscreen; |
3533 | [_sapp.macos.window toggleFullScreen:nil]; |
3534 | } |
3535 | |
3536 | _SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { |
3537 | @autoreleasepool { |
3538 | NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; |
3539 | [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; |
3540 | [pasteboard setString:@(str) forType:NSPasteboardTypeString]; |
3541 | } |
3542 | } |
3543 | |
3544 | _SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { |
3545 | SOKOL_ASSERT(_sapp.clipboard.buffer); |
3546 | @autoreleasepool { |
3547 | _sapp.clipboard.buffer[0] = 0; |
3548 | NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; |
3549 | if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { |
3550 | return _sapp.clipboard.buffer; |
3551 | } |
3552 | NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; |
3553 | if (!str) { |
3554 | return _sapp.clipboard.buffer; |
3555 | } |
3556 | _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); |
3557 | } |
3558 | return _sapp.clipboard.buffer; |
3559 | } |
3560 | |
3561 | _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { |
3562 | [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; |
3563 | } |
3564 | |
3565 | _SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { |
3566 | if (!_sapp.mouse.locked) { |
3567 | const NSPoint mouse_pos = event.locationInWindow; |
3568 | float new_x = mouse_pos.x * _sapp.dpi_scale; |
3569 | float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; |
3570 | /* don't update dx/dy in the very first update */ |
3571 | if (_sapp.mouse.pos_valid) { |
3572 | _sapp.mouse.dx = new_x - _sapp.mouse.x; |
3573 | _sapp.mouse.dy = new_y - _sapp.mouse.y; |
3574 | } |
3575 | _sapp.mouse.x = new_x; |
3576 | _sapp.mouse.y = new_y; |
3577 | _sapp.mouse.pos_valid = true; |
3578 | } |
3579 | } |
3580 | |
3581 | _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { |
3582 | /* NOTE: this function is only called when the mouse visibility actually changes */ |
3583 | if (visible) { |
3584 | CGDisplayShowCursor(kCGDirectMainDisplay); |
3585 | } |
3586 | else { |
3587 | CGDisplayHideCursor(kCGDirectMainDisplay); |
3588 | } |
3589 | } |
3590 | |
3591 | _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { |
3592 | if (lock == _sapp.mouse.locked) { |
3593 | return; |
3594 | } |
3595 | _sapp.mouse.dx = 0.0f; |
3596 | _sapp.mouse.dy = 0.0f; |
3597 | _sapp.mouse.locked = lock; |
3598 | /* |
3599 | NOTE that this code doesn't warp the mouse cursor to the window |
3600 | center as everybody else does it. This lead to a spike in the |
3601 | *second* mouse-moved event after the warp happened. The |
3602 | mouse centering doesn't seem to be required (mouse-moved events |
3603 | are reported correctly even when the cursor is at an edge of the screen). |
3604 | |
3605 | NOTE also that the hide/show of the mouse cursor should properly |
3606 | stack with calls to sapp_show_mouse() |
3607 | */ |
3608 | if (_sapp.mouse.locked) { |
3609 | CGAssociateMouseAndMouseCursorPosition(NO); |
3610 | [NSCursor hide]; |
3611 | } |
3612 | else { |
3613 | [NSCursor unhide]; |
3614 | CGAssociateMouseAndMouseCursorPosition(YES); |
3615 | } |
3616 | } |
3617 | |
3618 | _SOKOL_PRIVATE void _sapp_macos_update_cursor(sapp_mouse_cursor cursor, bool shown) { |
3619 | // show/hide cursor only if visibility status has changed (required because show/hide stacks) |
3620 | if (shown != _sapp.mouse.shown) { |
3621 | if (shown) { |
3622 | [NSCursor unhide]; |
3623 | } |
3624 | else { |
3625 | [NSCursor hide]; |
3626 | } |
3627 | } |
3628 | // update cursor type |
3629 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
3630 | if (_sapp.macos.cursors[cursor]) { |
3631 | [_sapp.macos.cursors[cursor] set]; |
3632 | } |
3633 | else { |
3634 | [[NSCursor arrowCursor] set]; |
3635 | } |
3636 | } |
3637 | |
3638 | _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) { |
3639 | NSDockTile* dock_tile = NSApp.dockTile; |
3640 | const int wanted_width = (int) dock_tile.size.width; |
3641 | const int wanted_height = (int) dock_tile.size.height; |
3642 | const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height); |
3643 | const sapp_image_desc* img_desc = &icon_desc->images[img_index]; |
3644 | |
3645 | CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB(); |
3646 | CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size); |
3647 | CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data); |
3648 | CGImageRef cg_img = CGImageCreate( |
3649 | (size_t)img_desc->width, // width |
3650 | (size_t)img_desc->height, // height |
3651 | 8, // bitsPerComponent |
3652 | 32, // bitsPerPixel |
3653 | (size_t)img_desc->width * 4,// bytesPerRow |
3654 | cg_color_space, // space |
3655 | kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo |
3656 | cg_data_provider, // provider |
3657 | NULL, // decode |
3658 | false, // shouldInterpolate |
3659 | kCGRenderingIntentDefault); |
3660 | CFRelease(cf_data); |
3661 | CGDataProviderRelease(cg_data_provider); |
3662 | CGColorSpaceRelease(cg_color_space); |
3663 | |
3664 | NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size]; |
3665 | dock_tile.contentView = [NSImageView imageViewWithImage:ns_image]; |
3666 | [dock_tile display]; |
3667 | _SAPP_OBJC_RELEASE(ns_image); |
3668 | CGImageRelease(cg_img); |
3669 | } |
3670 | |
3671 | _SOKOL_PRIVATE void _sapp_macos_frame(void) { |
3672 | _sapp_frame(); |
3673 | if (_sapp.quit_requested || _sapp.quit_ordered) { |
3674 | [_sapp.macos.window performClose:nil]; |
3675 | } |
3676 | } |
3677 | |
3678 | // __v_ start |
3679 | #include "sokol_app2.h" // __v_ |
3680 | // __v_ end |
3681 | |
3682 | @implementation _sapp_macos_app_delegate |
3683 | - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { |
3684 | _SOKOL_UNUSED(aNotification); |
3685 | _sapp_macos_init_cursors(); |
3686 | if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) { |
3687 | // use 4/5 of screen size as default size |
3688 | NSRect screen_rect = NSScreen.mainScreen.frame; |
3689 | if (_sapp.window_width == 0) { |
3690 | _sapp.window_width = (int)roundf((screen_rect.size.width * 4.0f) / 5.0f); |
3691 | } |
3692 | if (_sapp.window_height == 0) { |
3693 | _sapp.window_height = (int)roundf((screen_rect.size.height * 4.0f) / 5.0f); |
3694 | } |
3695 | } |
3696 | const NSUInteger style = |
3697 | // __v_ start |
3698 | _sapp.desc.fullscreen ? NSWindowStyleMaskBorderless : // __v_ |
3699 | // __v_ end |
3700 | NSWindowStyleMaskTitled | |
3701 | NSWindowStyleMaskClosable | |
3702 | NSWindowStyleMaskMiniaturizable | |
3703 | NSWindowStyleMaskResizable; |
3704 | NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); |
3705 | _sapp.macos.window = [[_sapp_macos_window alloc] |
3706 | initWithContentRect:window_rect |
3707 | styleMask:style |
3708 | backing:NSBackingStoreBuffered |
3709 | defer:NO]; |
3710 | _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate |
3711 | _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; |
3712 | _sapp.macos.window.acceptsMouseMovedEvents = YES; |
3713 | _sapp.macos.window.restorable = YES; |
3714 | |
3715 | // __v_ start |
3716 | // Quit menu |
3717 | NSMenu* menu_bar = [[NSMenu alloc] init]; |
3718 | NSMenuItem* app_menu_item = [[NSMenuItem alloc] init]; |
3719 | [menu_bar addItem:app_menu_item]; |
3720 | NSApp.mainMenu = menu_bar; |
3721 | NSMenu* app_menu = [[NSMenu alloc] init]; |
3722 | NSString* window_title_as_nsstring = [NSString stringWithUTF8String:_sapp.window_title]; |
3723 | // `quit_title` memory will be owned by the NSMenuItem, so no need to release it ourselves |
3724 | NSString* quit_title = [@"Quit " stringByAppendingString:window_title_as_nsstring]; |
3725 | NSMenuItem* quit_menu_item = [[NSMenuItem alloc] |
3726 | initWithTitle:quit_title |
3727 | action:@selector(terminate:) |
3728 | keyEquivalent:@"q"]; |
3729 | [app_menu addItem:quit_menu_item]; |
3730 | app_menu_item.submenu = app_menu; |
3731 | _SAPP_OBJC_RELEASE( window_title_as_nsstring ); |
3732 | _SAPP_OBJC_RELEASE( app_menu ); |
3733 | _SAPP_OBJC_RELEASE( app_menu_item ); |
3734 | _SAPP_OBJC_RELEASE( menu_bar ); |
3735 | // __v_ end |
3736 | |
3737 | _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; |
3738 | _sapp.macos.window.delegate = _sapp.macos.win_dlg; |
3739 | #if defined(SOKOL_METAL) |
3740 | NSInteger max_fps = 60; |
3741 | #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) |
3742 | if (@available(macOS 12.0, *)) { |
3743 | max_fps = [NSScreen.mainScreen maximumFramesPerSecond]; |
3744 | } |
3745 | #endif |
3746 | _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); |
3747 | _sapp.macos.view = [[_sapp_macos_view alloc] init]; |
3748 | [_sapp.macos.view updateTrackingAreas]; |
3749 | _sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; |
3750 | _sapp.macos.view.device = _sapp.macos.mtl_device; |
3751 | _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; |
3752 | _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; |
3753 | _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count; |
3754 | _sapp.macos.view.autoResizeDrawable = false; |
3755 | _sapp.macos.window.contentView = _sapp.macos.view; |
3756 | [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; |
3757 | _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; |
3758 | #elif defined(SOKOL_GLCORE33) |
3759 | NSOpenGLPixelFormatAttribute attrs[32]; |
3760 | int i = 0; |
3761 | attrs[i++] = NSOpenGLPFAAccelerated; |
3762 | attrs[i++] = NSOpenGLPFADoubleBuffer; |
3763 | attrs[i++] = NSOpenGLPFAOpenGLProfile; |
3764 | const int glVersion = _sapp.desc.gl_major_version * 10 + _sapp.desc.gl_minor_version; |
3765 | switch(glVersion) { |
3766 | case 10: attrs[i++] = NSOpenGLProfileVersionLegacy; break; |
3767 | case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break; |
3768 | case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break; |
3769 | default: |
3770 | _sapp_fail("Invalid NSOpenGLProfile (valid choices are 1.0, 3.2, and 4.1)\n"); |
3771 | } |
3772 | attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; |
3773 | attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; |
3774 | attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; |
3775 | attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; |
3776 | if (_sapp.sample_count > 1) { |
3777 | attrs[i++] = NSOpenGLPFAMultisample; |
3778 | attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; |
3779 | attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count; |
3780 | } |
3781 | else { |
3782 | attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; |
3783 | } |
3784 | attrs[i++] = 0; |
3785 | NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; |
3786 | SOKOL_ASSERT(glpixelformat_obj != nil); |
3787 | |
3788 | _sapp.macos.view = [[_sapp_macos_view alloc] |
3789 | initWithFrame:window_rect |
3790 | pixelFormat:glpixelformat_obj]; |
3791 | _SAPP_OBJC_RELEASE(glpixelformat_obj); |
3792 | [_sapp.macos.view updateTrackingAreas]; |
3793 | if (_sapp.desc.high_dpi) { |
3794 | [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; |
3795 | } |
3796 | else { |
3797 | [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; |
3798 | } |
3799 | |
3800 | _sapp.macos.window.contentView = _sapp.macos.view; |
3801 | [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; |
3802 | |
3803 | NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 |
3804 | target:_sapp.macos.view |
3805 | selector:@selector(timerFired:) |
3806 | userInfo:nil |
3807 | repeats:YES]; |
3808 | [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; |
3809 | timer_obj = nil; |
3810 | #endif |
3811 | [_sapp.macos.window center]; |
3812 | _sapp.valid = true; |
3813 | |
3814 | |
3815 | // __v_ start |
3816 | if (!_sapp.__v_native_render) { // __v_ |
3817 | |
3818 | if (_sapp.fullscreen) { |
3819 | /* ^^^ on GL, this already toggles a rendered frame, so set the valid flag before */ |
3820 | [_sapp.macos.window toggleFullScreen:self]; |
3821 | } |
3822 | |
3823 | else { |
3824 | [_sapp.macos.window center]; |
3825 | } |
3826 | } // __v_ |
3827 | // __v_ end |
3828 | |
3829 | |
3830 | NSApp.activationPolicy = NSApplicationActivationPolicyRegular; |
3831 | [NSApp activateIgnoringOtherApps:YES]; |
3832 | |
3833 | |
3834 | |
3835 | // __v start |
3836 | /////////////////////////////////////////////////////// |
3837 | // Create a child view for native rendering |
3838 | if (_sapp.__v_native_render) { |
3839 | |
3840 | CGRect wRect = _sapp.macos.window.frame; |
3841 | NSView *contentView =_sapp.macos.window.contentView; |
3842 | CGRect cRect = contentView.frame; |
3843 | |
3844 | CGRect rect = CGRectMake(wRect.origin.x, wRect.origin.y, cRect.size.width, cRect.size.height); |
3845 | NSWindow *overlayWindow = [[NSWindow alloc]initWithContentRect:rect |
3846 | styleMask:NSBorderlessWindowMask |
3847 | backing:NSBackingStoreBuffered |
3848 | defer:NO]; |
3849 | //overlayWindow.backgroundColor = [NSColor whiteColor]; |
3850 | |
3851 | //overlayWindow.backgroundColor = [[NSColor whiteColor] colorWithAlphaComponent:0]; |
3852 | [overlayWindow setOpaque:YES]; |
3853 | [_sapp.macos.window setIgnoresMouseEvents:NO]; |
3854 | g_view = [[MyView2 alloc] init]; |
3855 | overlayWindow.contentView = g_view; |
3856 | |
3857 | [ contentView addSubview:g_view]; |
3858 | //[ _sapp.macos.window addChildWindow:overlayWindow ordered:NSWindowAbove]; |
3859 | [_sapp.macos.window center]; |
3860 | } |
3861 | ////////////////////////////////// |
3862 | // __v_ end |
3863 | |
3864 | |
3865 | |
3866 | [_sapp.macos.window makeKeyAndOrderFront:nil]; |
3867 | _sapp_macos_update_dimensions(); |
3868 | |
3869 | // __v_ start |
3870 | // [NSEvent setMouseCoalescingEnabled:NO]; |
3871 | // __v_ end |
3872 | |
3873 | } |
3874 | |
3875 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { |
3876 | _SOKOL_UNUSED(sender); |
3877 | return YES; |
3878 | } |
3879 | |
3880 | - (void)applicationWillTerminate:(NSNotification*)notification { |
3881 | _SOKOL_UNUSED(notification); |
3882 | _sapp_call_cleanup(); |
3883 | _sapp_macos_discard_state(); |
3884 | _sapp_discard_state(); |
3885 | } |
3886 | @end |
3887 | |
3888 | @implementation _sapp_macos_window_delegate |
3889 | - (BOOL)windowShouldClose:(id)sender { |
3890 | _SOKOL_UNUSED(sender); |
3891 | /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ |
3892 | if (!_sapp.quit_ordered) { |
3893 | /* if window should be closed and event handling is enabled, give user code |
3894 | a chance to intervene via sapp_cancel_quit() |
3895 | */ |
3896 | _sapp.quit_requested = true; |
3897 | _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); |
3898 | /* user code hasn't intervened, quit the app */ |
3899 | if (_sapp.quit_requested) { |
3900 | _sapp.quit_ordered = true; |
3901 | } |
3902 | } |
3903 | if (_sapp.quit_ordered) { |
3904 | return YES; |
3905 | } |
3906 | else { |
3907 | return NO; |
3908 | } |
3909 | } |
3910 | |
3911 | - (void)windowDidResize:(NSNotification*)notification { |
3912 | _SOKOL_UNUSED(notification); |
3913 | _sapp_macos_update_dimensions(); |
3914 | } |
3915 | |
3916 | - (void)windowDidChangeScreen:(NSNotification*)notification { |
3917 | _SOKOL_UNUSED(notification); |
3918 | _sapp_timing_reset(&_sapp.timing); |
3919 | _sapp_macos_update_dimensions(); |
3920 | } |
3921 | |
3922 | - (void)windowDidMiniaturize:(NSNotification*)notification { |
3923 | _SOKOL_UNUSED(notification); |
3924 | _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); |
3925 | } |
3926 | |
3927 | - (void)windowDidDeminiaturize:(NSNotification*)notification { |
3928 | _SOKOL_UNUSED(notification); |
3929 | _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); |
3930 | } |
3931 | |
3932 | - (void)windowDidBecomeKey:(NSNotification*)notification { |
3933 | _SOKOL_UNUSED(notification); |
3934 | _sapp_macos_app_event(SAPP_EVENTTYPE_FOCUSED); |
3935 | } |
3936 | |
3937 | - (void)windowDidResignKey:(NSNotification*)notification { |
3938 | _SOKOL_UNUSED(notification); |
3939 | _sapp_macos_app_event(SAPP_EVENTTYPE_UNFOCUSED); |
3940 | } |
3941 | |
3942 | - (void)windowDidEnterFullScreen:(NSNotification*)notification { |
3943 | _SOKOL_UNUSED(notification); |
3944 | _sapp.fullscreen = true; |
3945 | } |
3946 | |
3947 | - (void)windowDidExitFullScreen:(NSNotification*)notification { |
3948 | _SOKOL_UNUSED(notification); |
3949 | _sapp.fullscreen = false; |
3950 | } |
3951 | @end |
3952 | |
3953 | @implementation _sapp_macos_window |
3954 | |
3955 | // __v_ start |
3956 | - (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless |
3957 | - (BOOL)canBecomeMainWindow { return YES; } |
3958 | // __v_ end |
3959 | |
3960 | - (instancetype)initWithContentRect:(NSRect)contentRect |
3961 | styleMask:(NSWindowStyleMask)style |
3962 | backing:(NSBackingStoreType)backingStoreType |
3963 | defer:(BOOL)flag { |
3964 | if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { |
3965 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 |
3966 | [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; |
3967 | #endif |
3968 | } |
3969 | return self; |
3970 | } |
3971 | |
3972 | - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { |
3973 | return NSDragOperationCopy; |
3974 | } |
3975 | |
3976 | - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { |
3977 | return NSDragOperationCopy; |
3978 | } |
3979 | |
3980 | - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { |
3981 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 |
3982 | NSPasteboard *pboard = [sender draggingPasteboard]; |
3983 | if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { |
3984 | _sapp_clear_drop_buffer(); |
3985 | _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : (int)pboard.pasteboardItems.count; |
3986 | bool drop_failed = false; |
3987 | for (int i = 0; i < _sapp.drop.num_files; i++) { |
3988 | NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; |
3989 | if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { |
3990 | SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); |
3991 | drop_failed = true; |
3992 | break; |
3993 | } |
3994 | } |
3995 | if (!drop_failed) { |
3996 | if (_sapp_events_enabled()) { |
3997 | _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); |
3998 | _sapp_call_event(&_sapp.event); |
3999 | } |
4000 | } |
4001 | else { |
4002 | _sapp_clear_drop_buffer(); |
4003 | _sapp.drop.num_files = 0; |
4004 | } |
4005 | return YES; |
4006 | } |
4007 | #endif |
4008 | return NO; |
4009 | } |
4010 | @end |
4011 | |
4012 | @implementation _sapp_macos_view |
4013 | #if defined(SOKOL_GLCORE33) |
4014 | /* NOTE: this is a hack/fix when the initial window size has been clipped by |
4015 | macOS because it didn't fit on the screen, in that case the |
4016 | frame size of the window is reported wrong if low-dpi rendering |
4017 | was requested (instead the high-dpi dimensions are returned) |
4018 | until the window is resized for the first time. |
4019 | |
4020 | Hooking into reshape and getting the frame dimensions seems to report |
4021 | the correct dimensions. |
4022 | */ |
4023 | - (void)reshape { |
4024 | _sapp_macos_update_dimensions(); |
4025 | [super reshape]; |
4026 | } |
4027 | - (void)timerFired:(id)sender { |
4028 | _SOKOL_UNUSED(sender); |
4029 | [self setNeedsDisplay:YES]; |
4030 | } |
4031 | - (void)prepareOpenGL { |
4032 | [super prepareOpenGL]; |
4033 | GLint swapInt = 1; |
4034 | NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; |
4035 | [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; |
4036 | [ctx makeCurrentContext]; |
4037 | } |
4038 | #endif |
4039 | |
4040 | _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { |
4041 | /* |
4042 | |
4043 | NOTE: late event polling temporarily out-commented to check if this |
4044 | causes infrequent and almost impossible to reproduce probelms with the |
4045 | window close events, see: |
4046 | https://github.com/floooh/sokol/pull/483#issuecomment-805148815 |
4047 | |
4048 | |
4049 | const NSEventMask mask = NSEventMaskLeftMouseDown | |
4050 | NSEventMaskLeftMouseUp| |
4051 | NSEventMaskRightMouseDown | |
4052 | NSEventMaskRightMouseUp | |
4053 | NSEventMaskMouseMoved | |
4054 | NSEventMaskLeftMouseDragged | |
4055 | NSEventMaskRightMouseDragged | |
4056 | NSEventMaskMouseEntered | |
4057 | NSEventMaskMouseExited | |
4058 | NSEventMaskKeyDown | |
4059 | NSEventMaskKeyUp | |
4060 | NSEventMaskCursorUpdate | |
4061 | NSEventMaskScrollWheel | |
4062 | NSEventMaskTabletPoint | |
4063 | NSEventMaskTabletProximity | |
4064 | NSEventMaskOtherMouseDown | |
4065 | NSEventMaskOtherMouseUp | |
4066 | NSEventMaskOtherMouseDragged | |
4067 | NSEventMaskPressure | |
4068 | NSEventMaskDirectTouch; |
4069 | @autoreleasepool { |
4070 | for (;;) { |
4071 | // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend, |
4072 | // see: https://github.com/floooh/sokol/issues/486 |
4073 | NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES]; |
4074 | if (event == nil) { |
4075 | break; |
4076 | } |
4077 | [NSApp sendEvent:event]; |
4078 | } |
4079 | } |
4080 | */ |
4081 | } |
4082 | |
4083 | - (void)drawRect:(NSRect)rect { |
4084 | _SOKOL_UNUSED(rect); |
4085 | _sapp_timing_measure(&_sapp.timing); |
4086 | /* Catch any last-moment input events */ |
4087 | _sapp_macos_poll_input_events(); |
4088 | @autoreleasepool { |
4089 | _sapp_macos_frame(); |
4090 | } |
4091 | #if !defined(SOKOL_METAL) |
4092 | [[_sapp.macos.view openGLContext] flushBuffer]; |
4093 | #endif |
4094 | } |
4095 | |
4096 | - (BOOL)isOpaque { |
4097 | return YES; |
4098 | } |
4099 | - (BOOL)canBecomeKeyView { |
4100 | return YES; |
4101 | } |
4102 | - (BOOL)acceptsFirstResponder { |
4103 | return YES; |
4104 | } |
4105 | - (void)updateTrackingAreas { |
4106 | if (_sapp.macos.tracking_area != nil) { |
4107 | [self removeTrackingArea:_sapp.macos.tracking_area]; |
4108 | _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); |
4109 | } |
4110 | const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | |
4111 | NSTrackingActiveInKeyWindow | |
4112 | NSTrackingEnabledDuringMouseDrag | |
4113 | NSTrackingCursorUpdate | |
4114 | NSTrackingInVisibleRect | |
4115 | NSTrackingAssumeInside; |
4116 | _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; |
4117 | [self addTrackingArea:_sapp.macos.tracking_area]; |
4118 | [super updateTrackingAreas]; |
4119 | } |
4120 | - (void)mouseEntered:(NSEvent*)event { |
4121 | _sapp_macos_update_mouse(event); |
4122 | /* don't send mouse enter/leave while dragging (so that it behaves the same as |
4123 | on Windows while SetCapture is active |
4124 | */ |
4125 | if (0 == _sapp.macos.mouse_buttons) { |
4126 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); |
4127 | } |
4128 | } |
4129 | - (void)mouseExited:(NSEvent*)event { |
4130 | _sapp_macos_update_mouse(event); |
4131 | if (0 == _sapp.macos.mouse_buttons) { |
4132 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); |
4133 | } |
4134 | } |
4135 | - (void)mouseDown:(NSEvent*)event { |
4136 | _sapp_macos_update_mouse(event); |
4137 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); |
4138 | _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT); |
4139 | } |
4140 | - (void)mouseUp:(NSEvent*)event { |
4141 | _sapp_macos_update_mouse(event); |
4142 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); |
4143 | _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT); |
4144 | } |
4145 | - (void)rightMouseDown:(NSEvent*)event { |
4146 | _sapp_macos_update_mouse(event); |
4147 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event)); |
4148 | _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT); |
4149 | } |
4150 | - (void)rightMouseUp:(NSEvent*)event { |
4151 | _sapp_macos_update_mouse(event); |
4152 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event)); |
4153 | _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT); |
4154 | } |
4155 | - (void)otherMouseDown:(NSEvent*)event { |
4156 | _sapp_macos_update_mouse(event); |
4157 | if (2 == event.buttonNumber) { |
4158 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event)); |
4159 | _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE); |
4160 | } |
4161 | } |
4162 | - (void)otherMouseUp:(NSEvent*)event { |
4163 | _sapp_macos_update_mouse(event); |
4164 | if (2 == event.buttonNumber) { |
4165 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event)); |
4166 | _sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE); |
4167 | } |
4168 | } |
4169 | - (void)otherMouseDragged:(NSEvent*)event { |
4170 | _sapp_macos_update_mouse(event); |
4171 | if (2 == event.buttonNumber) { |
4172 | if (_sapp.mouse.locked) { |
4173 | _sapp.mouse.dx = [event deltaX]; |
4174 | _sapp.mouse.dy = [event deltaY]; |
4175 | } |
4176 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); |
4177 | } |
4178 | } |
4179 | - (void)mouseMoved:(NSEvent*)event { |
4180 | _sapp_macos_update_mouse(event); |
4181 | if (_sapp.mouse.locked) { |
4182 | _sapp.mouse.dx = [event deltaX]; |
4183 | _sapp.mouse.dy = [event deltaY]; |
4184 | } |
4185 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event)); |
4186 | } |
4187 | - (void)mouseDragged:(NSEvent*)event { |
4188 | _sapp_macos_update_mouse(event); |
4189 | if (_sapp.mouse.locked) { |
4190 | _sapp.mouse.dx = [event deltaX]; |
4191 | _sapp.mouse.dy = [event deltaY]; |
4192 | } |
4193 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event)); |
4194 | } |
4195 | - (void)rightMouseDragged:(NSEvent*)event { |
4196 | _sapp_macos_update_mouse(event); |
4197 | if (_sapp.mouse.locked) { |
4198 | _sapp.mouse.dx = [event deltaX]; |
4199 | _sapp.mouse.dy = [event deltaY]; |
4200 | } |
4201 | _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); |
4202 | } |
4203 | - (void)scrollWheel:(NSEvent*)event { |
4204 | _sapp_macos_update_mouse(event); |
4205 | if (_sapp_events_enabled()) { |
4206 | float dx = (float) event.scrollingDeltaX; |
4207 | float dy = (float) event.scrollingDeltaY; |
4208 | if (event.hasPreciseScrollingDeltas) { |
4209 | dx *= 0.1; |
4210 | dy *= 0.1; |
4211 | } |
4212 | if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) { |
4213 | _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); |
4214 | _sapp.event.modifiers = _sapp_macos_mods(event); |
4215 | _sapp.event.scroll_x = dx; |
4216 | _sapp.event.scroll_y = dy; |
4217 | _sapp_call_event(&_sapp.event); |
4218 | } |
4219 | } |
4220 | } |
4221 | - (void)keyDown:(NSEvent*)event { |
4222 | if (_sapp_events_enabled()) { |
4223 | const uint32_t mods = _sapp_macos_mods(event); |
4224 | /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed, |
4225 | as a workaround, to prevent key presses from sticking we'll send |
4226 | a keyup event following right after the keydown if SUPER is also pressed |
4227 | */ |
4228 | const sapp_keycode key_code = _sapp_translate_key(event.keyCode); |
4229 | _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); |
4230 | if (0 != (mods & SAPP_MODIFIER_SUPER)) { |
4231 | _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods); |
4232 | } |
4233 | const NSString* chars = event.characters; |
4234 | const NSUInteger len = chars.length; |
4235 | if (len > 0) { |
4236 | _sapp_init_event(SAPP_EVENTTYPE_CHAR); |
4237 | _sapp.event.modifiers = mods; |
4238 | for (NSUInteger i = 0; i < len; i++) { |
4239 | const unichar codepoint = [chars characterAtIndex:i]; |
4240 | if ((codepoint & 0xFF00) == 0xF700) { |
4241 | continue; |
4242 | } |
4243 | _sapp.event.char_code = codepoint; |
4244 | _sapp.event.key_repeat = event.isARepeat; |
4245 | _sapp_call_event(&_sapp.event); |
4246 | } |
4247 | } |
4248 | /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ |
4249 | if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { |
4250 | _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); |
4251 | _sapp_call_event(&_sapp.event); |
4252 | } |
4253 | } |
4254 | } |
4255 | - (void)keyUp:(NSEvent*)event { |
4256 | _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, |
4257 | _sapp_translate_key(event.keyCode), |
4258 | event.isARepeat, |
4259 | _sapp_macos_mods(event)); |
4260 | } |
4261 | - (void)flagsChanged:(NSEvent*)event { |
4262 | const uint32_t old_f = _sapp.macos.flags_changed_store; |
4263 | const uint32_t new_f = (uint32_t)event.modifierFlags; |
4264 | _sapp.macos.flags_changed_store = new_f; |
4265 | sapp_keycode key_code = SAPP_KEYCODE_INVALID; |
4266 | bool down = false; |
4267 | if ((new_f ^ old_f) & NSEventModifierFlagShift) { |
4268 | key_code = SAPP_KEYCODE_LEFT_SHIFT; |
4269 | down = 0 != (new_f & NSEventModifierFlagShift); |
4270 | } |
4271 | if ((new_f ^ old_f) & NSEventModifierFlagControl) { |
4272 | key_code = SAPP_KEYCODE_LEFT_CONTROL; |
4273 | down = 0 != (new_f & NSEventModifierFlagControl); |
4274 | } |
4275 | if ((new_f ^ old_f) & NSEventModifierFlagOption) { |
4276 | key_code = SAPP_KEYCODE_LEFT_ALT; |
4277 | down = 0 != (new_f & NSEventModifierFlagOption); |
4278 | } |
4279 | if ((new_f ^ old_f) & NSEventModifierFlagCommand) { |
4280 | key_code = SAPP_KEYCODE_LEFT_SUPER; |
4281 | down = 0 != (new_f & NSEventModifierFlagCommand); |
4282 | } |
4283 | if (key_code != SAPP_KEYCODE_INVALID) { |
4284 | _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, |
4285 | key_code, |
4286 | false, |
4287 | _sapp_macos_mods(event)); |
4288 | } |
4289 | } |
4290 | @end |
4291 | |
4292 | #endif /* MacOS */ |
4293 | |
4294 | /*== iOS =====================================================================*/ |
4295 | #if defined(_SAPP_IOS) |
4296 | |
4297 | _SOKOL_PRIVATE void _sapp_ios_discard_state(void) { |
4298 | // NOTE: it's safe to call [release] on a nil object |
4299 | _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); |
4300 | _SAPP_OBJC_RELEASE(_sapp.ios.textfield); |
4301 | #if defined(SOKOL_METAL) |
4302 | _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); |
4303 | _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); |
4304 | #else |
4305 | _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); |
4306 | _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); |
4307 | #endif |
4308 | _SAPP_OBJC_RELEASE(_sapp.ios.view); |
4309 | _SAPP_OBJC_RELEASE(_sapp.ios.window); |
4310 | } |
4311 | |
4312 | _SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { |
4313 | _sapp_init_state(desc); |
4314 | static int argc = 1; |
4315 | static char* argv[] = { (char*)"sokol_app" }; |
4316 | UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); |
4317 | } |
4318 | |
4319 | /* iOS entry function */ |
4320 | #if !defined(SOKOL_NO_ENTRY) |
4321 | int main(int argc, char* argv[]) { |
4322 | sapp_desc desc = sokol_main(argc, argv); |
4323 | _sapp_ios_run(&desc); |
4324 | return 0; |
4325 | } |
4326 | #endif /* SOKOL_NO_ENTRY */ |
4327 | |
4328 | _SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { |
4329 | if (_sapp_events_enabled()) { |
4330 | _sapp_init_event(type); |
4331 | _sapp_call_event(&_sapp.event); |
4332 | } |
4333 | } |
4334 | |
4335 | _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>* touches, UIEvent* event) { |
4336 | if (_sapp_events_enabled()) { |
4337 | _sapp_init_event(type); |
4338 | NSEnumerator* enumerator = event.allTouches.objectEnumerator; |
4339 | UITouch* ios_touch; |
4340 | while ((ios_touch = [enumerator nextObject])) { |
4341 | if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { |
4342 | CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; |
4343 | sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; |
4344 | cur_point->identifier = (uintptr_t) ios_touch; |
4345 | cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; |
4346 | cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; |
4347 | cur_point->changed = [touches containsObject:ios_touch]; |
4348 | } |
4349 | } |
4350 | if (_sapp.event.num_touches > 0) { |
4351 | _sapp_call_event(&_sapp.event); |
4352 | } |
4353 | } |
4354 | } |
4355 | |
4356 | _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { |
4357 | CGRect screen_rect = UIScreen.mainScreen.bounds; |
4358 | _sapp.framebuffer_width = (int)roundf(screen_rect.size.width * _sapp.dpi_scale); |
4359 | _sapp.framebuffer_height = (int)roundf(screen_rect.size.height * _sapp.dpi_scale); |
4360 | _sapp.window_width = (int)roundf(screen_rect.size.width); |
4361 | _sapp.window_height = (int)roundf(screen_rect.size.height); |
4362 | int cur_fb_width, cur_fb_height; |
4363 | #if defined(SOKOL_METAL) |
4364 | const CGSize fb_size = _sapp.ios.view.drawableSize; |
4365 | cur_fb_width = (int)roundf(fb_size.width); |
4366 | cur_fb_height = (int)roundf(fb_size.height); |
4367 | #else |
4368 | cur_fb_width = (int)roundf(_sapp.ios.view.drawableWidth); |
4369 | cur_fb_height = (int)roundf(_sapp.ios.view.drawableHeight); |
4370 | #endif |
4371 | const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || |
4372 | (_sapp.framebuffer_height != cur_fb_height); |
4373 | if (dim_changed) { |
4374 | #if defined(SOKOL_METAL) |
4375 | const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; |
4376 | _sapp.ios.view.drawableSize = drawable_size; |
4377 | #else |
4378 | // nothing to do here, GLKView correctly respects the view's contentScaleFactor |
4379 | #endif |
4380 | if (!_sapp.first_frame) { |
4381 | _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); |
4382 | } |
4383 | } |
4384 | } |
4385 | |
4386 | _SOKOL_PRIVATE void _sapp_ios_frame(void) { |
4387 | _sapp_ios_update_dimensions(); |
4388 | _sapp_frame(); |
4389 | } |
4390 | |
4391 | _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { |
4392 | /* if not happened yet, create an invisible text field */ |
4393 | if (nil == _sapp.ios.textfield) { |
4394 | _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; |
4395 | _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; |
4396 | _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; |
4397 | _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; |
4398 | _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; |
4399 | _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; |
4400 | _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; |
4401 | _sapp.ios.textfield.hidden = YES; |
4402 | _sapp.ios.textfield.text = @"x"; |
4403 | _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; |
4404 | [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; |
4405 | |
4406 | [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg |
4407 | selector:@selector(keyboardWasShown:) |
4408 | name:UIKeyboardDidShowNotification object:nil]; |
4409 | [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg |
4410 | selector:@selector(keyboardWillBeHidden:) |
4411 | name:UIKeyboardWillHideNotification object:nil]; |
4412 | [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg |
4413 | selector:@selector(keyboardDidChangeFrame:) |
4414 | name:UIKeyboardDidChangeFrameNotification object:nil]; |
4415 | } |
4416 | if (shown) { |
4417 | /* setting the text field as first responder brings up the onscreen keyboard */ |
4418 | [_sapp.ios.textfield becomeFirstResponder]; |
4419 | } |
4420 | else { |
4421 | [_sapp.ios.textfield resignFirstResponder]; |
4422 | } |
4423 | } |
4424 | |
4425 | @implementation _sapp_app_delegate |
4426 | - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { |
4427 | CGRect screen_rect = UIScreen.mainScreen.bounds; |
4428 | _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; |
4429 | _sapp.window_width = (int)roundf(screen_rect.size.width); |
4430 | _sapp.window_height = (int)roundf(screen_rect.size.height); |
4431 | if (_sapp.desc.high_dpi) { |
4432 | _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale; |
4433 | } |
4434 | else { |
4435 | _sapp.dpi_scale = 1.0f; |
4436 | } |
4437 | _sapp.framebuffer_width = (int)roundf(_sapp.window_width * _sapp.dpi_scale); |
4438 | _sapp.framebuffer_height = (int)roundf(_sapp.window_height * _sapp.dpi_scale); |
4439 | NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond; |
4440 | #if defined(SOKOL_METAL) |
4441 | _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); |
4442 | _sapp.ios.view = [[_sapp_ios_view alloc] init]; |
4443 | _sapp.ios.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; |
4444 | _sapp.ios.view.device = _sapp.ios.mtl_device; |
4445 | _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; |
4446 | _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; |
4447 | _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; |
4448 | /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor |
4449 | and automatically renders at Retina resolution. We'll disable |
4450 | autoResize and instead do the resizing in _sapp_ios_update_dimensions() |
4451 | */ |
4452 | _sapp.ios.view.autoResizeDrawable = false; |
4453 | _sapp.ios.view.userInteractionEnabled = YES; |
4454 | _sapp.ios.view.multipleTouchEnabled = YES; |
4455 | _sapp.ios.view_ctrl = [[UIViewController alloc] init]; |
4456 | _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; |
4457 | _sapp.ios.view_ctrl.view = _sapp.ios.view; |
4458 | _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; |
4459 | #else |
4460 | if (_sapp.desc.gl_force_gles2) { |
4461 | _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
4462 | _sapp.gles2_fallback = true; |
4463 | } |
4464 | else { |
4465 | _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; |
4466 | if (_sapp.ios.eagl_ctx == nil) { |
4467 | _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
4468 | _sapp.gles2_fallback = true; |
4469 | } |
4470 | } |
4471 | _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; |
4472 | _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; |
4473 | _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; |
4474 | _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; |
4475 | GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone; |
4476 | _sapp.ios.view.drawableMultisample = msaa; |
4477 | _sapp.ios.view.context = _sapp.ios.eagl_ctx; |
4478 | _sapp.ios.view.enableSetNeedsDisplay = NO; |
4479 | _sapp.ios.view.userInteractionEnabled = YES; |
4480 | _sapp.ios.view.multipleTouchEnabled = YES; |
4481 | // on GLKView, contentScaleFactor appears to work just fine! |
4482 | if (_sapp.desc.high_dpi) { |
4483 | _sapp.ios.view.contentScaleFactor = _sapp.dpi_scale; |
4484 | } |
4485 | else { |
4486 | _sapp.ios.view.contentScaleFactor = 1.0; |
4487 | } |
4488 | _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; |
4489 | _sapp.ios.view_ctrl.view = _sapp.ios.view; |
4490 | _sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval; |
4491 | _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; |
4492 | #endif |
4493 | [_sapp.ios.window makeKeyAndVisible]; |
4494 | |
4495 | _sapp.valid = true; |
4496 | return YES; |
4497 | } |
4498 | |
4499 | - (void)applicationWillResignActive:(UIApplication *)application { |
4500 | if (!_sapp.ios.suspended) { |
4501 | _sapp.ios.suspended = true; |
4502 | _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); |
4503 | } |
4504 | } |
4505 | |
4506 | - (void)applicationDidBecomeActive:(UIApplication *)application { |
4507 | if (_sapp.ios.suspended) { |
4508 | _sapp.ios.suspended = false; |
4509 | _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); |
4510 | } |
4511 | } |
4512 | |
4513 | /* NOTE: this method will rarely ever be called, iOS application |
4514 | which are terminated by the user are usually killed via signal 9 |
4515 | by the operating system. |
4516 | */ |
4517 | - (void)applicationWillTerminate:(UIApplication *)application { |
4518 | _SOKOL_UNUSED(application); |
4519 | _sapp_call_cleanup(); |
4520 | _sapp_ios_discard_state(); |
4521 | _sapp_discard_state(); |
4522 | } |
4523 | @end |
4524 | |
4525 | @implementation _sapp_textfield_dlg |
4526 | - (void)keyboardWasShown:(NSNotification*)notif { |
4527 | _sapp.onscreen_keyboard_shown = true; |
4528 | /* query the keyboard's size, and modify the content view's size */ |
4529 | if (_sapp.desc.ios_keyboard_resizes_canvas) { |
4530 | NSDictionary* info = notif.userInfo; |
4531 | CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; |
4532 | CGRect view_frame = UIScreen.mainScreen.bounds; |
4533 | view_frame.size.height -= kbd_h; |
4534 | _sapp.ios.view.frame = view_frame; |
4535 | } |
4536 | } |
4537 | - (void)keyboardWillBeHidden:(NSNotification*)notif { |
4538 | _sapp.onscreen_keyboard_shown = false; |
4539 | if (_sapp.desc.ios_keyboard_resizes_canvas) { |
4540 | _sapp.ios.view.frame = UIScreen.mainScreen.bounds; |
4541 | } |
4542 | } |
4543 | - (void)keyboardDidChangeFrame:(NSNotification*)notif { |
4544 | /* this is for the case when the screen rotation changes while the keyboard is open */ |
4545 | if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { |
4546 | NSDictionary* info = notif.userInfo; |
4547 | CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; |
4548 | CGRect view_frame = UIScreen.mainScreen.bounds; |
4549 | view_frame.size.height -= kbd_h; |
4550 | _sapp.ios.view.frame = view_frame; |
4551 | } |
4552 | } |
4553 | - (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { |
4554 | if (_sapp_events_enabled()) { |
4555 | const NSUInteger len = string.length; |
4556 | if (len > 0) { |
4557 | for (NSUInteger i = 0; i < len; i++) { |
4558 | unichar c = [string characterAtIndex:i]; |
4559 | if (c >= 32) { |
4560 | /* ignore surrogates for now */ |
4561 | if ((c < 0xD800) || (c > 0xDFFF)) { |
4562 | _sapp_init_event(SAPP_EVENTTYPE_CHAR); |
4563 | _sapp.event.char_code = c; |
4564 | _sapp_call_event(&_sapp.event); |
4565 | } |
4566 | } |
4567 | if (c <= 32) { |
4568 | sapp_keycode k = SAPP_KEYCODE_INVALID; |
4569 | switch (c) { |
4570 | case 10: k = SAPP_KEYCODE_ENTER; break; |
4571 | case 32: k = SAPP_KEYCODE_SPACE; break; |
4572 | default: break; |
4573 | } |
4574 | if (k != SAPP_KEYCODE_INVALID) { |
4575 | _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); |
4576 | _sapp.event.key_code = k; |
4577 | _sapp_call_event(&_sapp.event); |
4578 | _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); |
4579 | _sapp.event.key_code = k; |
4580 | _sapp_call_event(&_sapp.event); |
4581 | } |
4582 | } |
4583 | } |
4584 | } |
4585 | else { |
4586 | /* this was a backspace */ |
4587 | _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); |
4588 | _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; |
4589 | _sapp_call_event(&_sapp.event); |
4590 | _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); |
4591 | _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; |
4592 | _sapp_call_event(&_sapp.event); |
4593 | } |
4594 | } |
4595 | return NO; |
4596 | } |
4597 | @end |
4598 | |
4599 | @implementation _sapp_ios_view |
4600 | - (void)drawRect:(CGRect)rect { |
4601 | _SOKOL_UNUSED(rect); |
4602 | _sapp_timing_measure(&_sapp.timing); |
4603 | @autoreleasepool { |
4604 | _sapp_ios_frame(); |
4605 | } |
4606 | } |
4607 | - (BOOL)isOpaque { |
4608 | return YES; |
4609 | } |
4610 | - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event { |
4611 | _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); |
4612 | } |
4613 | - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event { |
4614 | _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); |
4615 | } |
4616 | - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event { |
4617 | _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); |
4618 | } |
4619 | - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event { |
4620 | _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); |
4621 | } |
4622 | @end |
4623 | #endif /* TARGET_OS_IPHONE */ |
4624 | |
4625 | #endif /* _SAPP_APPLE */ |
4626 | |
4627 | /*== EMSCRIPTEN ==============================================================*/ |
4628 | #if defined(_SAPP_EMSCRIPTEN) |
4629 | |
4630 | #if defined(EM_JS_DEPS) |
4631 | EM_JS_DEPS(sokol_app, "$withStackSave,$allocateUTF8OnStack"); |
4632 | #endif |
4633 | |
4634 | #ifdef __cplusplus |
4635 | extern "C" { |
4636 | #endif |
4637 | |
4638 | typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); |
4639 | |
4640 | /* this function is called from a JS event handler when the user hides |
4641 | the onscreen keyboard pressing the 'dismiss keyboard key' |
4642 | */ |
4643 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { |
4644 | _sapp.onscreen_keyboard_shown = false; |
4645 | } |
4646 | |
4647 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { |
4648 | if (_sapp.clipboard.enabled) { |
4649 | _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); |
4650 | if (_sapp_events_enabled()) { |
4651 | _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); |
4652 | _sapp_call_event(&_sapp.event); |
4653 | } |
4654 | } |
4655 | } |
4656 | |
4657 | /* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ |
4658 | EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { |
4659 | return _sapp.html5_ask_leave_site ? 1 : 0; |
4660 | } |
4661 | |
4662 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { |
4663 | if (!_sapp.drop.enabled) { |
4664 | return; |
4665 | } |
4666 | if (num < 0) { |
4667 | num = 0; |
4668 | } |
4669 | if (num > _sapp.drop.max_files) { |
4670 | num = _sapp.drop.max_files; |
4671 | } |
4672 | _sapp.drop.num_files = num; |
4673 | _sapp_clear_drop_buffer(); |
4674 | } |
4675 | |
4676 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { |
4677 | /* NOTE: name is only the filename part, not a path */ |
4678 | if (!_sapp.drop.enabled) { |
4679 | return; |
4680 | } |
4681 | if (0 == name) { |
4682 | return; |
4683 | } |
4684 | SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); |
4685 | if ((i < 0) || (i >= _sapp.drop.num_files)) { |
4686 | return; |
4687 | } |
4688 | if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { |
4689 | SAPP_LOG("sokol_app.h: dropped file path too long!\n"); |
4690 | _sapp.drop.num_files = 0; |
4691 | } |
4692 | } |
4693 | |
4694 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { |
4695 | if (!_sapp.drop.enabled) { |
4696 | return; |
4697 | } |
4698 | if (0 == _sapp.drop.num_files) { |
4699 | /* there was an error copying the filenames */ |
4700 | _sapp_clear_drop_buffer(); |
4701 | return; |
4702 | |
4703 | } |
4704 | if (_sapp_events_enabled()) { |
4705 | _sapp.mouse.x = (float)x * _sapp.dpi_scale; |
4706 | _sapp.mouse.y = (float)y * _sapp.dpi_scale; |
4707 | _sapp.mouse.dx = 0.0f; |
4708 | _sapp.mouse.dy = 0.0f; |
4709 | _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); |
4710 | _sapp_call_event(&_sapp.event); |
4711 | } |
4712 | } |
4713 | |
4714 | EMSCRIPTEN_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) { |
4715 | sapp_html5_fetch_response response; |
4716 | _sapp_clear(&response, sizeof(response)); |
4717 | response.succeeded = (0 != success); |
4718 | response.error_code = (sapp_html5_fetch_error) error_code; |
4719 | response.file_index = index; |
4720 | response.data.ptr = buf_ptr; |
4721 | response.data.size = fetched_size; |
4722 | response.buffer.ptr = buf_ptr; |
4723 | response.buffer.size = buf_size; |
4724 | response.user_data = user_data; |
4725 | callback(&response); |
4726 | } |
4727 | |
4728 | #ifdef __cplusplus |
4729 | } /* extern "C" */ |
4730 | #endif |
4731 | |
4732 | /* Javascript helper functions for mobile virtual keyboard input */ |
4733 | EM_JS(void, sapp_js_create_textfield, (void), { |
4734 | const _sapp_inp = document.createElement("input"); |
4735 | _sapp_inp.type = "text"; |
4736 | _sapp_inp.id = "_sokol_app_input_element"; |
4737 | _sapp_inp.autocapitalize = "none"; |
4738 | _sapp_inp.addEventListener("focusout", function(_sapp_event) { |
4739 | __sapp_emsc_notify_keyboard_hidden() |
4740 | |
4741 | }); |
4742 | document.body.append(_sapp_inp); |
4743 | }); |
4744 | |
4745 | EM_JS(void, sapp_js_focus_textfield, (void), { |
4746 | document.getElementById("_sokol_app_input_element").focus(); |
4747 | }); |
4748 | |
4749 | EM_JS(void, sapp_js_unfocus_textfield, (void), { |
4750 | document.getElementById("_sokol_app_input_element").blur(); |
4751 | }); |
4752 | |
4753 | EM_JS(void, sapp_js_add_beforeunload_listener, (void), { |
4754 | Module.sokol_beforeunload = (event) => { |
4755 | if (__sapp_html5_get_ask_leave_site() != 0) { |
4756 | event.preventDefault(); |
4757 | event.returnValue = ' '; |
4758 | } |
4759 | }; |
4760 | window.addEventListener('beforeunload', Module.sokol_beforeunload); |
4761 | }); |
4762 | |
4763 | EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { |
4764 | window.removeEventListener('beforeunload', Module.sokol_beforeunload); |
4765 | }); |
4766 | |
4767 | EM_JS(void, sapp_js_add_clipboard_listener, (void), { |
4768 | Module.sokol_paste = (event) => { |
4769 | const pasted_str = event.clipboardData.getData('text'); |
4770 | withStackSave(() => { |
4771 | const cstr = allocateUTF8OnStack(pasted_str); |
4772 | __sapp_emsc_onpaste(cstr); |
4773 | }); |
4774 | }; |
4775 | window.addEventListener('paste', Module.sokol_paste); |
4776 | }); |
4777 | |
4778 | EM_JS(void, sapp_js_remove_clipboard_listener, (void), { |
4779 | window.removeEventListener('paste', Module.sokol_paste); |
4780 | }); |
4781 | |
4782 | EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { |
4783 | const str = UTF8ToString(c_str); |
4784 | const ta = document.createElement('textarea'); |
4785 | ta.setAttribute('autocomplete', 'off'); |
4786 | ta.setAttribute('autocorrect', 'off'); |
4787 | ta.setAttribute('autocapitalize', 'off'); |
4788 | ta.setAttribute('spellcheck', 'false'); |
4789 | ta.style.left = -100 + 'px'; |
4790 | ta.style.top = -100 + 'px'; |
4791 | ta.style.height = 1; |
4792 | ta.style.width = 1; |
4793 | ta.value = str; |
4794 | document.body.appendChild(ta); |
4795 | ta.select(); |
4796 | document.execCommand('copy'); |
4797 | document.body.removeChild(ta); |
4798 | }); |
4799 | |
4800 | _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { |
4801 | sapp_js_write_clipboard(str); |
4802 | } |
4803 | |
4804 | EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { |
4805 | Module.sokol_drop_files = []; |
4806 | const canvas_name = UTF8ToString(canvas_name_cstr); |
4807 | const canvas = document.getElementById(canvas_name); |
4808 | Module.sokol_dragenter = (event) => { |
4809 | event.stopPropagation(); |
4810 | event.preventDefault(); |
4811 | }; |
4812 | Module.sokol_dragleave = (event) => { |
4813 | event.stopPropagation(); |
4814 | event.preventDefault(); |
4815 | }; |
4816 | Module.sokol_dragover = (event) => { |
4817 | event.stopPropagation(); |
4818 | event.preventDefault(); |
4819 | }; |
4820 | Module.sokol_drop = (event) => { |
4821 | event.stopPropagation(); |
4822 | event.preventDefault(); |
4823 | const files = event.dataTransfer.files; |
4824 | Module.sokol_dropped_files = files; |
4825 | __sapp_emsc_begin_drop(files.length); |
4826 | for (let i = 0; i < files.length; i++) { |
4827 | withStackSave(() => { |
4828 | const cstr = allocateUTF8OnStack(files[i].name); |
4829 | __sapp_emsc_drop(i, cstr); |
4830 | }); |
4831 | } |
4832 | // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect |
4833 | __sapp_emsc_end_drop(event.clientX, event.clientY); |
4834 | }; |
4835 | canvas.addEventListener('dragenter', Module.sokol_dragenter, false); |
4836 | canvas.addEventListener('dragleave', Module.sokol_dragleave, false); |
4837 | canvas.addEventListener('dragover', Module.sokol_dragover, false); |
4838 | canvas.addEventListener('drop', Module.sokol_drop, false); |
4839 | }); |
4840 | |
4841 | EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { |
4842 | \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F |
4843 | const files = Module.sokol_dropped_files; |
4844 | if ((index < 0) || (index >= files.length)) { |
4845 | return 0; |
4846 | } |
4847 | else { |
4848 | return files[index].size; |
4849 | } |
4850 | }); |
4851 | |
4852 | EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), { |
4853 | const reader = new FileReader(); |
4854 | reader.onload = (loadEvent) => { |
4855 | const content = loadEvent.target.result; |
4856 | if (content.byteLength > buf_size) { |
4857 | // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL |
4858 | __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data); |
4859 | } |
4860 | else { |
4861 | HEAPU8.set(new Uint8Array(content), buf_ptr); |
4862 | __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); |
4863 | } |
4864 | }; |
4865 | reader.onerror = () => { |
4866 | // SAPP_HTML5_FETCH_ERROR_OTHER |
4867 | __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); |
4868 | }; |
4869 | \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F |
4870 | const files = Module.sokol_dropped_files; |
4871 | reader.readAsArrayBuffer(files[index]); |
4872 | }); |
4873 | |
4874 | EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { |
4875 | const canvas_name = UTF8ToString(canvas_name_cstr); |
4876 | const canvas = document.getElementById(canvas_name); |
4877 | canvas.removeEventListener('dragenter', Module.sokol_dragenter); |
4878 | canvas.removeEventListener('dragleave', Module.sokol_dragleave); |
4879 | canvas.removeEventListener('dragover', Module.sokol_dragover); |
4880 | canvas.removeEventListener('drop', Module.sokol_drop); |
4881 | }); |
4882 | |
4883 | /* called from the emscripten event handler to update the keyboard visibility |
4884 | state, this must happen from an JS input event handler, otherwise |
4885 | the request will be ignored by the browser |
4886 | */ |
4887 | _SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { |
4888 | if (_sapp.emsc.wants_show_keyboard) { |
4889 | /* create input text field on demand */ |
4890 | if (!_sapp.emsc.textfield_created) { |
4891 | _sapp.emsc.textfield_created = true; |
4892 | sapp_js_create_textfield(); |
4893 | } |
4894 | /* focus the text input field, this will bring up the keyboard */ |
4895 | _sapp.onscreen_keyboard_shown = true; |
4896 | _sapp.emsc.wants_show_keyboard = false; |
4897 | sapp_js_focus_textfield(); |
4898 | } |
4899 | if (_sapp.emsc.wants_hide_keyboard) { |
4900 | /* unfocus the text input field */ |
4901 | if (_sapp.emsc.textfield_created) { |
4902 | _sapp.onscreen_keyboard_shown = false; |
4903 | _sapp.emsc.wants_hide_keyboard = false; |
4904 | sapp_js_unfocus_textfield(); |
4905 | } |
4906 | } |
4907 | } |
4908 | |
4909 | /* actually showing the onscreen keyboard must be initiated from a JS |
4910 | input event handler, so we'll just keep track of the desired |
4911 | state, and the actual state change will happen with the next input event |
4912 | */ |
4913 | _SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { |
4914 | if (show) { |
4915 | _sapp.emsc.wants_show_keyboard = true; |
4916 | } |
4917 | else { |
4918 | _sapp.emsc.wants_hide_keyboard = true; |
4919 | } |
4920 | } |
4921 | |
4922 | EM_JS(void, sapp_js_init, (const char* c_str_target), { |
4923 | // lookup and store canvas object by name |
4924 | const target_str = UTF8ToString(c_str_target); |
4925 | Module.sapp_emsc_target = document.getElementById(target_str); |
4926 | if (!Module.sapp_emsc_target) { |
4927 | console.log("sokol_app.h: invalid target:" + target_str); |
4928 | } |
4929 | if (!Module.sapp_emsc_target.requestPointerLock) { |
4930 | console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); |
4931 | } |
4932 | }); |
4933 | |
4934 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { |
4935 | _SOKOL_UNUSED(emsc_type); |
4936 | _SOKOL_UNUSED(user_data); |
4937 | _sapp.mouse.locked = emsc_event->isActive; |
4938 | return EM_TRUE; |
4939 | } |
4940 | |
4941 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { |
4942 | _SOKOL_UNUSED(emsc_type); |
4943 | _SOKOL_UNUSED(reserved); |
4944 | _SOKOL_UNUSED(user_data); |
4945 | _sapp.mouse.locked = false; |
4946 | _sapp.emsc.mouse_lock_requested = false; |
4947 | return true; |
4948 | } |
4949 | |
4950 | EM_JS(void, sapp_js_request_pointerlock, (void), { |
4951 | if (Module.sapp_emsc_target) { |
4952 | if (Module.sapp_emsc_target.requestPointerLock) { |
4953 | Module.sapp_emsc_target.requestPointerLock(); |
4954 | } |
4955 | } |
4956 | }); |
4957 | |
4958 | EM_JS(void, sapp_js_exit_pointerlock, (void), { |
4959 | if (document.exitPointerLock) { |
4960 | document.exitPointerLock(); |
4961 | } |
4962 | }); |
4963 | |
4964 | _SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { |
4965 | if (lock) { |
4966 | /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ |
4967 | _sapp.emsc.mouse_lock_requested = true; |
4968 | } |
4969 | else { |
4970 | /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ |
4971 | _sapp.emsc.mouse_lock_requested = false; |
4972 | sapp_js_exit_pointerlock(); |
4973 | } |
4974 | } |
4975 | |
4976 | /* called from inside event handlers to check if mouse lock had been requested, |
4977 | and if yes, actually enter mouse lock. |
4978 | */ |
4979 | _SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { |
4980 | if (_sapp.emsc.mouse_lock_requested) { |
4981 | _sapp.emsc.mouse_lock_requested = false; |
4982 | sapp_js_request_pointerlock(); |
4983 | } |
4984 | } |
4985 | |
4986 | // set mouse cursor type |
4987 | EM_JS(void, sapp_js_set_cursor, (int cursor_type, int shown), { |
4988 | if (Module.sapp_emsc_target) { |
4989 | let cursor; |
4990 | if (shown === 0) { |
4991 | cursor = "none"; |
4992 | } |
4993 | else switch (cursor_type) { |
4994 | case 0: cursor = "auto"; break; // SAPP_MOUSECURSOR_DEFAULT |
4995 | case 1: cursor = "default"; break; // SAPP_MOUSECURSOR_ARROW |
4996 | case 2: cursor = "text"; break; // SAPP_MOUSECURSOR_IBEAM |
4997 | case 3: cursor = "crosshair"; break; // SAPP_MOUSECURSOR_CROSSHAIR |
4998 | case 4: cursor = "pointer"; break; // SAPP_MOUSECURSOR_POINTING_HAND |
4999 | case 5: cursor = "ew-resize"; break; // SAPP_MOUSECURSOR_RESIZE_EW |
5000 | case 6: cursor = "ns-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NS |
5001 | case 7: cursor = "nwse-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NWSE |
5002 | case 8: cursor = "nesw-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NESW |
5003 | case 9: cursor = "all-scroll"; break; // SAPP_MOUSECURSOR_RESIZE_ALL |
5004 | case 10: cursor = "not-allowed"; break; // SAPP_MOUSECURSOR_NOT_ALLOWED |
5005 | default: cursor = "auto"; break; |
5006 | } |
5007 | Module.sapp_emsc_target.style.cursor = cursor; |
5008 | } |
5009 | }); |
5010 | |
5011 | _SOKOL_PRIVATE void _sapp_emsc_update_cursor(sapp_mouse_cursor cursor, bool shown) { |
5012 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
5013 | sapp_js_set_cursor((int)cursor, shown ? 1 : 0); |
5014 | } |
5015 | |
5016 | /* JS helper functions to update browser tab favicon */ |
5017 | EM_JS(void, sapp_js_clear_favicon, (void), { |
5018 | const link = document.getElementById('sokol-app-favicon'); |
5019 | if (link) { |
5020 | document.head.removeChild(link); |
5021 | } |
5022 | }); |
5023 | |
5024 | EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), { |
5025 | const canvas = document.createElement('canvas'); |
5026 | canvas.width = w; |
5027 | canvas.height = h; |
5028 | const ctx = canvas.getContext('2d'); |
5029 | const img_data = ctx.createImageData(w, h); |
5030 | img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4)); |
5031 | ctx.putImageData(img_data, 0, 0); |
5032 | const new_link = document.createElement('link'); |
5033 | new_link.id = 'sokol-app-favicon'; |
5034 | new_link.rel = 'shortcut icon'; |
5035 | new_link.href = canvas.toDataURL(); |
5036 | document.head.appendChild(new_link); |
5037 | }); |
5038 | |
5039 | _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) { |
5040 | SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); |
5041 | sapp_js_clear_favicon(); |
5042 | // find the best matching image candidate for 16x16 pixels |
5043 | int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16); |
5044 | const sapp_image_desc* img_desc = &icon_desc->images[img_index]; |
5045 | sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr); |
5046 | } |
5047 | |
5048 | #if defined(SOKOL_WGPU) |
5049 | _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void); |
5050 | _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void); |
5051 | #endif |
5052 | |
5053 | _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) { |
5054 | uint32_t m = 0; |
5055 | if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; } |
5056 | if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug |
5057 | if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug |
5058 | return m; |
5059 | } |
5060 | |
5061 | _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) { |
5062 | uint32_t m = 0; |
5063 | if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } |
5064 | if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } |
5065 | if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } |
5066 | if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } |
5067 | m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); |
5068 | return m; |
5069 | } |
5070 | |
5071 | _SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) { |
5072 | uint32_t m = 0; |
5073 | if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } |
5074 | if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } |
5075 | if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } |
5076 | if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } |
5077 | m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); |
5078 | return m; |
5079 | } |
5080 | |
5081 | _SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) { |
5082 | uint32_t m = 0; |
5083 | if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } |
5084 | if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } |
5085 | if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } |
5086 | if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } |
5087 | m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); |
5088 | return m; |
5089 | } |
5090 | |
5091 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { |
5092 | _SOKOL_UNUSED(event_type); |
5093 | _SOKOL_UNUSED(user_data); |
5094 | double w, h; |
5095 | emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); |
5096 | /* The above method might report zero when toggling HTML5 fullscreen, |
5097 | in that case use the window's inner width reported by the |
5098 | emscripten event. This works ok when toggling *into* fullscreen |
5099 | but doesn't properly restore the previous canvas size when switching |
5100 | back from fullscreen. |
5101 | |
5102 | In general, due to the HTML5's fullscreen API's flaky nature it is |
5103 | recommended to use 'soft fullscreen' (stretching the WebGL canvas |
5104 | over the browser windows client rect) with a CSS definition like this: |
5105 | |
5106 | position: absolute; |
5107 | top: 0px; |
5108 | left: 0px; |
5109 | margin: 0px; |
5110 | border: 0; |
5111 | width: 100%; |
5112 | height: 100%; |
5113 | overflow: hidden; |
5114 | display: block; |
5115 | */ |
5116 | if (w < 1.0) { |
5117 | w = ui_event->windowInnerWidth; |
5118 | } |
5119 | else { |
5120 | _sapp.window_width = (int)roundf(w); |
5121 | } |
5122 | if (h < 1.0) { |
5123 | h = ui_event->windowInnerHeight; |
5124 | } |
5125 | else { |
5126 | _sapp.window_height = (int)roundf(h); |
5127 | } |
5128 | if (_sapp.desc.high_dpi) { |
5129 | _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); |
5130 | } |
5131 | _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); |
5132 | _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); |
5133 | SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); |
5134 | emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); |
5135 | #if defined(SOKOL_WGPU) |
5136 | /* on WebGPU: recreate size-dependent rendering surfaces */ |
5137 | _sapp_emsc_wgpu_surfaces_discard(); |
5138 | _sapp_emsc_wgpu_surfaces_create(); |
5139 | #endif |
5140 | if (_sapp_events_enabled()) { |
5141 | _sapp_init_event(SAPP_EVENTTYPE_RESIZED); |
5142 | _sapp_call_event(&_sapp.event); |
5143 | } |
5144 | return true; |
5145 | } |
5146 | |
5147 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { |
5148 | _SOKOL_UNUSED(user_data); |
5149 | _sapp.emsc.mouse_buttons = emsc_event->buttons; |
5150 | if (_sapp.mouse.locked) { |
5151 | _sapp.mouse.dx = (float) emsc_event->movementX; |
5152 | _sapp.mouse.dy = (float) emsc_event->movementY; |
5153 | } |
5154 | else { |
5155 | float new_x = emsc_event->targetX * _sapp.dpi_scale; |
5156 | float new_y = emsc_event->targetY * _sapp.dpi_scale; |
5157 | if (_sapp.mouse.pos_valid) { |
5158 | _sapp.mouse.dx = new_x - _sapp.mouse.x; |
5159 | _sapp.mouse.dy = new_y - _sapp.mouse.y; |
5160 | } |
5161 | _sapp.mouse.x = new_x; |
5162 | _sapp.mouse.y = new_y; |
5163 | _sapp.mouse.pos_valid = true; |
5164 | } |
5165 | if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { |
5166 | sapp_event_type type; |
5167 | bool is_button_event = false; |
5168 | switch (emsc_type) { |
5169 | case EMSCRIPTEN_EVENT_MOUSEDOWN: |
5170 | type = SAPP_EVENTTYPE_MOUSE_DOWN; |
5171 | is_button_event = true; |
5172 | break; |
5173 | case EMSCRIPTEN_EVENT_MOUSEUP: |
5174 | type = SAPP_EVENTTYPE_MOUSE_UP; |
5175 | is_button_event = true; |
5176 | break; |
5177 | case EMSCRIPTEN_EVENT_MOUSEMOVE: |
5178 | type = SAPP_EVENTTYPE_MOUSE_MOVE; |
5179 | break; |
5180 | case EMSCRIPTEN_EVENT_MOUSEENTER: |
5181 | type = SAPP_EVENTTYPE_MOUSE_ENTER; |
5182 | break; |
5183 | case EMSCRIPTEN_EVENT_MOUSELEAVE: |
5184 | type = SAPP_EVENTTYPE_MOUSE_LEAVE; |
5185 | break; |
5186 | default: |
5187 | type = SAPP_EVENTTYPE_INVALID; |
5188 | break; |
5189 | } |
5190 | if (type != SAPP_EVENTTYPE_INVALID) { |
5191 | _sapp_init_event(type); |
5192 | _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); |
5193 | if (is_button_event) { |
5194 | switch (emsc_event->button) { |
5195 | case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; |
5196 | case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; |
5197 | case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; |
5198 | default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; |
5199 | } |
5200 | } |
5201 | else { |
5202 | _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; |
5203 | } |
5204 | _sapp_call_event(&_sapp.event); |
5205 | } |
5206 | /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */ |
5207 | if (is_button_event) { |
5208 | _sapp_emsc_update_mouse_lock_state(); |
5209 | } |
5210 | } |
5211 | _sapp_emsc_update_keyboard_state(); |
5212 | return true; |
5213 | } |
5214 | |
5215 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { |
5216 | _SOKOL_UNUSED(emsc_type); |
5217 | _SOKOL_UNUSED(user_data); |
5218 | _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons; |
5219 | if (_sapp_events_enabled()) { |
5220 | _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); |
5221 | _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse); |
5222 | /* see https://github.com/floooh/sokol/issues/339 */ |
5223 | float scale; |
5224 | switch (emsc_event->deltaMode) { |
5225 | case DOM_DELTA_PIXEL: scale = -0.04f; break; |
5226 | case DOM_DELTA_LINE: scale = -1.33f; break; |
5227 | case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess |
5228 | default: scale = -0.1f; break; // shouldn't happen |
5229 | } |
5230 | _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; |
5231 | _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; |
5232 | _sapp_call_event(&_sapp.event); |
5233 | } |
5234 | _sapp_emsc_update_keyboard_state(); |
5235 | _sapp_emsc_update_mouse_lock_state(); |
5236 | return true; |
5237 | } |
5238 | |
5239 | static struct { |
5240 | const char* str; |
5241 | sapp_keycode code; |
5242 | } _sapp_emsc_keymap[] = { |
5243 | { "Backspace", SAPP_KEYCODE_BACKSPACE }, |
5244 | { "Tab", SAPP_KEYCODE_TAB }, |
5245 | { "Enter", SAPP_KEYCODE_ENTER }, |
5246 | { "ShiftLeft", SAPP_KEYCODE_LEFT_SHIFT }, |
5247 | { "ShiftRight", SAPP_KEYCODE_RIGHT_SHIFT }, |
5248 | { "ControlLeft", SAPP_KEYCODE_LEFT_CONTROL }, |
5249 | { "ControlRight", SAPP_KEYCODE_RIGHT_CONTROL }, |
5250 | { "AltLeft", SAPP_KEYCODE_LEFT_ALT }, |
5251 | { "AltRight", SAPP_KEYCODE_RIGHT_ALT }, |
5252 | { "Pause", SAPP_KEYCODE_PAUSE }, |
5253 | { "CapsLock", SAPP_KEYCODE_CAPS_LOCK }, |
5254 | { "Escape", SAPP_KEYCODE_ESCAPE }, |
5255 | { "Space", SAPP_KEYCODE_SPACE }, |
5256 | { "PageUp", SAPP_KEYCODE_PAGE_UP }, |
5257 | { "PageDown", SAPP_KEYCODE_PAGE_DOWN }, |
5258 | { "End", SAPP_KEYCODE_END }, |
5259 | { "Home", SAPP_KEYCODE_HOME }, |
5260 | { "ArrowLeft", SAPP_KEYCODE_LEFT }, |
5261 | { "ArrowUp", SAPP_KEYCODE_UP }, |
5262 | { "ArrowRight", SAPP_KEYCODE_RIGHT }, |
5263 | { "ArrowDown", SAPP_KEYCODE_DOWN }, |
5264 | { "PrintScreen", SAPP_KEYCODE_PRINT_SCREEN }, |
5265 | { "Insert", SAPP_KEYCODE_INSERT }, |
5266 | { "Delete", SAPP_KEYCODE_DELETE }, |
5267 | { "Digit0", SAPP_KEYCODE_0 }, |
5268 | { "Digit1", SAPP_KEYCODE_1 }, |
5269 | { "Digit2", SAPP_KEYCODE_2 }, |
5270 | { "Digit3", SAPP_KEYCODE_3 }, |
5271 | { "Digit4", SAPP_KEYCODE_4 }, |
5272 | { "Digit5", SAPP_KEYCODE_5 }, |
5273 | { "Digit6", SAPP_KEYCODE_6 }, |
5274 | { "Digit7", SAPP_KEYCODE_7 }, |
5275 | { "Digit8", SAPP_KEYCODE_8 }, |
5276 | { "Digit9", SAPP_KEYCODE_9 }, |
5277 | { "KeyA", SAPP_KEYCODE_A }, |
5278 | { "KeyB", SAPP_KEYCODE_B }, |
5279 | { "KeyC", SAPP_KEYCODE_C }, |
5280 | { "KeyD", SAPP_KEYCODE_D }, |
5281 | { "KeyE", SAPP_KEYCODE_E }, |
5282 | { "KeyF", SAPP_KEYCODE_F }, |
5283 | { "KeyG", SAPP_KEYCODE_G }, |
5284 | { "KeyH", SAPP_KEYCODE_H }, |
5285 | { "KeyI", SAPP_KEYCODE_I }, |
5286 | { "KeyJ", SAPP_KEYCODE_J }, |
5287 | { "KeyK", SAPP_KEYCODE_K }, |
5288 | { "KeyL", SAPP_KEYCODE_L }, |
5289 | { "KeyM", SAPP_KEYCODE_M }, |
5290 | { "KeyN", SAPP_KEYCODE_N }, |
5291 | { "KeyO", SAPP_KEYCODE_O }, |
5292 | { "KeyP", SAPP_KEYCODE_P }, |
5293 | { "KeyQ", SAPP_KEYCODE_Q }, |
5294 | { "KeyR", SAPP_KEYCODE_R }, |
5295 | { "KeyS", SAPP_KEYCODE_S }, |
5296 | { "KeyT", SAPP_KEYCODE_T }, |
5297 | { "KeyU", SAPP_KEYCODE_U }, |
5298 | { "KeyV", SAPP_KEYCODE_V }, |
5299 | { "KeyW", SAPP_KEYCODE_W }, |
5300 | { "KeyX", SAPP_KEYCODE_X }, |
5301 | { "KeyY", SAPP_KEYCODE_Y }, |
5302 | { "KeyZ", SAPP_KEYCODE_Z }, |
5303 | { "MetaLeft", SAPP_KEYCODE_LEFT_SUPER }, |
5304 | { "MetaRight", SAPP_KEYCODE_RIGHT_SUPER }, |
5305 | { "Numpad0", SAPP_KEYCODE_KP_0 }, |
5306 | { "Numpad1", SAPP_KEYCODE_KP_1 }, |
5307 | { "Numpad2", SAPP_KEYCODE_KP_2 }, |
5308 | { "Numpad3", SAPP_KEYCODE_KP_3 }, |
5309 | { "Numpad4", SAPP_KEYCODE_KP_4 }, |
5310 | { "Numpad5", SAPP_KEYCODE_KP_5 }, |
5311 | { "Numpad6", SAPP_KEYCODE_KP_6 }, |
5312 | { "Numpad7", SAPP_KEYCODE_KP_7 }, |
5313 | { "Numpad8", SAPP_KEYCODE_KP_8 }, |
5314 | { "Numpad9", SAPP_KEYCODE_KP_9 }, |
5315 | { "NumpadMultiply", SAPP_KEYCODE_KP_MULTIPLY }, |
5316 | { "NumpadAdd", SAPP_KEYCODE_KP_ADD }, |
5317 | { "NumpadSubtract", SAPP_KEYCODE_KP_SUBTRACT }, |
5318 | { "NumpadDecimal", SAPP_KEYCODE_KP_DECIMAL }, |
5319 | { "NumpadDivide", SAPP_KEYCODE_KP_DIVIDE }, |
5320 | { "F1", SAPP_KEYCODE_F1 }, |
5321 | { "F2", SAPP_KEYCODE_F2 }, |
5322 | { "F3", SAPP_KEYCODE_F3 }, |
5323 | { "F4", SAPP_KEYCODE_F4 }, |
5324 | { "F5", SAPP_KEYCODE_F5 }, |
5325 | { "F6", SAPP_KEYCODE_F6 }, |
5326 | { "F7", SAPP_KEYCODE_F7 }, |
5327 | { "F8", SAPP_KEYCODE_F8 }, |
5328 | { "F9", SAPP_KEYCODE_F9 }, |
5329 | { "F10", SAPP_KEYCODE_F10 }, |
5330 | { "F11", SAPP_KEYCODE_F11 }, |
5331 | { "F12", SAPP_KEYCODE_F12 }, |
5332 | { "NumLock", SAPP_KEYCODE_NUM_LOCK }, |
5333 | { "ScrollLock", SAPP_KEYCODE_SCROLL_LOCK }, |
5334 | { "Semicolon", SAPP_KEYCODE_SEMICOLON }, |
5335 | { "Equal", SAPP_KEYCODE_EQUAL }, |
5336 | { "Comma", SAPP_KEYCODE_COMMA }, |
5337 | { "Minus", SAPP_KEYCODE_MINUS }, |
5338 | { "Period", SAPP_KEYCODE_PERIOD }, |
5339 | { "Slash", SAPP_KEYCODE_SLASH }, |
5340 | { "Backquote", SAPP_KEYCODE_GRAVE_ACCENT }, |
5341 | { "BracketLeft", SAPP_KEYCODE_LEFT_BRACKET }, |
5342 | { "Backslash", SAPP_KEYCODE_BACKSLASH }, |
5343 | { "BracketRight", SAPP_KEYCODE_RIGHT_BRACKET }, |
5344 | { "Quote", SAPP_KEYCODE_GRAVE_ACCENT }, // FIXME: ??? |
5345 | { 0, SAPP_KEYCODE_INVALID }, |
5346 | }; |
5347 | |
5348 | _SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { |
5349 | int i = 0; |
5350 | const char* keystr; |
5351 | while (( keystr = _sapp_emsc_keymap[i].str )) { |
5352 | if (0 == strcmp(str, keystr)) { |
5353 | return _sapp_emsc_keymap[i].code; |
5354 | } |
5355 | i += 1; |
5356 | } |
5357 | return SAPP_KEYCODE_INVALID; |
5358 | } |
5359 | |
5360 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { |
5361 | _SOKOL_UNUSED(user_data); |
5362 | bool retval = true; |
5363 | if (_sapp_events_enabled()) { |
5364 | sapp_event_type type; |
5365 | switch (emsc_type) { |
5366 | case EMSCRIPTEN_EVENT_KEYDOWN: |
5367 | type = SAPP_EVENTTYPE_KEY_DOWN; |
5368 | break; |
5369 | case EMSCRIPTEN_EVENT_KEYUP: |
5370 | type = SAPP_EVENTTYPE_KEY_UP; |
5371 | break; |
5372 | case EMSCRIPTEN_EVENT_KEYPRESS: |
5373 | type = SAPP_EVENTTYPE_CHAR; |
5374 | break; |
5375 | default: |
5376 | type = SAPP_EVENTTYPE_INVALID; |
5377 | break; |
5378 | } |
5379 | if (type != SAPP_EVENTTYPE_INVALID) { |
5380 | bool send_keyup_followup = false; |
5381 | _sapp_init_event(type); |
5382 | _sapp.event.key_repeat = emsc_event->repeat; |
5383 | _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); |
5384 | if (type == SAPP_EVENTTYPE_CHAR) { |
5385 | _sapp.event.char_code = emsc_event->charCode; |
5386 | /* workaround to make Cmd+V work on Safari */ |
5387 | if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { |
5388 | retval = false; |
5389 | } |
5390 | } |
5391 | else { |
5392 | _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); |
5393 | /* Special hack for macOS: if the Super key is pressed, macOS doesn't |
5394 | send keyUp events. As a workaround, to prevent keys from |
5395 | "sticking", we'll send a keyup event following a keydown |
5396 | when the SUPER key is pressed |
5397 | */ |
5398 | if ((type == SAPP_EVENTTYPE_KEY_DOWN) && |
5399 | (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) && |
5400 | (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) && |
5401 | (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) |
5402 | { |
5403 | send_keyup_followup = true; |
5404 | } |
5405 | /* only forward a certain key ranges to the browser */ |
5406 | switch (_sapp.event.key_code) { |
5407 | case SAPP_KEYCODE_WORLD_1: |
5408 | case SAPP_KEYCODE_WORLD_2: |
5409 | case SAPP_KEYCODE_ESCAPE: |
5410 | case SAPP_KEYCODE_ENTER: |
5411 | case SAPP_KEYCODE_TAB: |
5412 | case SAPP_KEYCODE_BACKSPACE: |
5413 | case SAPP_KEYCODE_INSERT: |
5414 | case SAPP_KEYCODE_DELETE: |
5415 | case SAPP_KEYCODE_RIGHT: |
5416 | case SAPP_KEYCODE_LEFT: |
5417 | case SAPP_KEYCODE_DOWN: |
5418 | case SAPP_KEYCODE_UP: |
5419 | case SAPP_KEYCODE_PAGE_UP: |
5420 | case SAPP_KEYCODE_PAGE_DOWN: |
5421 | case SAPP_KEYCODE_HOME: |
5422 | case SAPP_KEYCODE_END: |
5423 | case SAPP_KEYCODE_CAPS_LOCK: |
5424 | case SAPP_KEYCODE_SCROLL_LOCK: |
5425 | case SAPP_KEYCODE_NUM_LOCK: |
5426 | case SAPP_KEYCODE_PRINT_SCREEN: |
5427 | case SAPP_KEYCODE_PAUSE: |
5428 | case SAPP_KEYCODE_F1: |
5429 | case SAPP_KEYCODE_F2: |
5430 | case SAPP_KEYCODE_F3: |
5431 | case SAPP_KEYCODE_F4: |
5432 | case SAPP_KEYCODE_F5: |
5433 | case SAPP_KEYCODE_F6: |
5434 | case SAPP_KEYCODE_F7: |
5435 | case SAPP_KEYCODE_F8: |
5436 | case SAPP_KEYCODE_F9: |
5437 | case SAPP_KEYCODE_F10: |
5438 | case SAPP_KEYCODE_F11: |
5439 | case SAPP_KEYCODE_F12: |
5440 | case SAPP_KEYCODE_F13: |
5441 | case SAPP_KEYCODE_F14: |
5442 | case SAPP_KEYCODE_F15: |
5443 | case SAPP_KEYCODE_F16: |
5444 | case SAPP_KEYCODE_F17: |
5445 | case SAPP_KEYCODE_F18: |
5446 | case SAPP_KEYCODE_F19: |
5447 | case SAPP_KEYCODE_F20: |
5448 | case SAPP_KEYCODE_F21: |
5449 | case SAPP_KEYCODE_F22: |
5450 | case SAPP_KEYCODE_F23: |
5451 | case SAPP_KEYCODE_F24: |
5452 | case SAPP_KEYCODE_F25: |
5453 | case SAPP_KEYCODE_LEFT_SHIFT: |
5454 | case SAPP_KEYCODE_LEFT_CONTROL: |
5455 | case SAPP_KEYCODE_LEFT_ALT: |
5456 | case SAPP_KEYCODE_LEFT_SUPER: |
5457 | case SAPP_KEYCODE_RIGHT_SHIFT: |
5458 | case SAPP_KEYCODE_RIGHT_CONTROL: |
5459 | case SAPP_KEYCODE_RIGHT_ALT: |
5460 | case SAPP_KEYCODE_RIGHT_SUPER: |
5461 | case SAPP_KEYCODE_MENU: |
5462 | /* consume the event */ |
5463 | break; |
5464 | default: |
5465 | /* forward key to browser */ |
5466 | retval = false; |
5467 | break; |
5468 | } |
5469 | } |
5470 | if (_sapp_call_event(&_sapp.event)) { |
5471 | /* consume event via sapp_consume_event() */ |
5472 | retval = true; |
5473 | } |
5474 | if (send_keyup_followup) { |
5475 | _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; |
5476 | if (_sapp_call_event(&_sapp.event)) { |
5477 | retval = true; |
5478 | } |
5479 | } |
5480 | } |
5481 | } |
5482 | _sapp_emsc_update_keyboard_state(); |
5483 | _sapp_emsc_update_mouse_lock_state(); |
5484 | return retval; |
5485 | } |
5486 | |
5487 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { |
5488 | _SOKOL_UNUSED(user_data); |
5489 | bool retval = true; |
5490 | if (_sapp_events_enabled()) { |
5491 | sapp_event_type type; |
5492 | switch (emsc_type) { |
5493 | case EMSCRIPTEN_EVENT_TOUCHSTART: |
5494 | type = SAPP_EVENTTYPE_TOUCHES_BEGAN; |
5495 | break; |
5496 | case EMSCRIPTEN_EVENT_TOUCHMOVE: |
5497 | type = SAPP_EVENTTYPE_TOUCHES_MOVED; |
5498 | break; |
5499 | case EMSCRIPTEN_EVENT_TOUCHEND: |
5500 | type = SAPP_EVENTTYPE_TOUCHES_ENDED; |
5501 | break; |
5502 | case EMSCRIPTEN_EVENT_TOUCHCANCEL: |
5503 | type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; |
5504 | break; |
5505 | default: |
5506 | type = SAPP_EVENTTYPE_INVALID; |
5507 | retval = false; |
5508 | break; |
5509 | } |
5510 | if (type != SAPP_EVENTTYPE_INVALID) { |
5511 | _sapp_init_event(type); |
5512 | _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event); |
5513 | _sapp.event.num_touches = emsc_event->numTouches; |
5514 | if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { |
5515 | _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; |
5516 | } |
5517 | for (int i = 0; i < _sapp.event.num_touches; i++) { |
5518 | const EmscriptenTouchPoint* src = &emsc_event->touches[i]; |
5519 | sapp_touchpoint* dst = &_sapp.event.touches[i]; |
5520 | dst->identifier = (uintptr_t)src->identifier; |
5521 | dst->pos_x = src->targetX * _sapp.dpi_scale; |
5522 | dst->pos_y = src->targetY * _sapp.dpi_scale; |
5523 | dst->changed = src->isChanged; |
5524 | } |
5525 | _sapp_call_event(&_sapp.event); |
5526 | } |
5527 | } |
5528 | _sapp_emsc_update_keyboard_state(); |
5529 | return retval; |
5530 | } |
5531 | |
5532 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { |
5533 | _SOKOL_UNUSED(emsc_type); |
5534 | _SOKOL_UNUSED(emsc_event); |
5535 | _SOKOL_UNUSED(user_data); |
5536 | if (_sapp_events_enabled()) { |
5537 | _sapp_init_event(SAPP_EVENTTYPE_FOCUSED); |
5538 | _sapp_call_event(&_sapp.event); |
5539 | } |
5540 | return true; |
5541 | } |
5542 | |
5543 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { |
5544 | _SOKOL_UNUSED(emsc_type); |
5545 | _SOKOL_UNUSED(emsc_event); |
5546 | _SOKOL_UNUSED(user_data); |
5547 | if (_sapp_events_enabled()) { |
5548 | _sapp_init_event(SAPP_EVENTTYPE_UNFOCUSED); |
5549 | _sapp_call_event(&_sapp.event); |
5550 | } |
5551 | return true; |
5552 | } |
5553 | |
5554 | #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) |
5555 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { |
5556 | _SOKOL_UNUSED(reserved); |
5557 | _SOKOL_UNUSED(user_data); |
5558 | sapp_event_type type; |
5559 | switch (emsc_type) { |
5560 | case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; |
5561 | case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; |
5562 | default: type = SAPP_EVENTTYPE_INVALID; break; |
5563 | } |
5564 | if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { |
5565 | _sapp_init_event(type); |
5566 | _sapp_call_event(&_sapp.event); |
5567 | } |
5568 | return true; |
5569 | } |
5570 | |
5571 | _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { |
5572 | EmscriptenWebGLContextAttributes attrs; |
5573 | emscripten_webgl_init_context_attributes(&attrs); |
5574 | attrs.alpha = _sapp.desc.alpha; |
5575 | attrs.depth = true; |
5576 | attrs.stencil = true; |
5577 | attrs.antialias = _sapp.sample_count > 1; |
5578 | attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; |
5579 | attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; |
5580 | attrs.enableExtensionsByDefault = true; |
5581 | #if defined(SOKOL_GLES3) |
5582 | if (_sapp.desc.gl_force_gles2) { |
5583 | attrs.majorVersion = 1; |
5584 | _sapp.gles2_fallback = true; |
5585 | } |
5586 | else { |
5587 | attrs.majorVersion = 2; |
5588 | } |
5589 | #endif |
5590 | EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); |
5591 | if (!ctx) { |
5592 | attrs.majorVersion = 1; |
5593 | ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); |
5594 | _sapp.gles2_fallback = true; |
5595 | } |
5596 | emscripten_webgl_make_context_current(ctx); |
5597 | |
5598 | /* some WebGL extension are not enabled automatically by emscripten */ |
5599 | emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); |
5600 | } |
5601 | #endif |
5602 | |
5603 | #if defined(SOKOL_WGPU) |
5604 | #define _SAPP_EMSC_WGPU_STATE_INITIAL (0) |
5605 | #define _SAPP_EMSC_WGPU_STATE_READY (1) |
5606 | #define _SAPP_EMSC_WGPU_STATE_RUNNING (2) |
5607 | |
5608 | #if defined(__cplusplus) |
5609 | extern "C" { |
5610 | #endif |
5611 | /* called when the asynchronous WebGPU device + swapchain init code in JS has finished */ |
5612 | EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) { |
5613 | SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device); |
5614 | _sapp.emsc.wgpu.device = (WGPUDevice) device_id; |
5615 | _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id; |
5616 | _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt; |
5617 | _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY; |
5618 | } |
5619 | #if defined(__cplusplus) |
5620 | } // extern "C" |
5621 | #endif |
5622 | |
5623 | /* embedded JS function to handle all the asynchronous WebGPU setup */ |
5624 | EM_JS(void, sapp_js_wgpu_init, (), { |
5625 | WebGPU.initManagers(); |
5626 | // FIXME: the extension activation must be more clever here |
5627 | navigator.gpu.requestAdapter().then((adapter) => { |
5628 | console.log("wgpu adapter extensions: " + adapter.extensions); |
5629 | adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then((device) => { |
5630 | var gpuContext = document.getElementById("canvas").getContext("gpupresent"); |
5631 | console.log("wgpu device extensions: " + adapter.extensions); |
5632 | gpuContext.getSwapChainPreferredFormat(device).then((fmt) => { |
5633 | const swapChainDescriptor = { device: device, format: fmt }; |
5634 | const swapChain = gpuContext.configureSwapChain(swapChainDescriptor); |
5635 | const deviceId = WebGPU.mgrDevice.create(device); |
5636 | const swapChainId = WebGPU.mgrSwapChain.create(swapChain); |
5637 | const fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); |
5638 | console.log("wgpu device: " + device); |
5639 | console.log("wgpu swap chain: " + swapChain); |
5640 | console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")"); |
5641 | __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId); |
5642 | }); |
5643 | }); |
5644 | }); |
5645 | }); |
5646 | |
5647 | _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { |
5648 | SOKOL_ASSERT(_sapp.emsc.wgpu.device); |
5649 | SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain); |
5650 | SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex); |
5651 | SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view); |
5652 | SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex); |
5653 | SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view); |
5654 | |
5655 | WGPUTextureDescriptor ds_desc; |
5656 | _sapp_clear(&ds_desc, sizeof(ds_desc)); |
5657 | ds_desc.usage = WGPUTextureUsage_OutputAttachment; |
5658 | ds_desc.dimension = WGPUTextureDimension_2D; |
5659 | ds_desc.size.width = (uint32_t) _sapp.framebuffer_width; |
5660 | ds_desc.size.height = (uint32_t) _sapp.framebuffer_height; |
5661 | ds_desc.size.depth = 1; |
5662 | ds_desc.arrayLayerCount = 1; |
5663 | ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8; |
5664 | ds_desc.mipLevelCount = 1; |
5665 | ds_desc.sampleCount = _sapp.sample_count; |
5666 | _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc); |
5667 | _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0); |
5668 | |
5669 | if (_sapp.sample_count > 1) { |
5670 | WGPUTextureDescriptor msaa_desc; |
5671 | _sapp_clear(&msaa_desc, sizeof(msaa_desc)); |
5672 | msaa_desc.usage = WGPUTextureUsage_OutputAttachment; |
5673 | msaa_desc.dimension = WGPUTextureDimension_2D; |
5674 | msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width; |
5675 | msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height; |
5676 | msaa_desc.size.depth = 1; |
5677 | msaa_desc.arrayLayerCount = 1; |
5678 | msaa_desc.format = _sapp.emsc.wgpu.render_format; |
5679 | msaa_desc.mipLevelCount = 1; |
5680 | msaa_desc.sampleCount = _sapp.sample_count; |
5681 | _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc); |
5682 | _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0); |
5683 | } |
5684 | } |
5685 | |
5686 | _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) { |
5687 | if (_sapp.emsc.wgpu.msaa_tex) { |
5688 | wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex); |
5689 | _sapp.emsc.wgpu.msaa_tex = 0; |
5690 | } |
5691 | if (_sapp.emsc.wgpu.msaa_view) { |
5692 | wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view); |
5693 | _sapp.emsc.wgpu.msaa_view = 0; |
5694 | } |
5695 | if (_sapp.emsc.wgpu.depth_stencil_tex) { |
5696 | wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex); |
5697 | _sapp.emsc.wgpu.depth_stencil_tex = 0; |
5698 | } |
5699 | if (_sapp.emsc.wgpu.depth_stencil_view) { |
5700 | wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view); |
5701 | _sapp.emsc.wgpu.depth_stencil_view = 0; |
5702 | } |
5703 | } |
5704 | |
5705 | _SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) { |
5706 | if (_sapp.emsc.wgpu.swapchain_view) { |
5707 | wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view); |
5708 | } |
5709 | _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain); |
5710 | } |
5711 | #endif |
5712 | |
5713 | _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { |
5714 | emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); |
5715 | emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); |
5716 | emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); |
5717 | emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); |
5718 | emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); |
5719 | emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb); |
5720 | emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); |
5721 | emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); |
5722 | emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); |
5723 | emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); |
5724 | emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); |
5725 | emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); |
5726 | emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); |
5727 | emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); |
5728 | emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); |
5729 | emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_focus_cb); |
5730 | emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_blur_cb); |
5731 | sapp_js_add_beforeunload_listener(); |
5732 | if (_sapp.clipboard.enabled) { |
5733 | sapp_js_add_clipboard_listener(); |
5734 | } |
5735 | if (_sapp.drop.enabled) { |
5736 | sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); |
5737 | } |
5738 | #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) |
5739 | emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); |
5740 | emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); |
5741 | #endif |
5742 | } |
5743 | |
5744 | _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { |
5745 | emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5746 | emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5747 | emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5748 | emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5749 | emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5750 | emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5751 | emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); |
5752 | emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); |
5753 | emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); |
5754 | emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5755 | emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5756 | emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5757 | emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5758 | emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); |
5759 | emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); |
5760 | emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); |
5761 | emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); |
5762 | sapp_js_remove_beforeunload_listener(); |
5763 | if (_sapp.clipboard.enabled) { |
5764 | sapp_js_remove_clipboard_listener(); |
5765 | } |
5766 | if (_sapp.drop.enabled) { |
5767 | sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); |
5768 | } |
5769 | #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) |
5770 | emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5771 | emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); |
5772 | #endif |
5773 | } |
5774 | |
5775 | _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { |
5776 | _SOKOL_UNUSED(userData); |
5777 | _sapp_timing_external(&_sapp.timing, time / 1000.0); |
5778 | |
5779 | #if defined(SOKOL_WGPU) |
5780 | /* |
5781 | on WebGPU, the emscripten frame callback will already be called while |
5782 | the asynchronous WebGPU device and swapchain initialization is still |
5783 | in progress |
5784 | */ |
5785 | switch (_sapp.emsc.wgpu.state) { |
5786 | case _SAPP_EMSC_WGPU_STATE_INITIAL: |
5787 | /* async JS init hasn't finished yet */ |
5788 | break; |
5789 | case _SAPP_EMSC_WGPU_STATE_READY: |
5790 | /* perform post-async init stuff */ |
5791 | _sapp_emsc_wgpu_surfaces_create(); |
5792 | _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING; |
5793 | break; |
5794 | case _SAPP_EMSC_WGPU_STATE_RUNNING: |
5795 | /* a regular frame */ |
5796 | _sapp_emsc_wgpu_next_frame(); |
5797 | _sapp_frame(); |
5798 | break; |
5799 | } |
5800 | #else |
5801 | /* WebGL code path */ |
5802 | _sapp_frame(); |
5803 | #endif |
5804 | |
5805 | /* quit-handling */ |
5806 | if (_sapp.quit_requested) { |
5807 | _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); |
5808 | _sapp_call_event(&_sapp.event); |
5809 | if (_sapp.quit_requested) { |
5810 | _sapp.quit_ordered = true; |
5811 | } |
5812 | } |
5813 | if (_sapp.quit_ordered) { |
5814 | _sapp_emsc_unregister_eventhandlers(); |
5815 | _sapp_call_cleanup(); |
5816 | _sapp_discard_state(); |
5817 | return EM_FALSE; |
5818 | } |
5819 | return EM_TRUE; |
5820 | } |
5821 | |
5822 | _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { |
5823 | _sapp_init_state(desc); |
5824 | sapp_js_init(&_sapp.html5_canvas_selector[1]); |
5825 | double w, h; |
5826 | if (_sapp.desc.html5_canvas_resize) { |
5827 | w = (double) _sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); |
5828 | h = (double) _sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); |
5829 | } |
5830 | else { |
5831 | emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); |
5832 | emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); |
5833 | } |
5834 | if (_sapp.desc.high_dpi) { |
5835 | _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); |
5836 | } |
5837 | _sapp.window_width = (int)roundf(w); |
5838 | _sapp.window_height = (int)roundf(h); |
5839 | _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); |
5840 | _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); |
5841 | emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); |
5842 | #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) |
5843 | _sapp_emsc_webgl_init(); |
5844 | #elif defined(SOKOL_WGPU) |
5845 | sapp_js_wgpu_init(); |
5846 | #endif |
5847 | _sapp.valid = true; |
5848 | _sapp_emsc_register_eventhandlers(); |
5849 | sapp_set_icon(&desc->icon); |
5850 | |
5851 | /* start the frame loop */ |
5852 | emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0); |
5853 | |
5854 | /* NOT A BUG: do not call _sapp_discard_state() here, instead this is |
5855 | called in _sapp_emsc_frame() when the application is ordered to quit |
5856 | */ |
5857 | } |
5858 | |
5859 | #if !defined(SOKOL_NO_ENTRY) |
5860 | int main(int argc, char* argv[]) { |
5861 | sapp_desc desc = sokol_main(argc, argv); |
5862 | _sapp_emsc_run(&desc); |
5863 | return 0; |
5864 | } |
5865 | #endif /* SOKOL_NO_ENTRY */ |
5866 | #endif /* _SAPP_EMSCRIPTEN */ |
5867 | |
5868 | /*== MISC GL SUPPORT FUNCTIONS ================================================*/ |
5869 | #if defined(SOKOL_GLCORE33) |
5870 | typedef struct { |
5871 | int red_bits; |
5872 | int green_bits; |
5873 | int blue_bits; |
5874 | int alpha_bits; |
5875 | int depth_bits; |
5876 | int stencil_bits; |
5877 | int samples; |
5878 | bool doublebuffer; |
5879 | uintptr_t handle; |
5880 | } _sapp_gl_fbconfig; |
5881 | |
5882 | _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { |
5883 | _sapp_clear(fbconfig, sizeof(_sapp_gl_fbconfig)); |
5884 | /* -1 means "don't care" */ |
5885 | fbconfig->red_bits = -1; |
5886 | fbconfig->green_bits = -1; |
5887 | fbconfig->blue_bits = -1; |
5888 | fbconfig->alpha_bits = -1; |
5889 | fbconfig->depth_bits = -1; |
5890 | fbconfig->stencil_bits = -1; |
5891 | fbconfig->samples = -1; |
5892 | } |
5893 | |
5894 | _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) { |
5895 | int missing, least_missing = 1000000; |
5896 | int color_diff, least_color_diff = 10000000; |
5897 | int extra_diff, least_extra_diff = 10000000; |
5898 | const _sapp_gl_fbconfig* current; |
5899 | const _sapp_gl_fbconfig* closest = 0; |
5900 | for (int i = 0; i < count; i++) { |
5901 | current = alternatives + i; |
5902 | if (desired->doublebuffer != current->doublebuffer) { |
5903 | continue; |
5904 | } |
5905 | missing = 0; |
5906 | if (desired->alpha_bits > 0 && current->alpha_bits == 0) { |
5907 | missing++; |
5908 | } |
5909 | if (desired->depth_bits > 0 && current->depth_bits == 0) { |
5910 | missing++; |
5911 | } |
5912 | if (desired->stencil_bits > 0 && current->stencil_bits == 0) { |
5913 | missing++; |
5914 | } |
5915 | if (desired->samples > 0 && current->samples == 0) { |
5916 | /* Technically, several multisampling buffers could be |
5917 | involved, but that's a lower level implementation detail and |
5918 | not important to us here, so we count them as one |
5919 | */ |
5920 | missing++; |
5921 | } |
5922 | |
5923 | /* These polynomials make many small channel size differences matter |
5924 | less than one large channel size difference |
5925 | Calculate color channel size difference value |
5926 | */ |
5927 | color_diff = 0; |
5928 | if (desired->red_bits != -1) { |
5929 | color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); |
5930 | } |
5931 | if (desired->green_bits != -1) { |
5932 | color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); |
5933 | } |
5934 | if (desired->blue_bits != -1) { |
5935 | color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); |
5936 | } |
5937 | |
5938 | /* Calculate non-color channel size difference value */ |
5939 | extra_diff = 0; |
5940 | if (desired->alpha_bits != -1) { |
5941 | extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); |
5942 | } |
5943 | if (desired->depth_bits != -1) { |
5944 | extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); |
5945 | } |
5946 | if (desired->stencil_bits != -1) { |
5947 | extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); |
5948 | } |
5949 | if (desired->samples != -1) { |
5950 | extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); |
5951 | } |
5952 | |
5953 | /* Figure out if the current one is better than the best one found so far |
5954 | Least number of missing buffers is the most important heuristic, |
5955 | then color buffer size match and lastly size match for other buffers |
5956 | */ |
5957 | if (missing < least_missing) { |
5958 | closest = current; |
5959 | } |
5960 | else if (missing == least_missing) { |
5961 | if ((color_diff < least_color_diff) || |
5962 | (color_diff == least_color_diff && extra_diff < least_extra_diff)) |
5963 | { |
5964 | closest = current; |
5965 | } |
5966 | } |
5967 | if (current == closest) { |
5968 | least_missing = missing; |
5969 | least_color_diff = color_diff; |
5970 | least_extra_diff = extra_diff; |
5971 | } |
5972 | } |
5973 | return closest; |
5974 | } |
5975 | #endif |
5976 | |
5977 | /*== WINDOWS DESKTOP and UWP====================================================*/ |
5978 | #if defined(_SAPP_WIN32) || defined(_SAPP_UWP) |
5979 | _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { |
5980 | SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); |
5981 | _sapp_clear(dst, (size_t)dst_num_bytes); |
5982 | const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); |
5983 | const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); |
5984 | if ((dst_needed > 0) && (dst_needed < dst_chars)) { |
5985 | MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); |
5986 | return true; |
5987 | } |
5988 | else { |
5989 | /* input string doesn't fit into destination buffer */ |
5990 | return false; |
5991 | } |
5992 | } |
5993 | |
5994 | _SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { |
5995 | if (_sapp_events_enabled()) { |
5996 | _sapp_init_event(type); |
5997 | _sapp_call_event(&_sapp.event); |
5998 | } |
5999 | } |
6000 | |
6001 | _SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { |
6002 | /* same as GLFW */ |
6003 | _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; |
6004 | _sapp.keycodes[0x002] = SAPP_KEYCODE_1; |
6005 | _sapp.keycodes[0x003] = SAPP_KEYCODE_2; |
6006 | _sapp.keycodes[0x004] = SAPP_KEYCODE_3; |
6007 | _sapp.keycodes[0x005] = SAPP_KEYCODE_4; |
6008 | _sapp.keycodes[0x006] = SAPP_KEYCODE_5; |
6009 | _sapp.keycodes[0x007] = SAPP_KEYCODE_6; |
6010 | _sapp.keycodes[0x008] = SAPP_KEYCODE_7; |
6011 | _sapp.keycodes[0x009] = SAPP_KEYCODE_8; |
6012 | _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; |
6013 | _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; |
6014 | _sapp.keycodes[0x030] = SAPP_KEYCODE_B; |
6015 | _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; |
6016 | _sapp.keycodes[0x020] = SAPP_KEYCODE_D; |
6017 | _sapp.keycodes[0x012] = SAPP_KEYCODE_E; |
6018 | _sapp.keycodes[0x021] = SAPP_KEYCODE_F; |
6019 | _sapp.keycodes[0x022] = SAPP_KEYCODE_G; |
6020 | _sapp.keycodes[0x023] = SAPP_KEYCODE_H; |
6021 | _sapp.keycodes[0x017] = SAPP_KEYCODE_I; |
6022 | _sapp.keycodes[0x024] = SAPP_KEYCODE_J; |
6023 | _sapp.keycodes[0x025] = SAPP_KEYCODE_K; |
6024 | _sapp.keycodes[0x026] = SAPP_KEYCODE_L; |
6025 | _sapp.keycodes[0x032] = SAPP_KEYCODE_M; |
6026 | _sapp.keycodes[0x031] = SAPP_KEYCODE_N; |
6027 | _sapp.keycodes[0x018] = SAPP_KEYCODE_O; |
6028 | _sapp.keycodes[0x019] = SAPP_KEYCODE_P; |
6029 | _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; |
6030 | _sapp.keycodes[0x013] = SAPP_KEYCODE_R; |
6031 | _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; |
6032 | _sapp.keycodes[0x014] = SAPP_KEYCODE_T; |
6033 | _sapp.keycodes[0x016] = SAPP_KEYCODE_U; |
6034 | _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; |
6035 | _sapp.keycodes[0x011] = SAPP_KEYCODE_W; |
6036 | _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; |
6037 | _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; |
6038 | _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; |
6039 | _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; |
6040 | _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; |
6041 | _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; |
6042 | _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; |
6043 | _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; |
6044 | _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; |
6045 | _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; |
6046 | _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; |
6047 | _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; |
6048 | _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; |
6049 | _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; |
6050 | _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; |
6051 | _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; |
6052 | _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; |
6053 | _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; |
6054 | _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; |
6055 | _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; |
6056 | _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; |
6057 | _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; |
6058 | _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; |
6059 | _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; |
6060 | _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; |
6061 | _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; |
6062 | _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; |
6063 | _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; |
6064 | _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; |
6065 | _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; |
6066 | _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; |
6067 | _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; |
6068 | _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; |
6069 | _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; |
6070 | _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; |
6071 | _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; |
6072 | _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; |
6073 | _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; |
6074 | _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; |
6075 | _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; |
6076 | _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; |
6077 | _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; |
6078 | _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; |
6079 | _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; |
6080 | _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; |
6081 | _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; |
6082 | _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; |
6083 | _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; |
6084 | _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; |
6085 | _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; |
6086 | _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; |
6087 | _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; |
6088 | _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; |
6089 | _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; |
6090 | _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; |
6091 | _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; |
6092 | _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; |
6093 | _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; |
6094 | _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; |
6095 | _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; |
6096 | _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; |
6097 | _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; |
6098 | _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; |
6099 | _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; |
6100 | _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; |
6101 | _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; |
6102 | _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; |
6103 | _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; |
6104 | _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; |
6105 | _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; |
6106 | _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; |
6107 | _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; |
6108 | _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; |
6109 | _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; |
6110 | _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; |
6111 | _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; |
6112 | _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; |
6113 | _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; |
6114 | _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; |
6115 | _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; |
6116 | _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; |
6117 | _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; |
6118 | _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; |
6119 | _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; |
6120 | _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; |
6121 | } |
6122 | #endif // _SAPP_WIN32 || _SAPP_UWP |
6123 | |
6124 | /*== WINDOWS DESKTOP===========================================================*/ |
6125 | #if defined(_SAPP_WIN32) |
6126 | |
6127 | #if defined(SOKOL_D3D11) |
6128 | |
6129 | #if defined(__cplusplus) |
6130 | #define _sapp_d3d11_Release(self) (self)->Release() |
6131 | #define _sapp_win32_refiid(iid) iid |
6132 | #else |
6133 | #define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self) |
6134 | #define _sapp_win32_refiid(iid) &iid |
6135 | #endif |
6136 | |
6137 | #define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; } |
6138 | |
6139 | |
6140 | static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89, {0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} }; |
6141 | static const IID _sapp_IID_IDXGIDevice1 = { 0x77db970f,0x6276,0x48ba, {0xba,0x28,0x07,0x01,0x43,0xb4,0x39,0x2c} }; |
6142 | static const IID _sapp_IID_IDXGIFactory = { 0x7b7166ec,0x21c7,0x44ae, {0xb2,0x1a,0xc9,0xae,0x32,0x1a,0xe3,0x69} }; |
6143 | |
6144 | static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) { |
6145 | #if defined(__cplusplus) |
6146 | return self->GetBuffer(Buffer, riid, ppSurface); |
6147 | #else |
6148 | return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface); |
6149 | #endif |
6150 | } |
6151 | |
6152 | static inline HRESULT _sapp_d3d11_QueryInterface(ID3D11Device* self, REFIID riid, void** ppvObject) { |
6153 | #if defined(__cplusplus) |
6154 | return self->QueryInterface(riid, ppvObject); |
6155 | #else |
6156 | return self->lpVtbl->QueryInterface(self, riid, ppvObject); |
6157 | #endif |
6158 | } |
6159 | |
6160 | static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { |
6161 | #if defined(__cplusplus) |
6162 | return self->CreateRenderTargetView(pResource, pDesc, ppRTView); |
6163 | #else |
6164 | return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); |
6165 | #endif |
6166 | } |
6167 | |
6168 | static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { |
6169 | #if defined(__cplusplus) |
6170 | return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); |
6171 | #else |
6172 | return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); |
6173 | #endif |
6174 | } |
6175 | |
6176 | static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { |
6177 | #if defined(__cplusplus) |
6178 | return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); |
6179 | #else |
6180 | return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); |
6181 | #endif |
6182 | } |
6183 | |
6184 | static inline void _sapp_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { |
6185 | #if defined(__cplusplus) |
6186 | self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); |
6187 | #else |
6188 | self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); |
6189 | #endif |
6190 | } |
6191 | |
6192 | static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { |
6193 | #if defined(__cplusplus) |
6194 | return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); |
6195 | #else |
6196 | return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags); |
6197 | #endif |
6198 | } |
6199 | |
6200 | static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) { |
6201 | #if defined(__cplusplus) |
6202 | return self->Present(SyncInterval, Flags); |
6203 | #else |
6204 | return self->lpVtbl->Present(self, SyncInterval, Flags); |
6205 | #endif |
6206 | } |
6207 | |
6208 | static inline HRESULT _sapp_dxgi_GetFrameStatistics(IDXGISwapChain* self, DXGI_FRAME_STATISTICS* pStats) { |
6209 | #if defined(__cplusplus) |
6210 | return self->GetFrameStatistics(pStats); |
6211 | #else |
6212 | return self->lpVtbl->GetFrameStatistics(self, pStats); |
6213 | #endif |
6214 | } |
6215 | |
6216 | static inline HRESULT _sapp_dxgi_SetMaximumFrameLatency(IDXGIDevice1* self, UINT MaxLatency) { |
6217 | #if defined(__cplusplus) |
6218 | return self->SetMaximumFrameLatency(MaxLatency); |
6219 | #else |
6220 | return self->lpVtbl->SetMaximumFrameLatency(self, MaxLatency); |
6221 | #endif |
6222 | } |
6223 | |
6224 | static inline HRESULT _sapp_dxgi_GetAdapter(IDXGIDevice1* self, IDXGIAdapter** pAdapter) { |
6225 | #if defined(__cplusplus) |
6226 | return self->GetAdapter(pAdapter); |
6227 | #else |
6228 | return self->lpVtbl->GetAdapter(self, pAdapter); |
6229 | #endif |
6230 | } |
6231 | |
6232 | static inline HRESULT _sapp_dxgi_GetParent(IDXGIObject* self, REFIID riid, void** ppParent) { |
6233 | #if defined(__cplusplus) |
6234 | return self->GetParent(riid, ppParent); |
6235 | #else |
6236 | return self->lpVtbl->GetParent(self, riid, ppParent); |
6237 | #endif |
6238 | } |
6239 | |
6240 | static inline HRESULT _sapp_dxgi_MakeWindowAssociation(IDXGIFactory* self, HWND WindowHandle, UINT Flags) { |
6241 | #if defined(__cplusplus) |
6242 | return self->MakeWindowAssociation(WindowHandle, Flags); |
6243 | #else |
6244 | return self->lpVtbl->MakeWindowAssociation(self, WindowHandle, Flags); |
6245 | #endif |
6246 | } |
6247 | |
6248 | _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { |
6249 | DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; |
6250 | sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width; |
6251 | sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height; |
6252 | sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; |
6253 | sc_desc->BufferDesc.RefreshRate.Numerator = 60; |
6254 | sc_desc->BufferDesc.RefreshRate.Denominator = 1; |
6255 | sc_desc->OutputWindow = _sapp.win32.hwnd; |
6256 | sc_desc->Windowed = true; |
6257 | if (_sapp.win32.is_win10_or_greater) { |
6258 | sc_desc->BufferCount = 2; |
6259 | sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD; |
6260 | _sapp.d3d11.use_dxgi_frame_stats = true; |
6261 | } |
6262 | else { |
6263 | sc_desc->BufferCount = 1; |
6264 | sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; |
6265 | _sapp.d3d11.use_dxgi_frame_stats = false; |
6266 | } |
6267 | sc_desc->SampleDesc.Count = 1; |
6268 | sc_desc->SampleDesc.Quality = 0; |
6269 | sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; |
6270 | UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; |
6271 | #if defined(SOKOL_DEBUG) |
6272 | create_flags |= D3D11_CREATE_DEVICE_DEBUG; |
6273 | #endif |
6274 | D3D_FEATURE_LEVEL feature_level; |
6275 | HRESULT hr = D3D11CreateDeviceAndSwapChain( |
6276 | NULL, /* pAdapter (use default) */ |
6277 | D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ |
6278 | NULL, /* Software */ |
6279 | create_flags, /* Flags */ |
6280 | NULL, /* pFeatureLevels */ |
6281 | 0, /* FeatureLevels */ |
6282 | D3D11_SDK_VERSION, /* SDKVersion */ |
6283 | sc_desc, /* pSwapChainDesc */ |
6284 | &_sapp.d3d11.swap_chain, /* ppSwapChain */ |
6285 | &_sapp.d3d11.device, /* ppDevice */ |
6286 | &feature_level, /* pFeatureLevel */ |
6287 | &_sapp.d3d11.device_context); /* ppImmediateContext */ |
6288 | _SOKOL_UNUSED(hr); |
6289 | #if defined(SOKOL_DEBUG) |
6290 | if (!SUCCEEDED(hr)) { |
6291 | // if initialization with D3D11_CREATE_DEVICE_DEBUG failes, this could be because the |
6292 | // 'D3D11 debug layer' stopped working, indicated by the error message: |
6293 | // === |
6294 | // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system. |
6295 | // These flags must be removed, or the Windows 10 SDK must be installed. |
6296 | // Flags include: D3D11_CREATE_DEVICE_DEBUG |
6297 | // === |
6298 | // |
6299 | // ...just retry with the DEBUG flag switched off |
6300 | SAPP_LOG("sokol_app.h: D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.\n"); |
6301 | create_flags &= ~D3D11_CREATE_DEVICE_DEBUG; |
6302 | hr = D3D11CreateDeviceAndSwapChain( |
6303 | NULL, /* pAdapter (use default) */ |
6304 | D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ |
6305 | NULL, /* Software */ |
6306 | create_flags, /* Flags */ |
6307 | NULL, /* pFeatureLevels */ |
6308 | 0, /* FeatureLevels */ |
6309 | D3D11_SDK_VERSION, /* SDKVersion */ |
6310 | sc_desc, /* pSwapChainDesc */ |
6311 | &_sapp.d3d11.swap_chain, /* ppSwapChain */ |
6312 | &_sapp.d3d11.device, /* ppDevice */ |
6313 | &feature_level, /* pFeatureLevel */ |
6314 | &_sapp.d3d11.device_context); /* ppImmediateContext */ |
6315 | } |
6316 | #endif |
6317 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); |
6318 | |
6319 | // mimimize frame latency, disable Alt-Enter |
6320 | hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device); |
6321 | if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) { |
6322 | _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1); |
6323 | IDXGIAdapter* dxgi_adapter = 0; |
6324 | hr = _sapp_dxgi_GetAdapter(_sapp.d3d11.dxgi_device, &dxgi_adapter); |
6325 | if (SUCCEEDED(hr) && dxgi_adapter) { |
6326 | IDXGIFactory* dxgi_factory = 0; |
6327 | hr = _sapp_dxgi_GetParent((IDXGIObject*)dxgi_adapter, _sapp_win32_refiid(_sapp_IID_IDXGIFactory), (void**)&dxgi_factory); |
6328 | if (SUCCEEDED(hr)) { |
6329 | _sapp_dxgi_MakeWindowAssociation(dxgi_factory, _sapp.win32.hwnd, DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN); |
6330 | _SAPP_SAFE_RELEASE(dxgi_factory); |
6331 | } |
6332 | else { |
6333 | SAPP_LOG("sokol_app.h: could not obtain IDXGIFactory object.\n"); |
6334 | } |
6335 | _SAPP_SAFE_RELEASE(dxgi_adapter); |
6336 | } |
6337 | else { |
6338 | SAPP_LOG("sokol_app.h: could not obtain IDXGIAdapter object.\n"); |
6339 | } |
6340 | } |
6341 | else { |
6342 | SAPP_LOG("sokol_app.h: could not obtain IDXGIDevice1 interface\n"); |
6343 | } |
6344 | } |
6345 | |
6346 | _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { |
6347 | _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); |
6348 | _SAPP_SAFE_RELEASE(_sapp.d3d11.dxgi_device); |
6349 | _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); |
6350 | _SAPP_SAFE_RELEASE(_sapp.d3d11.device); |
6351 | } |
6352 | |
6353 | _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { |
6354 | SOKOL_ASSERT(0 == _sapp.d3d11.rt); |
6355 | SOKOL_ASSERT(0 == _sapp.d3d11.rtv); |
6356 | SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); |
6357 | SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); |
6358 | SOKOL_ASSERT(0 == _sapp.d3d11.ds); |
6359 | SOKOL_ASSERT(0 == _sapp.d3d11.dsv); |
6360 | |
6361 | HRESULT hr; |
6362 | |
6363 | /* view for the swapchain-created framebuffer */ |
6364 | hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_win32_refiid(_sapp_IID_ID3D11Texture2D), (void**)&_sapp.d3d11.rt); |
6365 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); |
6366 | hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); |
6367 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); |
6368 | |
6369 | /* common desc for MSAA and depth-stencil texture */ |
6370 | D3D11_TEXTURE2D_DESC tex_desc; |
6371 | _sapp_clear(&tex_desc, sizeof(tex_desc)); |
6372 | tex_desc.Width = (UINT)_sapp.framebuffer_width; |
6373 | tex_desc.Height = (UINT)_sapp.framebuffer_height; |
6374 | tex_desc.MipLevels = 1; |
6375 | tex_desc.ArraySize = 1; |
6376 | tex_desc.Usage = D3D11_USAGE_DEFAULT; |
6377 | tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; |
6378 | tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count; |
6379 | tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); |
6380 | |
6381 | /* create MSAA texture and view if antialiasing requested */ |
6382 | if (_sapp.sample_count > 1) { |
6383 | tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; |
6384 | hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); |
6385 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); |
6386 | hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); |
6387 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); |
6388 | } |
6389 | |
6390 | /* texture and view for the depth-stencil-surface */ |
6391 | tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; |
6392 | tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; |
6393 | hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); |
6394 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); |
6395 | hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); |
6396 | SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); |
6397 | } |
6398 | |
6399 | _SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { |
6400 | _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); |
6401 | _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); |
6402 | _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); |
6403 | _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); |
6404 | _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); |
6405 | _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); |
6406 | } |
6407 | |
6408 | _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { |
6409 | if (_sapp.d3d11.swap_chain) { |
6410 | _sapp_d3d11_destroy_default_render_target(); |
6411 | _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); |
6412 | _sapp_d3d11_create_default_render_target(); |
6413 | } |
6414 | } |
6415 | |
6416 | _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { |
6417 | /* do MSAA resolve if needed */ |
6418 | if (_sapp.sample_count > 1) { |
6419 | SOKOL_ASSERT(_sapp.d3d11.rt); |
6420 | SOKOL_ASSERT(_sapp.d3d11.msaa_rt); |
6421 | _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); |
6422 | } |
6423 | UINT flags = 0; |
6424 | if (_sapp.win32.is_win10_or_greater && do_not_wait) { |
6425 | /* this hack/workaround somewhat improves window-movement and -sizing |
6426 | responsiveness when rendering is controlled via WM_TIMER during window |
6427 | move and resize on NVIDIA cards on Win10 with recent drivers. |
6428 | */ |
6429 | flags = DXGI_PRESENT_DO_NOT_WAIT; |
6430 | } |
6431 | _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, flags); |
6432 | } |
6433 | |
6434 | #endif /* SOKOL_D3D11 */ |
6435 | |
6436 | #if defined(SOKOL_GLCORE33) |
6437 | _SOKOL_PRIVATE void _sapp_wgl_init(void) { |
6438 | _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); |
6439 | if (!_sapp.wgl.opengl32) { |
6440 | _sapp_fail("Failed to load opengl32.dll\n"); |
6441 | } |
6442 | SOKOL_ASSERT(_sapp.wgl.opengl32); |
6443 | _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); |
6444 | SOKOL_ASSERT(_sapp.wgl.CreateContext); |
6445 | _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); |
6446 | SOKOL_ASSERT(_sapp.wgl.DeleteContext); |
6447 | _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); |
6448 | SOKOL_ASSERT(_sapp.wgl.GetProcAddress); |
6449 | _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); |
6450 | SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); |
6451 | _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); |
6452 | SOKOL_ASSERT(_sapp.wgl.MakeCurrent); |
6453 | |
6454 | _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, |
6455 | L"SOKOLAPP", |
6456 | L"sokol-app message window", |
6457 | WS_CLIPSIBLINGS|WS_CLIPCHILDREN, |
6458 | 0, 0, 1, 1, |
6459 | NULL, NULL, |
6460 | GetModuleHandleW(NULL), |
6461 | NULL); |
6462 | if (!_sapp.wgl.msg_hwnd) { |
6463 | _sapp_fail("Win32: failed to create helper window!\n"); |
6464 | } |
6465 | SOKOL_ASSERT(_sapp.wgl.msg_hwnd); |
6466 | ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); |
6467 | MSG msg; |
6468 | while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { |
6469 | TranslateMessage(&msg); |
6470 | DispatchMessageW(&msg); |
6471 | } |
6472 | _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); |
6473 | if (!_sapp.wgl.msg_dc) { |
6474 | _sapp_fail("Win32: failed to obtain helper window DC!\n"); |
6475 | } |
6476 | } |
6477 | |
6478 | _SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { |
6479 | SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); |
6480 | DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; |
6481 | FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; |
6482 | } |
6483 | |
6484 | _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { |
6485 | SOKOL_ASSERT(ext && extensions); |
6486 | const char* start = extensions; |
6487 | while (true) { |
6488 | const char* where = strstr(start, ext); |
6489 | if (!where) { |
6490 | return false; |
6491 | } |
6492 | const char* terminator = where + strlen(ext); |
6493 | if ((where == start) || (*(where - 1) == ' ')) { |
6494 | if (*terminator == ' ' || *terminator == '\0') { |
6495 | break; |
6496 | } |
6497 | } |
6498 | start = terminator; |
6499 | } |
6500 | return true; |
6501 | } |
6502 | |
6503 | _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { |
6504 | SOKOL_ASSERT(ext); |
6505 | if (_sapp.wgl.GetExtensionsStringEXT) { |
6506 | const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); |
6507 | if (extensions) { |
6508 | if (_sapp_wgl_has_ext(ext, extensions)) { |
6509 | return true; |
6510 | } |
6511 | } |
6512 | } |
6513 | if (_sapp.wgl.GetExtensionsStringARB) { |
6514 | const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); |
6515 | if (extensions) { |
6516 | if (_sapp_wgl_has_ext(ext, extensions)) { |
6517 | return true; |
6518 | } |
6519 | } |
6520 | } |
6521 | return false; |
6522 | } |
6523 | |
6524 | _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { |
6525 | SOKOL_ASSERT(_sapp.wgl.msg_dc); |
6526 | PIXELFORMATDESCRIPTOR pfd; |
6527 | _sapp_clear(&pfd, sizeof(pfd)); |
6528 | pfd.nSize = sizeof(pfd); |
6529 | pfd.nVersion = 1; |
6530 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; |
6531 | pfd.iPixelType = PFD_TYPE_RGBA; |
6532 | pfd.cColorBits = 24; |
6533 | if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { |
6534 | _sapp_fail("WGL: failed to set pixel format for dummy context\n"); |
6535 | } |
6536 | HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); |
6537 | if (!rc) { |
6538 | _sapp_fail("WGL: Failed to create dummy context\n"); |
6539 | } |
6540 | if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { |
6541 | _sapp_fail("WGL: Failed to make context current\n"); |
6542 | } |
6543 | _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); |
6544 | _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); |
6545 | _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); |
6546 | _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); |
6547 | _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); |
6548 | _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); |
6549 | _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); |
6550 | _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); |
6551 | _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); |
6552 | _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); |
6553 | _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); |
6554 | _sapp.wgl.DeleteContext(rc); |
6555 | } |
6556 | |
6557 | _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { |
6558 | SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); |
6559 | int value = 0; |
6560 | if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { |
6561 | _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); |
6562 | } |
6563 | return value; |
6564 | } |
6565 | |
6566 | _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { |
6567 | SOKOL_ASSERT(_sapp.win32.dc); |
6568 | SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); |
6569 | const _sapp_gl_fbconfig* closest; |
6570 | |
6571 | int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); |
6572 | _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); |
6573 | SOKOL_ASSERT(usable_configs); |
6574 | int usable_count = 0; |
6575 | for (int i = 0; i < native_count; i++) { |
6576 | const int n = i + 1; |
6577 | _sapp_gl_fbconfig* u = usable_configs + usable_count; |
6578 | _sapp_gl_init_fbconfig(u); |
6579 | if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { |
6580 | continue; |
6581 | } |
6582 | if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { |
6583 | continue; |
6584 | } |
6585 | if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { |
6586 | continue; |
6587 | } |
6588 | u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); |
6589 | u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); |
6590 | u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); |
6591 | u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); |
6592 | u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); |
6593 | u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); |
6594 | if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { |
6595 | u->doublebuffer = true; |
6596 | } |
6597 | if (_sapp.wgl.arb_multisample) { |
6598 | u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); |
6599 | } |
6600 | u->handle = (uintptr_t)n; |
6601 | usable_count++; |
6602 | } |
6603 | SOKOL_ASSERT(usable_count > 0); |
6604 | _sapp_gl_fbconfig desired; |
6605 | _sapp_gl_init_fbconfig(&desired); |
6606 | desired.red_bits = 8; |
6607 | desired.green_bits = 8; |
6608 | desired.blue_bits = 8; |
6609 | desired.alpha_bits = 8; |
6610 | desired.depth_bits = 24; |
6611 | desired.stencil_bits = 8; |
6612 | desired.doublebuffer = true; |
6613 | desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; |
6614 | closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); |
6615 | int pixel_format = 0; |
6616 | if (closest) { |
6617 | pixel_format = (int) closest->handle; |
6618 | } |
6619 | _sapp_free(usable_configs); |
6620 | return pixel_format; |
6621 | } |
6622 | |
6623 | _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { |
6624 | int pixel_format = _sapp_wgl_find_pixel_format(); |
6625 | if (0 == pixel_format) { |
6626 | _sapp_fail("WGL: Didn't find matching pixel format.\n"); |
6627 | } |
6628 | PIXELFORMATDESCRIPTOR pfd; |
6629 | if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { |
6630 | _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); |
6631 | } |
6632 | if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { |
6633 | _sapp_fail("WGL: Failed to set selected pixel format!\n"); |
6634 | } |
6635 | if (!_sapp.wgl.arb_create_context) { |
6636 | _sapp_fail("WGL: ARB_create_context required!\n"); |
6637 | } |
6638 | if (!_sapp.wgl.arb_create_context_profile) { |
6639 | _sapp_fail("WGL: ARB_create_context_profile required!\n"); |
6640 | } |
6641 | const int attrs[] = { |
6642 | WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, |
6643 | WGL_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, |
6644 | WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, |
6645 | WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, |
6646 | 0, 0 |
6647 | }; |
6648 | _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); |
6649 | if (!_sapp.wgl.gl_ctx) { |
6650 | const DWORD err = GetLastError(); |
6651 | if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { |
6652 | _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); |
6653 | } |
6654 | else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { |
6655 | _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); |
6656 | } |
6657 | else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { |
6658 | _sapp_fail("WGL: The share context is not compatible with the requested context"); |
6659 | } |
6660 | else { |
6661 | _sapp_fail("WGL: Failed to create OpenGL context"); |
6662 | } |
6663 | } |
6664 | _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); |
6665 | if (_sapp.wgl.ext_swap_control) { |
6666 | /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ |
6667 | _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); |
6668 | } |
6669 | } |
6670 | |
6671 | _SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { |
6672 | SOKOL_ASSERT(_sapp.wgl.gl_ctx); |
6673 | _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); |
6674 | _sapp.wgl.gl_ctx = 0; |
6675 | } |
6676 | |
6677 | _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { |
6678 | SOKOL_ASSERT(_sapp.win32.dc); |
6679 | /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ |
6680 | SwapBuffers(_sapp.win32.dc); |
6681 | } |
6682 | #endif /* SOKOL_GLCORE33 */ |
6683 | |
6684 | _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { |
6685 | SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); |
6686 | _sapp_clear(dst, (size_t)dst_num_bytes); |
6687 | const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); |
6688 | if (bytes_needed <= dst_num_bytes) { |
6689 | WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); |
6690 | return true; |
6691 | } |
6692 | else { |
6693 | return false; |
6694 | } |
6695 | } |
6696 | |
6697 | /* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ |
6698 | _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { |
6699 | RECT rect; |
6700 | if (GetClientRect(_sapp.win32.hwnd, &rect)) { |
6701 | float window_width = (float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale; |
6702 | float window_height = (float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale; |
6703 | _sapp.window_width = (int)roundf(window_width); |
6704 | _sapp.window_height = (int)roundf(window_height); |
6705 | int fb_width = (int)roundf(window_width * _sapp.win32.dpi.content_scale); |
6706 | int fb_height = (int)roundf(window_height * _sapp.win32.dpi.content_scale); |
6707 | /* prevent a framebuffer size of 0 when window is minimized */ |
6708 | if (0 == fb_width) { |
6709 | fb_width = 1; |
6710 | } |
6711 | if (0 == fb_height) { |
6712 | fb_height = 1; |
6713 | } |
6714 | if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { |
6715 | _sapp.framebuffer_width = fb_width; |
6716 | _sapp.framebuffer_height = fb_height; |
6717 | return true; |
6718 | } |
6719 | } |
6720 | else { |
6721 | _sapp.window_width = _sapp.window_height = 1; |
6722 | _sapp.framebuffer_width = _sapp.framebuffer_height = 1; |
6723 | } |
6724 | return false; |
6725 | } |
6726 | |
6727 | _SOKOL_PRIVATE void _sapp_win32_set_fullscreen(bool fullscreen, UINT swp_flags) { |
6728 | HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); |
6729 | MONITORINFO minfo; |
6730 | _sapp_clear(&minfo, sizeof(minfo)); |
6731 | minfo.cbSize = sizeof(MONITORINFO); |
6732 | GetMonitorInfo(monitor, &minfo); |
6733 | const RECT mr = minfo.rcMonitor; |
6734 | const int monitor_w = mr.right - mr.left; |
6735 | const int monitor_h = mr.bottom - mr.top; |
6736 | |
6737 | const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; |
6738 | DWORD win_style; |
6739 | RECT rect = { 0, 0, 0, 0 }; |
6740 | |
6741 | _sapp.fullscreen = fullscreen; |
6742 | if (!_sapp.fullscreen) { |
6743 | win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; |
6744 | rect = _sapp.win32.stored_window_rect; |
6745 | } |
6746 | else { |
6747 | GetWindowRect(_sapp.win32.hwnd, &_sapp.win32.stored_window_rect); |
6748 | win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; |
6749 | rect.left = mr.left; |
6750 | rect.top = mr.top; |
6751 | rect.right = rect.left + monitor_w; |
6752 | rect.bottom = rect.top + monitor_h; |
6753 | AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); |
6754 | } |
6755 | const int win_w = rect.right - rect.left; |
6756 | const int win_h = rect.bottom - rect.top; |
6757 | const int win_x = rect.left; |
6758 | const int win_y = rect.top; |
6759 | SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); |
6760 | SetWindowPos(_sapp.win32.hwnd, HWND_TOP, win_x, win_y, win_w, win_h, swp_flags | SWP_FRAMECHANGED); |
6761 | } |
6762 | |
6763 | _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { |
6764 | _sapp_win32_set_fullscreen(!_sapp.fullscreen, SWP_SHOWWINDOW); |
6765 | } |
6766 | |
6767 | _SOKOL_PRIVATE void _sapp_win32_init_cursor(sapp_mouse_cursor cursor) { |
6768 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
6769 | // NOTE: the OCR_* constants are only defined if OEMRESOURCE is defined |
6770 | // before windows.h is included, but we can't guarantee that because |
6771 | // the sokol_app.h implementation may be included with other implementations |
6772 | // in the same compilation unit |
6773 | int id = 0; |
6774 | switch (cursor) { |
6775 | case SAPP_MOUSECURSOR_ARROW: id = 32512; break; // OCR_NORMAL |
6776 | case SAPP_MOUSECURSOR_IBEAM: id = 32513; break; // OCR_IBEAM |
6777 | case SAPP_MOUSECURSOR_CROSSHAIR: id = 32515; break; // OCR_CROSS |
6778 | case SAPP_MOUSECURSOR_POINTING_HAND: id = 32649; break; // OCR_HAND |
6779 | case SAPP_MOUSECURSOR_RESIZE_EW: id = 32644; break; // OCR_SIZEWE |
6780 | case SAPP_MOUSECURSOR_RESIZE_NS: id = 32645; break; // OCR_SIZENS |
6781 | case SAPP_MOUSECURSOR_RESIZE_NWSE: id = 32642; break; // OCR_SIZENWSE |
6782 | case SAPP_MOUSECURSOR_RESIZE_NESW: id = 32643; break; // OCR_SIZENESW |
6783 | case SAPP_MOUSECURSOR_RESIZE_ALL: id = 32646; break; // OCR_SIZEALL |
6784 | case SAPP_MOUSECURSOR_NOT_ALLOWED: id = 32648; break; // OCR_NO |
6785 | default: break; |
6786 | } |
6787 | if (id != 0) { |
6788 | _sapp.win32.cursors[cursor] = (HCURSOR)LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); |
6789 | } |
6790 | // fallback: default cursor |
6791 | if (0 == _sapp.win32.cursors[cursor]) { |
6792 | // 32512 => IDC_ARROW |
6793 | _sapp.win32.cursors[cursor] = LoadCursorW(NULL, MAKEINTRESOURCEW(32512)); |
6794 | } |
6795 | SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); |
6796 | } |
6797 | |
6798 | _SOKOL_PRIVATE void _sapp_win32_init_cursors(void) { |
6799 | for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { |
6800 | _sapp_win32_init_cursor((sapp_mouse_cursor)i); |
6801 | } |
6802 | } |
6803 | |
6804 | _SOKOL_PRIVATE bool _sapp_win32_cursor_in_content_area(void) { |
6805 | POINT pos; |
6806 | if (!GetCursorPos(&pos)) { |
6807 | return false; |
6808 | } |
6809 | if (WindowFromPoint(pos) != _sapp.win32.hwnd) { |
6810 | return false; |
6811 | } |
6812 | RECT area; |
6813 | GetClientRect(_sapp.win32.hwnd, &area); |
6814 | ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.left); |
6815 | ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.right); |
6816 | return PtInRect(&area, pos) == TRUE; |
6817 | } |
6818 | |
6819 | _SOKOL_PRIVATE void _sapp_win32_update_cursor(sapp_mouse_cursor cursor, bool shown, bool skip_area_test) { |
6820 | // NOTE: when called from WM_SETCURSOR, the area test would be redundant |
6821 | if (!skip_area_test) { |
6822 | if (!_sapp_win32_cursor_in_content_area()) { |
6823 | return; |
6824 | } |
6825 | } |
6826 | if (!shown) { |
6827 | SetCursor(NULL); |
6828 | } |
6829 | else { |
6830 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
6831 | SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); |
6832 | SetCursor(_sapp.win32.cursors[cursor]); |
6833 | } |
6834 | } |
6835 | |
6836 | _SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { |
6837 | if (0 == _sapp.win32.mouse_capture_mask) { |
6838 | SetCapture(_sapp.win32.hwnd); |
6839 | } |
6840 | _sapp.win32.mouse_capture_mask |= btn_mask; |
6841 | } |
6842 | |
6843 | _SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { |
6844 | if (0 != _sapp.win32.mouse_capture_mask) { |
6845 | _sapp.win32.mouse_capture_mask &= ~btn_mask; |
6846 | if (0 == _sapp.win32.mouse_capture_mask) { |
6847 | ReleaseCapture(); |
6848 | } |
6849 | } |
6850 | } |
6851 | |
6852 | _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { |
6853 | if (lock == _sapp.mouse.locked) { |
6854 | return; |
6855 | } |
6856 | _sapp.mouse.dx = 0.0f; |
6857 | _sapp.mouse.dy = 0.0f; |
6858 | _sapp.mouse.locked = lock; |
6859 | _sapp_win32_release_mouse(0xFF); |
6860 | if (_sapp.mouse.locked) { |
6861 | /* store the current mouse position, so it can be restored when unlocked */ |
6862 | POINT pos; |
6863 | BOOL res = GetCursorPos(&pos); |
6864 | SOKOL_ASSERT(res); _SOKOL_UNUSED(res); |
6865 | _sapp.win32.mouse_locked_x = pos.x; |
6866 | _sapp.win32.mouse_locked_y = pos.y; |
6867 | |
6868 | /* while the mouse is locked, make the mouse cursor invisible and |
6869 | confine the mouse movement to a small rectangle inside our window |
6870 | (so that we dont miss any mouse up events) |
6871 | */ |
6872 | RECT client_rect = { |
6873 | _sapp.win32.mouse_locked_x, |
6874 | _sapp.win32.mouse_locked_y, |
6875 | _sapp.win32.mouse_locked_x, |
6876 | _sapp.win32.mouse_locked_y |
6877 | }; |
6878 | ClipCursor(&client_rect); |
6879 | |
6880 | /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */ |
6881 | ShowCursor(FALSE); |
6882 | |
6883 | /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ |
6884 | const RAWINPUTDEVICE rid = { |
6885 | 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC |
6886 | 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE |
6887 | 0, // dwFlags |
6888 | _sapp.win32.hwnd // hwndTarget |
6889 | }; |
6890 | if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { |
6891 | SAPP_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); |
6892 | } |
6893 | /* in case the raw mouse device only supports absolute position reporting, |
6894 | we need to skip the dx/dy compution for the first WM_INPUT event |
6895 | */ |
6896 | _sapp.win32.raw_input_mousepos_valid = false; |
6897 | } |
6898 | else { |
6899 | /* disable raw input for mouse */ |
6900 | const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; |
6901 | if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { |
6902 | SAPP_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); |
6903 | } |
6904 | |
6905 | /* let the mouse roam freely again */ |
6906 | ClipCursor(NULL); |
6907 | ShowCursor(TRUE); |
6908 | |
6909 | /* restore the 'pre-locked' mouse position */ |
6910 | BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); |
6911 | SOKOL_ASSERT(res); _SOKOL_UNUSED(res); |
6912 | } |
6913 | } |
6914 | |
6915 | _SOKOL_PRIVATE bool _sapp_win32_update_monitor(void) { |
6916 | const HMONITOR cur_monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); |
6917 | if (cur_monitor != _sapp.win32.hmonitor) { |
6918 | _sapp.win32.hmonitor = cur_monitor; |
6919 | return true; |
6920 | } |
6921 | else { |
6922 | return false; |
6923 | } |
6924 | } |
6925 | |
6926 | _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { |
6927 | uint32_t mods = 0; |
6928 | if (GetKeyState(VK_SHIFT) & (1<<15)) { |
6929 | mods |= SAPP_MODIFIER_SHIFT; |
6930 | } |
6931 | if (GetKeyState(VK_CONTROL) & (1<<15)) { |
6932 | mods |= SAPP_MODIFIER_CTRL; |
6933 | } |
6934 | if (GetKeyState(VK_MENU) & (1<<15)) { |
6935 | mods |= SAPP_MODIFIER_ALT; |
6936 | } |
6937 | if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { |
6938 | mods |= SAPP_MODIFIER_SUPER; |
6939 | } |
6940 | const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON)); |
6941 | if (GetAsyncKeyState(VK_LBUTTON)) { |
6942 | mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB; |
6943 | } |
6944 | if (GetAsyncKeyState(VK_RBUTTON)) { |
6945 | mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB; |
6946 | } |
6947 | if (GetAsyncKeyState(VK_MBUTTON)) { |
6948 | mods |= SAPP_MODIFIER_MMB; |
6949 | } |
6950 | return mods; |
6951 | } |
6952 | |
6953 | _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { |
6954 | if (_sapp_events_enabled()) { |
6955 | _sapp_init_event(type); |
6956 | _sapp.event.modifiers = _sapp_win32_mods(); |
6957 | _sapp.event.mouse_button = btn; |
6958 | _sapp_call_event(&_sapp.event); |
6959 | } |
6960 | } |
6961 | |
6962 | _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { |
6963 | if (_sapp_events_enabled()) { |
6964 | _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); |
6965 | _sapp.event.modifiers = _sapp_win32_mods(); |
6966 | _sapp.event.scroll_x = -x / 30.0f; |
6967 | _sapp.event.scroll_y = y / 30.0f; |
6968 | _sapp_call_event(&_sapp.event); |
6969 | } |
6970 | } |
6971 | |
6972 | _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { |
6973 | if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { |
6974 | _sapp_init_event(type); |
6975 | _sapp.event.modifiers = _sapp_win32_mods(); |
6976 | _sapp.event.key_code = _sapp.keycodes[vk]; |
6977 | _sapp.event.key_repeat = repeat; |
6978 | _sapp_call_event(&_sapp.event); |
6979 | /* check if a CLIPBOARD_PASTED event must be sent too */ |
6980 | if (_sapp.clipboard.enabled && |
6981 | (type == SAPP_EVENTTYPE_KEY_DOWN) && |
6982 | (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && |
6983 | (_sapp.event.key_code == SAPP_KEYCODE_V)) |
6984 | { |
6985 | _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); |
6986 | _sapp_call_event(&_sapp.event); |
6987 | } |
6988 | } |
6989 | } |
6990 | |
6991 | _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { |
6992 | if (_sapp_events_enabled() && (c >= 32)) { |
6993 | _sapp_init_event(SAPP_EVENTTYPE_CHAR); |
6994 | _sapp.event.modifiers = _sapp_win32_mods(); |
6995 | _sapp.event.char_code = c; |
6996 | _sapp.event.key_repeat = repeat; |
6997 | _sapp_call_event(&_sapp.event); |
6998 | } |
6999 | } |
7000 | |
7001 | _SOKOL_PRIVATE void _sapp_win32_dpi_changed(HWND hWnd, LPRECT proposed_win_rect) { |
7002 | /* called on WM_DPICHANGED, which will only be sent to the application |
7003 | if sapp_desc.high_dpi is true and the Windows version is recent enough |
7004 | to support DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 |
7005 | */ |
7006 | SOKOL_ASSERT(_sapp.desc.high_dpi); |
7007 | HINSTANCE user32 = LoadLibraryA("user32.dll"); |
7008 | if (!user32) { |
7009 | return; |
7010 | } |
7011 | typedef UINT(WINAPI * GETDPIFORWINDOW_T)(HWND hwnd); |
7012 | GETDPIFORWINDOW_T fn_getdpiforwindow = (GETDPIFORWINDOW_T)(void*)GetProcAddress(user32, "GetDpiForWindow"); |
7013 | if (fn_getdpiforwindow) { |
7014 | UINT dpix = fn_getdpiforwindow(_sapp.win32.hwnd); |
7015 | // NOTE: for high-dpi apps, mouse_scale remains one |
7016 | _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; |
7017 | _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; |
7018 | _sapp.dpi_scale = _sapp.win32.dpi.window_scale; |
7019 | SetWindowPos(hWnd, 0, |
7020 | proposed_win_rect->left, |
7021 | proposed_win_rect->top, |
7022 | proposed_win_rect->right - proposed_win_rect->left, |
7023 | proposed_win_rect->bottom - proposed_win_rect->top, |
7024 | SWP_NOZORDER | SWP_NOACTIVATE); |
7025 | } |
7026 | FreeLibrary(user32); |
7027 | } |
7028 | |
7029 | _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { |
7030 | if (!_sapp.drop.enabled) { |
7031 | return; |
7032 | } |
7033 | _sapp_clear_drop_buffer(); |
7034 | bool drop_failed = false; |
7035 | const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); |
7036 | _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; |
7037 | for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) { |
7038 | const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; |
7039 | WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR)); |
7040 | DragQueryFileW(hdrop, i, buffer, num_chars); |
7041 | if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { |
7042 | SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); |
7043 | drop_failed = true; |
7044 | } |
7045 | _sapp_free(buffer); |
7046 | } |
7047 | DragFinish(hdrop); |
7048 | if (!drop_failed) { |
7049 | if (_sapp_events_enabled()) { |
7050 | _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); |
7051 | _sapp_call_event(&_sapp.event); |
7052 | } |
7053 | } |
7054 | else { |
7055 | _sapp_clear_drop_buffer(); |
7056 | _sapp.drop.num_files = 0; |
7057 | } |
7058 | } |
7059 | |
7060 | _SOKOL_PRIVATE void _sapp_win32_timing_measure(void) { |
7061 | #if defined(SOKOL_D3D11) |
7062 | // on D3D11, use the more precise DXGI timestamp |
7063 | if (_sapp.d3d11.use_dxgi_frame_stats) { |
7064 | DXGI_FRAME_STATISTICS dxgi_stats; |
7065 | _sapp_clear(&dxgi_stats, sizeof(dxgi_stats)); |
7066 | HRESULT hr = _sapp_dxgi_GetFrameStatistics(_sapp.d3d11.swap_chain, &dxgi_stats); |
7067 | if (SUCCEEDED(hr)) { |
7068 | if (dxgi_stats.SyncRefreshCount != _sapp.d3d11.sync_refresh_count) { |
7069 | if ((_sapp.d3d11.sync_refresh_count + 1) != dxgi_stats.SyncRefreshCount) { |
7070 | _sapp_timing_discontinuity(&_sapp.timing); |
7071 | } |
7072 | _sapp.d3d11.sync_refresh_count = dxgi_stats.SyncRefreshCount; |
7073 | LARGE_INTEGER qpc = dxgi_stats.SyncQPCTime; |
7074 | const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - _sapp.timing.timestamp.win.start.QuadPart, 1000000000, _sapp.timing.timestamp.win.freq.QuadPart); |
7075 | _sapp_timing_external(&_sapp.timing, (double)now / 1000000000.0); |
7076 | } |
7077 | return; |
7078 | } |
7079 | } |
7080 | // fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason |
7081 | _sapp_timing_measure(&_sapp.timing); |
7082 | #endif |
7083 | #if defined(SOKOL_GLCORE33) |
7084 | _sapp_timing_measure(&_sapp.timing); |
7085 | #endif |
7086 | } |
7087 | |
7088 | _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { |
7089 | if (!_sapp.win32.in_create_window) { |
7090 | switch (uMsg) { |
7091 | case WM_CLOSE: |
7092 | /* only give user a chance to intervene when sapp_quit() wasn't already called */ |
7093 | if (!_sapp.quit_ordered) { |
7094 | /* if window should be closed and event handling is enabled, give user code |
7095 | a change to intervene via sapp_cancel_quit() |
7096 | */ |
7097 | _sapp.quit_requested = true; |
7098 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); |
7099 | /* if user code hasn't intervened, quit the app */ |
7100 | if (_sapp.quit_requested) { |
7101 | _sapp.quit_ordered = true; |
7102 | } |
7103 | } |
7104 | if (_sapp.quit_ordered) { |
7105 | PostQuitMessage(0); |
7106 | } |
7107 | return 0; |
7108 | case WM_SYSCOMMAND: |
7109 | switch (wParam & 0xFFF0) { |
7110 | case SC_SCREENSAVE: |
7111 | case SC_MONITORPOWER: |
7112 | if (_sapp.fullscreen) { |
7113 | /* disable screen saver and blanking in fullscreen mode */ |
7114 | return 0; |
7115 | } |
7116 | break; |
7117 | case SC_KEYMENU: |
7118 | /* user trying to access menu via ALT */ |
7119 | return 0; |
7120 | } |
7121 | break; |
7122 | case WM_ERASEBKGND: |
7123 | return 1; |
7124 | case WM_SIZE: |
7125 | { |
7126 | const bool iconified = wParam == SIZE_MINIMIZED; |
7127 | if (iconified != _sapp.win32.iconified) { |
7128 | _sapp.win32.iconified = iconified; |
7129 | if (iconified) { |
7130 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); |
7131 | } |
7132 | else { |
7133 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); |
7134 | } |
7135 | } |
7136 | } |
7137 | break; |
7138 | case WM_SETFOCUS: |
7139 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_FOCUSED); |
7140 | break; |
7141 | case WM_KILLFOCUS: |
7142 | /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ |
7143 | if (_sapp.mouse.locked) { |
7144 | _sapp_win32_lock_mouse(false); |
7145 | } |
7146 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UNFOCUSED); |
7147 | break; |
7148 | case WM_SETCURSOR: |
7149 | if (LOWORD(lParam) == HTCLIENT) { |
7150 | _sapp_win32_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown, true); |
7151 | return TRUE; |
7152 | } |
7153 | break; |
7154 | case WM_DPICHANGED: |
7155 | { |
7156 | /* Update window's DPI and size if its moved to another monitor with a different DPI |
7157 | Only sent if DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 is used. |
7158 | */ |
7159 | _sapp_win32_dpi_changed(hWnd, (LPRECT)lParam); |
7160 | break; |
7161 | } |
7162 | case WM_LBUTTONDOWN: |
7163 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); |
7164 | _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_LEFT); |
7165 | break; |
7166 | case WM_RBUTTONDOWN: |
7167 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT); |
7168 | _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_RIGHT); |
7169 | break; |
7170 | case WM_MBUTTONDOWN: |
7171 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE); |
7172 | _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE); |
7173 | break; |
7174 | case WM_LBUTTONUP: |
7175 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT); |
7176 | _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_LEFT); |
7177 | break; |
7178 | case WM_RBUTTONUP: |
7179 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT); |
7180 | _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_RIGHT); |
7181 | break; |
7182 | case WM_MBUTTONUP: |
7183 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE); |
7184 | _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE); |
7185 | break; |
7186 | case WM_MOUSEMOVE: |
7187 | if (!_sapp.mouse.locked) { |
7188 | const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; |
7189 | const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; |
7190 | /* don't update dx/dy in the very first event */ |
7191 | if (_sapp.mouse.pos_valid) { |
7192 | _sapp.mouse.dx = new_x - _sapp.mouse.x; |
7193 | _sapp.mouse.dy = new_y - _sapp.mouse.y; |
7194 | } |
7195 | _sapp.mouse.x = new_x; |
7196 | _sapp.mouse.y = new_y; |
7197 | _sapp.mouse.pos_valid = true; |
7198 | if (!_sapp.win32.mouse_tracked) { |
7199 | _sapp.win32.mouse_tracked = true; |
7200 | TRACKMOUSEEVENT tme; |
7201 | _sapp_clear(&tme, sizeof(tme)); |
7202 | tme.cbSize = sizeof(tme); |
7203 | tme.dwFlags = TME_LEAVE; |
7204 | tme.hwndTrack = _sapp.win32.hwnd; |
7205 | TrackMouseEvent(&tme); |
7206 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); |
7207 | } |
7208 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); |
7209 | } |
7210 | break; |
7211 | case WM_INPUT: |
7212 | /* raw mouse input during mouse-lock */ |
7213 | if (_sapp.mouse.locked) { |
7214 | HRAWINPUT ri = (HRAWINPUT) lParam; |
7215 | UINT size = sizeof(_sapp.win32.raw_input_data); |
7216 | // see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdata |
7217 | if ((UINT)-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) { |
7218 | SAPP_LOG("GetRawInputData() failed\n"); |
7219 | break; |
7220 | } |
7221 | const RAWINPUT* raw_mouse_data = (const RAWINPUT*) &_sapp.win32.raw_input_data; |
7222 | if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { |
7223 | /* mouse only reports absolute position |
7224 | NOTE: THIS IS UNTESTED, it's unclear from reading the |
7225 | Win32 RawInput docs under which circumstances absolute |
7226 | positions are sent. |
7227 | */ |
7228 | if (_sapp.win32.raw_input_mousepos_valid) { |
7229 | LONG new_x = raw_mouse_data->data.mouse.lLastX; |
7230 | LONG new_y = raw_mouse_data->data.mouse.lLastY; |
7231 | _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); |
7232 | _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); |
7233 | _sapp.win32.raw_input_mousepos_x = new_x; |
7234 | _sapp.win32.raw_input_mousepos_y = new_y; |
7235 | _sapp.win32.raw_input_mousepos_valid = true; |
7236 | } |
7237 | } |
7238 | else { |
7239 | /* mouse reports movement delta (this seems to be the common case) */ |
7240 | _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; |
7241 | _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; |
7242 | } |
7243 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); |
7244 | } |
7245 | break; |
7246 | |
7247 | case WM_MOUSELEAVE: |
7248 | if (!_sapp.mouse.locked) { |
7249 | _sapp.win32.mouse_tracked = false; |
7250 | _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); |
7251 | } |
7252 | break; |
7253 | case WM_MOUSEWHEEL: |
7254 | _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); |
7255 | break; |
7256 | case WM_MOUSEHWHEEL: |
7257 | _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); |
7258 | break; |
7259 | case WM_CHAR: |
7260 | _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); |
7261 | break; |
7262 | case WM_KEYDOWN: |
7263 | case WM_SYSKEYDOWN: |
7264 | _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); |
7265 | break; |
7266 | case WM_KEYUP: |
7267 | case WM_SYSKEYUP: |
7268 | _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); |
7269 | break; |
7270 | case WM_ENTERSIZEMOVE: |
7271 | SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); |
7272 | break; |
7273 | case WM_EXITSIZEMOVE: |
7274 | KillTimer(_sapp.win32.hwnd, 1); |
7275 | break; |
7276 | case WM_TIMER: |
7277 | _sapp_win32_timing_measure(); |
7278 | _sapp_frame(); |
7279 | #if defined(SOKOL_D3D11) |
7280 | // present with DXGI_PRESENT_DO_NOT_WAIT |
7281 | _sapp_d3d11_present(true); |
7282 | #endif |
7283 | #if defined(SOKOL_GLCORE33) |
7284 | _sapp_wgl_swap_buffers(); |
7285 | #endif |
7286 | /* NOTE: resizing the swap-chain during resize leads to a substantial |
7287 | memory spike (hundreds of megabytes for a few seconds). |
7288 | |
7289 | if (_sapp_win32_update_dimensions()) { |
7290 | #if defined(SOKOL_D3D11) |
7291 | _sapp_d3d11_resize_default_render_target(); |
7292 | #endif |
7293 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); |
7294 | } |
7295 | */ |
7296 | break; |
7297 | case WM_NCLBUTTONDOWN: |
7298 | /* workaround for half-second pause when starting to move window |
7299 | see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/ |
7300 | */ |
7301 | if (SendMessage(_sapp.win32.hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) { |
7302 | POINT point; |
7303 | GetCursorPos(&point); |
7304 | ScreenToClient(_sapp.win32.hwnd, &point); |
7305 | PostMessage(_sapp.win32.hwnd, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16)); |
7306 | } |
7307 | break; |
7308 | case WM_DROPFILES: |
7309 | _sapp_win32_files_dropped((HDROP)wParam); |
7310 | break; |
7311 | case WM_DISPLAYCHANGE: |
7312 | // refresh rate might have changed |
7313 | _sapp_timing_reset(&_sapp.timing); |
7314 | break; |
7315 | |
7316 | default: |
7317 | break; |
7318 | } |
7319 | } |
7320 | return DefWindowProcW(hWnd, uMsg, wParam, lParam); |
7321 | } |
7322 | |
7323 | _SOKOL_PRIVATE void _sapp_win32_create_window(void) { |
7324 | WNDCLASSW wndclassw; |
7325 | _sapp_clear(&wndclassw, sizeof(wndclassw)); |
7326 | wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; |
7327 | wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; |
7328 | wndclassw.hInstance = GetModuleHandleW(NULL); |
7329 | wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); |
7330 | wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); |
7331 | wndclassw.lpszClassName = L"SOKOLAPP"; |
7332 | RegisterClassW(&wndclassw); |
7333 | |
7334 | /* NOTE: regardless whether fullscreen is requested or not, a regular |
7335 | windowed-mode window will always be created first (however in hidden |
7336 | mode, so that no windowed-mode window pops up before the fullscreen window) |
7337 | */ |
7338 | const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; |
7339 | RECT rect = { 0, 0, 0, 0 }; |
7340 | DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; |
7341 | rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); |
7342 | rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); |
7343 | const bool use_default_width = 0 == _sapp.window_width; |
7344 | const bool use_default_height = 0 == _sapp.window_height; |
7345 | AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); |
7346 | const int win_width = rect.right - rect.left; |
7347 | const int win_height = rect.bottom - rect.top; |
7348 | _sapp.win32.in_create_window = true; |
7349 | _sapp.win32.hwnd = CreateWindowExW( |
7350 | win_ex_style, // dwExStyle |
7351 | L"SOKOLAPP", // lpClassName |
7352 | _sapp.window_title_wide, // lpWindowName |
7353 | win_style, // dwStyle |
7354 | CW_USEDEFAULT, // X |
7355 | SW_HIDE, // Y (NOTE: CW_USEDEFAULT is not used for position here, but internally calls ShowWindow! |
7356 | use_default_width ? CW_USEDEFAULT : win_width, // nWidth |
7357 | use_default_height ? CW_USEDEFAULT : win_height, // nHeight (NOTE: if width is CW_USEDEFAULT, height is actually ignored) |
7358 | NULL, // hWndParent |
7359 | NULL, // hMenu |
7360 | GetModuleHandle(NULL), // hInstance |
7361 | NULL); // lParam |
7362 | _sapp.win32.in_create_window = false; |
7363 | _sapp.win32.dc = GetDC(_sapp.win32.hwnd); |
7364 | _sapp.win32.hmonitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); |
7365 | SOKOL_ASSERT(_sapp.win32.dc); |
7366 | |
7367 | /* this will get the actual windowed-mode window size, if fullscreen |
7368 | is requested, the set_fullscreen function will then capture the |
7369 | current window rectangle, which then might be used later to |
7370 | restore the window position when switching back to windowed |
7371 | */ |
7372 | _sapp_win32_update_dimensions(); |
7373 | if (_sapp.fullscreen) { |
7374 | _sapp_win32_set_fullscreen(_sapp.fullscreen, SWP_HIDEWINDOW); |
7375 | _sapp_win32_update_dimensions(); |
7376 | } |
7377 | ShowWindow(_sapp.win32.hwnd, SW_SHOW); |
7378 | DragAcceptFiles(_sapp.win32.hwnd, 1); |
7379 | } |
7380 | |
7381 | _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { |
7382 | DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; |
7383 | UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); |
7384 | } |
7385 | |
7386 | _SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) { |
7387 | if (_sapp.win32.big_icon) { |
7388 | DestroyIcon(_sapp.win32.big_icon); |
7389 | _sapp.win32.big_icon = 0; |
7390 | } |
7391 | if (_sapp.win32.small_icon) { |
7392 | DestroyIcon(_sapp.win32.small_icon); |
7393 | _sapp.win32.small_icon = 0; |
7394 | } |
7395 | } |
7396 | |
7397 | _SOKOL_PRIVATE void _sapp_win32_init_console(void) { |
7398 | if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) { |
7399 | BOOL con_valid = FALSE; |
7400 | if (_sapp.desc.win32_console_create) { |
7401 | con_valid = AllocConsole(); |
7402 | } |
7403 | else if (_sapp.desc.win32_console_attach) { |
7404 | con_valid = AttachConsole(ATTACH_PARENT_PROCESS); |
7405 | } |
7406 | if (con_valid) { |
7407 | FILE* res_fp = 0; |
7408 | errno_t err; |
7409 | err = freopen_s(&res_fp, "CON", "w", stdout); |
7410 | (void)err; |
7411 | err = freopen_s(&res_fp, "CON", "w", stderr); |
7412 | (void)err; |
7413 | } |
7414 | } |
7415 | if (_sapp.desc.win32_console_utf8) { |
7416 | _sapp.win32.orig_codepage = GetConsoleOutputCP(); |
7417 | SetConsoleOutputCP(CP_UTF8); |
7418 | } |
7419 | } |
7420 | |
7421 | _SOKOL_PRIVATE void _sapp_win32_restore_console(void) { |
7422 | if (_sapp.desc.win32_console_utf8) { |
7423 | SetConsoleOutputCP(_sapp.win32.orig_codepage); |
7424 | } |
7425 | } |
7426 | |
7427 | _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { |
7428 | |
7429 | DECLARE_HANDLE(DPI_AWARENESS_CONTEXT_T); |
7430 | typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); |
7431 | typedef bool (WINAPI * SETPROCESSDPIAWARENESSCONTEXT_T)(DPI_AWARENESS_CONTEXT_T); // since Windows 10, version 1703 |
7432 | typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); |
7433 | typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); |
7434 | |
7435 | SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; |
7436 | SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; |
7437 | GETDPIFORMONITOR_T fn_getdpiformonitor = 0; |
7438 | SETPROCESSDPIAWARENESSCONTEXT_T fn_setprocessdpiawarenesscontext =0; |
7439 | |
7440 | HINSTANCE user32 = LoadLibraryA("user32.dll"); |
7441 | if (user32) { |
7442 | fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware"); |
7443 | fn_setprocessdpiawarenesscontext = (SETPROCESSDPIAWARENESSCONTEXT_T)(void*) GetProcAddress(user32, "SetProcessDpiAwarenessContext"); |
7444 | } |
7445 | HINSTANCE shcore = LoadLibraryA("shcore.dll"); |
7446 | if (shcore) { |
7447 | fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness"); |
7448 | fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor"); |
7449 | } |
7450 | /* |
7451 | NOTE on SetProcessDpiAware() vs SetProcessDpiAwareness() vs SetProcessDpiAwarenessContext(): |
7452 | |
7453 | These are different attempts to get DPI handling on Windows right, from oldest |
7454 | to newest. SetProcessDpiAwarenessContext() is required for the new |
7455 | DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 method. |
7456 | */ |
7457 | if (fn_setprocessdpiawareness) { |
7458 | if (_sapp.desc.high_dpi) { |
7459 | /* app requests HighDPI rendering, first try the Win10 Creator Update per-monitor-dpi awareness, |
7460 | if that fails, fall back to system-dpi-awareness |
7461 | */ |
7462 | _sapp.win32.dpi.aware = true; |
7463 | DPI_AWARENESS_CONTEXT_T per_monitor_aware_v2 = (DPI_AWARENESS_CONTEXT_T)-4; |
7464 | if (!(fn_setprocessdpiawarenesscontext && fn_setprocessdpiawarenesscontext(per_monitor_aware_v2))) { |
7465 | // fallback to system-dpi-aware |
7466 | fn_setprocessdpiawareness(PROCESS_SYSTEM_DPI_AWARE); |
7467 | } |
7468 | } |
7469 | else { |
7470 | /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ |
7471 | _sapp.win32.dpi.aware = false; |
7472 | fn_setprocessdpiawareness(PROCESS_DPI_UNAWARE); |
7473 | } |
7474 | } |
7475 | else if (fn_setprocessdpiaware) { |
7476 | // fallback for Windows 7 |
7477 | _sapp.win32.dpi.aware = true; |
7478 | fn_setprocessdpiaware(); |
7479 | } |
7480 | /* get dpi scale factor for main monitor */ |
7481 | if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { |
7482 | POINT pt = { 1, 1 }; |
7483 | HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); |
7484 | UINT dpix, dpiy; |
7485 | HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); |
7486 | _SOKOL_UNUSED(hr); |
7487 | SOKOL_ASSERT(SUCCEEDED(hr)); |
7488 | /* clamp window scale to an integer factor */ |
7489 | _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; |
7490 | } |
7491 | else { |
7492 | _sapp.win32.dpi.window_scale = 1.0f; |
7493 | } |
7494 | if (_sapp.desc.high_dpi) { |
7495 | _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; |
7496 | _sapp.win32.dpi.mouse_scale = 1.0f; |
7497 | } |
7498 | else { |
7499 | _sapp.win32.dpi.content_scale = 1.0f; |
7500 | _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; |
7501 | } |
7502 | _sapp.dpi_scale = _sapp.win32.dpi.content_scale; |
7503 | if (user32) { |
7504 | FreeLibrary(user32); |
7505 | } |
7506 | if (shcore) { |
7507 | FreeLibrary(shcore); |
7508 | } |
7509 | } |
7510 | |
7511 | _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { |
7512 | SOKOL_ASSERT(str); |
7513 | SOKOL_ASSERT(_sapp.win32.hwnd); |
7514 | SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); |
7515 | |
7516 | wchar_t* wchar_buf = 0; |
7517 | const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); |
7518 | HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); |
7519 | if (!object) { |
7520 | goto error; |
7521 | } |
7522 | wchar_buf = (wchar_t*) GlobalLock(object); |
7523 | if (!wchar_buf) { |
7524 | goto error; |
7525 | } |
7526 | if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { |
7527 | goto error; |
7528 | } |
7529 | GlobalUnlock(wchar_buf); |
7530 | wchar_buf = 0; |
7531 | if (!OpenClipboard(_sapp.win32.hwnd)) { |
7532 | goto error; |
7533 | } |
7534 | EmptyClipboard(); |
7535 | SetClipboardData(CF_UNICODETEXT, object); |
7536 | CloseClipboard(); |
7537 | return true; |
7538 | |
7539 | error: |
7540 | if (wchar_buf) { |
7541 | GlobalUnlock(object); |
7542 | } |
7543 | if (object) { |
7544 | GlobalFree(object); |
7545 | } |
7546 | return false; |
7547 | } |
7548 | |
7549 | _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { |
7550 | SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); |
7551 | SOKOL_ASSERT(_sapp.win32.hwnd); |
7552 | if (!OpenClipboard(_sapp.win32.hwnd)) { |
7553 | /* silently ignore any errors and just return the current |
7554 | content of the local clipboard buffer |
7555 | */ |
7556 | return _sapp.clipboard.buffer; |
7557 | } |
7558 | HANDLE object = GetClipboardData(CF_UNICODETEXT); |
7559 | if (!object) { |
7560 | CloseClipboard(); |
7561 | return _sapp.clipboard.buffer; |
7562 | } |
7563 | const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); |
7564 | if (!wchar_buf) { |
7565 | CloseClipboard(); |
7566 | return _sapp.clipboard.buffer; |
7567 | } |
7568 | if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { |
7569 | SAPP_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); |
7570 | } |
7571 | GlobalUnlock(object); |
7572 | CloseClipboard(); |
7573 | return _sapp.clipboard.buffer; |
7574 | } |
7575 | |
7576 | _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { |
7577 | _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); |
7578 | SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); |
7579 | } |
7580 | |
7581 | _SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc) { |
7582 | BITMAPV5HEADER bi; |
7583 | _sapp_clear(&bi, sizeof(bi)); |
7584 | bi.bV5Size = sizeof(bi); |
7585 | bi.bV5Width = desc->width; |
7586 | bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left |
7587 | bi.bV5Planes = 1; |
7588 | bi.bV5BitCount = 32; |
7589 | bi.bV5Compression = BI_BITFIELDS; |
7590 | bi.bV5RedMask = 0x00FF0000; |
7591 | bi.bV5GreenMask = 0x0000FF00; |
7592 | bi.bV5BlueMask = 0x000000FF; |
7593 | bi.bV5AlphaMask = 0xFF000000; |
7594 | |
7595 | uint8_t* target = 0; |
7596 | const uint8_t* source = (const uint8_t*)desc->pixels.ptr; |
7597 | |
7598 | HDC dc = GetDC(NULL); |
7599 | HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0); |
7600 | ReleaseDC(NULL, dc); |
7601 | if (0 == color) { |
7602 | return NULL; |
7603 | } |
7604 | SOKOL_ASSERT(target); |
7605 | |
7606 | HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL); |
7607 | if (0 == mask) { |
7608 | DeleteObject(color); |
7609 | return NULL; |
7610 | } |
7611 | |
7612 | for (int i = 0; i < (desc->width*desc->height); i++) { |
7613 | target[0] = source[2]; |
7614 | target[1] = source[1]; |
7615 | target[2] = source[0]; |
7616 | target[3] = source[3]; |
7617 | target += 4; |
7618 | source += 4; |
7619 | } |
7620 | |
7621 | ICONINFO icon_info; |
7622 | _sapp_clear(&icon_info, sizeof(icon_info)); |
7623 | icon_info.fIcon = true; |
7624 | icon_info.xHotspot = 0; |
7625 | icon_info.yHotspot = 0; |
7626 | icon_info.hbmMask = mask; |
7627 | icon_info.hbmColor = color; |
7628 | HICON icon_handle = CreateIconIndirect(&icon_info); |
7629 | DeleteObject(color); |
7630 | DeleteObject(mask); |
7631 | |
7632 | return icon_handle; |
7633 | } |
7634 | |
7635 | _SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) { |
7636 | SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); |
7637 | |
7638 | int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); |
7639 | int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); |
7640 | HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index]); |
7641 | HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index]); |
7642 | |
7643 | // if icon creation or lookup has failed for some reason, leave the currently set icon untouched |
7644 | if (0 != big_icon) { |
7645 | SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon); |
7646 | if (0 != _sapp.win32.big_icon) { |
7647 | DestroyIcon(_sapp.win32.big_icon); |
7648 | } |
7649 | _sapp.win32.big_icon = big_icon; |
7650 | } |
7651 | if (0 != sml_icon) { |
7652 | SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon); |
7653 | if (0 != _sapp.win32.small_icon) { |
7654 | DestroyIcon(_sapp.win32.small_icon); |
7655 | } |
7656 | _sapp.win32.small_icon = sml_icon; |
7657 | } |
7658 | } |
7659 | |
7660 | /* don't laugh, but this seems to be the easiest and most robust |
7661 | way to check if we're running on Win10 |
7662 | |
7663 | From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263 |
7664 | */ |
7665 | _SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { |
7666 | HMODULE h = GetModuleHandleW(L"kernel32.dll"); |
7667 | if (NULL != h) { |
7668 | return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation")); |
7669 | } |
7670 | else { |
7671 | return false; |
7672 | } |
7673 | } |
7674 | |
7675 | _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { |
7676 | _sapp_init_state(desc); |
7677 | _sapp_win32_init_console(); |
7678 | _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); |
7679 | _sapp_win32_uwp_init_keytable(); |
7680 | _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); |
7681 | _sapp_win32_init_dpi(); |
7682 | _sapp_win32_init_cursors(); |
7683 | _sapp_win32_create_window(); |
7684 | sapp_set_icon(&desc->icon); |
7685 | #if defined(SOKOL_D3D11) |
7686 | _sapp_d3d11_create_device_and_swapchain(); |
7687 | _sapp_d3d11_create_default_render_target(); |
7688 | #endif |
7689 | #if defined(SOKOL_GLCORE33) |
7690 | _sapp_wgl_init(); |
7691 | _sapp_wgl_load_extensions(); |
7692 | _sapp_wgl_create_context(); |
7693 | #endif |
7694 | _sapp.valid = true; |
7695 | |
7696 | bool done = false; |
7697 | while (!(done || _sapp.quit_ordered)) { |
7698 | _sapp_win32_timing_measure(); |
7699 | MSG msg; |
7700 | while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { |
7701 | if (WM_QUIT == msg.message) { |
7702 | done = true; |
7703 | continue; |
7704 | } |
7705 | else { |
7706 | TranslateMessage(&msg); |
7707 | DispatchMessageW(&msg); |
7708 | } |
7709 | } |
7710 | _sapp_frame(); |
7711 | #if defined(SOKOL_D3D11) |
7712 | _sapp_d3d11_present(false); |
7713 | if (IsIconic(_sapp.win32.hwnd)) { |
7714 | Sleep((DWORD)(16 * _sapp.swap_interval)); |
7715 | } |
7716 | #endif |
7717 | #if defined(SOKOL_GLCORE33) |
7718 | _sapp_wgl_swap_buffers(); |
7719 | #endif |
7720 | /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ |
7721 | if (_sapp_win32_update_dimensions()) { |
7722 | #if defined(SOKOL_D3D11) |
7723 | _sapp_d3d11_resize_default_render_target(); |
7724 | #endif |
7725 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); |
7726 | } |
7727 | /* check if the window monitor has changed, need to reset timing because |
7728 | the new monitor might have a different refresh rate |
7729 | */ |
7730 | if (_sapp_win32_update_monitor()) { |
7731 | _sapp_timing_reset(&_sapp.timing); |
7732 | } |
7733 | if (_sapp.quit_requested) { |
7734 | PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); |
7735 | } |
7736 | } |
7737 | _sapp_call_cleanup(); |
7738 | |
7739 | #if defined(SOKOL_D3D11) |
7740 | _sapp_d3d11_destroy_default_render_target(); |
7741 | _sapp_d3d11_destroy_device_and_swapchain(); |
7742 | #else |
7743 | _sapp_wgl_destroy_context(); |
7744 | _sapp_wgl_shutdown(); |
7745 | #endif |
7746 | _sapp_win32_destroy_window(); |
7747 | _sapp_win32_destroy_icons(); |
7748 | _sapp_win32_restore_console(); |
7749 | _sapp_discard_state(); |
7750 | } |
7751 | |
7752 | _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { |
7753 | int argc = 0; |
7754 | char** argv = 0; |
7755 | char* args; |
7756 | |
7757 | LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); |
7758 | if (w_argv == NULL) { |
7759 | _sapp_fail("Win32: failed to parse command line"); |
7760 | } else { |
7761 | size_t size = wcslen(w_command_line) * 4; |
7762 | argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size); |
7763 | SOKOL_ASSERT(argv); |
7764 | args = (char*) &argv[argc + 1]; |
7765 | int n; |
7766 | for (int i = 0; i < argc; ++i) { |
7767 | n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); |
7768 | if (n == 0) { |
7769 | _sapp_fail("Win32: failed to convert all arguments to utf8"); |
7770 | break; |
7771 | } |
7772 | argv[i] = args; |
7773 | size -= (size_t)n; |
7774 | args += n; |
7775 | } |
7776 | LocalFree(w_argv); |
7777 | } |
7778 | *o_argc = argc; |
7779 | return argv; |
7780 | } |
7781 | |
7782 | #if !defined(SOKOL_NO_ENTRY) |
7783 | #if defined(SOKOL_WIN32_FORCE_MAIN) |
7784 | int main(int argc, char* argv[]) { |
7785 | sapp_desc desc = sokol_main(argc, argv); |
7786 | _sapp_win32_run(&desc); |
7787 | return 0; |
7788 | } |
7789 | #else |
7790 | int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { |
7791 | _SOKOL_UNUSED(hInstance); |
7792 | _SOKOL_UNUSED(hPrevInstance); |
7793 | _SOKOL_UNUSED(lpCmdLine); |
7794 | _SOKOL_UNUSED(nCmdShow); |
7795 | int argc_utf8 = 0; |
7796 | char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); |
7797 | sapp_desc desc = sokol_main(argc_utf8, argv_utf8); |
7798 | _sapp_win32_run(&desc); |
7799 | _sapp_free(argv_utf8); |
7800 | return 0; |
7801 | } |
7802 | #endif /* SOKOL_WIN32_FORCE_MAIN */ |
7803 | #endif /* SOKOL_NO_ENTRY */ |
7804 | |
7805 | #ifdef _MSC_VER |
7806 | #pragma warning(pop) |
7807 | #endif |
7808 | |
7809 | #endif /* _SAPP_WIN32 */ |
7810 | |
7811 | /*== UWP ================================================================*/ |
7812 | #if defined(_SAPP_UWP) |
7813 | |
7814 | // Helper functions |
7815 | _SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { |
7816 | _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; |
7817 | if (_sapp.desc.high_dpi) { |
7818 | _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; |
7819 | _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; |
7820 | } |
7821 | else { |
7822 | _sapp.uwp.dpi.content_scale = 1.0f; |
7823 | _sapp.uwp.dpi.mouse_scale = 1.0f; |
7824 | } |
7825 | _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; |
7826 | } |
7827 | |
7828 | _SOKOL_PRIVATE void _sapp_uwp_update_cursor(sapp_mouse_cursor cursor, bool shown) { |
7829 | using namespace winrt::Windows::UI::Core; |
7830 | |
7831 | CoreCursor uwp_cursor(nullptr); |
7832 | if (shown) { |
7833 | switch (cursor) { |
7834 | case SAPP_MOUSECURSOR_ARROW: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; |
7835 | case SAPP_MOUSECURSOR_IBEAM: uwp_cursor = CoreCursor(CoreCursorType::IBeam, 0); break; |
7836 | case SAPP_MOUSECURSOR_CROSSHAIR: uwp_cursor = CoreCursor(CoreCursorType::Cross, 0); break; |
7837 | case SAPP_MOUSECURSOR_POINTING_HAND: uwp_cursor = CoreCursor(CoreCursorType::Hand, 0); break; |
7838 | case SAPP_MOUSECURSOR_RESIZE_EW: uwp_cursor = CoreCursor(CoreCursorType::SizeWestEast, 0); break; |
7839 | case SAPP_MOUSECURSOR_RESIZE_NS: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthSouth, 0); break; |
7840 | case SAPP_MOUSECURSOR_RESIZE_NWSE: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); break; |
7841 | case SAPP_MOUSECURSOR_RESIZE_NESW: uwp_cursor = CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); break; |
7842 | case SAPP_MOUSECURSOR_RESIZE_ALL: uwp_cursor = CoreCursor(CoreCursorType::SizeAll, 0); break; |
7843 | case SAPP_MOUSECURSOR_NOT_ALLOWED: uwp_cursor = CoreCursor(CoreCursorType::UniversalNo, 0); break; |
7844 | default: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; |
7845 | } |
7846 | } |
7847 | CoreWindow::GetForCurrentThread().PointerCursor(uwp_cursor); |
7848 | } |
7849 | |
7850 | _SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { |
7851 | using namespace winrt::Windows::System; |
7852 | using namespace winrt::Windows::UI::Core; |
7853 | |
7854 | uint32_t mods = 0; |
7855 | if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { |
7856 | mods |= SAPP_MODIFIER_SHIFT; |
7857 | } |
7858 | if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { |
7859 | mods |= SAPP_MODIFIER_CTRL; |
7860 | } |
7861 | if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { |
7862 | mods |= SAPP_MODIFIER_ALT; |
7863 | } |
7864 | if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || |
7865 | ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) |
7866 | { |
7867 | mods |= SAPP_MODIFIER_SUPER; |
7868 | } |
7869 | if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_LEFT))) { |
7870 | mods |= SAPP_MODIFIER_LMB; |
7871 | } |
7872 | if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_MIDDLE))) { |
7873 | mods |= SAPP_MODIFIER_MMB; |
7874 | } |
7875 | if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_RIGHT))) { |
7876 | mods |= SAPP_MODIFIER_RMB; |
7877 | } |
7878 | return mods; |
7879 | } |
7880 | |
7881 | _SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton btn, winrt::Windows::UI::Core::CoreWindow const& sender_window) { |
7882 | if (_sapp_events_enabled()) { |
7883 | _sapp_init_event(type); |
7884 | _sapp.event.modifiers = _sapp_uwp_mods(sender_window); |
7885 | _sapp.event.mouse_button = btn; |
7886 | _sapp_call_event(&_sapp.event); |
7887 | } |
7888 | } |
7889 | |
7890 | _SOKOL_PRIVATE void _sapp_uwp_scroll_event(float delta, bool horizontal, winrt::Windows::UI::Core::CoreWindow const& sender_window) { |
7891 | if (_sapp_events_enabled()) { |
7892 | _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); |
7893 | _sapp.event.modifiers = _sapp_uwp_mods(sender_window); |
7894 | _sapp.event.scroll_x = horizontal ? (-delta / 30.0f) : 0.0f; |
7895 | _sapp.event.scroll_y = horizontal ? 0.0f : (delta / 30.0f); |
7896 | _sapp_call_event(&_sapp.event); |
7897 | } |
7898 | } |
7899 | |
7900 | _SOKOL_PRIVATE void _sapp_uwp_extract_mouse_button_events(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
7901 | |
7902 | // we need to figure out ourselves what mouse buttons have been pressed and released, |
7903 | // because UWP doesn't properly send down/up mouse button events when multiple buttons |
7904 | // are pressed down, so we also need to check the mouse button state in other mouse events |
7905 | // to track what buttons have been pressed down and released |
7906 | // |
7907 | auto properties = args.CurrentPoint().Properties(); |
7908 | const uint8_t lmb_bit = (1 << SAPP_MOUSEBUTTON_LEFT); |
7909 | const uint8_t rmb_bit = (1 << SAPP_MOUSEBUTTON_RIGHT); |
7910 | const uint8_t mmb_bit = (1 << SAPP_MOUSEBUTTON_MIDDLE); |
7911 | uint8_t new_btns = 0; |
7912 | if (properties.IsLeftButtonPressed()) { |
7913 | new_btns |= lmb_bit; |
7914 | } |
7915 | if (properties.IsRightButtonPressed()) { |
7916 | new_btns |= rmb_bit; |
7917 | } |
7918 | if (properties.IsMiddleButtonPressed()) { |
7919 | new_btns |= mmb_bit; |
7920 | } |
7921 | const uint8_t old_btns = _sapp.uwp.mouse_buttons; |
7922 | const uint8_t chg_btns = new_btns ^ old_btns; |
7923 | |
7924 | _sapp.uwp.mouse_buttons = new_btns; |
7925 | |
7926 | sapp_event_type type = SAPP_EVENTTYPE_INVALID; |
7927 | sapp_mousebutton btn = SAPP_MOUSEBUTTON_INVALID; |
7928 | if (chg_btns & lmb_bit) { |
7929 | btn = SAPP_MOUSEBUTTON_LEFT; |
7930 | type = (new_btns & lmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; |
7931 | } |
7932 | if (chg_btns & rmb_bit) { |
7933 | btn = SAPP_MOUSEBUTTON_RIGHT; |
7934 | type = (new_btns & rmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; |
7935 | } |
7936 | if (chg_btns & mmb_bit) { |
7937 | btn = SAPP_MOUSEBUTTON_MIDDLE; |
7938 | type = (new_btns & mmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; |
7939 | } |
7940 | if (type != SAPP_EVENTTYPE_INVALID) { |
7941 | _sapp_uwp_mouse_event(type, btn, sender); |
7942 | } |
7943 | } |
7944 | |
7945 | _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) { |
7946 | auto key_status = args.KeyStatus(); |
7947 | uint32_t ext_scan_code = key_status.ScanCode | (key_status.IsExtendedKey ? 0x100 : 0); |
7948 | if (_sapp_events_enabled() && (ext_scan_code < SAPP_MAX_KEYCODES)) { |
7949 | _sapp_init_event(type); |
7950 | _sapp.event.modifiers = _sapp_uwp_mods(sender_window); |
7951 | _sapp.event.key_code = _sapp.keycodes[ext_scan_code]; |
7952 | _sapp.event.key_repeat = type == SAPP_EVENTTYPE_KEY_UP ? false : key_status.WasKeyDown; |
7953 | _sapp_call_event(&_sapp.event); |
7954 | /* check if a CLIPBOARD_PASTED event must be sent too */ |
7955 | if (_sapp.clipboard.enabled && |
7956 | (type == SAPP_EVENTTYPE_KEY_DOWN) && |
7957 | (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && |
7958 | (_sapp.event.key_code == SAPP_KEYCODE_V)) |
7959 | { |
7960 | _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); |
7961 | _sapp_call_event(&_sapp.event); |
7962 | } |
7963 | } |
7964 | } |
7965 | |
7966 | _SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows::UI::Core::CoreWindow const& sender_window) { |
7967 | if (_sapp_events_enabled() && (c >= 32)) { |
7968 | _sapp_init_event(SAPP_EVENTTYPE_CHAR); |
7969 | _sapp.event.modifiers = _sapp_uwp_mods(sender_window); |
7970 | _sapp.event.char_code = c; |
7971 | _sapp.event.key_repeat = repeat; |
7972 | _sapp_call_event(&_sapp.event); |
7973 | } |
7974 | } |
7975 | |
7976 | _SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { |
7977 | auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); |
7978 | _sapp.fullscreen = appView.IsFullScreenMode(); |
7979 | if (!_sapp.fullscreen) { |
7980 | appView.TryEnterFullScreenMode(); |
7981 | } |
7982 | else { |
7983 | appView.ExitFullScreenMode(); |
7984 | } |
7985 | _sapp.fullscreen = appView.IsFullScreenMode(); |
7986 | } |
7987 | |
7988 | namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ |
7989 | |
7990 | // Controls all the DirectX device resources. |
7991 | class DeviceResources { |
7992 | public: |
7993 | // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. |
7994 | interface IDeviceNotify { |
7995 | virtual void OnDeviceLost() = 0; |
7996 | virtual void OnDeviceRestored() = 0; |
7997 | }; |
7998 | |
7999 | DeviceResources(); |
8000 | ~DeviceResources(); |
8001 | void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); |
8002 | void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); |
8003 | void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); |
8004 | void SetDpi(float dpi); |
8005 | void ValidateDevice(); |
8006 | void HandleDeviceLost(); |
8007 | void RegisterDeviceNotify(IDeviceNotify* deviceNotify); |
8008 | void Trim(); |
8009 | void Present(); |
8010 | |
8011 | private: |
8012 | |
8013 | // Swapchain Rotation Matrices (Z-rotation) |
8014 | static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { |
8015 | 1.0f, 0.0f, 0.0f, 0.0f, |
8016 | 0.0f, 1.0f, 0.0f, 0.0f, |
8017 | 0.0f, 0.0f, 1.0f, 0.0f, |
8018 | 0.0f, 0.0f, 0.0f, 1.0f |
8019 | }; |
8020 | static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { |
8021 | 0.0f, 1.0f, 0.0f, 0.0f, |
8022 | -1.0f, 0.0f, 0.0f, 0.0f, |
8023 | 0.0f, 0.0f, 1.0f, 0.0f, |
8024 | 0.0f, 0.0f, 0.0f, 1.0f |
8025 | }; |
8026 | static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { |
8027 | -1.0f, 0.0f, 0.0f, 0.0f, |
8028 | 0.0f, -1.0f, 0.0f, 0.0f, |
8029 | 0.0f, 0.0f, 1.0f, 0.0f, |
8030 | 0.0f, 0.0f, 0.0f, 1.0f |
8031 | }; |
8032 | static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { |
8033 | 0.0f, -1.0f, 0.0f, 0.0f, |
8034 | 1.0f, 0.0f, 0.0f, 0.0f, |
8035 | 0.0f, 0.0f, 1.0f, 0.0f, |
8036 | 0.0f, 0.0f, 0.0f, 1.0f |
8037 | }; |
8038 | |
8039 | void CreateDeviceResources(); |
8040 | void CreateWindowSizeDependentResources(); |
8041 | void UpdateRenderTargetSize(); |
8042 | DXGI_MODE_ROTATION ComputeDisplayRotation(); |
8043 | bool SdkLayersAvailable(); |
8044 | |
8045 | // Direct3D objects. |
8046 | winrt::com_ptr<ID3D11Device3> m_d3dDevice; |
8047 | winrt::com_ptr<ID3D11DeviceContext3> m_d3dContext; |
8048 | winrt::com_ptr<IDXGISwapChain3> m_swapChain; |
8049 | |
8050 | // Direct3D rendering objects. Required for 3D. |
8051 | winrt::com_ptr<ID3D11Texture2D1> m_d3dRenderTarget; |
8052 | winrt::com_ptr<ID3D11RenderTargetView1> m_d3dRenderTargetView; |
8053 | winrt::com_ptr<ID3D11Texture2D1> m_d3dMSAARenderTarget; |
8054 | winrt::com_ptr<ID3D11RenderTargetView1> m_d3dMSAARenderTargetView; |
8055 | winrt::com_ptr<ID3D11Texture2D1> m_d3dDepthStencil; |
8056 | winrt::com_ptr<ID3D11DepthStencilView> m_d3dDepthStencilView; |
8057 | D3D11_VIEWPORT m_screenViewport = { }; |
8058 | |
8059 | // Cached reference to the Window. |
8060 | winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; |
8061 | |
8062 | // Cached device properties. |
8063 | D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; |
8064 | winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; |
8065 | winrt::Windows::Foundation::Size m_outputSize = { }; |
8066 | winrt::Windows::Foundation::Size m_logicalSize = { }; |
8067 | winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; |
8068 | winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; |
8069 | float m_dpi = -1.0f; |
8070 | |
8071 | // Transforms used for display orientation. |
8072 | DirectX::XMFLOAT4X4 m_orientationTransform3D; |
8073 | |
8074 | // The IDeviceNotify can be held directly as it owns the DeviceResources. |
8075 | IDeviceNotify* m_deviceNotify = nullptr; |
8076 | }; |
8077 | |
8078 | // Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. |
8079 | struct App : winrt::implements<App, winrt::Windows::ApplicationModel::Core::IFrameworkViewSource, winrt::Windows::ApplicationModel::Core::IFrameworkView> { |
8080 | public: |
8081 | // IFrameworkViewSource Methods |
8082 | winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } |
8083 | |
8084 | // IFrameworkView Methods. |
8085 | virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); |
8086 | virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); |
8087 | virtual void Load(winrt::hstring const& entryPoint); |
8088 | virtual void Run(); |
8089 | virtual void Uninitialize(); |
8090 | |
8091 | protected: |
8092 | // Application lifecycle event handlers |
8093 | void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); |
8094 | void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); |
8095 | void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); |
8096 | |
8097 | // Window event handlers |
8098 | void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); |
8099 | void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); |
8100 | |
8101 | // Navigation event handlers |
8102 | void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); |
8103 | |
8104 | // Input event handlers |
8105 | void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); |
8106 | void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); |
8107 | void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); |
8108 | |
8109 | // Pointer event handlers |
8110 | void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8111 | void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8112 | void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8113 | void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8114 | void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8115 | void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); |
8116 | |
8117 | // DisplayInformation event handlers. |
8118 | void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); |
8119 | void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); |
8120 | void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); |
8121 | |
8122 | private: |
8123 | std::unique_ptr<DeviceResources> m_deviceResources; |
8124 | bool m_windowVisible = true; |
8125 | }; |
8126 | |
8127 | DeviceResources::DeviceResources() { |
8128 | CreateDeviceResources(); |
8129 | } |
8130 | |
8131 | DeviceResources::~DeviceResources() { |
8132 | // Cleanup Sokol Context |
8133 | _sapp.d3d11.device = nullptr; |
8134 | _sapp.d3d11.device_context = nullptr; |
8135 | } |
8136 | |
8137 | void DeviceResources::CreateDeviceResources() { |
8138 | // This flag adds support for surfaces with a different color channel ordering |
8139 | // than the API default. It is required for compatibility with Direct2D. |
8140 | UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; |
8141 | |
8142 | #if defined(_DEBUG) |
8143 | if (SdkLayersAvailable()) { |
8144 | // If the project is in a debug build, enable debugging via SDK Layers with this flag. |
8145 | creationFlags |= D3D11_CREATE_DEVICE_DEBUG; |
8146 | } |
8147 | #endif |
8148 | |
8149 | // This array defines the set of DirectX hardware feature levels this app will support. |
8150 | // Note the ordering should be preserved. |
8151 | // Don't forget to declare your application's minimum required feature level in its |
8152 | // description. All applications are assumed to support 9.1 unless otherwise stated. |
8153 | D3D_FEATURE_LEVEL featureLevels[] = { |
8154 | D3D_FEATURE_LEVEL_12_1, |
8155 | D3D_FEATURE_LEVEL_12_0, |
8156 | D3D_FEATURE_LEVEL_11_1, |
8157 | D3D_FEATURE_LEVEL_11_0, |
8158 | D3D_FEATURE_LEVEL_10_1, |
8159 | D3D_FEATURE_LEVEL_10_0, |
8160 | D3D_FEATURE_LEVEL_9_3, |
8161 | D3D_FEATURE_LEVEL_9_2, |
8162 | D3D_FEATURE_LEVEL_9_1 |
8163 | }; |
8164 | |
8165 | // Create the Direct3D 11 API device object and a corresponding context. |
8166 | winrt::com_ptr<ID3D11Device> device; |
8167 | winrt::com_ptr<ID3D11DeviceContext> context; |
8168 | |
8169 | HRESULT hr = D3D11CreateDevice( |
8170 | nullptr, // Specify nullptr to use the default adapter. |
8171 | D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. |
8172 | 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. |
8173 | creationFlags, // Set debug and Direct2D compatibility flags. |
8174 | featureLevels, // List of feature levels this app can support. |
8175 | ARRAYSIZE(featureLevels), // Size of the list above. |
8176 | D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. |
8177 | device.put(), // Returns the Direct3D device created. |
8178 | &m_d3dFeatureLevel, // Returns feature level of device created. |
8179 | context.put() // Returns the device immediate context. |
8180 | ); |
8181 | |
8182 | if (FAILED(hr)) { |
8183 | // If the initialization fails, fall back to the WARP device. |
8184 | // For more information on WARP, see: |
8185 | // https://go.microsoft.com/fwlink/?LinkId=286690 |
8186 | winrt::check_hresult( |
8187 | D3D11CreateDevice( |
8188 | nullptr, |
8189 | D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. |
8190 | 0, |
8191 | creationFlags, |
8192 | featureLevels, |
8193 | ARRAYSIZE(featureLevels), |
8194 | D3D11_SDK_VERSION, |
8195 | device.put(), |
8196 | &m_d3dFeatureLevel, |
8197 | context.put() |
8198 | ) |
8199 | ); |
8200 | } |
8201 | |
8202 | // Store pointers to the Direct3D 11.3 API device and immediate context. |
8203 | m_d3dDevice = device.as<ID3D11Device3>(); |
8204 | m_d3dContext = context.as<ID3D11DeviceContext3>(); |
8205 | |
8206 | // Setup Sokol Context |
8207 | _sapp.d3d11.device = m_d3dDevice.get(); |
8208 | _sapp.d3d11.device_context = m_d3dContext.get(); |
8209 | } |
8210 | |
8211 | void DeviceResources::CreateWindowSizeDependentResources() { |
8212 | // Cleanup Sokol Context (these are non-owning raw pointers) |
8213 | _sapp.d3d11.rt = nullptr; |
8214 | _sapp.d3d11.rtv = nullptr; |
8215 | _sapp.d3d11.msaa_rt = nullptr; |
8216 | _sapp.d3d11.msaa_rtv = nullptr; |
8217 | _sapp.d3d11.ds = nullptr; |
8218 | _sapp.d3d11.dsv = nullptr; |
8219 | |
8220 | // Clear the previous window size specific context. |
8221 | ID3D11RenderTargetView* nullViews[] = { nullptr }; |
8222 | m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); |
8223 | // these are smart pointers, setting to nullptr will delete the objects |
8224 | m_d3dRenderTarget = nullptr; |
8225 | m_d3dRenderTargetView = nullptr; |
8226 | m_d3dMSAARenderTarget = nullptr; |
8227 | m_d3dMSAARenderTargetView = nullptr; |
8228 | m_d3dDepthStencilView = nullptr; |
8229 | m_d3dDepthStencil = nullptr; |
8230 | m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); |
8231 | |
8232 | UpdateRenderTargetSize(); |
8233 | |
8234 | // The width and height of the swap chain must be based on the window's |
8235 | // natively-oriented width and height. If the window is not in the native |
8236 | // orientation, the dimensions must be reversed. |
8237 | DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); |
8238 | |
8239 | bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; |
8240 | m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; |
8241 | m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; |
8242 | |
8243 | if (m_swapChain != nullptr) { |
8244 | // If the swap chain already exists, resize it. |
8245 | HRESULT hr = m_swapChain->ResizeBuffers( |
8246 | 2, // Double-buffered swap chain. |
8247 | lround(m_d3dRenderTargetSize.Width), |
8248 | lround(m_d3dRenderTargetSize.Height), |
8249 | DXGI_FORMAT_B8G8R8A8_UNORM, |
8250 | 0 |
8251 | ); |
8252 | |
8253 | if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { |
8254 | // If the device was removed for any reason, a new device and swap chain will need to be created. |
8255 | HandleDeviceLost(); |
8256 | |
8257 | // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method |
8258 | // and correctly set up the new device. |
8259 | return; |
8260 | } |
8261 | else { |
8262 | winrt::check_hresult(hr); |
8263 | } |
8264 | } |
8265 | else { |
8266 | // Otherwise, create a new one using the same adapter as the existing Direct3D device. |
8267 | DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; |
8268 | DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; |
8269 | |
8270 | swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. |
8271 | swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); |
8272 | swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. |
8273 | swapChainDesc.Stereo = false; |
8274 | swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. |
8275 | swapChainDesc.SampleDesc.Quality = 0; |
8276 | swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; |
8277 | swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. |
8278 | swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. |
8279 | swapChainDesc.Flags = 0; |
8280 | swapChainDesc.Scaling = scaling; |
8281 | swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; |
8282 | |
8283 | // This sequence obtains the DXGI factory that was used to create the Direct3D device above. |
8284 | winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as<IDXGIDevice3>(); |
8285 | winrt::com_ptr<IDXGIAdapter> dxgiAdapter; |
8286 | winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); |
8287 | winrt::com_ptr<IDXGIFactory4> dxgiFactory; |
8288 | winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); |
8289 | winrt::com_ptr<IDXGISwapChain1> swapChain; |
8290 | winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); |
8291 | m_swapChain = swapChain.as<IDXGISwapChain3>(); |
8292 | |
8293 | // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and |
8294 | // ensures that the application will only render after each VSync, minimizing power consumption. |
8295 | winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); |
8296 | |
8297 | // Setup Sokol Context |
8298 | winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); |
8299 | _sapp.d3d11.swap_chain = m_swapChain.as<IDXGISwapChain3>().detach(); |
8300 | } |
8301 | |
8302 | // Set the proper orientation for the swap chain, and generate 2D and |
8303 | // 3D matrix transformations for rendering to the rotated swap chain. |
8304 | // Note the rotation angle for the 2D and 3D transforms are different. |
8305 | // This is due to the difference in coordinate spaces. Additionally, |
8306 | // the 3D matrix is specified explicitly to avoid rounding errors. |
8307 | switch (displayRotation) { |
8308 | case DXGI_MODE_ROTATION_IDENTITY: |
8309 | m_orientationTransform3D = m_rotation0; |
8310 | break; |
8311 | |
8312 | case DXGI_MODE_ROTATION_ROTATE90: |
8313 | m_orientationTransform3D = m_rotation270; |
8314 | break; |
8315 | |
8316 | case DXGI_MODE_ROTATION_ROTATE180: |
8317 | m_orientationTransform3D = m_rotation180; |
8318 | break; |
8319 | |
8320 | case DXGI_MODE_ROTATION_ROTATE270: |
8321 | m_orientationTransform3D = m_rotation90; |
8322 | break; |
8323 | } |
8324 | winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); |
8325 | |
8326 | // Create a render target view of the swap chain back buffer. |
8327 | winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); |
8328 | winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); |
8329 | |
8330 | // Create MSAA texture and view if needed |
8331 | if (_sapp.sample_count > 1) { |
8332 | CD3D11_TEXTURE2D_DESC1 msaaTexDesc( |
8333 | DXGI_FORMAT_B8G8R8A8_UNORM, |
8334 | lround(m_d3dRenderTargetSize.Width), |
8335 | lround(m_d3dRenderTargetSize.Height), |
8336 | 1, // arraySize |
8337 | 1, // mipLevels |
8338 | D3D11_BIND_RENDER_TARGET, |
8339 | D3D11_USAGE_DEFAULT, |
8340 | 0, // cpuAccessFlags |
8341 | _sapp.sample_count, |
8342 | _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 |
8343 | ); |
8344 | winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); |
8345 | winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); |
8346 | } |
8347 | |
8348 | // Create a depth stencil view for use with 3D rendering if needed. |
8349 | CD3D11_TEXTURE2D_DESC1 depthStencilDesc( |
8350 | DXGI_FORMAT_D24_UNORM_S8_UINT, |
8351 | lround(m_d3dRenderTargetSize.Width), |
8352 | lround(m_d3dRenderTargetSize.Height), |
8353 | 1, // This depth stencil view has only one texture. |
8354 | 1, // Use a single mipmap level. |
8355 | D3D11_BIND_DEPTH_STENCIL, |
8356 | D3D11_USAGE_DEFAULT, |
8357 | 0, // cpuAccessFlag |
8358 | _sapp.sample_count, |
8359 | _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 |
8360 | ); |
8361 | winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); |
8362 | |
8363 | CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); |
8364 | winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); |
8365 | |
8366 | // Set sokol window and framebuffer sizes |
8367 | _sapp.window_width = (int) m_logicalSize.Width; |
8368 | _sapp.window_height = (int) m_logicalSize.Height; |
8369 | _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); |
8370 | _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); |
8371 | |
8372 | // Setup Sokol Context |
8373 | _sapp.d3d11.rt = m_d3dRenderTarget.as<ID3D11Texture2D>().get(); |
8374 | _sapp.d3d11.rtv = m_d3dRenderTargetView.as<ID3D11RenderTargetView>().get(); |
8375 | _sapp.d3d11.ds = m_d3dDepthStencil.as<ID3D11Texture2D>().get(); |
8376 | _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); |
8377 | if (_sapp.sample_count > 1) { |
8378 | _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as<ID3D11Texture2D>().get(); |
8379 | _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as<ID3D11RenderTargetView>().get(); |
8380 | } |
8381 | |
8382 | // Sokol app is now valid |
8383 | _sapp.valid = true; |
8384 | } |
8385 | |
8386 | // Determine the dimensions of the render target and whether it will be scaled down. |
8387 | void DeviceResources::UpdateRenderTargetSize() { |
8388 | // Calculate the necessary render target size in pixels. |
8389 | m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; |
8390 | m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; |
8391 | |
8392 | // Prevent zero size DirectX content from being created. |
8393 | m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); |
8394 | m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); |
8395 | } |
8396 | |
8397 | // This method is called when the CoreWindow is created (or re-created). |
8398 | void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { |
8399 | auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); |
8400 | m_window = window; |
8401 | m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); |
8402 | m_nativeOrientation = currentDisplayInformation.NativeOrientation(); |
8403 | m_currentOrientation = currentDisplayInformation.CurrentOrientation(); |
8404 | m_dpi = currentDisplayInformation.LogicalDpi(); |
8405 | _sapp_uwp_configure_dpi(m_dpi); |
8406 | CreateWindowSizeDependentResources(); |
8407 | } |
8408 | |
8409 | // This method is called in the event handler for the SizeChanged event. |
8410 | void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { |
8411 | if (m_logicalSize != logicalSize) { |
8412 | m_logicalSize = logicalSize; |
8413 | CreateWindowSizeDependentResources(); |
8414 | } |
8415 | } |
8416 | |
8417 | // This method is called in the event handler for the DpiChanged event. |
8418 | void DeviceResources::SetDpi(float dpi) { |
8419 | if (dpi != m_dpi) { |
8420 | m_dpi = dpi; |
8421 | _sapp_uwp_configure_dpi(m_dpi); |
8422 | // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. |
8423 | auto window = m_window.get(); |
8424 | m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); |
8425 | CreateWindowSizeDependentResources(); |
8426 | } |
8427 | } |
8428 | |
8429 | // This method is called in the event handler for the OrientationChanged event. |
8430 | void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { |
8431 | if (m_currentOrientation != currentOrientation) { |
8432 | m_currentOrientation = currentOrientation; |
8433 | CreateWindowSizeDependentResources(); |
8434 | } |
8435 | } |
8436 | |
8437 | // This method is called in the event handler for the DisplayContentsInvalidated event. |
8438 | void DeviceResources::ValidateDevice() { |
8439 | // The D3D Device is no longer valid if the default adapter changed since the device |
8440 | // was created or if the device has been removed. |
8441 | |
8442 | // First, get the information for the default adapter from when the device was created. |
8443 | winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); |
8444 | winrt::com_ptr<IDXGIAdapter> deviceAdapter; |
8445 | winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); |
8446 | winrt::com_ptr<IDXGIFactory4> deviceFactory; |
8447 | winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); |
8448 | winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter; |
8449 | winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); |
8450 | DXGI_ADAPTER_DESC1 previousDesc; |
8451 | winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); |
8452 | |
8453 | // Next, get the information for the current default adapter. |
8454 | winrt::com_ptr<IDXGIFactory4> currentFactory; |
8455 | winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); |
8456 | winrt::com_ptr<IDXGIAdapter1> currentDefaultAdapter; |
8457 | winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); |
8458 | DXGI_ADAPTER_DESC1 currentDesc; |
8459 | winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); |
8460 | |
8461 | // If the adapter LUIDs don't match, or if the device reports that it has been removed, |
8462 | // a new D3D device must be created. |
8463 | if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || |
8464 | previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || |
8465 | FAILED(m_d3dDevice->GetDeviceRemovedReason())) |
8466 | { |
8467 | // Release references to resources related to the old device. |
8468 | dxgiDevice = nullptr; |
8469 | deviceAdapter = nullptr; |
8470 | deviceFactory = nullptr; |
8471 | previousDefaultAdapter = nullptr; |
8472 | |
8473 | // Create a new device and swap chain. |
8474 | HandleDeviceLost(); |
8475 | } |
8476 | } |
8477 | |
8478 | // Recreate all device resources and set them back to the current state. |
8479 | void DeviceResources::HandleDeviceLost() { |
8480 | m_swapChain = nullptr; |
8481 | if (m_deviceNotify != nullptr) { |
8482 | m_deviceNotify->OnDeviceLost(); |
8483 | } |
8484 | CreateDeviceResources(); |
8485 | CreateWindowSizeDependentResources(); |
8486 | if (m_deviceNotify != nullptr) { |
8487 | m_deviceNotify->OnDeviceRestored(); |
8488 | } |
8489 | } |
8490 | |
8491 | // Register our DeviceNotify to be informed on device lost and creation. |
8492 | void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { |
8493 | m_deviceNotify = deviceNotify; |
8494 | } |
8495 | |
8496 | // Call this method when the app suspends. It provides a hint to the driver that the app |
8497 | // is entering an idle state and that temporary buffers can be reclaimed for use by other apps. |
8498 | void DeviceResources::Trim() { |
8499 | m_d3dDevice.as<IDXGIDevice3>()->Trim(); |
8500 | } |
8501 | |
8502 | // Present the contents of the swap chain to the screen. |
8503 | void DeviceResources::Present() { |
8504 | |
8505 | // MSAA resolve if needed |
8506 | if (_sapp.sample_count > 1) { |
8507 | m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); |
8508 | m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); |
8509 | } |
8510 | |
8511 | // The first argument instructs DXGI to block until VSync, putting the application |
8512 | // to sleep until the next VSync. This ensures we don't waste any cycles rendering |
8513 | // frames that will never be displayed to the screen. |
8514 | DXGI_PRESENT_PARAMETERS parameters = { 0 }; |
8515 | HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); |
8516 | |
8517 | // Discard the contents of the render target. |
8518 | // This is a valid operation only when the existing contents will be entirely |
8519 | // overwritten. If dirty or scroll rects are used, this call should be removed. |
8520 | m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); |
8521 | |
8522 | // Discard the contents of the depth stencil. |
8523 | m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); |
8524 | |
8525 | // If the device was removed either by a disconnection or a driver upgrade, we |
8526 | // must recreate all device resources. |
8527 | if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { |
8528 | HandleDeviceLost(); |
8529 | } |
8530 | else { |
8531 | winrt::check_hresult(hr); |
8532 | } |
8533 | } |
8534 | |
8535 | // This method determines the rotation between the display device's native orientation and the |
8536 | // current display orientation. |
8537 | DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { |
8538 | DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; |
8539 | |
8540 | // Note: NativeOrientation can only be Landscape or Portrait even though |
8541 | // the DisplayOrientations enum has other values. |
8542 | switch (m_nativeOrientation) { |
8543 | case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: |
8544 | switch (m_currentOrientation) { |
8545 | case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: |
8546 | rotation = DXGI_MODE_ROTATION_IDENTITY; |
8547 | break; |
8548 | |
8549 | case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: |
8550 | rotation = DXGI_MODE_ROTATION_ROTATE270; |
8551 | break; |
8552 | |
8553 | case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: |
8554 | rotation = DXGI_MODE_ROTATION_ROTATE180; |
8555 | break; |
8556 | |
8557 | case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: |
8558 | rotation = DXGI_MODE_ROTATION_ROTATE90; |
8559 | break; |
8560 | } |
8561 | break; |
8562 | |
8563 | case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: |
8564 | switch (m_currentOrientation) { |
8565 | case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: |
8566 | rotation = DXGI_MODE_ROTATION_ROTATE90; |
8567 | break; |
8568 | |
8569 | case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: |
8570 | rotation = DXGI_MODE_ROTATION_IDENTITY; |
8571 | break; |
8572 | |
8573 | case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: |
8574 | rotation = DXGI_MODE_ROTATION_ROTATE270; |
8575 | break; |
8576 | |
8577 | case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: |
8578 | rotation = DXGI_MODE_ROTATION_ROTATE180; |
8579 | break; |
8580 | } |
8581 | break; |
8582 | } |
8583 | return rotation; |
8584 | } |
8585 | |
8586 | // Check for SDK Layer support. |
8587 | bool DeviceResources::SdkLayersAvailable() { |
8588 | #if defined(_DEBUG) |
8589 | HRESULT hr = D3D11CreateDevice( |
8590 | nullptr, |
8591 | D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. |
8592 | 0, |
8593 | D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. |
8594 | nullptr, // Any feature level will do. |
8595 | 0, |
8596 | D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. |
8597 | nullptr, // No need to keep the D3D device reference. |
8598 | nullptr, // No need to know the feature level. |
8599 | nullptr // No need to keep the D3D device context reference. |
8600 | ); |
8601 | return SUCCEEDED(hr); |
8602 | #else |
8603 | return false; |
8604 | #endif |
8605 | } |
8606 | |
8607 | // The first method called when the IFrameworkView is being created. |
8608 | void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { |
8609 | // Register event handlers for app lifecycle. This example includes Activated, so that we |
8610 | // can make the CoreWindow active and start rendering on the window. |
8611 | applicationView.Activated({ this, &App::OnActivated }); |
8612 | |
8613 | winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); |
8614 | winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); |
8615 | |
8616 | // At this point we have access to the device. |
8617 | // We can create the device-dependent resources. |
8618 | m_deviceResources = std::make_unique<DeviceResources>(); |
8619 | } |
8620 | |
8621 | // Called when the CoreWindow object is created (or re-created). |
8622 | void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { |
8623 | window.SizeChanged({ this, &App::OnWindowSizeChanged }); |
8624 | window.VisibilityChanged({ this, &App::OnVisibilityChanged }); |
8625 | |
8626 | window.KeyDown({ this, &App::OnKeyDown }); |
8627 | window.KeyUp({ this, &App::OnKeyUp }); |
8628 | window.CharacterReceived({ this, &App::OnCharacterReceived }); |
8629 | |
8630 | window.PointerEntered({ this, &App::OnPointerEntered }); |
8631 | window.PointerExited({ this, &App::OnPointerExited }); |
8632 | window.PointerPressed({ this, &App::OnPointerPressed }); |
8633 | window.PointerReleased({ this, &App::OnPointerReleased }); |
8634 | window.PointerMoved({ this, &App::OnPointerMoved }); |
8635 | window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); |
8636 | |
8637 | auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); |
8638 | |
8639 | currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); |
8640 | currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); |
8641 | winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); |
8642 | |
8643 | winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); |
8644 | |
8645 | m_deviceResources->SetWindow(window); |
8646 | } |
8647 | |
8648 | // Initializes scene resources, or loads a previously saved app state. |
8649 | void App::Load(winrt::hstring const& entryPoint) { |
8650 | _SOKOL_UNUSED(entryPoint); |
8651 | } |
8652 | |
8653 | // This method is called after the window becomes active. |
8654 | void App::Run() { |
8655 | // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed |
8656 | while (true) { |
8657 | if (m_windowVisible) { |
8658 | _sapp_timing_measure(&_sapp.timing); |
8659 | winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); |
8660 | _sapp_frame(); |
8661 | m_deviceResources->Present(); |
8662 | } |
8663 | else { |
8664 | winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); |
8665 | } |
8666 | } |
8667 | } |
8668 | |
8669 | // Required for IFrameworkView. |
8670 | // Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView |
8671 | // class is torn down while the app is in the foreground. |
8672 | void App::Uninitialize() { |
8673 | // empty |
8674 | } |
8675 | |
8676 | // Application lifecycle event handlers. |
8677 | void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { |
8678 | _SOKOL_UNUSED(args); |
8679 | _SOKOL_UNUSED(applicationView); |
8680 | auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); |
8681 | const float window_width = (float)_sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); |
8682 | const float window_height = (float)_sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); |
8683 | auto targetSize = winrt::Windows::Foundation::Size(window_width, window_height); |
8684 | winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchViewSize(targetSize); |
8685 | winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchWindowingMode(winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::PreferredLaunchViewSize); |
8686 | appView.SetPreferredMinSize(targetSize); |
8687 | appView.TryResizeView(targetSize); |
8688 | |
8689 | // Disabling this since it can only append the title to the app name (Title - Appname). |
8690 | // There's no way of just setting a string to be the window title. |
8691 | //appView.Title(_sapp.window_title_wide); |
8692 | |
8693 | // Run() won't start until the CoreWindow is activated. |
8694 | winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); |
8695 | if (_sapp.desc.fullscreen) { |
8696 | appView.TryEnterFullScreenMode(); |
8697 | } |
8698 | _sapp.fullscreen = appView.IsFullScreenMode(); |
8699 | } |
8700 | |
8701 | void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { |
8702 | _SOKOL_UNUSED(sender); |
8703 | _SOKOL_UNUSED(args); |
8704 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); |
8705 | } |
8706 | |
8707 | void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { |
8708 | _SOKOL_UNUSED(args); |
8709 | _SOKOL_UNUSED(sender); |
8710 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); |
8711 | } |
8712 | |
8713 | void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { |
8714 | _SOKOL_UNUSED(args); |
8715 | m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); |
8716 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); |
8717 | } |
8718 | |
8719 | void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { |
8720 | _SOKOL_UNUSED(sender); |
8721 | m_windowVisible = args.Visible(); |
8722 | _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); |
8723 | } |
8724 | |
8725 | void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { |
8726 | _SOKOL_UNUSED(sender); |
8727 | args.Handled(true); |
8728 | } |
8729 | |
8730 | void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { |
8731 | auto status = args.KeyStatus(); |
8732 | _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); |
8733 | } |
8734 | |
8735 | void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { |
8736 | auto status = args.KeyStatus(); |
8737 | _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); |
8738 | } |
8739 | |
8740 | void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { |
8741 | _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); |
8742 | } |
8743 | |
8744 | void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8745 | _SOKOL_UNUSED(args); |
8746 | _sapp.uwp.mouse_tracked = true; |
8747 | _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); |
8748 | } |
8749 | |
8750 | void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8751 | _SOKOL_UNUSED(args); |
8752 | _sapp.uwp.mouse_tracked = false; |
8753 | _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); |
8754 | } |
8755 | |
8756 | void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8757 | _sapp_uwp_extract_mouse_button_events(sender, args); |
8758 | } |
8759 | |
8760 | // NOTE: for some reason this event handler is never called?? |
8761 | void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8762 | _sapp_uwp_extract_mouse_button_events(sender, args); |
8763 | } |
8764 | |
8765 | void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8766 | auto position = args.CurrentPoint().Position(); |
8767 | const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); |
8768 | const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); |
8769 | // don't update dx/dy in the very first event |
8770 | if (_sapp.mouse.pos_valid) { |
8771 | _sapp.mouse.dx = new_x - _sapp.mouse.x; |
8772 | _sapp.mouse.dy = new_y - _sapp.mouse.y; |
8773 | } |
8774 | _sapp.mouse.x = new_x; |
8775 | _sapp.mouse.y = new_y; |
8776 | _sapp.mouse.pos_valid = true; |
8777 | if (!_sapp.uwp.mouse_tracked) { |
8778 | _sapp.uwp.mouse_tracked = true; |
8779 | _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); |
8780 | } |
8781 | _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); |
8782 | |
8783 | // HACK for detecting multiple mouse button presses |
8784 | _sapp_uwp_extract_mouse_button_events(sender, args); |
8785 | } |
8786 | |
8787 | void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { |
8788 | auto properties = args.CurrentPoint().Properties(); |
8789 | _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); |
8790 | } |
8791 | |
8792 | void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { |
8793 | // NOTE: UNTESTED |
8794 | _SOKOL_UNUSED(args); |
8795 | m_deviceResources->SetDpi(sender.LogicalDpi()); |
8796 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); |
8797 | } |
8798 | |
8799 | void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { |
8800 | // NOTE: UNTESTED |
8801 | _SOKOL_UNUSED(args); |
8802 | m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); |
8803 | _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); |
8804 | } |
8805 | |
8806 | void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { |
8807 | // NOTE: UNTESTED |
8808 | _SOKOL_UNUSED(args); |
8809 | _SOKOL_UNUSED(sender); |
8810 | m_deviceResources->ValidateDevice(); |
8811 | } |
8812 | |
8813 | } /* End empty namespace */ |
8814 | |
8815 | _SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { |
8816 | _sapp_init_state(desc); |
8817 | _sapp_win32_uwp_init_keytable(); |
8818 | _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); |
8819 | winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make<App>()); |
8820 | } |
8821 | |
8822 | #if !defined(SOKOL_NO_ENTRY) |
8823 | #if defined(UNICODE) |
8824 | int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { |
8825 | #else |
8826 | int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { |
8827 | #endif |
8828 | _SOKOL_UNUSED(hInstance); |
8829 | _SOKOL_UNUSED(hPrevInstance); |
8830 | _SOKOL_UNUSED(lpCmdLine); |
8831 | _SOKOL_UNUSED(nCmdShow); |
8832 | sapp_desc desc = sokol_main(0, nullptr); |
8833 | _sapp_uwp_run(&desc); |
8834 | return 0; |
8835 | } |
8836 | #endif /* SOKOL_NO_ENTRY */ |
8837 | #endif /* _SAPP_UWP */ |
8838 | |
8839 | /*== Android ================================================================*/ |
8840 | #if defined(_SAPP_ANDROID) |
8841 | |
8842 | /* android loop thread */ |
8843 | _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { |
8844 | SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); |
8845 | SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); |
8846 | |
8847 | EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
8848 | if (display == EGL_NO_DISPLAY) { |
8849 | return false; |
8850 | } |
8851 | if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { |
8852 | return false; |
8853 | } |
8854 | _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; |
8855 | |
8856 | EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; |
8857 | const EGLint cfg_attributes[] = { |
8858 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
8859 | #if defined(SOKOL_GLES3) |
8860 | EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2?EGL_OPENGL_ES2_BIT:EGL_OPENGL_ES3_BIT, |
8861 | #else |
8862 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
8863 | #endif |
8864 | EGL_RED_SIZE, 8, |
8865 | EGL_GREEN_SIZE, 8, |
8866 | EGL_BLUE_SIZE, 8, |
8867 | EGL_ALPHA_SIZE, alpha_size, |
8868 | EGL_DEPTH_SIZE, 16, |
8869 | EGL_STENCIL_SIZE, 0, |
8870 | EGL_NONE, |
8871 | }; |
8872 | EGLConfig available_cfgs[32]; |
8873 | EGLint cfg_count; |
8874 | eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); |
8875 | SOKOL_ASSERT(cfg_count > 0); |
8876 | SOKOL_ASSERT(cfg_count <= 32); |
8877 | |
8878 | /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ |
8879 | EGLConfig config; |
8880 | bool exact_cfg_found = false; |
8881 | for (int i = 0; i < cfg_count; ++i) { |
8882 | EGLConfig c = available_cfgs[i]; |
8883 | EGLint r, g, b, a, d; |
8884 | if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && |
8885 | eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && |
8886 | eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && |
8887 | eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && |
8888 | eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && |
8889 | r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { |
8890 | exact_cfg_found = true; |
8891 | config = c; |
8892 | break; |
8893 | } |
8894 | } |
8895 | if (!exact_cfg_found) { |
8896 | config = available_cfgs[0]; |
8897 | } |
8898 | |
8899 | EGLint ctx_attributes[] = { |
8900 | #if defined(SOKOL_GLES3) |
8901 | EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, |
8902 | #else |
8903 | EGL_CONTEXT_CLIENT_VERSION, 2, |
8904 | #endif |
8905 | EGL_NONE, |
8906 | }; |
8907 | EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); |
8908 | if (context == EGL_NO_CONTEXT) { |
8909 | return false; |
8910 | } |
8911 | |
8912 | _sapp.android.config = config; |
8913 | _sapp.android.display = display; |
8914 | _sapp.android.context = context; |
8915 | return true; |
8916 | } |
8917 | |
8918 | _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { |
8919 | if (_sapp.android.display != EGL_NO_DISPLAY) { |
8920 | eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
8921 | if (_sapp.android.surface != EGL_NO_SURFACE) { |
8922 | SAPP_LOG("Destroying egl surface"); |
8923 | eglDestroySurface(_sapp.android.display, _sapp.android.surface); |
8924 | _sapp.android.surface = EGL_NO_SURFACE; |
8925 | } |
8926 | if (_sapp.android.context != EGL_NO_CONTEXT) { |
8927 | SAPP_LOG("Destroying egl context"); |
8928 | eglDestroyContext(_sapp.android.display, _sapp.android.context); |
8929 | _sapp.android.context = EGL_NO_CONTEXT; |
8930 | } |
8931 | SAPP_LOG("Terminating egl display"); |
8932 | eglTerminate(_sapp.android.display); |
8933 | _sapp.android.display = EGL_NO_DISPLAY; |
8934 | } |
8935 | } |
8936 | |
8937 | _SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { |
8938 | SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); |
8939 | SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); |
8940 | SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); |
8941 | SOKOL_ASSERT(window); |
8942 | |
8943 | /* TODO: set window flags */ |
8944 | /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ |
8945 | |
8946 | /* create egl surface and make it current */ |
8947 | EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); |
8948 | if (surface == EGL_NO_SURFACE) { |
8949 | return false; |
8950 | } |
8951 | if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { |
8952 | return false; |
8953 | } |
8954 | _sapp.android.surface = surface; |
8955 | return true; |
8956 | } |
8957 | |
8958 | _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { |
8959 | if (_sapp.android.display == EGL_NO_DISPLAY) { |
8960 | return; |
8961 | } |
8962 | eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
8963 | if (_sapp.android.surface != EGL_NO_SURFACE) { |
8964 | eglDestroySurface(_sapp.android.display, _sapp.android.surface); |
8965 | _sapp.android.surface = EGL_NO_SURFACE; |
8966 | } |
8967 | } |
8968 | |
8969 | _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { |
8970 | if (_sapp_events_enabled()) { |
8971 | _sapp_init_event(type); |
8972 | SAPP_LOG("event_cb()"); |
8973 | _sapp_call_event(&_sapp.event); |
8974 | } |
8975 | } |
8976 | |
8977 | _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { |
8978 | SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); |
8979 | SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); |
8980 | SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); |
8981 | SOKOL_ASSERT(window); |
8982 | |
8983 | const int32_t win_w = ANativeWindow_getWidth(window); |
8984 | const int32_t win_h = ANativeWindow_getHeight(window); |
8985 | SOKOL_ASSERT(win_w >= 0 && win_h >= 0); |
8986 | const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); |
8987 | _sapp.window_width = win_w; |
8988 | _sapp.window_height = win_h; |
8989 | if (win_changed || force_update) { |
8990 | if (!_sapp.desc.high_dpi) { |
8991 | const int32_t buf_w = win_w / 2; |
8992 | const int32_t buf_h = win_h / 2; |
8993 | EGLint format; |
8994 | EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); |
8995 | SOKOL_ASSERT(egl_result == EGL_TRUE); _SOKOL_UNUSED(egl_result); |
8996 | /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions |
8997 | as the ANativeWindow size results in weird display artefacts, that's |
8998 | why it's only called when the buffer geometry is different from |
8999 | the window size |
9000 | */ |
9001 | int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); |
9002 | SOKOL_ASSERT(result == 0); _SOKOL_UNUSED(result); |
9003 | } |
9004 | } |
9005 | |
9006 | /* query surface size */ |
9007 | EGLint fb_w, fb_h; |
9008 | EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); |
9009 | EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); |
9010 | SOKOL_ASSERT(egl_result_w == EGL_TRUE); _SOKOL_UNUSED(egl_result_w); |
9011 | SOKOL_ASSERT(egl_result_h == EGL_TRUE); _SOKOL_UNUSED(egl_result_h); |
9012 | const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); |
9013 | _sapp.framebuffer_width = fb_w; |
9014 | _sapp.framebuffer_height = fb_h; |
9015 | _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; |
9016 | if (win_changed || fb_changed || force_update) { |
9017 | if (!_sapp.first_frame) { |
9018 | SAPP_LOG("SAPP_EVENTTYPE_RESIZED"); |
9019 | _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); |
9020 | } |
9021 | } |
9022 | } |
9023 | |
9024 | _SOKOL_PRIVATE void _sapp_android_cleanup(void) { |
9025 | SAPP_LOG("Cleaning up"); |
9026 | if (_sapp.android.surface != EGL_NO_SURFACE) { |
9027 | /* egl context is bound, cleanup gracefully */ |
9028 | if (_sapp.init_called && !_sapp.cleanup_called) { |
9029 | SAPP_LOG("cleanup_cb()"); |
9030 | _sapp_call_cleanup(); |
9031 | } |
9032 | } |
9033 | /* always try to cleanup by destroying egl context */ |
9034 | _sapp_android_cleanup_egl(); |
9035 | } |
9036 | |
9037 | _SOKOL_PRIVATE void _sapp_android_shutdown(void) { |
9038 | /* try to cleanup while we still have a surface and can call cleanup_cb() */ |
9039 | _sapp_android_cleanup(); |
9040 | /* request exit */ |
9041 | ANativeActivity_finish(_sapp.android.activity); |
9042 | } |
9043 | |
9044 | _SOKOL_PRIVATE void _sapp_android_frame(void) { |
9045 | SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); |
9046 | SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); |
9047 | SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); |
9048 | _sapp_timing_measure(&_sapp.timing); |
9049 | _sapp_android_update_dimensions(_sapp.android.current.window, false); |
9050 | _sapp_frame(); |
9051 | eglSwapBuffers(_sapp.android.display, _sapp.android.surface); |
9052 | } |
9053 | |
9054 | _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { |
9055 | if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { |
9056 | return false; |
9057 | } |
9058 | if (!_sapp_events_enabled()) { |
9059 | return false; |
9060 | } |
9061 | int32_t action_idx = AMotionEvent_getAction(e); |
9062 | int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; |
9063 | sapp_event_type type = SAPP_EVENTTYPE_INVALID; |
9064 | switch (action) { |
9065 | case AMOTION_EVENT_ACTION_DOWN: |
9066 | SAPP_LOG("Touch: down"); |
9067 | case AMOTION_EVENT_ACTION_POINTER_DOWN: |
9068 | SAPP_LOG("Touch: ptr down"); |
9069 | type = SAPP_EVENTTYPE_TOUCHES_BEGAN; |
9070 | break; |
9071 | case AMOTION_EVENT_ACTION_MOVE: |
9072 | type = SAPP_EVENTTYPE_TOUCHES_MOVED; |
9073 | break; |
9074 | case AMOTION_EVENT_ACTION_UP: |
9075 | SAPP_LOG("Touch: up"); |
9076 | case AMOTION_EVENT_ACTION_POINTER_UP: |
9077 | SAPP_LOG("Touch: ptr up"); |
9078 | type = SAPP_EVENTTYPE_TOUCHES_ENDED; |
9079 | break; |
9080 | case AMOTION_EVENT_ACTION_CANCEL: |
9081 | SAPP_LOG("Touch: cancel"); |
9082 | type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; |
9083 | break; |
9084 | default: |
9085 | break; |
9086 | } |
9087 | if (type == SAPP_EVENTTYPE_INVALID) { |
9088 | return false; |
9089 | } |
9090 | int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; |
9091 | _sapp_init_event(type); |
9092 | _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e); |
9093 | if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { |
9094 | _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; |
9095 | } |
9096 | for (int32_t i = 0; i < _sapp.event.num_touches; i++) { |
9097 | sapp_touchpoint* dst = &_sapp.event.touches[i]; |
9098 | dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); |
9099 | dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; |
9100 | dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; |
9101 | dst->android_tooltype = (sapp_android_tooltype) AMotionEvent_getToolType(e, (size_t)i); |
9102 | if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || |
9103 | action == AMOTION_EVENT_ACTION_POINTER_UP) { |
9104 | dst->changed = (i == idx); |
9105 | } else { |
9106 | dst->changed = true; |
9107 | } |
9108 | } |
9109 | _sapp_call_event(&_sapp.event); |
9110 | return true; |
9111 | } |
9112 | |
9113 | _SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { |
9114 | if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { |
9115 | return false; |
9116 | } |
9117 | if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { |
9118 | /* FIXME: this should be hooked into a "really quit?" mechanism |
9119 | so the app can ask the user for confirmation, this is currently |
9120 | generally missing in sokol_app.h |
9121 | */ |
9122 | _sapp_android_shutdown(); |
9123 | return true; |
9124 | } |
9125 | return false; |
9126 | } |
9127 | |
9128 | _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { |
9129 | _SOKOL_UNUSED(fd); |
9130 | _SOKOL_UNUSED(data); |
9131 | if ((events & ALOOPER_EVENT_INPUT) == 0) { |
9132 | SAPP_LOG("_sapp_android_input_cb() encountered unsupported event"); |
9133 | return 1; |
9134 | } |
9135 | SOKOL_ASSERT(_sapp.android.current.input); |
9136 | AInputEvent* event = NULL; |
9137 | while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { |
9138 | if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { |
9139 | continue; |
9140 | } |
9141 | int32_t handled = 0; |
9142 | if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { |
9143 | handled = 1; |
9144 | } |
9145 | AInputQueue_finishEvent(_sapp.android.current.input, event, handled); |
9146 | } |
9147 | return 1; |
9148 | } |
9149 | |
9150 | _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { |
9151 | _SOKOL_UNUSED(data); |
9152 | if ((events & ALOOPER_EVENT_INPUT) == 0) { |
9153 | SAPP_LOG("_sapp_android_main_cb() encountered unsupported event"); |
9154 | return 1; |
9155 | } |
9156 | |
9157 | _sapp_android_msg_t msg; |
9158 | if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { |
9159 | SAPP_LOG("Could not write to read_from_main_fd"); |
9160 | return 1; |
9161 | } |
9162 | |
9163 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9164 | switch (msg) { |
9165 | case _SOKOL_ANDROID_MSG_CREATE: |
9166 | { |
9167 | SAPP_LOG("MSG_CREATE"); |
9168 | SOKOL_ASSERT(!_sapp.valid); |
9169 | bool result = _sapp_android_init_egl(); |
9170 | SOKOL_ASSERT(result); _SOKOL_UNUSED(result); |
9171 | _sapp.valid = true; |
9172 | _sapp.android.has_created = true; |
9173 | } |
9174 | break; |
9175 | case _SOKOL_ANDROID_MSG_RESUME: |
9176 | SAPP_LOG("MSG_RESUME"); |
9177 | _sapp.android.has_resumed = true; |
9178 | _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); |
9179 | break; |
9180 | case _SOKOL_ANDROID_MSG_PAUSE: |
9181 | SAPP_LOG("MSG_PAUSE"); |
9182 | _sapp.android.has_resumed = false; |
9183 | _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); |
9184 | break; |
9185 | case _SOKOL_ANDROID_MSG_FOCUS: |
9186 | SAPP_LOG("MSG_FOCUS"); |
9187 | _sapp.android.has_focus = true; |
9188 | break; |
9189 | case _SOKOL_ANDROID_MSG_NO_FOCUS: |
9190 | SAPP_LOG("MSG_NO_FOCUS"); |
9191 | _sapp.android.has_focus = false; |
9192 | break; |
9193 | case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: |
9194 | SAPP_LOG("MSG_SET_NATIVE_WINDOW"); |
9195 | if (_sapp.android.current.window != _sapp.android.pending.window) { |
9196 | if (_sapp.android.current.window != NULL) { |
9197 | _sapp_android_cleanup_egl_surface(); |
9198 | } |
9199 | if (_sapp.android.pending.window != NULL) { |
9200 | SAPP_LOG("Creating egl surface ..."); |
9201 | if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { |
9202 | SAPP_LOG("... ok!"); |
9203 | _sapp_android_update_dimensions(_sapp.android.pending.window, true); |
9204 | } else { |
9205 | SAPP_LOG("... failed!"); |
9206 | _sapp_android_shutdown(); |
9207 | } |
9208 | } |
9209 | } |
9210 | _sapp.android.current.window = _sapp.android.pending.window; |
9211 | break; |
9212 | case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: |
9213 | SAPP_LOG("MSG_SET_INPUT_QUEUE"); |
9214 | if (_sapp.android.current.input != _sapp.android.pending.input) { |
9215 | if (_sapp.android.current.input != NULL) { |
9216 | AInputQueue_detachLooper(_sapp.android.current.input); |
9217 | } |
9218 | if (_sapp.android.pending.input != NULL) { |
9219 | AInputQueue_attachLooper( |
9220 | _sapp.android.pending.input, |
9221 | _sapp.android.looper, |
9222 | ALOOPER_POLL_CALLBACK, |
9223 | _sapp_android_input_cb, |
9224 | NULL); /* data */ |
9225 | } |
9226 | } |
9227 | _sapp.android.current.input = _sapp.android.pending.input; |
9228 | break; |
9229 | case _SOKOL_ANDROID_MSG_DESTROY: |
9230 | SAPP_LOG("MSG_DESTROY"); |
9231 | _sapp_android_cleanup(); |
9232 | _sapp.valid = false; |
9233 | _sapp.android.is_thread_stopping = true; |
9234 | break; |
9235 | default: |
9236 | SAPP_LOG("Unknown msg type received"); |
9237 | break; |
9238 | } |
9239 | pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ |
9240 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9241 | return 1; |
9242 | } |
9243 | |
9244 | _SOKOL_PRIVATE bool _sapp_android_should_update(void) { |
9245 | bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; |
9246 | bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; |
9247 | return is_in_front && has_surface; |
9248 | } |
9249 | |
9250 | _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { |
9251 | SOKOL_ASSERT(_sapp.valid); |
9252 | /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ |
9253 | if (shown) { |
9254 | SAPP_LOG("Showing keyboard"); |
9255 | ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); |
9256 | } else { |
9257 | SAPP_LOG("Hiding keyboard"); |
9258 | ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); |
9259 | } |
9260 | } |
9261 | |
9262 | _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { |
9263 | _SOKOL_UNUSED(arg); |
9264 | SAPP_LOG("Loop thread started"); |
9265 | |
9266 | _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); |
9267 | ALooper_addFd(_sapp.android.looper, |
9268 | _sapp.android.pt.read_from_main_fd, |
9269 | ALOOPER_POLL_CALLBACK, |
9270 | ALOOPER_EVENT_INPUT, |
9271 | _sapp_android_main_cb, |
9272 | NULL); /* data */ |
9273 | |
9274 | /* signal start to main thread */ |
9275 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9276 | _sapp.android.is_thread_started = true; |
9277 | pthread_cond_broadcast(&_sapp.android.pt.cond); |
9278 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9279 | |
9280 | /* main loop */ |
9281 | while (!_sapp.android.is_thread_stopping) { |
9282 | /* sokol frame */ |
9283 | if (_sapp_android_should_update()) { |
9284 | _sapp_android_frame(); |
9285 | } |
9286 | |
9287 | /* process all events (or stop early if app is requested to quit) */ |
9288 | bool process_events = true; |
9289 | while (process_events && !_sapp.android.is_thread_stopping) { |
9290 | bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); |
9291 | process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; |
9292 | } |
9293 | } |
9294 | |
9295 | /* cleanup thread */ |
9296 | if (_sapp.android.current.input != NULL) { |
9297 | AInputQueue_detachLooper(_sapp.android.current.input); |
9298 | } |
9299 | |
9300 | /* the following causes heap corruption on exit, why?? |
9301 | ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); |
9302 | ALooper_release(_sapp.android.looper);*/ |
9303 | |
9304 | /* signal "destroyed" */ |
9305 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9306 | _sapp.android.is_thread_stopped = true; |
9307 | pthread_cond_broadcast(&_sapp.android.pt.cond); |
9308 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9309 | SAPP_LOG("Loop thread done"); |
9310 | return NULL; |
9311 | } |
9312 | |
9313 | /* android main/ui thread */ |
9314 | _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { |
9315 | if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { |
9316 | SAPP_LOG("Could not write to write_from_main_fd"); |
9317 | } |
9318 | } |
9319 | |
9320 | _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { |
9321 | _SOKOL_UNUSED(activity); |
9322 | SAPP_LOG("NativeActivity onStart()"); |
9323 | } |
9324 | |
9325 | _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { |
9326 | _SOKOL_UNUSED(activity); |
9327 | SAPP_LOG("NativeActivity onResume()"); |
9328 | _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); |
9329 | } |
9330 | |
9331 | _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { |
9332 | _SOKOL_UNUSED(activity); |
9333 | SAPP_LOG("NativeActivity onSaveInstanceState()"); |
9334 | *out_size = 0; |
9335 | return NULL; |
9336 | } |
9337 | |
9338 | _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { |
9339 | _SOKOL_UNUSED(activity); |
9340 | SAPP_LOG("NativeActivity onWindowFocusChanged()"); |
9341 | if (has_focus) { |
9342 | _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); |
9343 | } else { |
9344 | _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); |
9345 | } |
9346 | } |
9347 | |
9348 | _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { |
9349 | _SOKOL_UNUSED(activity); |
9350 | SAPP_LOG("NativeActivity onPause()"); |
9351 | _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); |
9352 | } |
9353 | |
9354 | _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { |
9355 | _SOKOL_UNUSED(activity); |
9356 | SAPP_LOG("NativeActivity onStop()"); |
9357 | } |
9358 | |
9359 | _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { |
9360 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9361 | _sapp.android.pending.window = window; |
9362 | _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); |
9363 | while (_sapp.android.current.window != window) { |
9364 | pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); |
9365 | } |
9366 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9367 | } |
9368 | |
9369 | _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { |
9370 | _SOKOL_UNUSED(activity); |
9371 | SAPP_LOG("NativeActivity onNativeWindowCreated()"); |
9372 | _sapp_android_msg_set_native_window(window); |
9373 | } |
9374 | |
9375 | _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { |
9376 | _SOKOL_UNUSED(activity); |
9377 | _SOKOL_UNUSED(window); |
9378 | SAPP_LOG("NativeActivity onNativeWindowDestroyed()"); |
9379 | _sapp_android_msg_set_native_window(NULL); |
9380 | } |
9381 | |
9382 | _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { |
9383 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9384 | _sapp.android.pending.input = input; |
9385 | _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); |
9386 | while (_sapp.android.current.input != input) { |
9387 | pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); |
9388 | } |
9389 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9390 | } |
9391 | |
9392 | _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { |
9393 | _SOKOL_UNUSED(activity); |
9394 | SAPP_LOG("NativeActivity onInputQueueCreated()"); |
9395 | _sapp_android_msg_set_input_queue(queue); |
9396 | } |
9397 | |
9398 | _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { |
9399 | _SOKOL_UNUSED(activity); |
9400 | _SOKOL_UNUSED(queue); |
9401 | SAPP_LOG("NativeActivity onInputQueueDestroyed()"); |
9402 | _sapp_android_msg_set_input_queue(NULL); |
9403 | } |
9404 | |
9405 | _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { |
9406 | _SOKOL_UNUSED(activity); |
9407 | SAPP_LOG("NativeActivity onConfigurationChanged()"); |
9408 | /* see android:configChanges in manifest */ |
9409 | } |
9410 | |
9411 | _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { |
9412 | _SOKOL_UNUSED(activity); |
9413 | SAPP_LOG("NativeActivity onLowMemory()"); |
9414 | } |
9415 | |
9416 | _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { |
9417 | /* |
9418 | * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) |
9419 | * on my device (Moto X 2nd gen) when the app is removed from the task view |
9420 | * (TaskStackView: onTaskViewDismissed). |
9421 | * |
9422 | * However, if ANativeActivity_finish() is explicitly called from for example |
9423 | * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? |
9424 | */ |
9425 | _SOKOL_UNUSED(activity); |
9426 | SAPP_LOG("NativeActivity onDestroy()"); |
9427 | |
9428 | /* send destroy msg */ |
9429 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9430 | _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); |
9431 | while (!_sapp.android.is_thread_stopped) { |
9432 | pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); |
9433 | } |
9434 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9435 | |
9436 | /* clean up main thread */ |
9437 | pthread_cond_destroy(&_sapp.android.pt.cond); |
9438 | pthread_mutex_destroy(&_sapp.android.pt.mutex); |
9439 | |
9440 | close(_sapp.android.pt.read_from_main_fd); |
9441 | close(_sapp.android.pt.write_from_main_fd); |
9442 | |
9443 | SAPP_LOG("NativeActivity done"); |
9444 | |
9445 | /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ |
9446 | exit(0); |
9447 | } |
9448 | |
9449 | JNIEXPORT |
9450 | void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { |
9451 | _SOKOL_UNUSED(saved_state); |
9452 | _SOKOL_UNUSED(saved_state_size); |
9453 | SAPP_LOG("NativeActivity onCreate()"); |
9454 | |
9455 | // the NativeActity pointer needs to be available inside sokol_main() |
9456 | // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state() |
9457 | // will clear the global _sapp_t struct, so we need to initialize the native |
9458 | // activity pointer twice, once before sokol_main() and once after _sapp_init_state() |
9459 | _sapp_clear(&_sapp, sizeof(_sapp)); |
9460 | _sapp.android.activity = activity; |
9461 | sapp_desc desc = sokol_main(0, NULL); |
9462 | _sapp_init_state(&desc); |
9463 | _sapp.android.activity = activity; |
9464 | |
9465 | int pipe_fd[2]; |
9466 | if (pipe(pipe_fd) != 0) { |
9467 | SAPP_LOG("Could not create thread pipe"); |
9468 | return; |
9469 | } |
9470 | _sapp.android.pt.read_from_main_fd = pipe_fd[0]; |
9471 | _sapp.android.pt.write_from_main_fd = pipe_fd[1]; |
9472 | |
9473 | pthread_mutex_init(&_sapp.android.pt.mutex, NULL); |
9474 | pthread_cond_init(&_sapp.android.pt.cond, NULL); |
9475 | |
9476 | pthread_attr_t attr; |
9477 | pthread_attr_init(&attr); |
9478 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
9479 | pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); |
9480 | pthread_attr_destroy(&attr); |
9481 | |
9482 | /* wait until main loop has started */ |
9483 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9484 | while (!_sapp.android.is_thread_started) { |
9485 | pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); |
9486 | } |
9487 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9488 | |
9489 | /* send create msg */ |
9490 | pthread_mutex_lock(&_sapp.android.pt.mutex); |
9491 | _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); |
9492 | while (!_sapp.android.has_created) { |
9493 | pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); |
9494 | } |
9495 | pthread_mutex_unlock(&_sapp.android.pt.mutex); |
9496 | |
9497 | /* register for callbacks */ |
9498 | activity->callbacks->onStart = _sapp_android_on_start; |
9499 | activity->callbacks->onResume = _sapp_android_on_resume; |
9500 | activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; |
9501 | activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; |
9502 | activity->callbacks->onPause = _sapp_android_on_pause; |
9503 | activity->callbacks->onStop = _sapp_android_on_stop; |
9504 | activity->callbacks->onDestroy = _sapp_android_on_destroy; |
9505 | activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; |
9506 | /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ |
9507 | /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ |
9508 | activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; |
9509 | activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; |
9510 | activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; |
9511 | /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ |
9512 | activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; |
9513 | activity->callbacks->onLowMemory = _sapp_android_on_low_memory; |
9514 | |
9515 | SAPP_LOG("NativeActivity successfully created"); |
9516 | |
9517 | /* NOT A BUG: do NOT call sapp_discard_state() */ |
9518 | } |
9519 | |
9520 | #endif /* _SAPP_ANDROID */ |
9521 | |
9522 | /*== LINUX ==================================================================*/ |
9523 | #if defined(_SAPP_LINUX) |
9524 | |
9525 | /* see GLFW's xkb_unicode.c */ |
9526 | static const struct _sapp_x11_codepair { |
9527 | uint16_t keysym; |
9528 | uint16_t ucs; |
9529 | } _sapp_x11_keysymtab[] = { |
9530 | { 0x01a1, 0x0104 }, |
9531 | { 0x01a2, 0x02d8 }, |
9532 | { 0x01a3, 0x0141 }, |
9533 | { 0x01a5, 0x013d }, |
9534 | { 0x01a6, 0x015a }, |
9535 | { 0x01a9, 0x0160 }, |
9536 | { 0x01aa, 0x015e }, |
9537 | { 0x01ab, 0x0164 }, |
9538 | { 0x01ac, 0x0179 }, |
9539 | { 0x01ae, 0x017d }, |
9540 | { 0x01af, 0x017b }, |
9541 | { 0x01b1, 0x0105 }, |
9542 | { 0x01b2, 0x02db }, |
9543 | { 0x01b3, 0x0142 }, |
9544 | { 0x01b5, 0x013e }, |
9545 | { 0x01b6, 0x015b }, |
9546 | { 0x01b7, 0x02c7 }, |
9547 | { 0x01b9, 0x0161 }, |
9548 | { 0x01ba, 0x015f }, |
9549 | { 0x01bb, 0x0165 }, |
9550 | { 0x01bc, 0x017a }, |
9551 | { 0x01bd, 0x02dd }, |
9552 | { 0x01be, 0x017e }, |
9553 | { 0x01bf, 0x017c }, |
9554 | { 0x01c0, 0x0154 }, |
9555 | { 0x01c3, 0x0102 }, |
9556 | { 0x01c5, 0x0139 }, |
9557 | { 0x01c6, 0x0106 }, |
9558 | { 0x01c8, 0x010c }, |
9559 | { 0x01ca, 0x0118 }, |
9560 | { 0x01cc, 0x011a }, |
9561 | { 0x01cf, 0x010e }, |
9562 | { 0x01d0, 0x0110 }, |
9563 | { 0x01d1, 0x0143 }, |
9564 | { 0x01d2, 0x0147 }, |
9565 | { 0x01d5, 0x0150 }, |
9566 | { 0x01d8, 0x0158 }, |
9567 | { 0x01d9, 0x016e }, |
9568 | { 0x01db, 0x0170 }, |
9569 | { 0x01de, 0x0162 }, |
9570 | { 0x01e0, 0x0155 }, |
9571 | { 0x01e3, 0x0103 }, |
9572 | { 0x01e5, 0x013a }, |
9573 | { 0x01e6, 0x0107 }, |
9574 | { 0x01e8, 0x010d }, |
9575 | { 0x01ea, 0x0119 }, |
9576 | { 0x01ec, 0x011b }, |
9577 | { 0x01ef, 0x010f }, |
9578 | { 0x01f0, 0x0111 }, |
9579 | { 0x01f1, 0x0144 }, |
9580 | { 0x01f2, 0x0148 }, |
9581 | { 0x01f5, 0x0151 }, |
9582 | { 0x01f8, 0x0159 }, |
9583 | { 0x01f9, 0x016f }, |
9584 | { 0x01fb, 0x0171 }, |
9585 | { 0x01fe, 0x0163 }, |
9586 | { 0x01ff, 0x02d9 }, |
9587 | { 0x02a1, 0x0126 }, |
9588 | { 0x02a6, 0x0124 }, |
9589 | { 0x02a9, 0x0130 }, |
9590 | { 0x02ab, 0x011e }, |
9591 | { 0x02ac, 0x0134 }, |
9592 | { 0x02b1, 0x0127 }, |
9593 | { 0x02b6, 0x0125 }, |
9594 | { 0x02b9, 0x0131 }, |
9595 | { 0x02bb, 0x011f }, |
9596 | { 0x02bc, 0x0135 }, |
9597 | { 0x02c5, 0x010a }, |
9598 | { 0x02c6, 0x0108 }, |
9599 | { 0x02d5, 0x0120 }, |
9600 | { 0x02d8, 0x011c }, |
9601 | { 0x02dd, 0x016c }, |
9602 | { 0x02de, 0x015c }, |
9603 | { 0x02e5, 0x010b }, |
9604 | { 0x02e6, 0x0109 }, |
9605 | { 0x02f5, 0x0121 }, |
9606 | { 0x02f8, 0x011d }, |
9607 | { 0x02fd, 0x016d }, |
9608 | { 0x02fe, 0x015d }, |
9609 | { 0x03a2, 0x0138 }, |
9610 | { 0x03a3, 0x0156 }, |
9611 | { 0x03a5, 0x0128 }, |
9612 | { 0x03a6, 0x013b }, |
9613 | { 0x03aa, 0x0112 }, |
9614 | { 0x03ab, 0x0122 }, |
9615 | { 0x03ac, 0x0166 }, |
9616 | { 0x03b3, 0x0157 }, |
9617 | { 0x03b5, 0x0129 }, |
9618 | { 0x03b6, 0x013c }, |
9619 | { 0x03ba, 0x0113 }, |
9620 | { 0x03bb, 0x0123 }, |
9621 | { 0x03bc, 0x0167 }, |
9622 | { 0x03bd, 0x014a }, |
9623 | { 0x03bf, 0x014b }, |
9624 | { 0x03c0, 0x0100 }, |
9625 | { 0x03c7, 0x012e }, |
9626 | { 0x03cc, 0x0116 }, |
9627 | { 0x03cf, 0x012a }, |
9628 | { 0x03d1, 0x0145 }, |
9629 | { 0x03d2, 0x014c }, |
9630 | { 0x03d3, 0x0136 }, |
9631 | { 0x03d9, 0x0172 }, |
9632 | { 0x03dd, 0x0168 }, |
9633 | { 0x03de, 0x016a }, |
9634 | { 0x03e0, 0x0101 }, |
9635 | { 0x03e7, 0x012f }, |
9636 | { 0x03ec, 0x0117 }, |
9637 | { 0x03ef, 0x012b }, |
9638 | { 0x03f1, 0x0146 }, |
9639 | { 0x03f2, 0x014d }, |
9640 | { 0x03f3, 0x0137 }, |
9641 | { 0x03f9, 0x0173 }, |
9642 | { 0x03fd, 0x0169 }, |
9643 | { 0x03fe, 0x016b }, |
9644 | { 0x047e, 0x203e }, |
9645 | { 0x04a1, 0x3002 }, |
9646 | { 0x04a2, 0x300c }, |
9647 | { 0x04a3, 0x300d }, |
9648 | { 0x04a4, 0x3001 }, |
9649 | { 0x04a5, 0x30fb }, |
9650 | { 0x04a6, 0x30f2 }, |
9651 | { 0x04a7, 0x30a1 }, |
9652 | { 0x04a8, 0x30a3 }, |
9653 | { 0x04a9, 0x30a5 }, |
9654 | { 0x04aa, 0x30a7 }, |
9655 | { 0x04ab, 0x30a9 }, |
9656 | { 0x04ac, 0x30e3 }, |
9657 | { 0x04ad, 0x30e5 }, |
9658 | { 0x04ae, 0x30e7 }, |
9659 | { 0x04af, 0x30c3 }, |
9660 | { 0x04b0, 0x30fc }, |
9661 | { 0x04b1, 0x30a2 }, |
9662 | { 0x04b2, 0x30a4 }, |
9663 | { 0x04b3, 0x30a6 }, |
9664 | { 0x04b4, 0x30a8 }, |
9665 | { 0x04b5, 0x30aa }, |
9666 | { 0x04b6, 0x30ab }, |
9667 | { 0x04b7, 0x30ad }, |
9668 | { 0x04b8, 0x30af }, |
9669 | { 0x04b9, 0x30b1 }, |
9670 | { 0x04ba, 0x30b3 }, |
9671 | { 0x04bb, 0x30b5 }, |
9672 | { 0x04bc, 0x30b7 }, |
9673 | { 0x04bd, 0x30b9 }, |
9674 | { 0x04be, 0x30bb }, |
9675 | { 0x04bf, 0x30bd }, |
9676 | { 0x04c0, 0x30bf }, |
9677 | { 0x04c1, 0x30c1 }, |
9678 | { 0x04c2, 0x30c4 }, |
9679 | { 0x04c3, 0x30c6 }, |
9680 | { 0x04c4, 0x30c8 }, |
9681 | { 0x04c5, 0x30ca }, |
9682 | { 0x04c6, 0x30cb }, |
9683 | { 0x04c7, 0x30cc }, |
9684 | { 0x04c8, 0x30cd }, |
9685 | { 0x04c9, 0x30ce }, |
9686 | { 0x04ca, 0x30cf }, |
9687 | { 0x04cb, 0x30d2 }, |
9688 | { 0x04cc, 0x30d5 }, |
9689 | { 0x04cd, 0x30d8 }, |
9690 | { 0x04ce, 0x30db }, |
9691 | { 0x04cf, 0x30de }, |
9692 | { 0x04d0, 0x30df }, |
9693 | { 0x04d1, 0x30e0 }, |
9694 | { 0x04d2, 0x30e1 }, |
9695 | { 0x04d3, 0x30e2 }, |
9696 | { 0x04d4, 0x30e4 }, |
9697 | { 0x04d5, 0x30e6 }, |
9698 | { 0x04d6, 0x30e8 }, |
9699 | { 0x04d7, 0x30e9 }, |
9700 | { 0x04d8, 0x30ea }, |
9701 | { 0x04d9, 0x30eb }, |
9702 | { 0x04da, 0x30ec }, |
9703 | { 0x04db, 0x30ed }, |
9704 | { 0x04dc, 0x30ef }, |
9705 | { 0x04dd, 0x30f3 }, |
9706 | { 0x04de, 0x309b }, |
9707 | { 0x04df, 0x309c }, |
9708 | { 0x05ac, 0x060c }, |
9709 | { 0x05bb, 0x061b }, |
9710 | { 0x05bf, 0x061f }, |
9711 | { 0x05c1, 0x0621 }, |
9712 | { 0x05c2, 0x0622 }, |
9713 | { 0x05c3, 0x0623 }, |
9714 | { 0x05c4, 0x0624 }, |
9715 | { 0x05c5, 0x0625 }, |
9716 | { 0x05c6, 0x0626 }, |
9717 | { 0x05c7, 0x0627 }, |
9718 | { 0x05c8, 0x0628 }, |
9719 | { 0x05c9, 0x0629 }, |
9720 | { 0x05ca, 0x062a }, |
9721 | { 0x05cb, 0x062b }, |
9722 | { 0x05cc, 0x062c }, |
9723 | { 0x05cd, 0x062d }, |
9724 | { 0x05ce, 0x062e }, |
9725 | { 0x05cf, 0x062f }, |
9726 | { 0x05d0, 0x0630 }, |
9727 | { 0x05d1, 0x0631 }, |
9728 | { 0x05d2, 0x0632 }, |
9729 | { 0x05d3, 0x0633 }, |
9730 | { 0x05d4, 0x0634 }, |
9731 | { 0x05d5, 0x0635 }, |
9732 | { 0x05d6, 0x0636 }, |
9733 | { 0x05d7, 0x0637 }, |
9734 | { 0x05d8, 0x0638 }, |
9735 | { 0x05d9, 0x0639 }, |
9736 | { 0x05da, 0x063a }, |
9737 | { 0x05e0, 0x0640 }, |
9738 | { 0x05e1, 0x0641 }, |
9739 | { 0x05e2, 0x0642 }, |
9740 | { 0x05e3, 0x0643 }, |
9741 | { 0x05e4, 0x0644 }, |
9742 | { 0x05e5, 0x0645 }, |
9743 | { 0x05e6, 0x0646 }, |
9744 | { 0x05e7, 0x0647 }, |
9745 | { 0x05e8, 0x0648 }, |
9746 | { 0x05e9, 0x0649 }, |
9747 | { 0x05ea, 0x064a }, |
9748 | { 0x05eb, 0x064b }, |
9749 | { 0x05ec, 0x064c }, |
9750 | { 0x05ed, 0x064d }, |
9751 | { 0x05ee, 0x064e }, |
9752 | { 0x05ef, 0x064f }, |
9753 | { 0x05f0, 0x0650 }, |
9754 | { 0x05f1, 0x0651 }, |
9755 | { 0x05f2, 0x0652 }, |
9756 | { 0x06a1, 0x0452 }, |
9757 | { 0x06a2, 0x0453 }, |
9758 | { 0x06a3, 0x0451 }, |
9759 | { 0x06a4, 0x0454 }, |
9760 | { 0x06a5, 0x0455 }, |
9761 | { 0x06a6, 0x0456 }, |
9762 | { 0x06a7, 0x0457 }, |
9763 | { 0x06a8, 0x0458 }, |
9764 | { 0x06a9, 0x0459 }, |
9765 | { 0x06aa, 0x045a }, |
9766 | { 0x06ab, 0x045b }, |
9767 | { 0x06ac, 0x045c }, |
9768 | { 0x06ae, 0x045e }, |
9769 | { 0x06af, 0x045f }, |
9770 | { 0x06b0, 0x2116 }, |
9771 | { 0x06b1, 0x0402 }, |
9772 | { 0x06b2, 0x0403 }, |
9773 | { 0x06b3, 0x0401 }, |
9774 | { 0x06b4, 0x0404 }, |
9775 | { 0x06b5, 0x0405 }, |
9776 | { 0x06b6, 0x0406 }, |
9777 | { 0x06b7, 0x0407 }, |
9778 | { 0x06b8, 0x0408 }, |
9779 | { 0x06b9, 0x0409 }, |
9780 | { 0x06ba, 0x040a }, |
9781 | { 0x06bb, 0x040b }, |
9782 | { 0x06bc, 0x040c }, |
9783 | { 0x06be, 0x040e }, |
9784 | { 0x06bf, 0x040f }, |
9785 | { 0x06c0, 0x044e }, |
9786 | { 0x06c1, 0x0430 }, |
9787 | { 0x06c2, 0x0431 }, |
9788 | { 0x06c3, 0x0446 }, |
9789 | { 0x06c4, 0x0434 }, |
9790 | { 0x06c5, 0x0435 }, |
9791 | { 0x06c6, 0x0444 }, |
9792 | { 0x06c7, 0x0433 }, |
9793 | { 0x06c8, 0x0445 }, |
9794 | { 0x06c9, 0x0438 }, |
9795 | { 0x06ca, 0x0439 }, |
9796 | { 0x06cb, 0x043a }, |
9797 | { 0x06cc, 0x043b }, |
9798 | { 0x06cd, 0x043c }, |
9799 | { 0x06ce, 0x043d }, |
9800 | { 0x06cf, 0x043e }, |
9801 | { 0x06d0, 0x043f }, |
9802 | { 0x06d1, 0x044f }, |
9803 | { 0x06d2, 0x0440 }, |
9804 | { 0x06d3, 0x0441 }, |
9805 | { 0x06d4, 0x0442 }, |
9806 | { 0x06d5, 0x0443 }, |
9807 | { 0x06d6, 0x0436 }, |
9808 | { 0x06d7, 0x0432 }, |
9809 | { 0x06d8, 0x044c }, |
9810 | { 0x06d9, 0x044b }, |
9811 | { 0x06da, 0x0437 }, |
9812 | { 0x06db, 0x0448 }, |
9813 | { 0x06dc, 0x044d }, |
9814 | { 0x06dd, 0x0449 }, |
9815 | { 0x06de, 0x0447 }, |
9816 | { 0x06df, 0x044a }, |
9817 | { 0x06e0, 0x042e }, |
9818 | { 0x06e1, 0x0410 }, |
9819 | { 0x06e2, 0x0411 }, |
9820 | { 0x06e3, 0x0426 }, |
9821 | { 0x06e4, 0x0414 }, |
9822 | { 0x06e5, 0x0415 }, |
9823 | { 0x06e6, 0x0424 }, |
9824 | { 0x06e7, 0x0413 }, |
9825 | { 0x06e8, 0x0425 }, |
9826 | { 0x06e9, 0x0418 }, |
9827 | { 0x06ea, 0x0419 }, |
9828 | { 0x06eb, 0x041a }, |
9829 | { 0x06ec, 0x041b }, |
9830 | { 0x06ed, 0x041c }, |
9831 | { 0x06ee, 0x041d }, |
9832 | { 0x06ef, 0x041e }, |
9833 | { 0x06f0, 0x041f }, |
9834 | { 0x06f1, 0x042f }, |
9835 | { 0x06f2, 0x0420 }, |
9836 | { 0x06f3, 0x0421 }, |
9837 | { 0x06f4, 0x0422 }, |
9838 | { 0x06f5, 0x0423 }, |
9839 | { 0x06f6, 0x0416 }, |
9840 | { 0x06f7, 0x0412 }, |
9841 | { 0x06f8, 0x042c }, |
9842 | { 0x06f9, 0x042b }, |
9843 | { 0x06fa, 0x0417 }, |
9844 | { 0x06fb, 0x0428 }, |
9845 | { 0x06fc, 0x042d }, |
9846 | { 0x06fd, 0x0429 }, |
9847 | { 0x06fe, 0x0427 }, |
9848 | { 0x06ff, 0x042a }, |
9849 | { 0x07a1, 0x0386 }, |
9850 | { 0x07a2, 0x0388 }, |
9851 | { 0x07a3, 0x0389 }, |
9852 | { 0x07a4, 0x038a }, |
9853 | { 0x07a5, 0x03aa }, |
9854 | { 0x07a7, 0x038c }, |
9855 | { 0x07a8, 0x038e }, |
9856 | { 0x07a9, 0x03ab }, |
9857 | { 0x07ab, 0x038f }, |
9858 | { 0x07ae, 0x0385 }, |
9859 | { 0x07af, 0x2015 }, |
9860 | { 0x07b1, 0x03ac }, |
9861 | { 0x07b2, 0x03ad }, |
9862 | { 0x07b3, 0x03ae }, |
9863 | { 0x07b4, 0x03af }, |
9864 | { 0x07b5, 0x03ca }, |
9865 | { 0x07b6, 0x0390 }, |
9866 | { 0x07b7, 0x03cc }, |
9867 | { 0x07b8, 0x03cd }, |
9868 | { 0x07b9, 0x03cb }, |
9869 | { 0x07ba, 0x03b0 }, |
9870 | { 0x07bb, 0x03ce }, |
9871 | { 0x07c1, 0x0391 }, |
9872 | { 0x07c2, 0x0392 }, |
9873 | { 0x07c3, 0x0393 }, |
9874 | { 0x07c4, 0x0394 }, |
9875 | { 0x07c5, 0x0395 }, |
9876 | { 0x07c6, 0x0396 }, |
9877 | { 0x07c7, 0x0397 }, |
9878 | { 0x07c8, 0x0398 }, |
9879 | { 0x07c9, 0x0399 }, |
9880 | { 0x07ca, 0x039a }, |
9881 | { 0x07cb, 0x039b }, |
9882 | { 0x07cc, 0x039c }, |
9883 | { 0x07cd, 0x039d }, |
9884 | { 0x07ce, 0x039e }, |
9885 | { 0x07cf, 0x039f }, |
9886 | { 0x07d0, 0x03a0 }, |
9887 | { 0x07d1, 0x03a1 }, |
9888 | { 0x07d2, 0x03a3 }, |
9889 | { 0x07d4, 0x03a4 }, |
9890 | { 0x07d5, 0x03a5 }, |
9891 | { 0x07d6, 0x03a6 }, |
9892 | { 0x07d7, 0x03a7 }, |
9893 | { 0x07d8, 0x03a8 }, |
9894 | { 0x07d9, 0x03a9 }, |
9895 | { 0x07e1, 0x03b1 }, |
9896 | { 0x07e2, 0x03b2 }, |
9897 | { 0x07e3, 0x03b3 }, |
9898 | { 0x07e4, 0x03b4 }, |
9899 | { 0x07e5, 0x03b5 }, |
9900 | { 0x07e6, 0x03b6 }, |
9901 | { 0x07e7, 0x03b7 }, |
9902 | { 0x07e8, 0x03b8 }, |
9903 | { 0x07e9, 0x03b9 }, |
9904 | { 0x07ea, 0x03ba }, |
9905 | { 0x07eb, 0x03bb }, |
9906 | { 0x07ec, 0x03bc }, |
9907 | { 0x07ed, 0x03bd }, |
9908 | { 0x07ee, 0x03be }, |
9909 | { 0x07ef, 0x03bf }, |
9910 | { 0x07f0, 0x03c0 }, |
9911 | { 0x07f1, 0x03c1 }, |
9912 | { 0x07f2, 0x03c3 }, |
9913 | { 0x07f3, 0x03c2 }, |
9914 | { 0x07f4, 0x03c4 }, |
9915 | { 0x07f5, 0x03c5 }, |
9916 | { 0x07f6, 0x03c6 }, |
9917 | { 0x07f7, 0x03c7 }, |
9918 | { 0x07f8, 0x03c8 }, |
9919 | { 0x07f9, 0x03c9 }, |
9920 | { 0x08a1, 0x23b7 }, |
9921 | { 0x08a2, 0x250c }, |
9922 | { 0x08a3, 0x2500 }, |
9923 | { 0x08a4, 0x2320 }, |
9924 | { 0x08a5, 0x2321 }, |
9925 | { 0x08a6, 0x2502 }, |
9926 | { 0x08a7, 0x23a1 }, |
9927 | { 0x08a8, 0x23a3 }, |
9928 | { 0x08a9, 0x23a4 }, |
9929 | { 0x08aa, 0x23a6 }, |
9930 | { 0x08ab, 0x239b }, |
9931 | { 0x08ac, 0x239d }, |
9932 | { 0x08ad, 0x239e }, |
9933 | { 0x08ae, 0x23a0 }, |
9934 | { 0x08af, 0x23a8 }, |
9935 | { 0x08b0, 0x23ac }, |
9936 | { 0x08bc, 0x2264 }, |
9937 | { 0x08bd, 0x2260 }, |
9938 | { 0x08be, 0x2265 }, |
9939 | { 0x08bf, 0x222b }, |
9940 | { 0x08c0, 0x2234 }, |
9941 | { 0x08c1, 0x221d }, |
9942 | { 0x08c2, 0x221e }, |
9943 | { 0x08c5, 0x2207 }, |
9944 | { 0x08c8, 0x223c }, |
9945 | { 0x08c9, 0x2243 }, |
9946 | { 0x08cd, 0x21d4 }, |
9947 | { 0x08ce, 0x21d2 }, |
9948 | { 0x08cf, 0x2261 }, |
9949 | { 0x08d6, 0x221a }, |
9950 | { 0x08da, 0x2282 }, |
9951 | { 0x08db, 0x2283 }, |
9952 | { 0x08dc, 0x2229 }, |
9953 | { 0x08dd, 0x222a }, |
9954 | { 0x08de, 0x2227 }, |
9955 | { 0x08df, 0x2228 }, |
9956 | { 0x08ef, 0x2202 }, |
9957 | { 0x08f6, 0x0192 }, |
9958 | { 0x08fb, 0x2190 }, |
9959 | { 0x08fc, 0x2191 }, |
9960 | { 0x08fd, 0x2192 }, |
9961 | { 0x08fe, 0x2193 }, |
9962 | { 0x09e0, 0x25c6 }, |
9963 | { 0x09e1, 0x2592 }, |
9964 | { 0x09e2, 0x2409 }, |
9965 | { 0x09e3, 0x240c }, |
9966 | { 0x09e4, 0x240d }, |
9967 | { 0x09e5, 0x240a }, |
9968 | { 0x09e8, 0x2424 }, |
9969 | { 0x09e9, 0x240b }, |
9970 | { 0x09ea, 0x2518 }, |
9971 | { 0x09eb, 0x2510 }, |
9972 | { 0x09ec, 0x250c }, |
9973 | { 0x09ed, 0x2514 }, |
9974 | { 0x09ee, 0x253c }, |
9975 | { 0x09ef, 0x23ba }, |
9976 | { 0x09f0, 0x23bb }, |
9977 | { 0x09f1, 0x2500 }, |
9978 | { 0x09f2, 0x23bc }, |
9979 | { 0x09f3, 0x23bd }, |
9980 | { 0x09f4, 0x251c }, |
9981 | { 0x09f5, 0x2524 }, |
9982 | { 0x09f6, 0x2534 }, |
9983 | { 0x09f7, 0x252c }, |
9984 | { 0x09f8, 0x2502 }, |
9985 | { 0x0aa1, 0x2003 }, |
9986 | { 0x0aa2, 0x2002 }, |
9987 | { 0x0aa3, 0x2004 }, |
9988 | { 0x0aa4, 0x2005 }, |
9989 | { 0x0aa5, 0x2007 }, |
9990 | { 0x0aa6, 0x2008 }, |
9991 | { 0x0aa7, 0x2009 }, |
9992 | { 0x0aa8, 0x200a }, |
9993 | { 0x0aa9, 0x2014 }, |
9994 | { 0x0aaa, 0x2013 }, |
9995 | { 0x0aae, 0x2026 }, |
9996 | { 0x0aaf, 0x2025 }, |
9997 | { 0x0ab0, 0x2153 }, |
9998 | { 0x0ab1, 0x2154 }, |
9999 | { 0x0ab2, 0x2155 }, |
10000 | { 0x0ab3, 0x2156 }, |
10001 | { 0x0ab4, 0x2157 }, |
10002 | { 0x0ab5, 0x2158 }, |
10003 | { 0x0ab6, 0x2159 }, |
10004 | { 0x0ab7, 0x215a }, |
10005 | { 0x0ab8, 0x2105 }, |
10006 | { 0x0abb, 0x2012 }, |
10007 | { 0x0abc, 0x2329 }, |
10008 | { 0x0abe, 0x232a }, |
10009 | { 0x0ac3, 0x215b }, |
10010 | { 0x0ac4, 0x215c }, |
10011 | { 0x0ac5, 0x215d }, |
10012 | { 0x0ac6, 0x215e }, |
10013 | { 0x0ac9, 0x2122 }, |
10014 | { 0x0aca, 0x2613 }, |
10015 | { 0x0acc, 0x25c1 }, |
10016 | { 0x0acd, 0x25b7 }, |
10017 | { 0x0ace, 0x25cb }, |
10018 | { 0x0acf, 0x25af }, |
10019 | { 0x0ad0, 0x2018 }, |
10020 | { 0x0ad1, 0x2019 }, |
10021 | { 0x0ad2, 0x201c }, |
10022 | { 0x0ad3, 0x201d }, |
10023 | { 0x0ad4, 0x211e }, |
10024 | { 0x0ad6, 0x2032 }, |
10025 | { 0x0ad7, 0x2033 }, |
10026 | { 0x0ad9, 0x271d }, |
10027 | { 0x0adb, 0x25ac }, |
10028 | { 0x0adc, 0x25c0 }, |
10029 | { 0x0add, 0x25b6 }, |
10030 | { 0x0ade, 0x25cf }, |
10031 | { 0x0adf, 0x25ae }, |
10032 | { 0x0ae0, 0x25e6 }, |
10033 | { 0x0ae1, 0x25ab }, |
10034 | { 0x0ae2, 0x25ad }, |
10035 | { 0x0ae3, 0x25b3 }, |
10036 | { 0x0ae4, 0x25bd }, |
10037 | { 0x0ae5, 0x2606 }, |
10038 | { 0x0ae6, 0x2022 }, |
10039 | { 0x0ae7, 0x25aa }, |
10040 | { 0x0ae8, 0x25b2 }, |
10041 | { 0x0ae9, 0x25bc }, |
10042 | { 0x0aea, 0x261c }, |
10043 | { 0x0aeb, 0x261e }, |
10044 | { 0x0aec, 0x2663 }, |
10045 | { 0x0aed, 0x2666 }, |
10046 | { 0x0aee, 0x2665 }, |
10047 | { 0x0af0, 0x2720 }, |
10048 | { 0x0af1, 0x2020 }, |
10049 | { 0x0af2, 0x2021 }, |
10050 | { 0x0af3, 0x2713 }, |
10051 | { 0x0af4, 0x2717 }, |
10052 | { 0x0af5, 0x266f }, |
10053 | { 0x0af6, 0x266d }, |
10054 | { 0x0af7, 0x2642 }, |
10055 | { 0x0af8, 0x2640 }, |
10056 | { 0x0af9, 0x260e }, |
10057 | { 0x0afa, 0x2315 }, |
10058 | { 0x0afb, 0x2117 }, |
10059 | { 0x0afc, 0x2038 }, |
10060 | { 0x0afd, 0x201a }, |
10061 | { 0x0afe, 0x201e }, |
10062 | { 0x0ba3, 0x003c }, |
10063 | { 0x0ba6, 0x003e }, |
10064 | { 0x0ba8, 0x2228 }, |
10065 | { 0x0ba9, 0x2227 }, |
10066 | { 0x0bc0, 0x00af }, |
10067 | { 0x0bc2, 0x22a5 }, |
10068 | { 0x0bc3, 0x2229 }, |
10069 | { 0x0bc4, 0x230a }, |
10070 | { 0x0bc6, 0x005f }, |
10071 | { 0x0bca, 0x2218 }, |
10072 | { 0x0bcc, 0x2395 }, |
10073 | { 0x0bce, 0x22a4 }, |
10074 | { 0x0bcf, 0x25cb }, |
10075 | { 0x0bd3, 0x2308 }, |
10076 | { 0x0bd6, 0x222a }, |
10077 | { 0x0bd8, 0x2283 }, |
10078 | { 0x0bda, 0x2282 }, |
10079 | { 0x0bdc, 0x22a2 }, |
10080 | { 0x0bfc, 0x22a3 }, |
10081 | { 0x0cdf, 0x2017 }, |
10082 | { 0x0ce0, 0x05d0 }, |
10083 | { 0x0ce1, 0x05d1 }, |
10084 | { 0x0ce2, 0x05d2 }, |
10085 | { 0x0ce3, 0x05d3 }, |
10086 | { 0x0ce4, 0x05d4 }, |
10087 | { 0x0ce5, 0x05d5 }, |
10088 | { 0x0ce6, 0x05d6 }, |
10089 | { 0x0ce7, 0x05d7 }, |
10090 | { 0x0ce8, 0x05d8 }, |
10091 | { 0x0ce9, 0x05d9 }, |
10092 | { 0x0cea, 0x05da }, |
10093 | { 0x0ceb, 0x05db }, |
10094 | { 0x0cec, 0x05dc }, |
10095 | { 0x0ced, 0x05dd }, |
10096 | { 0x0cee, 0x05de }, |
10097 | { 0x0cef, 0x05df }, |
10098 | { 0x0cf0, 0x05e0 }, |
10099 | { 0x0cf1, 0x05e1 }, |
10100 | { 0x0cf2, 0x05e2 }, |
10101 | { 0x0cf3, 0x05e3 }, |
10102 | { 0x0cf4, 0x05e4 }, |
10103 | { 0x0cf5, 0x05e5 }, |
10104 | { 0x0cf6, 0x05e6 }, |
10105 | { 0x0cf7, 0x05e7 }, |
10106 | { 0x0cf8, 0x05e8 }, |
10107 | { 0x0cf9, 0x05e9 }, |
10108 | { 0x0cfa, 0x05ea }, |
10109 | { 0x0da1, 0x0e01 }, |
10110 | { 0x0da2, 0x0e02 }, |
10111 | { 0x0da3, 0x0e03 }, |
10112 | { 0x0da4, 0x0e04 }, |
10113 | { 0x0da5, 0x0e05 }, |
10114 | { 0x0da6, 0x0e06 }, |
10115 | { 0x0da7, 0x0e07 }, |
10116 | { 0x0da8, 0x0e08 }, |
10117 | { 0x0da9, 0x0e09 }, |
10118 | { 0x0daa, 0x0e0a }, |
10119 | { 0x0dab, 0x0e0b }, |
10120 | { 0x0dac, 0x0e0c }, |
10121 | { 0x0dad, 0x0e0d }, |
10122 | { 0x0dae, 0x0e0e }, |
10123 | { 0x0daf, 0x0e0f }, |
10124 | { 0x0db0, 0x0e10 }, |
10125 | { 0x0db1, 0x0e11 }, |
10126 | { 0x0db2, 0x0e12 }, |
10127 | { 0x0db3, 0x0e13 }, |
10128 | { 0x0db4, 0x0e14 }, |
10129 | { 0x0db5, 0x0e15 }, |
10130 | { 0x0db6, 0x0e16 }, |
10131 | { 0x0db7, 0x0e17 }, |
10132 | { 0x0db8, 0x0e18 }, |
10133 | { 0x0db9, 0x0e19 }, |
10134 | { 0x0dba, 0x0e1a }, |
10135 | { 0x0dbb, 0x0e1b }, |
10136 | { 0x0dbc, 0x0e1c }, |
10137 | { 0x0dbd, 0x0e1d }, |
10138 | { 0x0dbe, 0x0e1e }, |
10139 | { 0x0dbf, 0x0e1f }, |
10140 | { 0x0dc0, 0x0e20 }, |
10141 | { 0x0dc1, 0x0e21 }, |
10142 | { 0x0dc2, 0x0e22 }, |
10143 | { 0x0dc3, 0x0e23 }, |
10144 | { 0x0dc4, 0x0e24 }, |
10145 | { 0x0dc5, 0x0e25 }, |
10146 | { 0x0dc6, 0x0e26 }, |
10147 | { 0x0dc7, 0x0e27 }, |
10148 | { 0x0dc8, 0x0e28 }, |
10149 | { 0x0dc9, 0x0e29 }, |
10150 | { 0x0dca, 0x0e2a }, |
10151 | { 0x0dcb, 0x0e2b }, |
10152 | { 0x0dcc, 0x0e2c }, |
10153 | { 0x0dcd, 0x0e2d }, |
10154 | { 0x0dce, 0x0e2e }, |
10155 | { 0x0dcf, 0x0e2f }, |
10156 | { 0x0dd0, 0x0e30 }, |
10157 | { 0x0dd1, 0x0e31 }, |
10158 | { 0x0dd2, 0x0e32 }, |
10159 | { 0x0dd3, 0x0e33 }, |
10160 | { 0x0dd4, 0x0e34 }, |
10161 | { 0x0dd5, 0x0e35 }, |
10162 | { 0x0dd6, 0x0e36 }, |
10163 | { 0x0dd7, 0x0e37 }, |
10164 | { 0x0dd8, 0x0e38 }, |
10165 | { 0x0dd9, 0x0e39 }, |
10166 | { 0x0dda, 0x0e3a }, |
10167 | { 0x0ddf, 0x0e3f }, |
10168 | { 0x0de0, 0x0e40 }, |
10169 | { 0x0de1, 0x0e41 }, |
10170 | { 0x0de2, 0x0e42 }, |
10171 | { 0x0de3, 0x0e43 }, |
10172 | { 0x0de4, 0x0e44 }, |
10173 | { 0x0de5, 0x0e45 }, |
10174 | { 0x0de6, 0x0e46 }, |
10175 | { 0x0de7, 0x0e47 }, |
10176 | { 0x0de8, 0x0e48 }, |
10177 | { 0x0de9, 0x0e49 }, |
10178 | { 0x0dea, 0x0e4a }, |
10179 | { 0x0deb, 0x0e4b }, |
10180 | { 0x0dec, 0x0e4c }, |
10181 | { 0x0ded, 0x0e4d }, |
10182 | { 0x0df0, 0x0e50 }, |
10183 | { 0x0df1, 0x0e51 }, |
10184 | { 0x0df2, 0x0e52 }, |
10185 | { 0x0df3, 0x0e53 }, |
10186 | { 0x0df4, 0x0e54 }, |
10187 | { 0x0df5, 0x0e55 }, |
10188 | { 0x0df6, 0x0e56 }, |
10189 | { 0x0df7, 0x0e57 }, |
10190 | { 0x0df8, 0x0e58 }, |
10191 | { 0x0df9, 0x0e59 }, |
10192 | { 0x0ea1, 0x3131 }, |
10193 | { 0x0ea2, 0x3132 }, |
10194 | { 0x0ea3, 0x3133 }, |
10195 | { 0x0ea4, 0x3134 }, |
10196 | { 0x0ea5, 0x3135 }, |
10197 | { 0x0ea6, 0x3136 }, |
10198 | { 0x0ea7, 0x3137 }, |
10199 | { 0x0ea8, 0x3138 }, |
10200 | { 0x0ea9, 0x3139 }, |
10201 | { 0x0eaa, 0x313a }, |
10202 | { 0x0eab, 0x313b }, |
10203 | { 0x0eac, 0x313c }, |
10204 | { 0x0ead, 0x313d }, |
10205 | { 0x0eae, 0x313e }, |
10206 | { 0x0eaf, 0x313f }, |
10207 | { 0x0eb0, 0x3140 }, |
10208 | { 0x0eb1, 0x3141 }, |
10209 | { 0x0eb2, 0x3142 }, |
10210 | { 0x0eb3, 0x3143 }, |
10211 | { 0x0eb4, 0x3144 }, |
10212 | { 0x0eb5, 0x3145 }, |
10213 | { 0x0eb6, 0x3146 }, |
10214 | { 0x0eb7, 0x3147 }, |
10215 | { 0x0eb8, 0x3148 }, |
10216 | { 0x0eb9, 0x3149 }, |
10217 | { 0x0eba, 0x314a }, |
10218 | { 0x0ebb, 0x314b }, |
10219 | { 0x0ebc, 0x314c }, |
10220 | { 0x0ebd, 0x314d }, |
10221 | { 0x0ebe, 0x314e }, |
10222 | { 0x0ebf, 0x314f }, |
10223 | { 0x0ec0, 0x3150 }, |
10224 | { 0x0ec1, 0x3151 }, |
10225 | { 0x0ec2, 0x3152 }, |
10226 | { 0x0ec3, 0x3153 }, |
10227 | { 0x0ec4, 0x3154 }, |
10228 | { 0x0ec5, 0x3155 }, |
10229 | { 0x0ec6, 0x3156 }, |
10230 | { 0x0ec7, 0x3157 }, |
10231 | { 0x0ec8, 0x3158 }, |
10232 | { 0x0ec9, 0x3159 }, |
10233 | { 0x0eca, 0x315a }, |
10234 | { 0x0ecb, 0x315b }, |
10235 | { 0x0ecc, 0x315c }, |
10236 | { 0x0ecd, 0x315d }, |
10237 | { 0x0ece, 0x315e }, |
10238 | { 0x0ecf, 0x315f }, |
10239 | { 0x0ed0, 0x3160 }, |
10240 | { 0x0ed1, 0x3161 }, |
10241 | { 0x0ed2, 0x3162 }, |
10242 | { 0x0ed3, 0x3163 }, |
10243 | { 0x0ed4, 0x11a8 }, |
10244 | { 0x0ed5, 0x11a9 }, |
10245 | { 0x0ed6, 0x11aa }, |
10246 | { 0x0ed7, 0x11ab }, |
10247 | { 0x0ed8, 0x11ac }, |
10248 | { 0x0ed9, 0x11ad }, |
10249 | { 0x0eda, 0x11ae }, |
10250 | { 0x0edb, 0x11af }, |
10251 | { 0x0edc, 0x11b0 }, |
10252 | { 0x0edd, 0x11b1 }, |
10253 | { 0x0ede, 0x11b2 }, |
10254 | { 0x0edf, 0x11b3 }, |
10255 | { 0x0ee0, 0x11b4 }, |
10256 | { 0x0ee1, 0x11b5 }, |
10257 | { 0x0ee2, 0x11b6 }, |
10258 | { 0x0ee3, 0x11b7 }, |
10259 | { 0x0ee4, 0x11b8 }, |
10260 | { 0x0ee5, 0x11b9 }, |
10261 | { 0x0ee6, 0x11ba }, |
10262 | { 0x0ee7, 0x11bb }, |
10263 | { 0x0ee8, 0x11bc }, |
10264 | { 0x0ee9, 0x11bd }, |
10265 | { 0x0eea, 0x11be }, |
10266 | { 0x0eeb, 0x11bf }, |
10267 | { 0x0eec, 0x11c0 }, |
10268 | { 0x0eed, 0x11c1 }, |
10269 | { 0x0eee, 0x11c2 }, |
10270 | { 0x0eef, 0x316d }, |
10271 | { 0x0ef0, 0x3171 }, |
10272 | { 0x0ef1, 0x3178 }, |
10273 | { 0x0ef2, 0x317f }, |
10274 | { 0x0ef3, 0x3181 }, |
10275 | { 0x0ef4, 0x3184 }, |
10276 | { 0x0ef5, 0x3186 }, |
10277 | { 0x0ef6, 0x318d }, |
10278 | { 0x0ef7, 0x318e }, |
10279 | { 0x0ef8, 0x11eb }, |
10280 | { 0x0ef9, 0x11f0 }, |
10281 | { 0x0efa, 0x11f9 }, |
10282 | { 0x0eff, 0x20a9 }, |
10283 | { 0x13a4, 0x20ac }, |
10284 | { 0x13bc, 0x0152 }, |
10285 | { 0x13bd, 0x0153 }, |
10286 | { 0x13be, 0x0178 }, |
10287 | { 0x20ac, 0x20ac }, |
10288 | { 0xfe50, '`' }, |
10289 | { 0xfe51, 0x00b4 }, |
10290 | { 0xfe52, '^' }, |
10291 | { 0xfe53, '~' }, |
10292 | { 0xfe54, 0x00af }, |
10293 | { 0xfe55, 0x02d8 }, |
10294 | { 0xfe56, 0x02d9 }, |
10295 | { 0xfe57, 0x00a8 }, |
10296 | { 0xfe58, 0x02da }, |
10297 | { 0xfe59, 0x02dd }, |
10298 | { 0xfe5a, 0x02c7 }, |
10299 | { 0xfe5b, 0x00b8 }, |
10300 | { 0xfe5c, 0x02db }, |
10301 | { 0xfe5d, 0x037a }, |
10302 | { 0xfe5e, 0x309b }, |
10303 | { 0xfe5f, 0x309c }, |
10304 | { 0xfe63, '/' }, |
10305 | { 0xfe64, 0x02bc }, |
10306 | { 0xfe65, 0x02bd }, |
10307 | { 0xfe66, 0x02f5 }, |
10308 | { 0xfe67, 0x02f3 }, |
10309 | { 0xfe68, 0x02cd }, |
10310 | { 0xfe69, 0xa788 }, |
10311 | { 0xfe6a, 0x02f7 }, |
10312 | { 0xfe6e, ',' }, |
10313 | { 0xfe6f, 0x00a4 }, |
10314 | { 0xfe80, 'a' }, /* XK_dead_a */ |
10315 | { 0xfe81, 'A' }, /* XK_dead_A */ |
10316 | { 0xfe82, 'e' }, /* XK_dead_e */ |
10317 | { 0xfe83, 'E' }, /* XK_dead_E */ |
10318 | { 0xfe84, 'i' }, /* XK_dead_i */ |
10319 | { 0xfe85, 'I' }, /* XK_dead_I */ |
10320 | { 0xfe86, 'o' }, /* XK_dead_o */ |
10321 | { 0xfe87, 'O' }, /* XK_dead_O */ |
10322 | { 0xfe88, 'u' }, /* XK_dead_u */ |
10323 | { 0xfe89, 'U' }, /* XK_dead_U */ |
10324 | { 0xfe8a, 0x0259 }, |
10325 | { 0xfe8b, 0x018f }, |
10326 | { 0xfe8c, 0x00b5 }, |
10327 | { 0xfe90, '_' }, |
10328 | { 0xfe91, 0x02c8 }, |
10329 | { 0xfe92, 0x02cc }, |
10330 | { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, |
10331 | { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, |
10332 | { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, |
10333 | { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, |
10334 | { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, |
10335 | { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, |
10336 | { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, |
10337 | { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, |
10338 | { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, |
10339 | { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, |
10340 | { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, |
10341 | { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, |
10342 | { 0xffab /*XKB_KEY_KP_Add*/, '+' }, |
10343 | { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, |
10344 | { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, |
10345 | { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, |
10346 | { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, |
10347 | { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, |
10348 | { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, |
10349 | { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, |
10350 | { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, |
10351 | { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, |
10352 | { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, |
10353 | { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, |
10354 | { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, |
10355 | { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, |
10356 | { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, |
10357 | { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } |
10358 | }; |
10359 | |
10360 | _SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { |
10361 | _SOKOL_UNUSED(display); |
10362 | _sapp.x11.error_code = event->error_code; |
10363 | return 0; |
10364 | } |
10365 | |
10366 | _SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { |
10367 | _sapp.x11.error_code = Success; |
10368 | XSetErrorHandler(_sapp_x11_error_handler); |
10369 | } |
10370 | |
10371 | _SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { |
10372 | XSync(_sapp.x11.display, False); |
10373 | XSetErrorHandler(NULL); |
10374 | } |
10375 | |
10376 | _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { |
10377 | _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); |
10378 | _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); |
10379 | _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); |
10380 | _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); |
10381 | _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); |
10382 | _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); |
10383 | _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False); |
10384 | _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); |
10385 | _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); |
10386 | if (_sapp.drop.enabled) { |
10387 | _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); |
10388 | _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); |
10389 | _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); |
10390 | _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); |
10391 | _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); |
10392 | _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); |
10393 | _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); |
10394 | _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); |
10395 | _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); |
10396 | _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); |
10397 | } |
10398 | |
10399 | /* check Xi extension for raw mouse input */ |
10400 | if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { |
10401 | _sapp.x11.xi.major = 2; |
10402 | _sapp.x11.xi.minor = 0; |
10403 | if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { |
10404 | _sapp.x11.xi.available = true; |
10405 | } |
10406 | } |
10407 | } |
10408 | |
10409 | _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { |
10410 | /* from GLFW: |
10411 | |
10412 | NOTE: Default to the display-wide DPI as we don't currently have a policy |
10413 | for which monitor a window is considered to be on |
10414 | |
10415 | _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * |
10416 | 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); |
10417 | |
10418 | NOTE: Basing the scale on Xft.dpi where available should provide the most |
10419 | consistent user experience (matches Qt, Gtk, etc), although not |
10420 | always the most accurate one |
10421 | */ |
10422 | bool dpi_ok = false; |
10423 | char* rms = XResourceManagerString(_sapp.x11.display); |
10424 | if (rms) { |
10425 | XrmDatabase db = XrmGetStringDatabase(rms); |
10426 | if (db) { |
10427 | XrmValue value; |
10428 | char* type = NULL; |
10429 | if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { |
10430 | if (type && strcmp(type, "String") == 0) { |
10431 | _sapp.x11.dpi = atof(value.addr); |
10432 | dpi_ok = true; |
10433 | } |
10434 | } |
10435 | XrmDestroyDatabase(db); |
10436 | } |
10437 | } |
10438 | // fallback if querying DPI had failed: assume the standard DPI 96.0f |
10439 | if (!dpi_ok) { |
10440 | _sapp.x11.dpi = 96.0f; |
10441 | SAPP_LOG("sokol_app.h: failed to query system dpi value, assuming default 96.0"); |
10442 | } |
10443 | } |
10444 | |
10445 | #if defined(_SAPP_GLX) |
10446 | |
10447 | _SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { |
10448 | SOKOL_ASSERT(ext); |
10449 | const char* start = extensions; |
10450 | while (true) { |
10451 | const char* where = strstr(start, ext); |
10452 | if (!where) { |
10453 | return false; |
10454 | } |
10455 | const char* terminator = where + strlen(ext); |
10456 | if ((where == start) || (*(where - 1) == ' ')) { |
10457 | if (*terminator == ' ' || *terminator == '\0') { |
10458 | break; |
10459 | } |
10460 | } |
10461 | start = terminator; |
10462 | } |
10463 | return true; |
10464 | } |
10465 | |
10466 | _SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { |
10467 | if (extensions) { |
10468 | return _sapp_glx_has_ext(ext, extensions); |
10469 | } |
10470 | else { |
10471 | return false; |
10472 | } |
10473 | } |
10474 | |
10475 | _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) |
10476 | { |
10477 | if (_sapp.glx.GetProcAddress) { |
10478 | return (void*) _sapp.glx.GetProcAddress(procname); |
10479 | } |
10480 | else if (_sapp.glx.GetProcAddressARB) { |
10481 | return (void*) _sapp.glx.GetProcAddressARB(procname); |
10482 | } |
10483 | else { |
10484 | return dlsym(_sapp.glx.libgl, procname); |
10485 | } |
10486 | } |
10487 | |
10488 | _SOKOL_PRIVATE void _sapp_glx_init() { |
10489 | const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; |
10490 | for (int i = 0; sonames[i]; i++) { |
10491 | _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); |
10492 | if (_sapp.glx.libgl) { |
10493 | break; |
10494 | } |
10495 | } |
10496 | if (!_sapp.glx.libgl) { |
10497 | _sapp_fail("GLX: failed to load libGL"); |
10498 | } |
10499 | _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); |
10500 | _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); |
10501 | _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); |
10502 | _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); |
10503 | _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); |
10504 | _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); |
10505 | _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); |
10506 | _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); |
10507 | _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); |
10508 | _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); |
10509 | _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); |
10510 | _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); |
10511 | _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); |
10512 | _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); |
10513 | if (!_sapp.glx.GetFBConfigs || |
10514 | !_sapp.glx.GetFBConfigAttrib || |
10515 | !_sapp.glx.GetClientString || |
10516 | !_sapp.glx.QueryExtension || |
10517 | !_sapp.glx.QueryVersion || |
10518 | !_sapp.glx.DestroyContext || |
10519 | !_sapp.glx.MakeCurrent || |
10520 | !_sapp.glx.SwapBuffers || |
10521 | !_sapp.glx.QueryExtensionsString || |
10522 | !_sapp.glx.CreateWindow || |
10523 | !_sapp.glx.DestroyWindow || |
10524 | !_sapp.glx.GetProcAddress || |
10525 | !_sapp.glx.GetProcAddressARB || |
10526 | !_sapp.glx.GetVisualFromFBConfig) |
10527 | { |
10528 | _sapp_fail("GLX: failed to load required entry points"); |
10529 | } |
10530 | |
10531 | if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { |
10532 | _sapp_fail("GLX: GLX extension not found"); |
10533 | } |
10534 | if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { |
10535 | _sapp_fail("GLX: Failed to query GLX version"); |
10536 | } |
10537 | if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { |
10538 | _sapp_fail("GLX: GLX version 1.3 is required"); |
10539 | } |
10540 | const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); |
10541 | if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { |
10542 | _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); |
10543 | _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; |
10544 | } |
10545 | if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { |
10546 | _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); |
10547 | _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; |
10548 | } |
10549 | _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); |
10550 | if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { |
10551 | _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); |
10552 | _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; |
10553 | } |
10554 | _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); |
10555 | } |
10556 | |
10557 | _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { |
10558 | int value; |
10559 | _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); |
10560 | return value; |
10561 | } |
10562 | |
10563 | _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { |
10564 | GLXFBConfig* native_configs; |
10565 | _sapp_gl_fbconfig* usable_configs; |
10566 | const _sapp_gl_fbconfig* closest; |
10567 | int i, native_count, usable_count; |
10568 | const char* vendor; |
10569 | bool trust_window_bit = true; |
10570 | |
10571 | /* HACK: This is a (hopefully temporary) workaround for Chromium |
10572 | (VirtualBox GL) not setting the window bit on any GLXFBConfigs |
10573 | */ |
10574 | vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); |
10575 | if (vendor && strcmp(vendor, "Chromium") == 0) { |
10576 | trust_window_bit = false; |
10577 | } |
10578 | |
10579 | native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); |
10580 | if (!native_configs || !native_count) { |
10581 | _sapp_fail("GLX: No GLXFBConfigs returned"); |
10582 | } |
10583 | |
10584 | usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); |
10585 | usable_count = 0; |
10586 | for (i = 0; i < native_count; i++) { |
10587 | const GLXFBConfig n = native_configs[i]; |
10588 | _sapp_gl_fbconfig* u = usable_configs + usable_count; |
10589 | _sapp_gl_init_fbconfig(u); |
10590 | |
10591 | /* Only consider RGBA GLXFBConfigs */ |
10592 | if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { |
10593 | continue; |
10594 | } |
10595 | /* Only consider window GLXFBConfigs */ |
10596 | if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { |
10597 | if (trust_window_bit) { |
10598 | continue; |
10599 | } |
10600 | } |
10601 | u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); |
10602 | u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); |
10603 | u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); |
10604 | u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); |
10605 | u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); |
10606 | u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); |
10607 | if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { |
10608 | u->doublebuffer = true; |
10609 | } |
10610 | if (_sapp.glx.ARB_multisample) { |
10611 | u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); |
10612 | } |
10613 | u->handle = (uintptr_t) n; |
10614 | usable_count++; |
10615 | } |
10616 | _sapp_gl_fbconfig desired; |
10617 | _sapp_gl_init_fbconfig(&desired); |
10618 | desired.red_bits = 8; |
10619 | desired.green_bits = 8; |
10620 | desired.blue_bits = 8; |
10621 | desired.alpha_bits = 8; |
10622 | desired.depth_bits = 24; |
10623 | desired.stencil_bits = 8; |
10624 | desired.doublebuffer = true; |
10625 | desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; |
10626 | closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); |
10627 | GLXFBConfig result = 0; |
10628 | if (closest) { |
10629 | result = (GLXFBConfig) closest->handle; |
10630 | } |
10631 | XFree(native_configs); |
10632 | _sapp_free(usable_configs); |
10633 | return result; |
10634 | } |
10635 | |
10636 | _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { |
10637 | GLXFBConfig native = _sapp_glx_choosefbconfig(); |
10638 | if (0 == native) { |
10639 | _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); |
10640 | } |
10641 | XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); |
10642 | if (!result) { |
10643 | _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); |
10644 | } |
10645 | *visual = result->visual; |
10646 | *depth = result->depth; |
10647 | XFree(result); |
10648 | } |
10649 | |
10650 | _SOKOL_PRIVATE void _sapp_glx_create_context(void) { |
10651 | GLXFBConfig native = _sapp_glx_choosefbconfig(); |
10652 | if (0 == native){ |
10653 | _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); |
10654 | } |
10655 | if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { |
10656 | _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); |
10657 | } |
10658 | _sapp_x11_grab_error_handler(); |
10659 | const int attribs[] = { |
10660 | GLX_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, |
10661 | GLX_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, |
10662 | GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, |
10663 | GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, |
10664 | 0, 0 |
10665 | }; |
10666 | _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); |
10667 | if (!_sapp.glx.ctx) { |
10668 | _sapp_fail("GLX: failed to create GL context"); |
10669 | } |
10670 | _sapp_x11_release_error_handler(); |
10671 | _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); |
10672 | if (!_sapp.glx.window) { |
10673 | _sapp_fail("GLX: failed to create window"); |
10674 | } |
10675 | } |
10676 | |
10677 | _SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { |
10678 | if (_sapp.glx.window) { |
10679 | _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); |
10680 | _sapp.glx.window = 0; |
10681 | } |
10682 | if (_sapp.glx.ctx) { |
10683 | _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); |
10684 | _sapp.glx.ctx = 0; |
10685 | } |
10686 | } |
10687 | |
10688 | _SOKOL_PRIVATE void _sapp_glx_make_current(void) { |
10689 | _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); |
10690 | } |
10691 | |
10692 | _SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { |
10693 | _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); |
10694 | } |
10695 | |
10696 | _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { |
10697 | _sapp_glx_make_current(); |
10698 | if (_sapp.glx.EXT_swap_control) { |
10699 | _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); |
10700 | } |
10701 | else if (_sapp.glx.MESA_swap_control) { |
10702 | _sapp.glx.SwapIntervalMESA(interval); |
10703 | } |
10704 | } |
10705 | |
10706 | #endif /* _SAPP_GLX */ |
10707 | |
10708 | _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { |
10709 | XEvent event; |
10710 | _sapp_clear(&event, sizeof(event)); |
10711 | |
10712 | event.type = ClientMessage; |
10713 | event.xclient.window = _sapp.x11.window; |
10714 | event.xclient.format = 32; |
10715 | event.xclient.message_type = type; |
10716 | event.xclient.data.l[0] = a; |
10717 | event.xclient.data.l[1] = b; |
10718 | event.xclient.data.l[2] = c; |
10719 | event.xclient.data.l[3] = d; |
10720 | event.xclient.data.l[4] = e; |
10721 | |
10722 | XSendEvent(_sapp.x11.display, _sapp.x11.root, |
10723 | False, |
10724 | SubstructureNotifyMask | SubstructureRedirectMask, |
10725 | &event); |
10726 | } |
10727 | |
10728 | _SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { |
10729 | XWindowAttributes attribs; |
10730 | XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); |
10731 | _sapp.window_width = attribs.width; |
10732 | _sapp.window_height = attribs.height; |
10733 | _sapp.framebuffer_width = _sapp.window_width; |
10734 | _sapp.framebuffer_height = _sapp.window_height; |
10735 | } |
10736 | |
10737 | _SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { |
10738 | /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ |
10739 | if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { |
10740 | if (enable) { |
10741 | const int _NET_WM_STATE_ADD = 1; |
10742 | _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, |
10743 | _NET_WM_STATE_ADD, |
10744 | _sapp.x11.NET_WM_STATE_FULLSCREEN, |
10745 | 0, 1, 0); |
10746 | } |
10747 | else { |
10748 | const int _NET_WM_STATE_REMOVE = 0; |
10749 | _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, |
10750 | _NET_WM_STATE_REMOVE, |
10751 | _sapp.x11.NET_WM_STATE_FULLSCREEN, |
10752 | 0, 1, 0); |
10753 | } |
10754 | } |
10755 | XFlush(_sapp.x11.display); |
10756 | } |
10757 | |
10758 | _SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { |
10759 | SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); |
10760 | const int w = 16; |
10761 | const int h = 16; |
10762 | XcursorImage* img = XcursorImageCreate(w, h); |
10763 | SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); |
10764 | img->xhot = 0; |
10765 | img->yhot = 0; |
10766 | const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); |
10767 | _sapp_clear(img->pixels, num_bytes); |
10768 | _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); |
10769 | XcursorImageDestroy(img); |
10770 | } |
10771 | |
10772 | _SOKOL_PRIVATE void _sapp_x11_create_standard_cursor(sapp_mouse_cursor cursor, const char* name, const char* theme, int size, uint32_t fallback_native) { |
10773 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
10774 | SOKOL_ASSERT(_sapp.x11.display); |
10775 | if (theme) { |
10776 | XcursorImage* img = XcursorLibraryLoadImage(name, theme, size); |
10777 | if (img) { |
10778 | _sapp.x11.cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img); |
10779 | XcursorImageDestroy(img); |
10780 | } |
10781 | } |
10782 | if (0 == _sapp.x11.cursors[cursor]) { |
10783 | _sapp.x11.cursors[cursor] = XCreateFontCursor(_sapp.x11.display, fallback_native); |
10784 | } |
10785 | } |
10786 | |
10787 | _SOKOL_PRIVATE void _sapp_x11_create_cursors(void) { |
10788 | SOKOL_ASSERT(_sapp.x11.display); |
10789 | const char* cursor_theme = XcursorGetTheme(_sapp.x11.display); |
10790 | const int size = XcursorGetDefaultSize(_sapp.x11.display); |
10791 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_ARROW, "default", cursor_theme, size, XC_left_ptr); |
10792 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_IBEAM, "text", cursor_theme, size, XC_xterm); |
10793 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_CROSSHAIR, "crosshair", cursor_theme, size, XC_crosshair); |
10794 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_POINTING_HAND, "pointer", cursor_theme, size, XC_hand2); |
10795 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_EW, "ew-resize", cursor_theme, size, XC_sb_h_double_arrow); |
10796 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NS, "ns-resize", cursor_theme, size, XC_sb_v_double_arrow); |
10797 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NWSE, "nwse-resize", cursor_theme, size, 0); |
10798 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NESW, "nesw-resize", cursor_theme, size, 0); |
10799 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_ALL, "all-scroll", cursor_theme, size, XC_fleur); |
10800 | _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_NOT_ALLOWED, "no-allowed", cursor_theme, size, 0); |
10801 | _sapp_x11_create_hidden_cursor(); |
10802 | } |
10803 | |
10804 | _SOKOL_PRIVATE void _sapp_x11_destroy_cursors(void) { |
10805 | SOKOL_ASSERT(_sapp.x11.display); |
10806 | if (_sapp.x11.hidden_cursor) { |
10807 | XFreeCursor(_sapp.x11.display, _sapp.x11.hidden_cursor); |
10808 | _sapp.x11.hidden_cursor = 0; |
10809 | } |
10810 | for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { |
10811 | if (_sapp.x11.cursors[i]) { |
10812 | XFreeCursor(_sapp.x11.display, _sapp.x11.cursors[i]); |
10813 | _sapp.x11.cursors[i] = 0; |
10814 | } |
10815 | } |
10816 | } |
10817 | |
10818 | _SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { |
10819 | _sapp.fullscreen = !_sapp.fullscreen; |
10820 | _sapp_x11_set_fullscreen(_sapp.fullscreen); |
10821 | _sapp_x11_query_window_size(); |
10822 | } |
10823 | |
10824 | _SOKOL_PRIVATE void _sapp_x11_update_cursor(sapp_mouse_cursor cursor, bool shown) { |
10825 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
10826 | if (shown) { |
10827 | if (_sapp.x11.cursors[cursor]) { |
10828 | XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.cursors[cursor]); |
10829 | } |
10830 | else { |
10831 | XUndefineCursor(_sapp.x11.display, _sapp.x11.window); |
10832 | } |
10833 | } |
10834 | else { |
10835 | XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); |
10836 | } |
10837 | XFlush(_sapp.x11.display); |
10838 | } |
10839 | |
10840 | _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { |
10841 | if (lock == _sapp.mouse.locked) { |
10842 | return; |
10843 | } |
10844 | _sapp.mouse.dx = 0.0f; |
10845 | _sapp.mouse.dy = 0.0f; |
10846 | _sapp.mouse.locked = lock; |
10847 | if (_sapp.mouse.locked) { |
10848 | if (_sapp.x11.xi.available) { |
10849 | XIEventMask em; |
10850 | unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro |
10851 | em.deviceid = XIAllMasterDevices; |
10852 | em.mask_len = sizeof(mask); |
10853 | em.mask = mask; |
10854 | XISetMask(mask, XI_RawMotion); |
10855 | XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); |
10856 | } |
10857 | XGrabPointer(_sapp.x11.display, // display |
10858 | _sapp.x11.window, // grab_window |
10859 | True, // owner_events |
10860 | ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask |
10861 | GrabModeAsync, // pointer_mode |
10862 | GrabModeAsync, // keyboard_mode |
10863 | _sapp.x11.window, // confine_to |
10864 | _sapp.x11.hidden_cursor, // cursor |
10865 | CurrentTime); // time |
10866 | } |
10867 | else { |
10868 | if (_sapp.x11.xi.available) { |
10869 | XIEventMask em; |
10870 | unsigned char mask[] = { 0 }; |
10871 | em.deviceid = XIAllMasterDevices; |
10872 | em.mask_len = sizeof(mask); |
10873 | em.mask = mask; |
10874 | XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); |
10875 | } |
10876 | XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); |
10877 | XUngrabPointer(_sapp.x11.display, CurrentTime); |
10878 | } |
10879 | XFlush(_sapp.x11.display); |
10880 | } |
10881 | |
10882 | _SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { |
10883 | Xutf8SetWMProperties(_sapp.x11.display, |
10884 | _sapp.x11.window, |
10885 | _sapp.window_title, _sapp.window_title, |
10886 | NULL, 0, NULL, NULL, NULL); |
10887 | XChangeProperty(_sapp.x11.display, _sapp.x11.window, |
10888 | _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, |
10889 | PropModeReplace, |
10890 | (unsigned char*)_sapp.window_title, |
10891 | strlen(_sapp.window_title)); |
10892 | XChangeProperty(_sapp.x11.display, _sapp.x11.window, |
10893 | _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, |
10894 | PropModeReplace, |
10895 | (unsigned char*)_sapp.window_title, |
10896 | strlen(_sapp.window_title)); |
10897 | XFlush(_sapp.x11.display); |
10898 | } |
10899 | |
10900 | _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) { |
10901 | SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); |
10902 | int long_count = 0; |
10903 | for (int i = 0; i < num_images; i++) { |
10904 | const sapp_image_desc* img_desc = &icon_desc->images[i]; |
10905 | long_count += 2 + (img_desc->width * img_desc->height); |
10906 | } |
10907 | long* icon_data = (long*) _sapp_malloc_clear((size_t)long_count * sizeof(long)); |
10908 | SOKOL_ASSERT(icon_data); |
10909 | long* dst = icon_data; |
10910 | for (int img_index = 0; img_index < num_images; img_index++) { |
10911 | const sapp_image_desc* img_desc = &icon_desc->images[img_index]; |
10912 | const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr; |
10913 | *dst++ = img_desc->width; |
10914 | *dst++ = img_desc->height; |
10915 | const int num_pixels = img_desc->width * img_desc->height; |
10916 | for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) { |
10917 | *dst++ = ((long)(src[pixel_index * 4 + 0]) << 16) | |
10918 | ((long)(src[pixel_index * 4 + 1]) << 8) | |
10919 | ((long)(src[pixel_index * 4 + 2]) << 0) | |
10920 | ((long)(src[pixel_index * 4 + 3]) << 24); |
10921 | } |
10922 | } |
10923 | XChangeProperty(_sapp.x11.display, _sapp.x11.window, |
10924 | _sapp.x11.NET_WM_ICON, |
10925 | XA_CARDINAL, 32, |
10926 | PropModeReplace, |
10927 | (unsigned char*)icon_data, |
10928 | long_count); |
10929 | _sapp_free(icon_data); |
10930 | XFlush(_sapp.x11.display); |
10931 | } |
10932 | |
10933 | _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { |
10934 | _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); |
10935 | XSetWindowAttributes wa; |
10936 | _sapp_clear(&wa, sizeof(wa)); |
10937 | const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; |
10938 | wa.colormap = _sapp.x11.colormap; |
10939 | wa.border_pixel = 0; |
10940 | wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | |
10941 | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | |
10942 | ExposureMask | FocusChangeMask | VisibilityChangeMask | |
10943 | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; |
10944 | |
10945 | int display_width = DisplayWidth(_sapp.x11.display, _sapp.x11.screen); |
10946 | int display_height = DisplayHeight(_sapp.x11.display, _sapp.x11.screen); |
10947 | int window_width = _sapp.window_width; |
10948 | int window_height = _sapp.window_height; |
10949 | if (0 == window_width) { |
10950 | window_width = (display_width * 4) / 5; |
10951 | } |
10952 | if (0 == window_height) { |
10953 | window_height = (display_height * 4) / 5; |
10954 | } |
10955 | int window_xpos = (display_width - window_width) / 2; |
10956 | int window_ypos = (display_height - window_height) / 2; |
10957 | if (window_xpos < 0) { |
10958 | window_xpos = 0; |
10959 | } |
10960 | if (window_ypos < 0) { |
10961 | window_ypos = 0; |
10962 | } |
10963 | _sapp_x11_grab_error_handler(); |
10964 | _sapp.x11.window = XCreateWindow(_sapp.x11.display, |
10965 | _sapp.x11.root, |
10966 | window_xpos, |
10967 | window_ypos, |
10968 | (uint32_t)window_width, |
10969 | (uint32_t)window_height, |
10970 | 0, /* border width */ |
10971 | depth, /* color depth */ |
10972 | InputOutput, |
10973 | visual, |
10974 | wamask, |
10975 | &wa); |
10976 | _sapp_x11_release_error_handler(); |
10977 | if (!_sapp.x11.window) { |
10978 | _sapp_fail("X11: Failed to create window"); |
10979 | } |
10980 | Atom protocols[] = { |
10981 | _sapp.x11.WM_DELETE_WINDOW |
10982 | }; |
10983 | XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); |
10984 | |
10985 | XSizeHints* hints = XAllocSizeHints(); |
10986 | hints->flags = (PWinGravity | PPosition | PSize); |
10987 | hints->win_gravity = StaticGravity; |
10988 | hints->x = window_xpos; |
10989 | hints->y = window_ypos; |
10990 | hints->width = window_width; |
10991 | hints->height = window_height; |
10992 | XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); |
10993 | XFree(hints); |
10994 | |
10995 | /* announce support for drag'n'drop */ |
10996 | if (_sapp.drop.enabled) { |
10997 | const Atom version = _SAPP_X11_XDND_VERSION; |
10998 | XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); |
10999 | } |
11000 | _sapp_x11_update_window_title(); |
11001 | _sapp_x11_query_window_size(); |
11002 | } |
11003 | |
11004 | _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { |
11005 | if (_sapp.x11.window) { |
11006 | XUnmapWindow(_sapp.x11.display, _sapp.x11.window); |
11007 | XDestroyWindow(_sapp.x11.display, _sapp.x11.window); |
11008 | _sapp.x11.window = 0; |
11009 | } |
11010 | if (_sapp.x11.colormap) { |
11011 | XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); |
11012 | _sapp.x11.colormap = 0; |
11013 | } |
11014 | XFlush(_sapp.x11.display); |
11015 | } |
11016 | |
11017 | _SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { |
11018 | XWindowAttributes wa; |
11019 | XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); |
11020 | return wa.map_state == IsViewable; |
11021 | } |
11022 | |
11023 | _SOKOL_PRIVATE void _sapp_x11_show_window(void) { |
11024 | if (!_sapp_x11_window_visible()) { |
11025 | XMapWindow(_sapp.x11.display, _sapp.x11.window); |
11026 | XRaiseWindow(_sapp.x11.display, _sapp.x11.window); |
11027 | XFlush(_sapp.x11.display); |
11028 | } |
11029 | } |
11030 | |
11031 | _SOKOL_PRIVATE void _sapp_x11_hide_window(void) { |
11032 | XUnmapWindow(_sapp.x11.display, _sapp.x11.window); |
11033 | XFlush(_sapp.x11.display); |
11034 | } |
11035 | |
11036 | _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) { |
11037 | Atom actualType; |
11038 | int actualFormat; |
11039 | unsigned long itemCount, bytesAfter; |
11040 | XGetWindowProperty(_sapp.x11.display, |
11041 | window, |
11042 | property, |
11043 | 0, |
11044 | LONG_MAX, |
11045 | False, |
11046 | type, |
11047 | &actualType, |
11048 | &actualFormat, |
11049 | &itemCount, |
11050 | &bytesAfter, |
11051 | value); |
11052 | return itemCount; |
11053 | } |
11054 | |
11055 | _SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { |
11056 | int result = WithdrawnState; |
11057 | struct { |
11058 | CARD32 state; |
11059 | Window icon; |
11060 | } *state = NULL; |
11061 | |
11062 | if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { |
11063 | result = (int)state->state; |
11064 | } |
11065 | if (state) { |
11066 | XFree(state); |
11067 | } |
11068 | return result; |
11069 | } |
11070 | |
11071 | _SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) { |
11072 | switch (key) { |
11073 | case SAPP_KEYCODE_LEFT_SHIFT: |
11074 | case SAPP_KEYCODE_RIGHT_SHIFT: |
11075 | return SAPP_MODIFIER_SHIFT; |
11076 | case SAPP_KEYCODE_LEFT_CONTROL: |
11077 | case SAPP_KEYCODE_RIGHT_CONTROL: |
11078 | return SAPP_MODIFIER_CTRL; |
11079 | case SAPP_KEYCODE_LEFT_ALT: |
11080 | case SAPP_KEYCODE_RIGHT_ALT: |
11081 | return SAPP_MODIFIER_ALT; |
11082 | case SAPP_KEYCODE_LEFT_SUPER: |
11083 | case SAPP_KEYCODE_RIGHT_SUPER: |
11084 | return SAPP_MODIFIER_SUPER; |
11085 | default: |
11086 | return 0; |
11087 | } |
11088 | } |
11089 | |
11090 | _SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) { |
11091 | switch (btn) { |
11092 | case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB; |
11093 | case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB; |
11094 | case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB; |
11095 | default: return 0; |
11096 | } |
11097 | } |
11098 | |
11099 | _SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) { |
11100 | uint32_t mods = 0; |
11101 | if (x11_mods & ShiftMask) { |
11102 | mods |= SAPP_MODIFIER_SHIFT; |
11103 | } |
11104 | if (x11_mods & ControlMask) { |
11105 | mods |= SAPP_MODIFIER_CTRL; |
11106 | } |
11107 | if (x11_mods & Mod1Mask) { |
11108 | mods |= SAPP_MODIFIER_ALT; |
11109 | } |
11110 | if (x11_mods & Mod4Mask) { |
11111 | mods |= SAPP_MODIFIER_SUPER; |
11112 | } |
11113 | if (x11_mods & Button1Mask) { |
11114 | mods |= SAPP_MODIFIER_LMB; |
11115 | } |
11116 | if (x11_mods & Button2Mask) { |
11117 | mods |= SAPP_MODIFIER_MMB; |
11118 | } |
11119 | if (x11_mods & Button3Mask) { |
11120 | mods |= SAPP_MODIFIER_RMB; |
11121 | } |
11122 | return mods; |
11123 | } |
11124 | |
11125 | _SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { |
11126 | if (_sapp_events_enabled()) { |
11127 | _sapp_init_event(type); |
11128 | _sapp_call_event(&_sapp.event); |
11129 | } |
11130 | } |
11131 | |
11132 | _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { |
11133 | switch (event->xbutton.button) { |
11134 | case Button1: return SAPP_MOUSEBUTTON_LEFT; |
11135 | case Button2: return SAPP_MOUSEBUTTON_MIDDLE; |
11136 | case Button3: return SAPP_MOUSEBUTTON_RIGHT; |
11137 | default: return SAPP_MOUSEBUTTON_INVALID; |
11138 | } |
11139 | } |
11140 | |
11141 | _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { |
11142 | if (_sapp_events_enabled()) { |
11143 | _sapp_init_event(type); |
11144 | _sapp.event.mouse_button = btn; |
11145 | _sapp.event.modifiers = mods; |
11146 | _sapp_call_event(&_sapp.event); |
11147 | } |
11148 | } |
11149 | |
11150 | _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { |
11151 | if (_sapp_events_enabled()) { |
11152 | _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); |
11153 | _sapp.event.modifiers = mods; |
11154 | _sapp.event.scroll_x = x; |
11155 | _sapp.event.scroll_y = y; |
11156 | _sapp_call_event(&_sapp.event); |
11157 | } |
11158 | } |
11159 | |
11160 | _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { |
11161 | if (_sapp_events_enabled()) { |
11162 | _sapp_init_event(type); |
11163 | _sapp.event.key_code = key; |
11164 | _sapp.event.key_repeat = repeat; |
11165 | _sapp.event.modifiers = mods; |
11166 | _sapp_call_event(&_sapp.event); |
11167 | /* check if a CLIPBOARD_PASTED event must be sent too */ |
11168 | if (_sapp.clipboard.enabled && |
11169 | (type == SAPP_EVENTTYPE_KEY_DOWN) && |
11170 | (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && |
11171 | (_sapp.event.key_code == SAPP_KEYCODE_V)) |
11172 | { |
11173 | _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); |
11174 | _sapp_call_event(&_sapp.event); |
11175 | } |
11176 | } |
11177 | } |
11178 | |
11179 | _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { |
11180 | if (_sapp_events_enabled()) { |
11181 | _sapp_init_event(SAPP_EVENTTYPE_CHAR); |
11182 | _sapp.event.char_code = chr; |
11183 | _sapp.event.key_repeat = repeat; |
11184 | _sapp.event.modifiers = mods; |
11185 | _sapp_call_event(&_sapp.event); |
11186 | } |
11187 | } |
11188 | |
11189 | _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { |
11190 | int dummy; |
11191 | KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); |
11192 | SOKOL_ASSERT(keysyms); |
11193 | KeySym keysym = keysyms[0]; |
11194 | XFree(keysyms); |
11195 | switch (keysym) { |
11196 | case XK_Escape: return SAPP_KEYCODE_ESCAPE; |
11197 | case XK_Tab: return SAPP_KEYCODE_TAB; |
11198 | case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; |
11199 | case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; |
11200 | case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; |
11201 | case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; |
11202 | case XK_Meta_L: |
11203 | case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; |
11204 | case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ |
11205 | case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ |
11206 | case XK_Meta_R: |
11207 | case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; |
11208 | case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; |
11209 | case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; |
11210 | case XK_Menu: return SAPP_KEYCODE_MENU; |
11211 | case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; |
11212 | case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; |
11213 | case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; |
11214 | case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; |
11215 | case XK_Pause: return SAPP_KEYCODE_PAUSE; |
11216 | case XK_Delete: return SAPP_KEYCODE_DELETE; |
11217 | case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; |
11218 | case XK_Return: return SAPP_KEYCODE_ENTER; |
11219 | case XK_Home: return SAPP_KEYCODE_HOME; |
11220 | case XK_End: return SAPP_KEYCODE_END; |
11221 | case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; |
11222 | case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; |
11223 | case XK_Insert: return SAPP_KEYCODE_INSERT; |
11224 | case XK_Left: return SAPP_KEYCODE_LEFT; |
11225 | case XK_Right: return SAPP_KEYCODE_RIGHT; |
11226 | case XK_Down: return SAPP_KEYCODE_DOWN; |
11227 | case XK_Up: return SAPP_KEYCODE_UP; |
11228 | case XK_F1: return SAPP_KEYCODE_F1; |
11229 | case XK_F2: return SAPP_KEYCODE_F2; |
11230 | case XK_F3: return SAPP_KEYCODE_F3; |
11231 | case XK_F4: return SAPP_KEYCODE_F4; |
11232 | case XK_F5: return SAPP_KEYCODE_F5; |
11233 | case XK_F6: return SAPP_KEYCODE_F6; |
11234 | case XK_F7: return SAPP_KEYCODE_F7; |
11235 | case XK_F8: return SAPP_KEYCODE_F8; |
11236 | case XK_F9: return SAPP_KEYCODE_F9; |
11237 | case XK_F10: return SAPP_KEYCODE_F10; |
11238 | case XK_F11: return SAPP_KEYCODE_F11; |
11239 | case XK_F12: return SAPP_KEYCODE_F12; |
11240 | case XK_F13: return SAPP_KEYCODE_F13; |
11241 | case XK_F14: return SAPP_KEYCODE_F14; |
11242 | case XK_F15: return SAPP_KEYCODE_F15; |
11243 | case XK_F16: return SAPP_KEYCODE_F16; |
11244 | case XK_F17: return SAPP_KEYCODE_F17; |
11245 | case XK_F18: return SAPP_KEYCODE_F18; |
11246 | case XK_F19: return SAPP_KEYCODE_F19; |
11247 | case XK_F20: return SAPP_KEYCODE_F20; |
11248 | case XK_F21: return SAPP_KEYCODE_F21; |
11249 | case XK_F22: return SAPP_KEYCODE_F22; |
11250 | case XK_F23: return SAPP_KEYCODE_F23; |
11251 | case XK_F24: return SAPP_KEYCODE_F24; |
11252 | case XK_F25: return SAPP_KEYCODE_F25; |
11253 | |
11254 | case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; |
11255 | case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; |
11256 | case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; |
11257 | case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; |
11258 | |
11259 | case XK_KP_Insert: return SAPP_KEYCODE_KP_0; |
11260 | case XK_KP_End: return SAPP_KEYCODE_KP_1; |
11261 | case XK_KP_Down: return SAPP_KEYCODE_KP_2; |
11262 | case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; |
11263 | case XK_KP_Left: return SAPP_KEYCODE_KP_4; |
11264 | case XK_KP_Begin: return SAPP_KEYCODE_KP_5; |
11265 | case XK_KP_Right: return SAPP_KEYCODE_KP_6; |
11266 | case XK_KP_Home: return SAPP_KEYCODE_KP_7; |
11267 | case XK_KP_Up: return SAPP_KEYCODE_KP_8; |
11268 | case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; |
11269 | case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; |
11270 | case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; |
11271 | case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; |
11272 | |
11273 | case XK_a: return SAPP_KEYCODE_A; |
11274 | case XK_b: return SAPP_KEYCODE_B; |
11275 | case XK_c: return SAPP_KEYCODE_C; |
11276 | case XK_d: return SAPP_KEYCODE_D; |
11277 | case XK_e: return SAPP_KEYCODE_E; |
11278 | case XK_f: return SAPP_KEYCODE_F; |
11279 | case XK_g: return SAPP_KEYCODE_G; |
11280 | case XK_h: return SAPP_KEYCODE_H; |
11281 | case XK_i: return SAPP_KEYCODE_I; |
11282 | case XK_j: return SAPP_KEYCODE_J; |
11283 | case XK_k: return SAPP_KEYCODE_K; |
11284 | case XK_l: return SAPP_KEYCODE_L; |
11285 | case XK_m: return SAPP_KEYCODE_M; |
11286 | case XK_n: return SAPP_KEYCODE_N; |
11287 | case XK_o: return SAPP_KEYCODE_O; |
11288 | case XK_p: return SAPP_KEYCODE_P; |
11289 | case XK_q: return SAPP_KEYCODE_Q; |
11290 | case XK_r: return SAPP_KEYCODE_R; |
11291 | case XK_s: return SAPP_KEYCODE_S; |
11292 | case XK_t: return SAPP_KEYCODE_T; |
11293 | case XK_u: return SAPP_KEYCODE_U; |
11294 | case XK_v: return SAPP_KEYCODE_V; |
11295 | case XK_w: return SAPP_KEYCODE_W; |
11296 | case XK_x: return SAPP_KEYCODE_X; |
11297 | case XK_y: return SAPP_KEYCODE_Y; |
11298 | case XK_z: return SAPP_KEYCODE_Z; |
11299 | case XK_1: return SAPP_KEYCODE_1; |
11300 | case XK_2: return SAPP_KEYCODE_2; |
11301 | case XK_3: return SAPP_KEYCODE_3; |
11302 | case XK_4: return SAPP_KEYCODE_4; |
11303 | case XK_5: return SAPP_KEYCODE_5; |
11304 | case XK_6: return SAPP_KEYCODE_6; |
11305 | case XK_7: return SAPP_KEYCODE_7; |
11306 | case XK_8: return SAPP_KEYCODE_8; |
11307 | case XK_9: return SAPP_KEYCODE_9; |
11308 | case XK_0: return SAPP_KEYCODE_0; |
11309 | case XK_space: return SAPP_KEYCODE_SPACE; |
11310 | case XK_minus: return SAPP_KEYCODE_MINUS; |
11311 | case XK_equal: return SAPP_KEYCODE_EQUAL; |
11312 | case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; |
11313 | case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; |
11314 | case XK_backslash: return SAPP_KEYCODE_BACKSLASH; |
11315 | case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; |
11316 | case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; |
11317 | case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; |
11318 | case XK_comma: return SAPP_KEYCODE_COMMA; |
11319 | case XK_period: return SAPP_KEYCODE_PERIOD; |
11320 | case XK_slash: return SAPP_KEYCODE_SLASH; |
11321 | case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ |
11322 | default: return SAPP_KEYCODE_INVALID; |
11323 | } |
11324 | } |
11325 | |
11326 | _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { |
11327 | int min = 0; |
11328 | int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; |
11329 | int mid; |
11330 | |
11331 | /* First check for Latin-1 characters (1:1 mapping) */ |
11332 | if ((keysym >= 0x0020 && keysym <= 0x007e) || |
11333 | (keysym >= 0x00a0 && keysym <= 0x00ff)) |
11334 | { |
11335 | return keysym; |
11336 | } |
11337 | |
11338 | /* Also check for directly encoded 24-bit UCS characters */ |
11339 | if ((keysym & 0xff000000) == 0x01000000) { |
11340 | return keysym & 0x00ffffff; |
11341 | } |
11342 | |
11343 | /* Binary search in table */ |
11344 | while (max >= min) { |
11345 | mid = (min + max) / 2; |
11346 | if (_sapp_x11_keysymtab[mid].keysym < keysym) { |
11347 | min = mid + 1; |
11348 | } |
11349 | else if (_sapp_x11_keysymtab[mid].keysym > keysym) { |
11350 | max = mid - 1; |
11351 | } |
11352 | else { |
11353 | return _sapp_x11_keysymtab[mid].ucs; |
11354 | } |
11355 | } |
11356 | |
11357 | /* No matching Unicode value found */ |
11358 | return -1; |
11359 | } |
11360 | |
11361 | _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { |
11362 | SOKOL_ASSERT(src); |
11363 | SOKOL_ASSERT(_sapp.drop.buffer); |
11364 | |
11365 | _sapp_clear_drop_buffer(); |
11366 | _sapp.drop.num_files = 0; |
11367 | |
11368 | /* |
11369 | src is (potentially percent-encoded) string made of one or multiple paths |
11370 | separated by \r\n, each path starting with 'file://' |
11371 | */ |
11372 | bool err = false; |
11373 | int src_count = 0; |
11374 | char src_chr = 0; |
11375 | char* dst_ptr = _sapp.drop.buffer; |
11376 | const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 |
11377 | while (0 != (src_chr = *src++)) { |
11378 | src_count++; |
11379 | char dst_chr = 0; |
11380 | /* check leading 'file://' */ |
11381 | if (src_count <= 7) { |
11382 | if (((src_count == 1) && (src_chr != 'f')) || |
11383 | ((src_count == 2) && (src_chr != 'i')) || |
11384 | ((src_count == 3) && (src_chr != 'l')) || |
11385 | ((src_count == 4) && (src_chr != 'e')) || |
11386 | ((src_count == 5) && (src_chr != ':')) || |
11387 | ((src_count == 6) && (src_chr != '/')) || |
11388 | ((src_count == 7) && (src_chr != '/'))) |
11389 | { |
11390 | SAPP_LOG("sokol_app.h: dropped file URI doesn't start with file://"); |
11391 | err = true; |
11392 | break; |
11393 | } |
11394 | } |
11395 | else if (src_chr == '\r') { |
11396 | // skip |
11397 | } |
11398 | else if (src_chr == '\n') { |
11399 | src_count = 0; |
11400 | _sapp.drop.num_files++; |
11401 | // too many files is not an error |
11402 | if (_sapp.drop.num_files >= _sapp.drop.max_files) { |
11403 | break; |
11404 | } |
11405 | dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; |
11406 | dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); |
11407 | } |
11408 | else if ((src_chr == '%') && src[0] && src[1]) { |
11409 | // a percent-encoded byte (most likely UTF-8 multibyte sequence) |
11410 | const char digits[3] = { src[0], src[1], 0 }; |
11411 | src += 2; |
11412 | dst_chr = (char) strtol(digits, 0, 16); |
11413 | } |
11414 | else { |
11415 | dst_chr = src_chr; |
11416 | } |
11417 | if (dst_chr) { |
11418 | // dst_end_ptr already has adjustment for terminating zero |
11419 | if (dst_ptr < dst_end_ptr) { |
11420 | *dst_ptr++ = dst_chr; |
11421 | } |
11422 | else { |
11423 | SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); |
11424 | err = true; |
11425 | break; |
11426 | } |
11427 | } |
11428 | } |
11429 | if (err) { |
11430 | _sapp_clear_drop_buffer(); |
11431 | _sapp.drop.num_files = 0; |
11432 | return false; |
11433 | } |
11434 | else { |
11435 | return true; |
11436 | } |
11437 | } |
11438 | |
11439 | // XLib manual says keycodes are in the range [8, 255] inclusive. |
11440 | // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html |
11441 | static bool _sapp_x11_keycodes[256]; |
11442 | |
11443 | _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { |
11444 | Bool filtered = XFilterEvent(event, None); |
11445 | switch (event->type) { |
11446 | case GenericEvent: |
11447 | if (_sapp.mouse.locked && _sapp.x11.xi.available) { |
11448 | if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { |
11449 | if (XGetEventData(_sapp.x11.display, &event->xcookie)) { |
11450 | if (event->xcookie.evtype == XI_RawMotion) { |
11451 | XIRawEvent* re = (XIRawEvent*) event->xcookie.data; |
11452 | if (re->valuators.mask_len) { |
11453 | const double* values = re->raw_values; |
11454 | if (XIMaskIsSet(re->valuators.mask, 0)) { |
11455 | _sapp.mouse.dx = (float) *values; |
11456 | values++; |
11457 | } |
11458 | if (XIMaskIsSet(re->valuators.mask, 1)) { |
11459 | _sapp.mouse.dy = (float) *values; |
11460 | } |
11461 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); |
11462 | } |
11463 | } |
11464 | XFreeEventData(_sapp.x11.display, &event->xcookie); |
11465 | } |
11466 | } |
11467 | } |
11468 | break; |
11469 | case FocusIn: |
11470 | // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW |
11471 | if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { |
11472 | _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); |
11473 | } |
11474 | break; |
11475 | case FocusOut: |
11476 | /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ |
11477 | if (_sapp.mouse.locked) { |
11478 | _sapp_x11_lock_mouse(false); |
11479 | } |
11480 | // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW |
11481 | if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { |
11482 | _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); |
11483 | } |
11484 | break; |
11485 | case KeyPress: |
11486 | { |
11487 | int keycode = (int)event->xkey.keycode; |
11488 | const sapp_keycode key = _sapp_x11_translate_key(keycode); |
11489 | bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; |
11490 | _sapp_x11_keycodes[keycode & 0xFF] = true; |
11491 | uint32_t mods = _sapp_x11_mods(event->xkey.state); |
11492 | // X11 doesn't set modifier bit on key down, so emulate that |
11493 | mods |= _sapp_x11_key_modifier_bit(key); |
11494 | if (key != SAPP_KEYCODE_INVALID) { |
11495 | _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); |
11496 | } |
11497 | KeySym keysym; |
11498 | XLookupString(&event->xkey, NULL, 0, &keysym, NULL); |
11499 | int32_t chr = _sapp_x11_keysym_to_unicode(keysym); |
11500 | if (chr > 0) { |
11501 | _sapp_x11_char_event((uint32_t)chr, repeat, mods); |
11502 | } |
11503 | } |
11504 | break; |
11505 | case KeyRelease: |
11506 | { |
11507 | int keycode = (int)event->xkey.keycode; |
11508 | const sapp_keycode key = _sapp_x11_translate_key(keycode); |
11509 | _sapp_x11_keycodes[keycode & 0xFF] = false; |
11510 | if (key != SAPP_KEYCODE_INVALID) { |
11511 | uint32_t mods = _sapp_x11_mods(event->xkey.state); |
11512 | // X11 doesn't clear modifier bit on key up, so emulate that |
11513 | mods &= ~_sapp_x11_key_modifier_bit(key); |
11514 | _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); |
11515 | } |
11516 | } |
11517 | break; |
11518 | case ButtonPress: |
11519 | { |
11520 | const sapp_mousebutton btn = _sapp_x11_translate_button(event); |
11521 | uint32_t mods = _sapp_x11_mods(event->xbutton.state); |
11522 | // X11 doesn't set modifier bit on button down, so emulate that |
11523 | mods |= _sapp_x11_button_modifier_bit(btn); |
11524 | if (btn != SAPP_MOUSEBUTTON_INVALID) { |
11525 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); |
11526 | _sapp.x11.mouse_buttons |= (1 << btn); |
11527 | } |
11528 | else { |
11529 | /* might be a scroll event */ |
11530 | switch (event->xbutton.button) { |
11531 | case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; |
11532 | case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; |
11533 | case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; |
11534 | case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; |
11535 | } |
11536 | } |
11537 | } |
11538 | break; |
11539 | case ButtonRelease: |
11540 | { |
11541 | const sapp_mousebutton btn = _sapp_x11_translate_button(event); |
11542 | if (btn != SAPP_MOUSEBUTTON_INVALID) { |
11543 | uint32_t mods = _sapp_x11_mods(event->xbutton.state); |
11544 | // X11 doesn't clear modifier bit on button up, so emulate that |
11545 | mods &= ~_sapp_x11_button_modifier_bit(btn); |
11546 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); |
11547 | _sapp.x11.mouse_buttons &= ~(1 << btn); |
11548 | } |
11549 | } |
11550 | break; |
11551 | case EnterNotify: |
11552 | /* don't send enter/leave events while mouse button held down */ |
11553 | if (0 == _sapp.x11.mouse_buttons) { |
11554 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); |
11555 | } |
11556 | break; |
11557 | case LeaveNotify: |
11558 | if (0 == _sapp.x11.mouse_buttons) { |
11559 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); |
11560 | } |
11561 | break; |
11562 | case MotionNotify: |
11563 | if (!_sapp.mouse.locked) { |
11564 | const float new_x = (float) event->xmotion.x; |
11565 | const float new_y = (float) event->xmotion.y; |
11566 | if (_sapp.mouse.pos_valid) { |
11567 | _sapp.mouse.dx = new_x - _sapp.mouse.x; |
11568 | _sapp.mouse.dy = new_y - _sapp.mouse.y; |
11569 | } |
11570 | _sapp.mouse.x = new_x; |
11571 | _sapp.mouse.y = new_y; |
11572 | _sapp.mouse.pos_valid = true; |
11573 | _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); |
11574 | } |
11575 | break; |
11576 | case ConfigureNotify: |
11577 | if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { |
11578 | _sapp.window_width = event->xconfigure.width; |
11579 | _sapp.window_height = event->xconfigure.height; |
11580 | _sapp.framebuffer_width = _sapp.window_width; |
11581 | _sapp.framebuffer_height = _sapp.window_height; |
11582 | _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); |
11583 | } |
11584 | break; |
11585 | case PropertyNotify: |
11586 | if (event->xproperty.state == PropertyNewValue) { |
11587 | if (event->xproperty.atom == _sapp.x11.WM_STATE) { |
11588 | const int state = _sapp_x11_get_window_state(); |
11589 | if (state != _sapp.x11.window_state) { |
11590 | _sapp.x11.window_state = state; |
11591 | if (state == IconicState) { |
11592 | _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); |
11593 | } |
11594 | else if (state == NormalState) { |
11595 | _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); |
11596 | } |
11597 | } |
11598 | } |
11599 | } |
11600 | break; |
11601 | case ClientMessage: |
11602 | if (filtered) { |
11603 | return; |
11604 | } |
11605 | if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { |
11606 | const Atom protocol = (Atom)event->xclient.data.l[0]; |
11607 | if (protocol == _sapp.x11.WM_DELETE_WINDOW) { |
11608 | _sapp.quit_requested = true; |
11609 | } |
11610 | } |
11611 | else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { |
11612 | const bool is_list = 0 != (event->xclient.data.l[1] & 1); |
11613 | _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; |
11614 | _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; |
11615 | _sapp.x11.xdnd.format = None; |
11616 | if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { |
11617 | return; |
11618 | } |
11619 | uint32_t count = 0; |
11620 | Atom* formats = 0; |
11621 | if (is_list) { |
11622 | count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); |
11623 | } |
11624 | else { |
11625 | count = 3; |
11626 | formats = (Atom*) event->xclient.data.l + 2; |
11627 | } |
11628 | for (uint32_t i = 0; i < count; i++) { |
11629 | if (formats[i] == _sapp.x11.xdnd.text_uri_list) { |
11630 | _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; |
11631 | break; |
11632 | } |
11633 | } |
11634 | if (is_list && formats) { |
11635 | XFree(formats); |
11636 | } |
11637 | } |
11638 | else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { |
11639 | if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { |
11640 | return; |
11641 | } |
11642 | Time time = CurrentTime; |
11643 | if (_sapp.x11.xdnd.format) { |
11644 | if (_sapp.x11.xdnd.version >= 1) { |
11645 | time = (Time)event->xclient.data.l[2]; |
11646 | } |
11647 | XConvertSelection(_sapp.x11.display, |
11648 | _sapp.x11.xdnd.XdndSelection, |
11649 | _sapp.x11.xdnd.format, |
11650 | _sapp.x11.xdnd.XdndSelection, |
11651 | _sapp.x11.window, |
11652 | time); |
11653 | } |
11654 | else if (_sapp.x11.xdnd.version >= 2) { |
11655 | XEvent reply; |
11656 | _sapp_clear(&reply, sizeof(reply)); |
11657 | reply.type = ClientMessage; |
11658 | reply.xclient.window = _sapp.x11.window; |
11659 | reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; |
11660 | reply.xclient.format = 32; |
11661 | reply.xclient.data.l[0] = (long)_sapp.x11.window; |
11662 | reply.xclient.data.l[1] = 0; // drag was rejected |
11663 | reply.xclient.data.l[2] = None; |
11664 | XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); |
11665 | XFlush(_sapp.x11.display); |
11666 | } |
11667 | } |
11668 | else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { |
11669 | /* drag operation has moved over the window |
11670 | FIXME: we could track the mouse position here, but |
11671 | this isn't implemented on other platforms either so far |
11672 | */ |
11673 | if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { |
11674 | return; |
11675 | } |
11676 | XEvent reply; |
11677 | _sapp_clear(&reply, sizeof(reply)); |
11678 | reply.type = ClientMessage; |
11679 | reply.xclient.window = _sapp.x11.xdnd.source; |
11680 | reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; |
11681 | reply.xclient.format = 32; |
11682 | reply.xclient.data.l[0] = (long)_sapp.x11.window; |
11683 | if (_sapp.x11.xdnd.format) { |
11684 | /* reply that we are ready to copy the dragged data */ |
11685 | reply.xclient.data.l[1] = 1; // accept with no rectangle |
11686 | if (_sapp.x11.xdnd.version >= 2) { |
11687 | reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; |
11688 | } |
11689 | } |
11690 | XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); |
11691 | XFlush(_sapp.x11.display); |
11692 | } |
11693 | break; |
11694 | case SelectionNotify: |
11695 | if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { |
11696 | char* data = 0; |
11697 | uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, |
11698 | event->xselection.property, |
11699 | event->xselection.target, |
11700 | (unsigned char**) &data); |
11701 | if (_sapp.drop.enabled && result) { |
11702 | if (_sapp_x11_parse_dropped_files_list(data)) { |
11703 | if (_sapp_events_enabled()) { |
11704 | _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); |
11705 | _sapp_call_event(&_sapp.event); |
11706 | } |
11707 | } |
11708 | } |
11709 | if (_sapp.x11.xdnd.version >= 2) { |
11710 | XEvent reply; |
11711 | _sapp_clear(&reply, sizeof(reply)); |
11712 | reply.type = ClientMessage; |
11713 | reply.xclient.window = _sapp.x11.window; |
11714 | reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; |
11715 | reply.xclient.format = 32; |
11716 | reply.xclient.data.l[0] = (long)_sapp.x11.window; |
11717 | reply.xclient.data.l[1] = result; |
11718 | reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; |
11719 | XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); |
11720 | XFlush(_sapp.x11.display); |
11721 | } |
11722 | } |
11723 | break; |
11724 | case DestroyNotify: |
11725 | break; |
11726 | } |
11727 | } |
11728 | |
11729 | #if !defined(_SAPP_GLX) |
11730 | |
11731 | _SOKOL_PRIVATE void _sapp_egl_init(void) { |
11732 | #if defined(SOKOL_GLCORE33) |
11733 | if (!eglBindAPI(EGL_OPENGL_API)) { |
11734 | _sapp_fail("EGL: failed to bind API"); |
11735 | } |
11736 | #else |
11737 | if (!eglBindAPI(EGL_OPENGL_ES_API)) { |
11738 | _sapp_fail("EGL: failed to bind API"); |
11739 | } |
11740 | #endif |
11741 | |
11742 | _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); |
11743 | if (EGL_NO_DISPLAY == _sapp.egl.display) { |
11744 | _sapp_fail("EGL: failed to get display"); |
11745 | } |
11746 | |
11747 | EGLint major, minor; |
11748 | if (!eglInitialize(_sapp.egl.display, &major, &minor)) { |
11749 | _sapp_fail("EGL: failed to initialize"); |
11750 | } |
11751 | |
11752 | EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0; |
11753 | EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; |
11754 | const EGLint config_attrs[] = { |
11755 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
11756 | #if defined(SOKOL_GLCORE33) |
11757 | EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, |
11758 | #elif defined(SOKOL_GLES3) |
11759 | EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT, |
11760 | #else |
11761 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
11762 | #endif |
11763 | EGL_RED_SIZE, 8, |
11764 | EGL_GREEN_SIZE, 8, |
11765 | EGL_BLUE_SIZE, 8, |
11766 | EGL_ALPHA_SIZE, alpha_size, |
11767 | EGL_DEPTH_SIZE, 24, |
11768 | EGL_STENCIL_SIZE, 8, |
11769 | EGL_SAMPLE_BUFFERS, _sapp.desc.sample_count > 1 ? 1 : 0, |
11770 | EGL_SAMPLES, sample_count, |
11771 | EGL_NONE, |
11772 | }; |
11773 | |
11774 | EGLConfig egl_configs[32]; |
11775 | EGLint config_count; |
11776 | if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) { |
11777 | _sapp_fail("EGL: no available configs"); |
11778 | } |
11779 | |
11780 | EGLConfig config = egl_configs[0]; |
11781 | for (int i = 0; i < config_count; ++i) { |
11782 | EGLConfig c = egl_configs[i]; |
11783 | EGLint r, g, b, a, d, s, n; |
11784 | if (eglGetConfigAttrib(_sapp.egl.display, c, EGL_RED_SIZE, &r) && |
11785 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_GREEN_SIZE, &g) && |
11786 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_BLUE_SIZE, &b) && |
11787 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_ALPHA_SIZE, &a) && |
11788 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_DEPTH_SIZE, &d) && |
11789 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_STENCIL_SIZE, &s) && |
11790 | eglGetConfigAttrib(_sapp.egl.display, c, EGL_SAMPLES, &n) && |
11791 | (r == 8) && (g == 8) && (b == 8) && (a == alpha_size) && (d == 24) && (s == 8) && (n == sample_count)) { |
11792 | config = c; |
11793 | break; |
11794 | } |
11795 | } |
11796 | |
11797 | EGLint visual_id; |
11798 | if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) { |
11799 | _sapp_fail("EGL: failed to get native visual"); |
11800 | } |
11801 | |
11802 | XVisualInfo visual_info_template; |
11803 | _sapp_clear(&visual_info_template, sizeof(visual_info_template)); |
11804 | visual_info_template.visualid = (VisualID)visual_id; |
11805 | |
11806 | int num_visuals; |
11807 | XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals); |
11808 | if (!visual_info) { |
11809 | _sapp_fail("EGL: failed to get x11 visual"); |
11810 | } |
11811 | |
11812 | _sapp_x11_create_window(visual_info->visual, visual_info->depth); |
11813 | XFree(visual_info); |
11814 | |
11815 | _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL); |
11816 | if (EGL_NO_SURFACE == _sapp.egl.surface) { |
11817 | _sapp_fail("EGL: failed to create EGL surface"); |
11818 | } |
11819 | |
11820 | EGLint ctx_attrs[] = { |
11821 | #if defined(SOKOL_GLCORE33) |
11822 | EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version, |
11823 | EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, |
11824 | EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, |
11825 | #elif defined(SOKOL_GLES3) |
11826 | EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, |
11827 | #else |
11828 | EGL_CONTEXT_CLIENT_VERSION, 2, |
11829 | #endif |
11830 | EGL_NONE, |
11831 | }; |
11832 | |
11833 | _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs); |
11834 | if (EGL_NO_CONTEXT == _sapp.egl.context) { |
11835 | _sapp_fail("EGL: failed to create GL context"); |
11836 | } |
11837 | |
11838 | if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) { |
11839 | _sapp_fail("EGL: failed to set current context"); |
11840 | } |
11841 | |
11842 | eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); |
11843 | |
11844 | #if defined(SOKOL_GLES3) |
11845 | _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; |
11846 | #endif |
11847 | } |
11848 | |
11849 | _SOKOL_PRIVATE void _sapp_egl_destroy(void) { |
11850 | if (_sapp.egl.display != EGL_NO_DISPLAY) { |
11851 | eglMakeCurrent(_sapp.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
11852 | |
11853 | if (_sapp.egl.context != EGL_NO_CONTEXT) { |
11854 | eglDestroyContext(_sapp.egl.display, _sapp.egl.context); |
11855 | _sapp.egl.context = EGL_NO_CONTEXT; |
11856 | } |
11857 | |
11858 | if (_sapp.egl.surface != EGL_NO_SURFACE) { |
11859 | eglDestroySurface(_sapp.egl.display, _sapp.egl.surface); |
11860 | _sapp.egl.surface = EGL_NO_SURFACE; |
11861 | } |
11862 | |
11863 | eglTerminate(_sapp.egl.display); |
11864 | _sapp.egl.display = EGL_NO_DISPLAY; |
11865 | } |
11866 | } |
11867 | |
11868 | #endif /* _SAPP_GLX */ |
11869 | |
11870 | _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { |
11871 | /* The following lines are here to trigger a linker error instead of an |
11872 | obscure runtime error if the user has forgotten to add -pthread to |
11873 | the compiler or linker options. They have no other purpose. |
11874 | */ |
11875 | pthread_attr_t pthread_attr; |
11876 | pthread_attr_init(&pthread_attr); |
11877 | pthread_attr_destroy(&pthread_attr); |
11878 | |
11879 | _sapp_init_state(desc); |
11880 | _sapp.x11.window_state = NormalState; |
11881 | |
11882 | XInitThreads(); |
11883 | XrmInitialize(); |
11884 | _sapp.x11.display = XOpenDisplay(NULL); |
11885 | if (!_sapp.x11.display) { |
11886 | _sapp_fail("XOpenDisplay() failed!\n"); |
11887 | } |
11888 | _sapp.x11.screen = DefaultScreen(_sapp.x11.display); |
11889 | _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); |
11890 | XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); |
11891 | _sapp_x11_query_system_dpi(); |
11892 | _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; |
11893 | _sapp_x11_init_extensions(); |
11894 | _sapp_x11_create_cursors(); |
11895 | #if defined(_SAPP_GLX) |
11896 | _sapp_glx_init(); |
11897 | Visual* visual = 0; |
11898 | int depth = 0; |
11899 | _sapp_glx_choose_visual(&visual, &depth); |
11900 | _sapp_x11_create_window(visual, depth); |
11901 | _sapp_glx_create_context(); |
11902 | _sapp_glx_swapinterval(_sapp.swap_interval); |
11903 | #else |
11904 | _sapp_egl_init(); |
11905 | #endif |
11906 | sapp_set_icon(&desc->icon); |
11907 | _sapp.valid = true; |
11908 | _sapp_x11_show_window(); |
11909 | if (_sapp.fullscreen) { |
11910 | _sapp_x11_set_fullscreen(true); |
11911 | } |
11912 | |
11913 | XFlush(_sapp.x11.display); |
11914 | while (!_sapp.quit_ordered) { |
11915 | _sapp_timing_measure(&_sapp.timing); |
11916 | int count = XPending(_sapp.x11.display); |
11917 | while (count--) { |
11918 | XEvent event; |
11919 | XNextEvent(_sapp.x11.display, &event); |
11920 | _sapp_x11_process_event(&event); |
11921 | } |
11922 | _sapp_frame(); |
11923 | #if defined(_SAPP_GLX) |
11924 | _sapp_glx_swap_buffers(); |
11925 | #else |
11926 | eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); |
11927 | #endif |
11928 | XFlush(_sapp.x11.display); |
11929 | /* handle quit-requested, either from window or from sapp_request_quit() */ |
11930 | if (_sapp.quit_requested && !_sapp.quit_ordered) { |
11931 | /* give user code a chance to intervene */ |
11932 | _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); |
11933 | /* if user code hasn't intervened, quit the app */ |
11934 | if (_sapp.quit_requested) { |
11935 | _sapp.quit_ordered = true; |
11936 | } |
11937 | } |
11938 | } |
11939 | _sapp_call_cleanup(); |
11940 | #if defined(_SAPP_GLX) |
11941 | _sapp_glx_destroy_context(); |
11942 | #else |
11943 | _sapp_egl_destroy(); |
11944 | #endif |
11945 | _sapp_x11_destroy_window(); |
11946 | _sapp_x11_destroy_cursors(); |
11947 | XCloseDisplay(_sapp.x11.display); |
11948 | _sapp_discard_state(); |
11949 | } |
11950 | |
11951 | #if !defined(SOKOL_NO_ENTRY) |
11952 | int main(int argc, char* argv[]) { |
11953 | sapp_desc desc = sokol_main(argc, argv); |
11954 | _sapp_linux_run(&desc); |
11955 | return 0; |
11956 | } |
11957 | #endif /* SOKOL_NO_ENTRY */ |
11958 | #endif /* _SAPP_LINUX */ |
11959 | |
11960 | /*== PUBLIC API FUNCTIONS ====================================================*/ |
11961 | #if defined(SOKOL_NO_ENTRY) |
11962 | SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { |
11963 | SOKOL_ASSERT(desc); |
11964 | #if defined(_SAPP_MACOS) |
11965 | _sapp_macos_run(desc); |
11966 | #elif defined(_SAPP_IOS) |
11967 | _sapp_ios_run(desc); |
11968 | #elif defined(_SAPP_EMSCRIPTEN) |
11969 | _sapp_emsc_run(desc); |
11970 | #elif defined(_SAPP_WIN32) |
11971 | _sapp_win32_run(desc); |
11972 | #elif defined(_SAPP_UWP) |
11973 | _sapp_uwp_run(desc); |
11974 | #elif defined(_SAPP_LINUX) |
11975 | _sapp_linux_run(desc); |
11976 | #else |
11977 | // calling sapp_run() directly is not supported on Android) |
11978 | _sapp_fail("sapp_run() not supported on this platform!"); |
11979 | #endif |
11980 | } |
11981 | |
11982 | /* this is just a stub so the linker doesn't complain */ |
11983 | sapp_desc sokol_main(int argc, char* argv[]) { |
11984 | _SOKOL_UNUSED(argc); |
11985 | _SOKOL_UNUSED(argv); |
11986 | sapp_desc desc; |
11987 | _sapp_clear(&desc, sizeof(desc)); |
11988 | return desc; |
11989 | } |
11990 | #else |
11991 | /* likewise, in normal mode, sapp_run() is just an empty stub */ |
11992 | SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { |
11993 | _SOKOL_UNUSED(desc); |
11994 | } |
11995 | #endif |
11996 | |
11997 | SOKOL_API_IMPL bool sapp_isvalid(void) { |
11998 | return _sapp.valid; |
11999 | } |
12000 | |
12001 | SOKOL_API_IMPL void* sapp_userdata(void) { |
12002 | return _sapp.desc.user_data; |
12003 | } |
12004 | |
12005 | SOKOL_API_IMPL sapp_desc sapp_query_desc(void) { |
12006 | return _sapp.desc; |
12007 | } |
12008 | |
12009 | SOKOL_API_IMPL uint64_t sapp_frame_count(void) { |
12010 | return _sapp.frame_count; |
12011 | } |
12012 | |
12013 | SOKOL_API_IMPL double sapp_frame_duration(void) { |
12014 | return _sapp_timing_get_avg(&_sapp.timing); |
12015 | } |
12016 | |
12017 | SOKOL_API_IMPL int sapp_width(void) { |
12018 | return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; |
12019 | } |
12020 | |
12021 | SOKOL_API_IMPL float sapp_widthf(void) { |
12022 | return (float)sapp_width(); |
12023 | } |
12024 | |
12025 | SOKOL_API_IMPL int sapp_height(void) { |
12026 | return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; |
12027 | } |
12028 | |
12029 | SOKOL_API_IMPL float sapp_heightf(void) { |
12030 | return (float)sapp_height(); |
12031 | } |
12032 | |
12033 | SOKOL_API_IMPL int sapp_color_format(void) { |
12034 | #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) |
12035 | switch (_sapp.emsc.wgpu.render_format) { |
12036 | case WGPUTextureFormat_RGBA8Unorm: |
12037 | return _SAPP_PIXELFORMAT_RGBA8; |
12038 | case WGPUTextureFormat_BGRA8Unorm: |
12039 | return _SAPP_PIXELFORMAT_BGRA8; |
12040 | default: |
12041 | SOKOL_UNREACHABLE; |
12042 | return 0; |
12043 | } |
12044 | #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) |
12045 | return _SAPP_PIXELFORMAT_BGRA8; |
12046 | #else |
12047 | return _SAPP_PIXELFORMAT_RGBA8; |
12048 | #endif |
12049 | } |
12050 | |
12051 | SOKOL_API_IMPL int sapp_depth_format(void) { |
12052 | return _SAPP_PIXELFORMAT_DEPTH_STENCIL; |
12053 | } |
12054 | |
12055 | SOKOL_API_IMPL int sapp_sample_count(void) { |
12056 | return _sapp.sample_count; |
12057 | } |
12058 | |
12059 | SOKOL_API_IMPL bool sapp_high_dpi(void) { |
12060 | return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); |
12061 | } |
12062 | |
12063 | SOKOL_API_IMPL float sapp_dpi_scale(void) { |
12064 | return _sapp.dpi_scale; |
12065 | } |
12066 | |
12067 | SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { |
12068 | SOKOL_ASSERT(_sapp.valid); |
12069 | #if defined(_SAPP_ANDROID) |
12070 | return _sapp.android.display; |
12071 | #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) |
12072 | return _sapp.egl.display; |
12073 | #else |
12074 | return 0; |
12075 | #endif |
12076 | } |
12077 | |
12078 | SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { |
12079 | SOKOL_ASSERT(_sapp.valid); |
12080 | #if defined(_SAPP_ANDROID) |
12081 | return _sapp.android.context; |
12082 | #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) |
12083 | return _sapp.egl.context; |
12084 | #else |
12085 | return 0; |
12086 | #endif |
12087 | } |
12088 | |
12089 | SOKOL_API_IMPL bool sapp_gles2(void) { |
12090 | return _sapp.gles2_fallback; |
12091 | } |
12092 | |
12093 | SOKOL_API_IMPL void sapp_show_keyboard(bool show) { |
12094 | #if defined(_SAPP_IOS) |
12095 | _sapp_ios_show_keyboard(show); |
12096 | #elif defined(_SAPP_EMSCRIPTEN) |
12097 | _sapp_emsc_show_keyboard(show); |
12098 | #elif defined(_SAPP_ANDROID) |
12099 | _sapp_android_show_keyboard(show); |
12100 | #else |
12101 | _SOKOL_UNUSED(show); |
12102 | #endif |
12103 | } |
12104 | |
12105 | SOKOL_API_IMPL bool sapp_keyboard_shown(void) { |
12106 | return _sapp.onscreen_keyboard_shown; |
12107 | } |
12108 | |
12109 | SOKOL_API_IMPL bool sapp_is_fullscreen(void) { |
12110 | return _sapp.fullscreen; |
12111 | } |
12112 | |
12113 | SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { |
12114 | #if defined(_SAPP_MACOS) |
12115 | _sapp_macos_toggle_fullscreen(); |
12116 | #elif defined(_SAPP_WIN32) |
12117 | _sapp_win32_toggle_fullscreen(); |
12118 | #elif defined(_SAPP_UWP) |
12119 | _sapp_uwp_toggle_fullscreen(); |
12120 | #elif defined(_SAPP_LINUX) |
12121 | _sapp_x11_toggle_fullscreen(); |
12122 | #endif |
12123 | } |
12124 | |
12125 | /* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ |
12126 | SOKOL_API_IMPL void sapp_show_mouse(bool show) { |
12127 | if (_sapp.mouse.shown != show) { |
12128 | #if defined(_SAPP_MACOS) |
12129 | _sapp_macos_update_cursor(_sapp.mouse.current_cursor, show); |
12130 | #elif defined(_SAPP_WIN32) |
12131 | _sapp_win32_update_cursor(_sapp.mouse.current_cursor, show, false); |
12132 | #elif defined(_SAPP_LINUX) |
12133 | _sapp_x11_update_cursor(_sapp.mouse.current_cursor, show); |
12134 | #elif defined(_SAPP_UWP) |
12135 | _sapp_uwp_update_cursor(_sapp.mouse.current_cursor, show); |
12136 | #elif defined(_SAPP_EMSCRIPTEN) |
12137 | _sapp_emsc_update_cursor(_sapp.mouse.current_cursor, show); |
12138 | #endif |
12139 | _sapp.mouse.shown = show; |
12140 | } |
12141 | } |
12142 | |
12143 | SOKOL_API_IMPL bool sapp_mouse_shown(void) { |
12144 | return _sapp.mouse.shown; |
12145 | } |
12146 | |
12147 | SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { |
12148 | #if defined(_SAPP_MACOS) |
12149 | _sapp_macos_lock_mouse(lock); |
12150 | #elif defined(_SAPP_EMSCRIPTEN) |
12151 | _sapp_emsc_lock_mouse(lock); |
12152 | #elif defined(_SAPP_WIN32) |
12153 | _sapp_win32_lock_mouse(lock); |
12154 | #elif defined(_SAPP_LINUX) |
12155 | _sapp_x11_lock_mouse(lock); |
12156 | #else |
12157 | _sapp.mouse.locked = lock; |
12158 | #endif |
12159 | } |
12160 | |
12161 | SOKOL_API_IMPL bool sapp_mouse_locked(void) { |
12162 | return _sapp.mouse.locked; |
12163 | } |
12164 | |
12165 | SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) { |
12166 | SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); |
12167 | if (_sapp.mouse.current_cursor != cursor) { |
12168 | #if defined(_SAPP_MACOS) |
12169 | _sapp_macos_update_cursor(cursor, _sapp.mouse.shown); |
12170 | #elif defined(_SAPP_WIN32) |
12171 | _sapp_win32_update_cursor(cursor, _sapp.mouse.shown, false); |
12172 | #elif defined(_SAPP_LINUX) |
12173 | _sapp_x11_update_cursor(cursor, _sapp.mouse.shown); |
12174 | #elif defined(_SAPP_UWP) |
12175 | _sapp_uwp_update_cursor(cursor, _sapp.mouse.shown); |
12176 | #elif defined(_SAPP_EMSCRIPTEN) |
12177 | _sapp_emsc_update_cursor(cursor, _sapp.mouse.shown); |
12178 | #endif |
12179 | _sapp.mouse.current_cursor = cursor; |
12180 | } |
12181 | } |
12182 | |
12183 | SOKOL_API_IMPL sapp_mouse_cursor sapp_get_mouse_cursor(void) { |
12184 | return _sapp.mouse.current_cursor; |
12185 | } |
12186 | |
12187 | SOKOL_API_IMPL void sapp_request_quit(void) { |
12188 | _sapp.quit_requested = true; |
12189 | } |
12190 | |
12191 | SOKOL_API_IMPL void sapp_cancel_quit(void) { |
12192 | _sapp.quit_requested = false; |
12193 | } |
12194 | |
12195 | SOKOL_API_IMPL void sapp_quit(void) { |
12196 | _sapp.quit_ordered = true; |
12197 | } |
12198 | |
12199 | SOKOL_API_IMPL void sapp_consume_event(void) { |
12200 | _sapp.event_consumed = true; |
12201 | } |
12202 | |
12203 | /* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ |
12204 | SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { |
12205 | if (!_sapp.clipboard.enabled) { |
12206 | return; |
12207 | } |
12208 | SOKOL_ASSERT(str); |
12209 | #if defined(_SAPP_MACOS) |
12210 | _sapp_macos_set_clipboard_string(str); |
12211 | #elif defined(_SAPP_EMSCRIPTEN) |
12212 | _sapp_emsc_set_clipboard_string(str); |
12213 | #elif defined(_SAPP_WIN32) |
12214 | _sapp_win32_set_clipboard_string(str); |
12215 | #else |
12216 | /* not implemented */ |
12217 | #endif |
12218 | _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); |
12219 | } |
12220 | |
12221 | SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { |
12222 | if (!_sapp.clipboard.enabled) { |
12223 | return ""; |
12224 | } |
12225 | #if defined(_SAPP_MACOS) |
12226 | return _sapp_macos_get_clipboard_string(); |
12227 | #elif defined(_SAPP_EMSCRIPTEN) |
12228 | return _sapp.clipboard.buffer; |
12229 | #elif defined(_SAPP_WIN32) |
12230 | return _sapp_win32_get_clipboard_string(); |
12231 | #else |
12232 | /* not implemented */ |
12233 | return _sapp.clipboard.buffer; |
12234 | #endif |
12235 | } |
12236 | |
12237 | SOKOL_API_IMPL void sapp_set_window_title(const char* title) { |
12238 | SOKOL_ASSERT(title); |
12239 | _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); |
12240 | #if defined(_SAPP_MACOS) |
12241 | _sapp_macos_update_window_title(); |
12242 | #elif defined(_SAPP_WIN32) |
12243 | _sapp_win32_update_window_title(); |
12244 | #elif defined(_SAPP_LINUX) |
12245 | _sapp_x11_update_window_title(); |
12246 | #endif |
12247 | } |
12248 | |
12249 | SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) { |
12250 | SOKOL_ASSERT(desc); |
12251 | if (desc->sokol_default) { |
12252 | if (0 == _sapp.default_icon_pixels) { |
12253 | _sapp_setup_default_icon(); |
12254 | } |
12255 | SOKOL_ASSERT(0 != _sapp.default_icon_pixels); |
12256 | desc = &_sapp.default_icon_desc; |
12257 | } |
12258 | const int num_images = _sapp_icon_num_images(desc); |
12259 | if (num_images == 0) { |
12260 | return; |
12261 | } |
12262 | SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); |
12263 | if (!_sapp_validate_icon_desc(desc, num_images)) { |
12264 | return; |
12265 | } |
12266 | #if defined(_SAPP_MACOS) |
12267 | _sapp_macos_set_icon(desc, num_images); |
12268 | #elif defined(_SAPP_WIN32) |
12269 | _sapp_win32_set_icon(desc, num_images); |
12270 | #elif defined(_SAPP_LINUX) |
12271 | _sapp_x11_set_icon(desc, num_images); |
12272 | #elif defined(_SAPP_EMSCRIPTEN) |
12273 | _sapp_emsc_set_icon(desc, num_images); |
12274 | #endif |
12275 | } |
12276 | |
12277 | SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { |
12278 | SOKOL_ASSERT(_sapp.drop.enabled); |
12279 | return _sapp.drop.num_files; |
12280 | } |
12281 | |
12282 | SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { |
12283 | SOKOL_ASSERT(_sapp.drop.enabled); |
12284 | SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); |
12285 | SOKOL_ASSERT(_sapp.drop.buffer); |
12286 | if (!_sapp.drop.enabled) { |
12287 | return ""; |
12288 | } |
12289 | if ((index < 0) || (index >= _sapp.drop.max_files)) { |
12290 | return ""; |
12291 | } |
12292 | return (const char*) _sapp_dropped_file_path_ptr(index); |
12293 | } |
12294 | |
12295 | SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { |
12296 | SOKOL_ASSERT(_sapp.drop.enabled); |
12297 | SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); |
12298 | #if defined(_SAPP_EMSCRIPTEN) |
12299 | if (!_sapp.drop.enabled) { |
12300 | return 0; |
12301 | } |
12302 | return sapp_js_dropped_file_size(index); |
12303 | #else |
12304 | (void)index; |
12305 | return 0; |
12306 | #endif |
12307 | } |
12308 | |
12309 | SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { |
12310 | SOKOL_ASSERT(_sapp.drop.enabled); |
12311 | SOKOL_ASSERT(request); |
12312 | SOKOL_ASSERT(request->callback); |
12313 | SOKOL_ASSERT(request->buffer.ptr); |
12314 | SOKOL_ASSERT(request->buffer.size > 0); |
12315 | #if defined(_SAPP_EMSCRIPTEN) |
12316 | const int index = request->dropped_file_index; |
12317 | sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; |
12318 | if ((index < 0) || (index >= _sapp.drop.num_files)) { |
12319 | error_code = SAPP_HTML5_FETCH_ERROR_OTHER; |
12320 | } |
12321 | if (sapp_html5_get_dropped_file_size(index) > request->buffer.size) { |
12322 | error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; |
12323 | } |
12324 | if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { |
12325 | _sapp_emsc_invoke_fetch_cb(index, |
12326 | false, // success |
12327 | (int)error_code, |
12328 | request->callback, |
12329 | 0, // fetched_size |
12330 | (void*)request->buffer.ptr, |
12331 | request->buffer.size, |
12332 | request->user_data); |
12333 | } |
12334 | else { |
12335 | sapp_js_fetch_dropped_file(index, |
12336 | request->callback, |
12337 | (void*)request->buffer.ptr, |
12338 | request->buffer.size, |
12339 | request->user_data); |
12340 | } |
12341 | #else |
12342 | (void)request; |
12343 | #endif |
12344 | } |
12345 | |
12346 | SOKOL_API_IMPL const void* sapp_metal_get_device(void) { |
12347 | SOKOL_ASSERT(_sapp.valid); |
12348 | #if defined(SOKOL_METAL) |
12349 | #if defined(_SAPP_MACOS) |
12350 | const void* obj = (__bridge const void*) _sapp.macos.mtl_device; |
12351 | #else |
12352 | const void* obj = (__bridge const void*) _sapp.ios.mtl_device; |
12353 | #endif |
12354 | SOKOL_ASSERT(obj); |
12355 | return obj; |
12356 | #else |
12357 | return 0; |
12358 | #endif |
12359 | } |
12360 | |
12361 | SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { |
12362 | SOKOL_ASSERT(_sapp.valid); |
12363 | #if defined(SOKOL_METAL) |
12364 | #if defined(_SAPP_MACOS) |
12365 | const void* obj = (__bridge const void*) [_sapp.macos.view currentRenderPassDescriptor]; |
12366 | #else |
12367 | const void* obj = (__bridge const void*) [_sapp.ios.view currentRenderPassDescriptor]; |
12368 | #endif |
12369 | SOKOL_ASSERT(obj); |
12370 | return obj; |
12371 | #else |
12372 | return 0; |
12373 | #endif |
12374 | } |
12375 | |
12376 | SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { |
12377 | SOKOL_ASSERT(_sapp.valid); |
12378 | #if defined(SOKOL_METAL) |
12379 | #if defined(_SAPP_MACOS) |
12380 | const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; |
12381 | #else |
12382 | const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; |
12383 | #endif |
12384 | SOKOL_ASSERT(obj); |
12385 | return obj; |
12386 | #else |
12387 | return 0; |
12388 | #endif |
12389 | } |
12390 | |
12391 | SOKOL_API_IMPL const void* sapp_macos_get_window(void) { |
12392 | #if defined(_SAPP_MACOS) |
12393 | const void* obj = (__bridge const void*) _sapp.macos.window; |
12394 | SOKOL_ASSERT(obj); |
12395 | return obj; |
12396 | #else |
12397 | return 0; |
12398 | #endif |
12399 | } |
12400 | |
12401 | SOKOL_API_IMPL const void* sapp_ios_get_window(void) { |
12402 | #if defined(_SAPP_IOS) |
12403 | const void* obj = (__bridge const void*) _sapp.ios.window; |
12404 | SOKOL_ASSERT(obj); |
12405 | return obj; |
12406 | #else |
12407 | return 0; |
12408 | #endif |
12409 | } |
12410 | |
12411 | SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { |
12412 | SOKOL_ASSERT(_sapp.valid); |
12413 | #if defined(SOKOL_D3D11) |
12414 | return _sapp.d3d11.device; |
12415 | #else |
12416 | return 0; |
12417 | #endif |
12418 | } |
12419 | |
12420 | SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { |
12421 | SOKOL_ASSERT(_sapp.valid); |
12422 | #if defined(SOKOL_D3D11) |
12423 | return _sapp.d3d11.device_context; |
12424 | #else |
12425 | return 0; |
12426 | #endif |
12427 | } |
12428 | |
12429 | SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) { |
12430 | SOKOL_ASSERT(_sapp.valid); |
12431 | #if defined(SOKOL_D3D11) |
12432 | return _sapp.d3d11.swap_chain; |
12433 | #else |
12434 | return 0; |
12435 | #endif |
12436 | } |
12437 | |
12438 | SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { |
12439 | SOKOL_ASSERT(_sapp.valid); |
12440 | #if defined(SOKOL_D3D11) |
12441 | if (_sapp.d3d11.msaa_rtv) { |
12442 | return _sapp.d3d11.msaa_rtv; |
12443 | } |
12444 | else { |
12445 | return _sapp.d3d11.rtv; |
12446 | } |
12447 | #else |
12448 | return 0; |
12449 | #endif |
12450 | } |
12451 | |
12452 | SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { |
12453 | SOKOL_ASSERT(_sapp.valid); |
12454 | #if defined(SOKOL_D3D11) |
12455 | return _sapp.d3d11.dsv; |
12456 | #else |
12457 | return 0; |
12458 | #endif |
12459 | } |
12460 | |
12461 | SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { |
12462 | SOKOL_ASSERT(_sapp.valid); |
12463 | #if defined(_SAPP_WIN32) |
12464 | return _sapp.win32.hwnd; |
12465 | #else |
12466 | return 0; |
12467 | #endif |
12468 | } |
12469 | |
12470 | SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { |
12471 | SOKOL_ASSERT(_sapp.valid); |
12472 | #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) |
12473 | return (const void*) _sapp.emsc.wgpu.device; |
12474 | #else |
12475 | return 0; |
12476 | #endif |
12477 | } |
12478 | |
12479 | SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { |
12480 | SOKOL_ASSERT(_sapp.valid); |
12481 | #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) |
12482 | if (_sapp.sample_count > 1) { |
12483 | return (const void*) _sapp.emsc.wgpu.msaa_view; |
12484 | } |
12485 | else { |
12486 | return (const void*) _sapp.emsc.wgpu.swapchain_view; |
12487 | } |
12488 | #else |
12489 | return 0; |
12490 | #endif |
12491 | } |
12492 | |
12493 | SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { |
12494 | SOKOL_ASSERT(_sapp.valid); |
12495 | #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) |
12496 | if (_sapp.sample_count > 1) { |
12497 | return (const void*) _sapp.emsc.wgpu.swapchain_view; |
12498 | } |
12499 | else { |
12500 | return 0; |
12501 | } |
12502 | #else |
12503 | return 0; |
12504 | #endif |
12505 | } |
12506 | |
12507 | SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { |
12508 | SOKOL_ASSERT(_sapp.valid); |
12509 | #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) |
12510 | return (const void*) _sapp.emsc.wgpu.depth_stencil_view; |
12511 | #else |
12512 | return 0; |
12513 | #endif |
12514 | } |
12515 | |
12516 | SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { |
12517 | // NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity() |
12518 | // needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708) |
12519 | #if defined(_SAPP_ANDROID) |
12520 | return (void*)_sapp.android.activity; |
12521 | #else |
12522 | return 0; |
12523 | #endif |
12524 | } |
12525 | |
12526 | SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { |
12527 | _sapp.html5_ask_leave_site = ask; |
12528 | } |
12529 | |
12530 | #endif /* SOKOL_APP_IMPL */ |