forked from floooh/sokol
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsokol_app.h
7971 lines (7475 loc) · 299 KB
/
sokol_app.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef SOKOL_APP_INCLUDED
/*
sokol_app.h -- cross-platform application wrapper
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
In the same place define one of the following to select the 3D-API
which should be initialized by sokol_app.h:
#define SOKOL_GLCORE33
#define SOKOL_GLES2
#define SOKOL_GLES3
#define SOKOL_D3D11
#define SOKOL_METAL
#define SOKOL_WGPU
If sokol_app.h is used together with sokol_gfx.h, the selected 3D-APIs
backend match.
Optionally provide the following defines with your own implementations:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_LOG(msg) - your own logging function (default: puts(msg))
SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
SOKOL_ABORT() - called after an unrecoverable error (default: abort())
SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain
SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function
SOKOL_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_IMPL - public function implementation prefix (default: -)
SOKOL_CALLOC - your own calloc function (default: calloc(n, s))
SOKOL_FREE - your own free function (default: free(p))
Optionally define the following to force debug checks and validations
even in release mode:
SOKOL_DEBUG - by default this is defined if _DEBUG is defined
If sokol_app.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
Portions of the Windows and Linux GL initialization and event code have been
taken from GLFW (http://www.glfw.org/)
iOS onscreen keyboard support 'inspired' by libgdx.
If you use sokol_app.h together with sokol_gfx.h, include both headers
in the implementation source file, and include sokol_app.h before
sokol_gfx.h since sokol_app.h will also include the required 3D-API
headers.
On Windows, a minimal 'GL header' and function loader is integrated which
contains just enough of GL for sokol_gfx.h. If you want to use your own
GL header-generator/loader instead, define SOKOL_WIN32_NO_GL_LOADER
before including the implementation part of sokol_app.h.
For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
FEATURE OVERVIEW
================
sokol_app.h provides a minimalistic cross-platform API which
implements the 'application-wrapper' parts of a 3D application:
- a common application entry function
- creates a window and 3D-API context/device with a 'default framebuffer'
- makes the rendered frame visible
- provides keyboard-, mouse- and low-level touch-events
- platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi)
- 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2
FEATURE/PLATFORM MATRIX
=======================
| Windows | macOS | Linux | iOS | Android | Raspi | HTML5
--------------------+---------+-------+-------+-------+---------+-------+-------
gl 3.x | YES | YES | YES | --- | --- | --- | ---
gles2/webgl | --- | --- | --- | YES | YES | TODO | YES
gles3/webgl2 | --- | --- | --- | YES | YES | --- | YES
metal | --- | YES | --- | YES | --- | --- | ---
d3d11 | YES | --- | --- | --- | --- | --- | ---
KEY_DOWN | YES | YES | YES | SOME | TODO | TODO | YES
KEY_UP | YES | YES | YES | SOME | TODO | TODO | YES
CHAR | YES | YES | YES | YES | TODO | TODO | YES
MOUSE_DOWN | YES | YES | YES | --- | --- | TODO | YES
MOUSE_UP | YES | YES | YES | --- | --- | TODO | YES
MOUSE_SCROLL | YES | YES | YES | --- | --- | TODO | YES
MOUSE_MOVE | YES | YES | YES | --- | --- | TODO | YES
MOUSE_ENTER | YES | YES | YES | --- | --- | TODO | YES
MOUSE_LEAVE | YES | YES | YES | --- | --- | TODO | YES
TOUCHES_BEGAN | --- | --- | --- | YES | YES | --- | YES
TOUCHES_MOVED | --- | --- | --- | YES | YES | --- | YES
TOUCHES_ENDED | --- | --- | --- | YES | YES | --- | YES
TOUCHES_CANCELLED | --- | --- | --- | YES | YES | --- | YES
RESIZED | YES | YES | YES | YES | YES | --- | YES
ICONIFIED | YES | YES | YES | --- | --- | --- | ---
RESTORED | YES | YES | YES | --- | --- | --- | ---
SUSPENDED | --- | --- | --- | YES | YES | --- | TODO
RESUMED | --- | --- | --- | YES | YES | --- | TODO
QUIT_REQUESTED | YES | YES | YES | --- | --- | TODO | YES
UPDATE_CURSOR | YES | YES | TODO | --- | --- | --- | TODO
IME | TODO | TODO? | TODO | ??? | TODO | ??? | ???
key repeat flag | YES | YES | YES | --- | --- | TODO | YES
windowed | YES | YES | YES | --- | --- | TODO | YES
fullscreen | YES | YES | TODO | YES | YES | TODO | ---
pointer lock | TODO | TODO | TODO | --- | --- | TODO | TODO
screen keyboard | --- | --- | --- | YES | TODO | --- | YES
swap interval | YES | YES | YES | YES | TODO | TODO | YES
high-dpi | YES | YES | TODO | YES | YES | TODO | YES
clipboard | YES | YES | TODO | --- | --- | --- | YES
TODO
====
- Linux clipboard support
- sapp_consume_event() on non-web platforms?
STEP BY STEP
============
--- Add a sokol_main() function to your code which returns a sapp_desc structure
with initialization parameters and callback function pointers. This
function is called very early, usually at the start of the
platform's entry function (e.g. main or WinMain). You should do as
little as possible here, since the rest of your code might be called
from another thread (this depends on the platform):
sapp_desc sokol_main(int argc, char* argv[]) {
return (sapp_desc) {
.width = 640,
.height = 480,
.init_cb = my_init_func,
.frame_cb = my_frame_func,
.cleanup_cb = my_cleanup_func,
.event_cb = my_event_func,
...
};
}
There are many more setup parameters, but these are the most important.
For a complete list search for the sapp_desc structure declaration
below.
DO NOT call any sokol-app function from inside sokol_main(), since
sokol-app will not be initialized at this point.
The .width and .height parameters are the preferred size of the 3D
rendering canvas. The actual size may differ from this depending on
platform and other circumstances. Also the canvas size may change at
any time (for instance when the user resizes the application window,
or rotates the mobile device).
All provided function callbacks will be called from the same thread,
but this may be different from the thread where sokol_main() was called.
.init_cb (void (*)(void))
This function is called once after the application window,
3D rendering context and swap chain have been created. The
function takes no arguments and has no return value.
.frame_cb (void (*)(void))
This is the per-frame callback, which is usually called 60
times per second. This is where your application would update
most of its state and perform all rendering.
.cleanup_cb (void (*)(void))
The cleanup callback is called once right before the application
quits.
.event_cb (void (*)(const sapp_event* event))
The event callback is mainly for input handling, but in the
future may also be used to communicate other types of events
to the application. Keep the event_cb struct member zero-initialized
if your application doesn't require event handling.
.fail_cb (void (*)(const char* msg))
The fail callback is called when a fatal error is encountered
during start which doesn't allow the program to continue.
Providing a callback here gives you a chance to show an error message
to the user. The default behaviour is SOKOL_LOG(msg)
As you can see, those 'standard callbacks' don't have a user_data
argument, so any data that needs to be preserved between callbacks
must live in global variables. If you're allergic to global variables
or cannot use them for other reasons, an alternative set of callbacks
can be defined in sapp_desc, together with a user_data pointer:
.user_data (void*)
The user-data argument for the callbacks below
.init_userdata_cb (void (*)(void* user_data))
.frame_userdata_cb (void (*)(void* user_data))
.cleanup_userdata_cb (void (*)(void* user_data))
.event_cb (void(*)(const sapp_event* event, void* user_data))
.fail_cb (void(*)(const char* msg, void* user_data))
These are the user-data versions of the callback functions. You
can mix those with the standard callbacks that don't have the
user_data argument.
The function sapp_userdata() can be used to query the user_data
pointer provided in the sapp_desc struct.
You can call sapp_query_desc() to get a copy of the
original sapp_desc structure.
NOTE that there's also an alternative compile mode where sokol_app.h
doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
--- Implement the initialization callback function (init_cb), this is called
once after the rendering surface, 3D API and swap chain have been
initialized by sokol_app. All sokol-app functions can be called
from inside the initialization callback, the most useful functions
at this point are:
int sapp_width(void)
Returns the current width of the default framebuffer, this may change
from one frame to the next.
int sapp_height(void)
Likewise, returns the current height of the default framebuffer.
int sapp_color_format(void)
int sapp_depth_format(void)
The color and depth-stencil pixelformats of the default framebuffer,
as int values which are compatible with sokol-gfx's
sg_pixel_format enum, so they can be plugged directly in places
where sg_pixel_format is expected:
23 == SG_PIXELFORMAT_RGBA8
27 == SG_PIXELFORMAT_BGRA8
41 == SG_PIXELFORMAT_DEPTH
42 == SG_PIXELFORMAT_DEPTH_STENCIL
int sapp_sample_count(void)
Return the MSAA sample count of the default framebuffer.
bool sapp_gles2(void)
Returns true if a GLES2 or WebGL context has been created. This
is useful when a GLES3/WebGL2 context was requested but is not
available so that sokol_app.h had to fallback to GLES2/WebGL.
const void* sapp_metal_get_device(void)
const void* sapp_metal_get_renderpass_descriptor(void)
const void* sapp_metal_get_drawable(void)
If the Metal backend has been selected, these functions return pointers
to various Metal API objects required for rendering, otherwise
they return a null pointer. These void pointers are actually
Objective-C ids converted with an ARC __bridge cast so that
they ids can be tunnel through C code. Also note that the returned
pointers to the renderpass-descriptor and drawable may change from one
frame to the next, only the Metal device object is guaranteed to
stay the same.
const void* sapp_macos_get_window(void)
On macOS, get the NSWindow object pointer, otherwise a null pointer.
Before being used as Objective-C object, the void* must be converted
back with an ARC __bridge cast.
const void* sapp_ios_get_window(void)
On iOS, get the UIWindow object pointer, otherwise a null pointer.
Before being used as Objective-C object, the void* must be converted
back with an ARC __bridge cast.
const void* sapp_win32_get_hwnd(void)
On Windows, get the window's HWND, otherwise a null pointer. The
HWND has been cast to a void pointer in order to be tunneled
through code which doesn't include Windows.h.
const void* sapp_d3d11_get_device(void)
const void* sapp_d3d11_get_device_context(void)
const void* sapp_d3d11_get_render_target_view(void)
const void* sapp_d3d11_get_depth_stencil_view(void)
Similar to the sapp_metal_* functions, the sapp_d3d11_* functions
return pointers to D3D11 API objects required for rendering,
only if the D3D11 backend has been selected. Otherwise they
return a null pointer. Note that the returned pointers to the
render-target-view and depth-stencil-view may change from one
frame to the next!
const void* sapp_wgpu_get_device(void)
const void* sapp_wgpu_get_render_view(void)
const void* sapp_wgpu_get_resolve_view(void)
const void* sapp_wgpu_get_depth_stencil_view(void)
These are the WebGPU-specific functions to get the WebGPU
objects and values required for rendering. If sokol_app.h
is not compiled with SOKOL_WGPU, these functions return null.
const void* sapp_android_get_native_activity(void);
On Android, get the native activity ANativeActivity pointer, otherwise
a null pointer.
--- Implement the frame-callback function, this function will be called
on the same thread as the init callback, but might be on a different
thread than the sokol_main() function. Note that the size of
the rendering framebuffer might have changed since the frame callback
was called last. Call the functions sapp_width() and sapp_height()
each frame to get the current size.
--- Optionally implement the event-callback to handle input events.
sokol-app provides the following type of input events:
- a 'virtual key' was pressed down or released
- a single text character was entered (provided as UTF-32 code point)
- a mouse button was pressed down or released (left, right, middle)
- mouse-wheel or 2D scrolling events
- the mouse was moved
- the mouse has entered or left the application window boundaries
- low-level, portable multi-touch events (began, moved, ended, cancelled)
- the application window was resized, iconified or restored
- the application was suspended or restored (on mobile platforms)
- the user or application code has asked to quit the application
- a string was pasted to the system clipboard
To explicitly 'consume' an event and prevent that the event is
forwarded for further handling to the operating system, call
sapp_consume_event() from inside the event handler (NOTE that
this behaviour is currently only implemented for some HTML5
events, support for other platforms and event types will
be added as needed, please open a github ticket and/or provide
a PR if needed).
NOTE: Do *not* call any 3D API functions in the event callback
function, since the 3D API context may not be active when the
event callback is called (it may work on some platforms and
3D APIs, but not others, and the exact behaviour may change
between sokol-app versions).
--- Implement the cleanup-callback function, this is called once
after the user quits the application (see the section
"APPLICATION QUIT" for detailed information on quitting
behaviour, and how to intercept a pending quit (for instance to show a
"Really Quit?" dialog box). Note that the cleanup-callback isn't
called on the web and mobile platforms.
CLIPBOARD SUPPORT
=================
Applications can send and receive UTF-8 encoded text data from and to the
system clipboard. By default, clipboard support is disabled and
must be enabled at startup via the following sapp_desc struct
members:
sapp_desc.enable_clipboard - set to true to enable clipboard support
sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes
Enabling the clipboard will dynamically allocate a clipboard buffer
for UTF-8 encoded text data of the requested size in bytes, the default
size if 8 KBytes. Strings that don't fit into the clipboard buffer
(including the terminating zero) will be silently clipped, so it's
important that you provide a big enough clipboard size for your
use case.
To send data to the clipboard, call sapp_set_clipboard_string() with
a pointer to an UTF-8 encoded, null-terminated C-string.
NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be
called from inside a 'short-lived event handler', and there are a few
other HTML5-specific caveats to workaround. You'll basically have to
tinker until it works in all browsers :/ (maybe the situation will
improve when all browsers agree on and implement the new
HTML5 navigator.clipboard API).
To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED
event in your event handler function, and then call sapp_get_clipboard_string()
to obtain the updated UTF-8 encoded text.
NOTE that behaviour of sapp_get_clipboard_string() is slightly different
depending on platform:
- on the HTML5 platform, the internal clipboard buffer will only be updated
right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent,
and sapp_get_clipboard_string() will simply return the current content
of the clipboard buffer
- on 'native' platforms, the call to sapp_get_clipboard_string() will
update the internal clipboard buffer with the most recent data
from the system clipboard
Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event,
and then call sapp_get_clipboard_string() right in the event handler.
The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app
as follows:
- on macOS: when the Cmd+V key is pressed down
- on HTML5: when the browser sends a 'paste' event to the global 'window' object
- on all other platforms: when the Ctrl+V key is pressed down
HIGH-DPI RENDERING
==================
You can set the sapp_desc.high_dpi flag during initialization to request
a full-resolution framebuffer on HighDPI displays. The default behaviour
is sapp_desc.high_dpi=false, this means that the application will
render to a lower-resolution framebuffer on HighDPI displays and the
rendered content will be upscaled by the window system composer.
In a HighDPI scenario, you still request the same window size during
sokol_main(), but the framebuffer sizes returned by sapp_width()
and sapp_height() will be scaled up according to the DPI scaling
ratio. You can also get a DPI scaling factor with the function
sapp_dpi_scale().
Here's an example on a Mac with Retina display:
sapp_desc sokol_main() {
return (sapp_desc) {
.width = 640,
.height = 480,
.high_dpi = true,
...
};
}
The functions sapp_width(), sapp_height() and sapp_dpi_scale() will
return the following values:
sapp_width -> 1280
sapp_height -> 960
sapp_dpi_scale -> 2.0
If the high_dpi flag is false, or you're not running on a Retina display,
the values would be:
sapp_width -> 640
sapp_height -> 480
sapp_dpi_scale -> 1.0
APPLICATION QUIT
================
Without special quit handling, a sokol_app.h application will quit
'gracefully' when the user clicks the window close-button unless a
platform's application model prevents this (e.g. on web or mobile).
'Graceful exit' means that the application-provided cleanup callback will
be called before the application quits.
On native desktop platforms sokol_app.h provides more control over the
application-quit-process. It's possible to initiate a 'programmatic quit'
from the application code, and a quit initiated by the application user can
be intercepted (for instance to show a custom dialog box).
This 'programmatic quit protocol' is implemented trough 3 functions
and 1 event:
- sapp_quit(): This function simply quits the application without
giving the user a chance to intervene. Usually this might
be called when the user clicks the 'Ok' button in a 'Really Quit?'
dialog box
- sapp_request_quit(): Calling sapp_request_quit() will send the
event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler
callback, giving the user code a chance to intervene and cancel the
pending quit process (for instance to show a 'Really Quit?' dialog
box). If the event handler callback does nothing, the application
will be quit as usual. To prevent this, call the function
sapp_cancel_quit() from inside the event handler.
- sapp_cancel_quit(): Cancels a pending quit request, either initiated
by the user clicking the window close button, or programmatically
by calling sapp_request_quit(). The only place where calling this
function makes sense is from inside the event handler callback when
the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received.
- SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user
clicks the window's close button or application code calls the
sapp_request_quit() function. The event handler callback code can handle
this event by calling sapp_cancel_quit() to cancel the quit.
If the event is ignored, the application will quit as usual.
On the web platform, the quit behaviour differs from native platforms,
because of web-specific restrictions:
A `programmatic quit` initiated by calling sapp_quit() or
sapp_request_quit() will work as described above: the cleanup callback is
called, platform-specific cleanup is performed (on the web
this means that JS event handlers are unregisters), and then
the request-animation-loop will be exited. However that's all. The
web page itself will continue to exist (e.g. it's not possible to
programmatically close the browser tab).
On the web it's also not possible to run custom code when the user
closes a brower tab, so it's not possible to prevent this with a
fancy custom dialog box.
Instead the standard "Leave Site?" dialog box can be activated (or
deactivated) with the following function:
sapp_html5_ask_leave_site(bool ask);
The initial state of the associated internal flag can be provided
at startup via sapp_desc.html5_ask_leave_site.
This feature should only be used sparingly in critical situations - for
instance when the user would loose data - since popping up modal dialog
boxes is considered quite rude in the web world. Note that there's no way
to customize the content of this dialog box or run any code as a result
of the user's decision. Also note that the user must have interacted with
the site before the dialog box will appear. These are all security measures
to prevent fishing.
The Dear ImGui HighDPI sample contains example code of how to
implement a 'Really Quit?' dialog box with Dear ImGui (native desktop
platforms only), and for showing the hardwired "Leave Site?" dialog box
when running on the web platform:
https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html
FULLSCREEN
==========
If the sapp_desc.fullscreen flag is true, sokol-app will try to create
a fullscreen window on platforms with a 'proper' window system
(mobile devices will always use fullscreen). The implementation details
depend on the target platform, in general sokol-app will use a
'soft approach' which doesn't interfere too much with the platform's
window system (for instance borderless fullscreen window instead of
a 'real' fullscreen mode). Such details might change over time
as sokol-app is adapted for different needs.
The most important effect of fullscreen mode to keep in mind is that
the requested canvas width and height will be ignored for the initial
window size, calling sapp_width() and sapp_height() will instead return
the resolution of the fullscreen canvas (however the provided size
might still be used for the non-fullscreen window, in case the user can
switch back from fullscreen- to windowed-mode).
ONSCREEN KEYBOARD
=================
On some platforms which don't provide a physical keyboard, sokol-app
can display the platform's integrated onscreen keyboard for text
input. To request that the onscreen keyboard is shown, call
sapp_show_keyboard(true);
Likewise, to hide the keyboard call:
sapp_show_keyboard(false);
Note that on the web platform, the keyboard can only be shown from
inside an input handler. On such platforms, sapp_show_keyboard()
will only work as expected when it is called from inside the
sokol-app event callback function. When called from other places,
an internal flag will be set, and the onscreen keyboard will be
called at the next 'legal' opportunity (when the next input event
is handled).
OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
======================================================
In its default configuration, sokol_app.h "hijacks" the platform's
standard main() function. This was done because different platforms
have different main functions which are not compatible with
C's main() (for instance WinMain on Windows has completely different
arguments). However, this "main hijacking" posed a problem for
usage scenarios like integrating sokol_app.h with other languages than
C or C++, so an alternative SOKOL_NO_ENTRY mode has been added
in which the user code provides the platform's main function:
- define SOKOL_NO_ENTRY before including the sokol_app.h implementation
- do *not* provide a sokol_main() function
- instead provide the standard main() function of the platform
- from the main function, call the function ```sapp_run()``` which
takes a pointer to an ```sapp_desc``` structure.
- ```sapp_run()``` takes over control and calls the provided init-, frame-,
shutdown- and event-callbacks just like in the default model, it
will only return when the application quits (or not at all on some
platforms, like emscripten)
NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
TEMP NOTE DUMP
==============
- onscreen keyboard support on Android requires Java :(, should we even bother?
- sapp_desc needs a bool whether to initialize depth-stencil surface
- GL context initialization needs more control (at least what GL version to initialize)
- application icon
- mouse pointer visibility(?)
- the UPDATE_CURSOR event currently behaves differently between Win32 and OSX
(Win32 sends the event each frame when the mouse moves and is inside the window
client area, OSX sends it only once when the mouse enters the client area)
- the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy
at the latest but should do it earlier, in onStop, as an app is "killable" after onStop
on Android Honeycomb and later (it can't be done at the moment as the app may be started
again after onStop and the sokol lifecycle does not yet handle context teardown/bringup)
FIXME: ERROR HANDLING (this will need an error callback function)
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_APP_INCLUDED (1)
#include <stdint.h>
#include <stdbool.h>
#ifndef SOKOL_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMPL)
#define SOKOL_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_API_DECL __declspec(dllimport)
#else
#define SOKOL_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
SAPP_MAX_TOUCHPOINTS = 8,
SAPP_MAX_MOUSEBUTTONS = 3,
SAPP_MAX_KEYCODES = 512,
};
typedef enum sapp_event_type {
SAPP_EVENTTYPE_INVALID,
SAPP_EVENTTYPE_KEY_DOWN,
SAPP_EVENTTYPE_KEY_UP,
SAPP_EVENTTYPE_CHAR,
SAPP_EVENTTYPE_MOUSE_DOWN,
SAPP_EVENTTYPE_MOUSE_UP,
SAPP_EVENTTYPE_MOUSE_SCROLL,
SAPP_EVENTTYPE_MOUSE_MOVE,
SAPP_EVENTTYPE_MOUSE_ENTER,
SAPP_EVENTTYPE_MOUSE_LEAVE,
SAPP_EVENTTYPE_TOUCHES_BEGAN,
SAPP_EVENTTYPE_TOUCHES_MOVED,
SAPP_EVENTTYPE_TOUCHES_ENDED,
SAPP_EVENTTYPE_TOUCHES_CANCELLED,
SAPP_EVENTTYPE_RESIZED,
SAPP_EVENTTYPE_ICONIFIED,
SAPP_EVENTTYPE_RESTORED,
SAPP_EVENTTYPE_SUSPENDED,
SAPP_EVENTTYPE_RESUMED,
SAPP_EVENTTYPE_UPDATE_CURSOR,
SAPP_EVENTTYPE_QUIT_REQUESTED,
SAPP_EVENTTYPE_CLIPBOARD_PASTED,
_SAPP_EVENTTYPE_NUM,
_SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF
} sapp_event_type;
/* key codes are the same names and values as GLFW */
typedef enum sapp_keycode {
SAPP_KEYCODE_INVALID = 0,
SAPP_KEYCODE_SPACE = 32,
SAPP_KEYCODE_APOSTROPHE = 39, /* ' */
SAPP_KEYCODE_COMMA = 44, /* , */
SAPP_KEYCODE_MINUS = 45, /* - */
SAPP_KEYCODE_PERIOD = 46, /* . */
SAPP_KEYCODE_SLASH = 47, /* / */
SAPP_KEYCODE_0 = 48,
SAPP_KEYCODE_1 = 49,
SAPP_KEYCODE_2 = 50,
SAPP_KEYCODE_3 = 51,
SAPP_KEYCODE_4 = 52,
SAPP_KEYCODE_5 = 53,
SAPP_KEYCODE_6 = 54,
SAPP_KEYCODE_7 = 55,
SAPP_KEYCODE_8 = 56,
SAPP_KEYCODE_9 = 57,
SAPP_KEYCODE_SEMICOLON = 59, /* ; */
SAPP_KEYCODE_EQUAL = 61, /* = */
SAPP_KEYCODE_A = 65,
SAPP_KEYCODE_B = 66,
SAPP_KEYCODE_C = 67,
SAPP_KEYCODE_D = 68,
SAPP_KEYCODE_E = 69,
SAPP_KEYCODE_F = 70,
SAPP_KEYCODE_G = 71,
SAPP_KEYCODE_H = 72,
SAPP_KEYCODE_I = 73,
SAPP_KEYCODE_J = 74,
SAPP_KEYCODE_K = 75,
SAPP_KEYCODE_L = 76,
SAPP_KEYCODE_M = 77,
SAPP_KEYCODE_N = 78,
SAPP_KEYCODE_O = 79,
SAPP_KEYCODE_P = 80,
SAPP_KEYCODE_Q = 81,
SAPP_KEYCODE_R = 82,
SAPP_KEYCODE_S = 83,
SAPP_KEYCODE_T = 84,
SAPP_KEYCODE_U = 85,
SAPP_KEYCODE_V = 86,
SAPP_KEYCODE_W = 87,
SAPP_KEYCODE_X = 88,
SAPP_KEYCODE_Y = 89,
SAPP_KEYCODE_Z = 90,
SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */
SAPP_KEYCODE_BACKSLASH = 92, /* \ */
SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */
SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */
SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */
SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */
SAPP_KEYCODE_ESCAPE = 256,
SAPP_KEYCODE_ENTER = 257,
SAPP_KEYCODE_TAB = 258,
SAPP_KEYCODE_BACKSPACE = 259,
SAPP_KEYCODE_INSERT = 260,
SAPP_KEYCODE_DELETE = 261,
SAPP_KEYCODE_RIGHT = 262,
SAPP_KEYCODE_LEFT = 263,
SAPP_KEYCODE_DOWN = 264,
SAPP_KEYCODE_UP = 265,
SAPP_KEYCODE_PAGE_UP = 266,
SAPP_KEYCODE_PAGE_DOWN = 267,
SAPP_KEYCODE_HOME = 268,
SAPP_KEYCODE_END = 269,
SAPP_KEYCODE_CAPS_LOCK = 280,
SAPP_KEYCODE_SCROLL_LOCK = 281,
SAPP_KEYCODE_NUM_LOCK = 282,
SAPP_KEYCODE_PRINT_SCREEN = 283,
SAPP_KEYCODE_PAUSE = 284,
SAPP_KEYCODE_F1 = 290,
SAPP_KEYCODE_F2 = 291,
SAPP_KEYCODE_F3 = 292,
SAPP_KEYCODE_F4 = 293,
SAPP_KEYCODE_F5 = 294,
SAPP_KEYCODE_F6 = 295,
SAPP_KEYCODE_F7 = 296,
SAPP_KEYCODE_F8 = 297,
SAPP_KEYCODE_F9 = 298,
SAPP_KEYCODE_F10 = 299,
SAPP_KEYCODE_F11 = 300,
SAPP_KEYCODE_F12 = 301,
SAPP_KEYCODE_F13 = 302,
SAPP_KEYCODE_F14 = 303,
SAPP_KEYCODE_F15 = 304,
SAPP_KEYCODE_F16 = 305,
SAPP_KEYCODE_F17 = 306,
SAPP_KEYCODE_F18 = 307,
SAPP_KEYCODE_F19 = 308,
SAPP_KEYCODE_F20 = 309,
SAPP_KEYCODE_F21 = 310,
SAPP_KEYCODE_F22 = 311,
SAPP_KEYCODE_F23 = 312,
SAPP_KEYCODE_F24 = 313,
SAPP_KEYCODE_F25 = 314,
SAPP_KEYCODE_KP_0 = 320,
SAPP_KEYCODE_KP_1 = 321,
SAPP_KEYCODE_KP_2 = 322,
SAPP_KEYCODE_KP_3 = 323,
SAPP_KEYCODE_KP_4 = 324,
SAPP_KEYCODE_KP_5 = 325,
SAPP_KEYCODE_KP_6 = 326,
SAPP_KEYCODE_KP_7 = 327,
SAPP_KEYCODE_KP_8 = 328,
SAPP_KEYCODE_KP_9 = 329,
SAPP_KEYCODE_KP_DECIMAL = 330,
SAPP_KEYCODE_KP_DIVIDE = 331,
SAPP_KEYCODE_KP_MULTIPLY = 332,
SAPP_KEYCODE_KP_SUBTRACT = 333,
SAPP_KEYCODE_KP_ADD = 334,
SAPP_KEYCODE_KP_ENTER = 335,
SAPP_KEYCODE_KP_EQUAL = 336,
SAPP_KEYCODE_LEFT_SHIFT = 340,
SAPP_KEYCODE_LEFT_CONTROL = 341,
SAPP_KEYCODE_LEFT_ALT = 342,
SAPP_KEYCODE_LEFT_SUPER = 343,
SAPP_KEYCODE_RIGHT_SHIFT = 344,
SAPP_KEYCODE_RIGHT_CONTROL = 345,
SAPP_KEYCODE_RIGHT_ALT = 346,
SAPP_KEYCODE_RIGHT_SUPER = 347,
SAPP_KEYCODE_MENU = 348,
} sapp_keycode;
typedef struct sapp_touchpoint {
uintptr_t identifier;
float pos_x;
float pos_y;
bool changed;
} sapp_touchpoint;
typedef enum sapp_mousebutton {
SAPP_MOUSEBUTTON_INVALID = -1,
SAPP_MOUSEBUTTON_LEFT = 0,
SAPP_MOUSEBUTTON_RIGHT = 1,
SAPP_MOUSEBUTTON_MIDDLE = 2,
} sapp_mousebutton;
enum {
SAPP_MODIFIER_SHIFT = (1<<0),
SAPP_MODIFIER_CTRL = (1<<1),
SAPP_MODIFIER_ALT = (1<<2),
SAPP_MODIFIER_SUPER = (1<<3)
};
typedef struct sapp_event {
uint64_t frame_count;
sapp_event_type type;
sapp_keycode key_code;
uint32_t char_code;
bool key_repeat;
uint32_t modifiers;
sapp_mousebutton mouse_button;
float mouse_x;
float mouse_y;
float scroll_x;
float scroll_y;
int num_touches;
sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS];
int window_width;
int window_height;
int framebuffer_width;
int framebuffer_height;
} sapp_event;
typedef struct sapp_desc {
void (*init_cb)(void); /* these are the user-provided callbacks without user data */
void (*frame_cb)(void);
void (*cleanup_cb)(void);
void (*event_cb)(const sapp_event*);
void (*fail_cb)(const char*);
void* user_data; /* these are the user-provided callbacks with user data */
void (*init_userdata_cb)(void*);
void (*frame_userdata_cb)(void*);
void (*cleanup_userdata_cb)(void*);
void (*event_userdata_cb)(const sapp_event*, void*);
void (*fail_userdata_cb)(const char*, void*);
int width; /* the preferred width of the window / canvas */
int height; /* the preferred height of the window / canvas */
int sample_count; /* MSAA sample count */
int swap_interval; /* the preferred swap interval (ignored on some platforms) */
bool high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */
bool fullscreen; /* whether the window should be created in fullscreen mode */
bool alpha; /* whether the framebuffer should have an alpha channel (ignored on some platforms) */
const char* window_title; /* the window title as UTF-8 encoded string */
bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */
bool enable_clipboard; /* enable clipboard access, default is false */
int clipboard_size; /* max size of clipboard content in bytes */
const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */
bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */
bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */
bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */
bool html5_ask_leave_site; /* initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) */
bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */
bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */
} sapp_desc;
/* user-provided functions */
extern sapp_desc sokol_main(int argc, char* argv[]);
/* returns true after sokol-app has been initialized */
SOKOL_API_DECL bool sapp_isvalid(void);
/* returns the current framebuffer width in pixels */
SOKOL_API_DECL int sapp_width(void);
/* returns the current framebuffer height in pixels */
SOKOL_API_DECL int sapp_height(void);
/* get default framebuffer color pixel format */
SOKOL_API_DECL int sapp_color_format(void);
/* get default framebuffer depth pixel format */
SOKOL_API_DECL int sapp_depth_format(void);
/* get default framebuffer sample count */
SOKOL_API_DECL int sapp_sample_count(void);
/* returns true when high_dpi was requested and actually running in a high-dpi scenario */
SOKOL_API_DECL bool sapp_high_dpi(void);
/* returns the dpi scaling factor (window pixels to framebuffer pixels) */
SOKOL_API_DECL float sapp_dpi_scale(void);
/* show or hide the mobile device onscreen keyboard */
SOKOL_API_DECL void sapp_show_keyboard(bool visible);
/* return true if the mobile device onscreen keyboard is currently shown */
SOKOL_API_DECL bool sapp_keyboard_shown(void);
/* show or hide the mouse cursor */
SOKOL_API_DECL void sapp_show_mouse(bool visible);
/* show or hide the mouse cursor */
SOKOL_API_DECL bool sapp_mouse_shown();
/* return the userdata pointer optionally provided in sapp_desc */
SOKOL_API_DECL void* sapp_userdata(void);
/* return a copy of the sapp_desc structure */
SOKOL_API_DECL sapp_desc sapp_query_desc(void);
/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */
SOKOL_API_DECL void sapp_request_quit(void);
/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */
SOKOL_API_DECL void sapp_cancel_quit(void);
/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */
SOKOL_API_DECL void sapp_quit(void);
/* call from inside event callback to consume the current event (don't forward to platform) */
SOKOL_API_DECL void sapp_consume_event(void);
/* get the current frame counter (for comparison with sapp_event.frame_count) */
SOKOL_API_DECL uint64_t sapp_frame_count(void);
/* write string into clipboard */
SOKOL_API_DECL void sapp_set_clipboard_string(const char* str);
/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */
SOKOL_API_DECL const char* sapp_get_clipboard_string(void);
/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */
SOKOL_API_DECL int sapp_run(const sapp_desc* desc);
/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */
SOKOL_API_DECL bool sapp_gles2(void);
/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */
SOKOL_API_DECL void sapp_html5_ask_leave_site(bool ask);
/* Metal: get ARC-bridged pointer to Metal device object */
SOKOL_API_DECL const void* sapp_metal_get_device(void);
/* Metal: get ARC-bridged pointer to this frame's renderpass descriptor */
SOKOL_API_DECL const void* sapp_metal_get_renderpass_descriptor(void);
/* Metal: get ARC-bridged pointer to current drawable */
SOKOL_API_DECL const void* sapp_metal_get_drawable(void);
/* macOS: get ARC-bridged pointer to macOS NSWindow */
SOKOL_API_DECL const void* sapp_macos_get_window(void);
/* iOS: get ARC-bridged pointer to iOS UIWindow */
SOKOL_API_DECL const void* sapp_ios_get_window(void);
/* D3D11: get pointer to ID3D11Device object */
SOKOL_API_DECL const void* sapp_d3d11_get_device(void);
/* D3D11: get pointer to ID3D11DeviceContext object */
SOKOL_API_DECL const void* sapp_d3d11_get_device_context(void);
/* D3D11: get pointer to ID3D11RenderTargetView object */
SOKOL_API_DECL const void* sapp_d3d11_get_render_target_view(void);
/* D3D11: get pointer to ID3D11DepthStencilView */
SOKOL_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void);
/* Win32: get the HWND window handle */
SOKOL_API_DECL const void* sapp_win32_get_hwnd(void);
/* WebGPU: get WGPUDevice handle */
SOKOL_API_DECL const void* sapp_wgpu_get_device(void);
/* WebGPU: get swapchain's WGPUTextureView handle for rendering */
SOKOL_API_DECL const void* sapp_wgpu_get_render_view(void);
/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */
SOKOL_API_DECL const void* sapp_wgpu_get_resolve_view(void);
/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */
SOKOL_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void);
/* Android: get native activity handle */
SOKOL_API_DECL const void* sapp_android_get_native_activity(void);
#ifdef __cplusplus
} /* extern "C" */
/* reference-based equivalents for C++ */
inline int sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#endif
#endif // SOKOL_APP_INCLUDED
/*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_IMPL
#define SOKOL_APP_IMPL_INCLUDED (1)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
#pragma warning(disable:4115) /* named type definition in parentheses */
#pragma warning(disable:4054) /* 'type cast': from function pointer */
#pragma warning(disable:4055) /* 'type cast': from data pointer */
#pragma warning(disable:4505) /* unreferenced local function has been removed */
#pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
#endif
#include <string.h> /* memset */
/* check if the config defines are alright */
#if defined(__APPLE__)
#if !__has_feature(objc_arc)
#error "sokol_app.h requires ARC (Automatic Reference Counting) on MacOS and iOS"
#endif
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
/* iOS */
#if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3)
#error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3")
#endif
#else
/* MacOS */
#if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33)
#error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33")
#endif
#endif
#elif defined(__EMSCRIPTEN__)
/* emscripten (asm.js or wasm) */
#if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU)
#error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU")
#endif
#elif defined(_WIN32)
/* Windows (D3D11 or GL) */
#if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33)
#error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33")
#endif
#elif defined(__ANDROID__)
/* Android */
#if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2)
#error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2")
#endif
#if defined(SOKOL_NO_ENTRY)
#error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android")