7567 lines
284 KiB
Objective-C
Executable File
7567 lines
284 KiB
Objective-C
Executable File
#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.
|
|
|
|
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 (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 | ---
|
|
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
|
|
- document sapp_consume_event()
|
|
- 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.
|
|
|
|
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_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
|
|
|
|
--- 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 exist
|
|
'gracefully' when the user clicks the window close-button. 'Graceful
|
|
exit' means that the application-provided cleanup callback will be
|
|
called.
|
|
|
|
This 'graceful exit' is only supported on native desktop platforms, on
|
|
the web and mobile platforms an application may be terminated at any time
|
|
by the user or browser/OS runtime environment without a chance to run
|
|
custom shutdown code.
|
|
|
|
On the web platform, you can call the following function to let the
|
|
browser open a standard popup dialog before the user wants to leave a site:
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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);
|
|
/* 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);
|
|
/* intiate 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);
|
|
|
|
/* Android: get native activity handle */
|
|
SOKOL_API_DECL const void* sapp_android_get_native_activity(void);
|
|
|
|
#ifdef __cplusplus
|
|
} /* extern "C" */
|
|
#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)
|
|
#error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_GLES2")
|
|
#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")
|
|
#endif
|
|
#elif defined(__linux__) || defined(__unix__)
|
|
/* Linux */
|
|
#if !defined(SOKOL_GLCORE33)
|
|
#error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33")
|
|
#endif
|
|
#else
|
|
#error "sokol_app.h: Unknown platform"
|
|
#endif
|
|
|
|
#ifndef SOKOL_API_IMPL
|
|
#define SOKOL_API_IMPL
|
|
#endif
|
|
#ifndef SOKOL_DEBUG
|
|
#ifndef NDEBUG
|
|
#define SOKOL_DEBUG (1)
|
|
#endif
|
|
#endif
|
|
#ifndef SOKOL_ASSERT
|
|
#include <assert.h>
|
|
#define SOKOL_ASSERT(c) assert(c)
|
|
#endif
|
|
#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE)
|
|
#include <stdlib.h>
|
|
#endif
|
|
#if !defined(SOKOL_CALLOC)
|
|
#define SOKOL_CALLOC(n,s) calloc(n,s)
|
|
#endif
|
|
#if !defined(SOKOL_FREE)
|
|
#define SOKOL_FREE(p) free(p)
|
|
#endif
|
|
#ifndef SOKOL_LOG
|
|
#ifdef SOKOL_DEBUG
|
|
#if defined(__ANDROID__)
|
|
#include <android/log.h>
|
|
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); }
|
|
#else
|
|
#include <stdio.h>
|
|
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
|
|
#endif
|
|
#else
|
|
#define SOKOL_LOG(s)
|
|
#endif
|
|
#endif
|
|
#ifndef SOKOL_ABORT
|
|
#include <stdlib.h>
|
|
#define SOKOL_ABORT() abort()
|
|
#endif
|
|
#ifndef _SOKOL_PRIVATE
|
|
#if defined(__GNUC__)
|
|
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
|
#else
|
|
#define _SOKOL_PRIVATE static
|
|
#endif
|
|
#endif
|
|
#ifndef _SOKOL_UNUSED
|
|
#define _SOKOL_UNUSED(x) (void)(x)
|
|
#endif
|
|
|
|
/* helper macros */
|
|
#define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
|
|
#define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
|
|
|
|
enum {
|
|
_SAPP_MAX_TITLE_LENGTH = 128,
|
|
};
|
|
|
|
typedef struct {
|
|
bool valid;
|
|
int window_width;
|
|
int window_height;
|
|
int framebuffer_width;
|
|
int framebuffer_height;
|
|
int sample_count;
|
|
int swap_interval;
|
|
float dpi_scale;
|
|
bool gles2_fallback;
|
|
bool first_frame;
|
|
bool init_called;
|
|
bool cleanup_called;
|
|
bool quit_requested;
|
|
bool quit_ordered;
|
|
bool event_consumed;
|
|
const char* html5_canvas_name;
|
|
bool html5_ask_leave_site;
|
|
char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */
|
|
wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */
|
|
uint64_t frame_count;
|
|
float mouse_x;
|
|
float mouse_y;
|
|
bool win32_mouse_tracked;
|
|
bool onscreen_keyboard_shown;
|
|
sapp_event event;
|
|
sapp_desc desc;
|
|
sapp_keycode keycodes[SAPP_MAX_KEYCODES];
|
|
bool clipboard_enabled;
|
|
int clipboard_size;
|
|
char* clipboard;
|
|
} _sapp_state;
|
|
static _sapp_state _sapp;
|
|
|
|
_SOKOL_PRIVATE void _sapp_fail(const char* msg) {
|
|
if (_sapp.desc.fail_cb) {
|
|
_sapp.desc.fail_cb(msg);
|
|
}
|
|
else if (_sapp.desc.fail_userdata_cb) {
|
|
_sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data);
|
|
}
|
|
else {
|
|
SOKOL_LOG(msg);
|
|
}
|
|
SOKOL_ABORT();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_call_init(void) {
|
|
if (_sapp.desc.init_cb) {
|
|
_sapp.desc.init_cb();
|
|
}
|
|
else if (_sapp.desc.init_userdata_cb) {
|
|
_sapp.desc.init_userdata_cb(_sapp.desc.user_data);
|
|
}
|
|
_sapp.init_called = true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_call_frame(void) {
|
|
if (_sapp.init_called && !_sapp.cleanup_called) {
|
|
if (_sapp.desc.frame_cb) {
|
|
_sapp.desc.frame_cb();
|
|
}
|
|
else if (_sapp.desc.frame_userdata_cb) {
|
|
_sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_call_cleanup(void) {
|
|
if (!_sapp.cleanup_called) {
|
|
if (_sapp.desc.cleanup_cb) {
|
|
_sapp.desc.cleanup_cb();
|
|
}
|
|
else if (_sapp.desc.cleanup_userdata_cb) {
|
|
_sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
|
|
}
|
|
_sapp.cleanup_called = true;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
|
|
if (!_sapp.cleanup_called) {
|
|
if (_sapp.desc.event_cb) {
|
|
_sapp.desc.event_cb(e);
|
|
}
|
|
else if (_sapp.desc.event_userdata_cb) {
|
|
_sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
|
|
}
|
|
}
|
|
if (_sapp.event_consumed) {
|
|
_sapp.event_consumed = false;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) {
|
|
SOKOL_ASSERT(src && dst && (max_len > 0));
|
|
char* const end = &(dst[max_len-1]);
|
|
char c = 0;
|
|
for (int i = 0; i < max_len; i++) {
|
|
c = *src;
|
|
if (c != 0) {
|
|
src++;
|
|
}
|
|
*dst++ = c;
|
|
}
|
|
/* truncated? */
|
|
if (c != 0) {
|
|
*end = 0;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
|
|
memset(&_sapp, 0, sizeof(_sapp));
|
|
_sapp.desc = *desc;
|
|
_sapp.first_frame = true;
|
|
_sapp.window_width = _sapp_def(_sapp.desc.width, 640);
|
|
_sapp.window_height = _sapp_def(_sapp.desc.height, 480);
|
|
_sapp.framebuffer_width = _sapp.window_width;
|
|
_sapp.framebuffer_height = _sapp.window_height;
|
|
_sapp.sample_count = _sapp_def(_sapp.desc.sample_count, 1);
|
|
_sapp.swap_interval = _sapp_def(_sapp.desc.swap_interval, 1);
|
|
_sapp.html5_canvas_name = _sapp_def(_sapp.desc.html5_canvas_name, "canvas");
|
|
_sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site;
|
|
_sapp.clipboard_enabled = _sapp.desc.enable_clipboard;
|
|
if (_sapp.clipboard_enabled) {
|
|
_sapp.clipboard_size = _sapp_def(_sapp.desc.clipboard_size, 8192);
|
|
_sapp.clipboard = (char*) SOKOL_CALLOC(1, _sapp.clipboard_size);
|
|
}
|
|
if (_sapp.desc.window_title) {
|
|
_sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title));
|
|
}
|
|
else {
|
|
_sapp_strcpy("sokol_app", _sapp.window_title, sizeof(_sapp.window_title));
|
|
}
|
|
_sapp.dpi_scale = 1.0f;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_discard_state(void) {
|
|
if (_sapp.clipboard_enabled) {
|
|
SOKOL_ASSERT(_sapp.clipboard);
|
|
SOKOL_FREE((void*)_sapp.clipboard);
|
|
}
|
|
memset(&_sapp, 0, sizeof(_sapp));
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) {
|
|
memset(&_sapp.event, 0, sizeof(_sapp.event));
|
|
_sapp.event.type = type;
|
|
_sapp.event.frame_count = _sapp.frame_count;
|
|
_sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
|
|
_sapp.event.window_width = _sapp.window_width;
|
|
_sapp.event.window_height = _sapp.window_height;
|
|
_sapp.event.framebuffer_width = _sapp.framebuffer_width;
|
|
_sapp.event.framebuffer_height = _sapp.framebuffer_height;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_events_enabled(void) {
|
|
/* only send events when an event callback is set, and the init function was called */
|
|
return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called;
|
|
}
|
|
|
|
_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
|
|
if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) {
|
|
return _sapp.keycodes[scan_code];
|
|
}
|
|
else {
|
|
return SAPP_KEYCODE_INVALID;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_frame(void) {
|
|
if (_sapp.first_frame) {
|
|
_sapp.first_frame = false;
|
|
_sapp_call_init();
|
|
}
|
|
_sapp_call_frame();
|
|
_sapp.frame_count++;
|
|
}
|
|
|
|
/*== MacOS/iOS ===============================================================*/
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
/*== MacOS ===================================================================*/
|
|
#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
|
|
|
|
#if defined(SOKOL_METAL)
|
|
#import <Metal/Metal.h>
|
|
#import <MetalKit/MetalKit.h>
|
|
#elif defined(SOKOL_GLCORE33)
|
|
#ifndef GL_SILENCE_DEPRECATION
|
|
#define GL_SILENCE_DEPRECATION
|
|
#endif
|
|
#include <Cocoa/Cocoa.h>
|
|
#include <OpenGL/gl3.h>
|
|
#endif
|
|
|
|
@interface _sapp_macos_app_delegate : NSObject<NSApplicationDelegate>
|
|
@end
|
|
@interface _sapp_macos_window_delegate : NSObject<NSWindowDelegate>
|
|
@end
|
|
#if defined(SOKOL_METAL)
|
|
@interface _sapp_macos_mtk_view_dlg : NSObject<MTKViewDelegate>
|
|
@end
|
|
@interface _sapp_macos_view : MTKView
|
|
{
|
|
NSTrackingArea* trackingArea;
|
|
}
|
|
@end
|
|
#elif defined(SOKOL_GLCORE33)
|
|
@interface _sapp_macos_view : NSOpenGLView
|
|
{
|
|
NSTrackingArea* trackingArea;
|
|
}
|
|
- (void)timerFired:(id)sender;
|
|
- (void)prepareOpenGL;
|
|
- (void)drawRect:(NSRect)bounds;
|
|
@end
|
|
#endif
|
|
|
|
static NSWindow* _sapp_macos_window_obj;
|
|
static _sapp_macos_window_delegate* _sapp_macos_win_dlg_obj;
|
|
static _sapp_macos_app_delegate* _sapp_macos_app_dlg_obj;
|
|
static _sapp_macos_view* _sapp_view_obj;
|
|
#if defined(SOKOL_METAL)
|
|
static _sapp_macos_mtk_view_dlg* _sapp_macos_mtk_view_dlg_obj;
|
|
static id<MTLDevice> _sapp_mtl_device_obj;
|
|
#elif defined(SOKOL_GLCORE33)
|
|
static NSOpenGLPixelFormat* _sapp_macos_glpixelformat_obj;
|
|
static NSTimer* _sapp_macos_timer_obj;
|
|
#endif
|
|
static uint32_t _sapp_macos_flags_changed_store;
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) {
|
|
_sapp.keycodes[0x1D] = SAPP_KEYCODE_0;
|
|
_sapp.keycodes[0x12] = SAPP_KEYCODE_1;
|
|
_sapp.keycodes[0x13] = SAPP_KEYCODE_2;
|
|
_sapp.keycodes[0x14] = SAPP_KEYCODE_3;
|
|
_sapp.keycodes[0x15] = SAPP_KEYCODE_4;
|
|
_sapp.keycodes[0x17] = SAPP_KEYCODE_5;
|
|
_sapp.keycodes[0x16] = SAPP_KEYCODE_6;
|
|
_sapp.keycodes[0x1A] = SAPP_KEYCODE_7;
|
|
_sapp.keycodes[0x1C] = SAPP_KEYCODE_8;
|
|
_sapp.keycodes[0x19] = SAPP_KEYCODE_9;
|
|
_sapp.keycodes[0x00] = SAPP_KEYCODE_A;
|
|
_sapp.keycodes[0x0B] = SAPP_KEYCODE_B;
|
|
_sapp.keycodes[0x08] = SAPP_KEYCODE_C;
|
|
_sapp.keycodes[0x02] = SAPP_KEYCODE_D;
|
|
_sapp.keycodes[0x0E] = SAPP_KEYCODE_E;
|
|
_sapp.keycodes[0x03] = SAPP_KEYCODE_F;
|
|
_sapp.keycodes[0x05] = SAPP_KEYCODE_G;
|
|
_sapp.keycodes[0x04] = SAPP_KEYCODE_H;
|
|
_sapp.keycodes[0x22] = SAPP_KEYCODE_I;
|
|
_sapp.keycodes[0x26] = SAPP_KEYCODE_J;
|
|
_sapp.keycodes[0x28] = SAPP_KEYCODE_K;
|
|
_sapp.keycodes[0x25] = SAPP_KEYCODE_L;
|
|
_sapp.keycodes[0x2E] = SAPP_KEYCODE_M;
|
|
_sapp.keycodes[0x2D] = SAPP_KEYCODE_N;
|
|
_sapp.keycodes[0x1F] = SAPP_KEYCODE_O;
|
|
_sapp.keycodes[0x23] = SAPP_KEYCODE_P;
|
|
_sapp.keycodes[0x0C] = SAPP_KEYCODE_Q;
|
|
_sapp.keycodes[0x0F] = SAPP_KEYCODE_R;
|
|
_sapp.keycodes[0x01] = SAPP_KEYCODE_S;
|
|
_sapp.keycodes[0x11] = SAPP_KEYCODE_T;
|
|
_sapp.keycodes[0x20] = SAPP_KEYCODE_U;
|
|
_sapp.keycodes[0x09] = SAPP_KEYCODE_V;
|
|
_sapp.keycodes[0x0D] = SAPP_KEYCODE_W;
|
|
_sapp.keycodes[0x07] = SAPP_KEYCODE_X;
|
|
_sapp.keycodes[0x10] = SAPP_KEYCODE_Y;
|
|
_sapp.keycodes[0x06] = SAPP_KEYCODE_Z;
|
|
_sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE;
|
|
_sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH;
|
|
_sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA;
|
|
_sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL;
|
|
_sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT;
|
|
_sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET;
|
|
_sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS;
|
|
_sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD;
|
|
_sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET;
|
|
_sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON;
|
|
_sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH;
|
|
_sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1;
|
|
_sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE;
|
|
_sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK;
|
|
_sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE;
|
|
_sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN;
|
|
_sapp.keycodes[0x77] = SAPP_KEYCODE_END;
|
|
_sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER;
|
|
_sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE;
|
|
_sapp.keycodes[0x7A] = SAPP_KEYCODE_F1;
|
|
_sapp.keycodes[0x78] = SAPP_KEYCODE_F2;
|
|
_sapp.keycodes[0x63] = SAPP_KEYCODE_F3;
|
|
_sapp.keycodes[0x76] = SAPP_KEYCODE_F4;
|
|
_sapp.keycodes[0x60] = SAPP_KEYCODE_F5;
|
|
_sapp.keycodes[0x61] = SAPP_KEYCODE_F6;
|
|
_sapp.keycodes[0x62] = SAPP_KEYCODE_F7;
|
|
_sapp.keycodes[0x64] = SAPP_KEYCODE_F8;
|
|
_sapp.keycodes[0x65] = SAPP_KEYCODE_F9;
|
|
_sapp.keycodes[0x6D] = SAPP_KEYCODE_F10;
|
|
_sapp.keycodes[0x67] = SAPP_KEYCODE_F11;
|
|
_sapp.keycodes[0x6F] = SAPP_KEYCODE_F12;
|
|
_sapp.keycodes[0x69] = SAPP_KEYCODE_F13;
|
|
_sapp.keycodes[0x6B] = SAPP_KEYCODE_F14;
|
|
_sapp.keycodes[0x71] = SAPP_KEYCODE_F15;
|
|
_sapp.keycodes[0x6A] = SAPP_KEYCODE_F16;
|
|
_sapp.keycodes[0x40] = SAPP_KEYCODE_F17;
|
|
_sapp.keycodes[0x4F] = SAPP_KEYCODE_F18;
|
|
_sapp.keycodes[0x50] = SAPP_KEYCODE_F19;
|
|
_sapp.keycodes[0x5A] = SAPP_KEYCODE_F20;
|
|
_sapp.keycodes[0x73] = SAPP_KEYCODE_HOME;
|
|
_sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT;
|
|
_sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT;
|
|
_sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT;
|
|
_sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL;
|
|
_sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT;
|
|
_sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER;
|
|
_sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU;
|
|
_sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK;
|
|
_sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN;
|
|
_sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP;
|
|
_sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT;
|
|
_sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT;
|
|
_sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL;
|
|
_sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT;
|
|
_sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER;
|
|
_sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE;
|
|
_sapp.keycodes[0x30] = SAPP_KEYCODE_TAB;
|
|
_sapp.keycodes[0x7E] = SAPP_KEYCODE_UP;
|
|
_sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0;
|
|
_sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1;
|
|
_sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2;
|
|
_sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3;
|
|
_sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4;
|
|
_sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5;
|
|
_sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6;
|
|
_sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7;
|
|
_sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8;
|
|
_sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9;
|
|
_sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD;
|
|
_sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL;
|
|
_sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE;
|
|
_sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER;
|
|
_sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL;
|
|
_sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY;
|
|
_sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
|
|
puts("RUN");
|
|
_sapp_init_state(desc);
|
|
_sapp_macos_init_keytable();
|
|
[NSApplication sharedApplication];
|
|
NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
|
|
_sapp_macos_app_dlg_obj = [[_sapp_macos_app_delegate alloc] init];
|
|
NSApp.delegate = _sapp_macos_app_dlg_obj;
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
[NSApp run];
|
|
_sapp_discard_state();
|
|
}
|
|
|
|
/* MacOS entry function */
|
|
#if !defined(SOKOL_NO_ENTRY)
|
|
int main(int argc, char* argv[]) {
|
|
sapp_desc desc = sokol_main(argc, argv);
|
|
_sapp_run(&desc);
|
|
return 0;
|
|
}
|
|
#endif /* SOKOL_NO_ENTRY */
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
|
|
#if defined(SOKOL_METAL)
|
|
const CGSize fb_size = [_sapp_view_obj drawableSize];
|
|
_sapp.framebuffer_width = fb_size.width;
|
|
_sapp.framebuffer_height = fb_size.height;
|
|
#elif defined(SOKOL_GLCORE33)
|
|
const NSRect fb_rect = [_sapp_view_obj convertRectToBacking:[_sapp_view_obj frame]];
|
|
_sapp.framebuffer_width = fb_rect.size.width;
|
|
_sapp.framebuffer_height = fb_rect.size.height;
|
|
#endif
|
|
const NSRect bounds = [_sapp_view_obj bounds];
|
|
_sapp.window_width = bounds.size.width;
|
|
_sapp.window_height = bounds.size.height;
|
|
SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
|
|
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_frame(void) {
|
|
const NSPoint mouse_pos = [_sapp_macos_window_obj mouseLocationOutsideOfEventStream];
|
|
_sapp.mouse_x = mouse_pos.x * _sapp.dpi_scale;
|
|
_sapp.mouse_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1;
|
|
_sapp_frame();
|
|
if (_sapp.quit_requested || _sapp.quit_ordered) {
|
|
[_sapp_macos_window_obj performClose:nil];
|
|
}
|
|
}
|
|
|
|
@implementation _sapp_macos_app_delegate
|
|
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
|
|
if (_sapp.desc.fullscreen) {
|
|
NSRect screen_rect = NSScreen.mainScreen.frame;
|
|
_sapp.window_width = screen_rect.size.width;
|
|
_sapp.window_height = screen_rect.size.height;
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp.framebuffer_width = 2 * _sapp.window_width;
|
|
_sapp.framebuffer_height = 2 * _sapp.window_height;
|
|
}
|
|
else {
|
|
_sapp.framebuffer_width = _sapp.window_width;
|
|
_sapp.framebuffer_height = _sapp.window_height;
|
|
}
|
|
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width;
|
|
}
|
|
const NSUInteger style =
|
|
NSWindowStyleMaskTitled |
|
|
NSWindowStyleMaskClosable |
|
|
NSWindowStyleMaskMiniaturizable |
|
|
NSWindowStyleMaskResizable;
|
|
NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height);
|
|
_sapp_macos_window_obj = [[NSWindow alloc]
|
|
initWithContentRect:window_rect
|
|
styleMask:style
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO];
|
|
_sapp_macos_window_obj.title = [NSString stringWithUTF8String:_sapp.window_title];
|
|
_sapp_macos_window_obj.acceptsMouseMovedEvents = YES;
|
|
_sapp_macos_window_obj.restorable = YES;
|
|
_sapp_macos_win_dlg_obj = [[_sapp_macos_window_delegate alloc] init];
|
|
_sapp_macos_window_obj.delegate = _sapp_macos_win_dlg_obj;
|
|
#if defined(SOKOL_METAL)
|
|
_sapp_mtl_device_obj = MTLCreateSystemDefaultDevice();
|
|
_sapp_macos_mtk_view_dlg_obj = [[_sapp_macos_mtk_view_dlg alloc] init];
|
|
_sapp_view_obj = [[_sapp_macos_view alloc] init];
|
|
[_sapp_view_obj updateTrackingAreas];
|
|
_sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval;
|
|
_sapp_view_obj.delegate = _sapp_macos_mtk_view_dlg_obj;
|
|
_sapp_view_obj.device = _sapp_mtl_device_obj;
|
|
_sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
_sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
|
_sapp_view_obj.sampleCount = _sapp.sample_count;
|
|
_sapp_macos_window_obj.contentView = _sapp_view_obj;
|
|
[_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj];
|
|
if (!_sapp.desc.high_dpi) {
|
|
CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
|
|
_sapp_view_obj.drawableSize = drawable_size;
|
|
}
|
|
_sapp_macos_update_dimensions();
|
|
_sapp_view_obj.layer.magnificationFilter = kCAFilterNearest;
|
|
#elif defined(SOKOL_GLCORE33)
|
|
NSOpenGLPixelFormatAttribute attrs[32];
|
|
int i = 0;
|
|
attrs[i++] = NSOpenGLPFAAccelerated;
|
|
attrs[i++] = NSOpenGLPFADoubleBuffer;
|
|
attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core;
|
|
attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
|
|
attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
|
|
attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
|
|
attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8;
|
|
if (_sapp.sample_count > 1) {
|
|
attrs[i++] = NSOpenGLPFAMultisample;
|
|
attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
|
|
attrs[i++] = NSOpenGLPFASamples; attrs[i++] = _sapp.sample_count;
|
|
}
|
|
else {
|
|
attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0;
|
|
}
|
|
attrs[i++] = 0;
|
|
_sapp_macos_glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
|
SOKOL_ASSERT(_sapp_macos_glpixelformat_obj != nil);
|
|
|
|
_sapp_view_obj = [[_sapp_macos_view alloc]
|
|
initWithFrame:window_rect
|
|
pixelFormat:_sapp_macos_glpixelformat_obj];
|
|
[_sapp_view_obj updateTrackingAreas];
|
|
if (_sapp.desc.high_dpi) {
|
|
[_sapp_view_obj setWantsBestResolutionOpenGLSurface:YES];
|
|
}
|
|
else {
|
|
[_sapp_view_obj setWantsBestResolutionOpenGLSurface:NO];
|
|
}
|
|
|
|
_sapp_macos_window_obj.contentView = _sapp_view_obj;
|
|
[_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj];
|
|
|
|
_sapp_macos_timer_obj = [NSTimer timerWithTimeInterval:0.001
|
|
target:_sapp_view_obj
|
|
selector:@selector(timerFired:)
|
|
userInfo:nil
|
|
repeats:YES];
|
|
[[NSRunLoop currentRunLoop] addTimer:_sapp_macos_timer_obj forMode:NSDefaultRunLoopMode];
|
|
#endif
|
|
_sapp.valid = true;
|
|
if (_sapp.desc.fullscreen) {
|
|
/* on GL, this already toggles a rendered frame, so set the valid flag before */
|
|
[_sapp_macos_window_obj toggleFullScreen:self];
|
|
}
|
|
else {
|
|
[_sapp_macos_window_obj center];
|
|
}
|
|
[_sapp_macos_window_obj makeKeyAndOrderFront:nil];
|
|
}
|
|
|
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender {
|
|
return YES;
|
|
}
|
|
@end
|
|
|
|
_SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) {
|
|
uint32_t m = 0;
|
|
if (f & NSEventModifierFlagShift) {
|
|
m |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (f & NSEventModifierFlagControl) {
|
|
m |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (f & NSEventModifierFlagOption) {
|
|
m |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (f & NSEventModifierFlagCommand) {
|
|
m |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.mouse_button = btn;
|
|
_sapp.event.modifiers = mod;
|
|
_sapp.event.mouse_x = _sapp.mouse_x;
|
|
_sapp.event.mouse_y = _sapp.mouse_y;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.key_code = key;
|
|
_sapp.event.key_repeat = repeat;
|
|
_sapp.event.modifiers = mod;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
@implementation _sapp_macos_window_delegate
|
|
- (BOOL)windowShouldClose:(id)sender {
|
|
/* only give user-code a chance to intervene when sapp_quit() wasn't already called */
|
|
if (!_sapp.quit_ordered) {
|
|
/* if window should be closed and event handling is enabled, give user code
|
|
a chance to intervene via sapp_cancel_quit()
|
|
*/
|
|
_sapp.quit_requested = true;
|
|
_sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
|
|
/* user code hasn't intervened, quit the app */
|
|
if (_sapp.quit_requested) {
|
|
_sapp.quit_ordered = true;
|
|
}
|
|
}
|
|
if (_sapp.quit_ordered) {
|
|
_sapp_call_cleanup();
|
|
return YES;
|
|
}
|
|
else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (void)windowDidResize:(NSNotification*)notification {
|
|
_sapp_macos_update_dimensions();
|
|
_sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED);
|
|
}
|
|
|
|
- (void)windowDidMiniaturize:(NSNotification*)notification {
|
|
_sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED);
|
|
}
|
|
|
|
- (void)windowDidDeminiaturize:(NSNotification*)notification {
|
|
_sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED);
|
|
}
|
|
@end
|
|
|
|
#if defined(SOKOL_METAL)
|
|
@implementation _sapp_macos_mtk_view_dlg
|
|
- (void)drawInMTKView:(MTKView*)view {
|
|
@autoreleasepool {
|
|
_sapp_macos_frame();
|
|
}
|
|
}
|
|
- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size {
|
|
/* this is required by the protocol, but we can't do anything useful here */
|
|
}
|
|
@end
|
|
#endif
|
|
|
|
@implementation _sapp_macos_view
|
|
#if defined(SOKOL_GLCORE33)
|
|
- (void)timerFired:(id)sender {
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
- (void)prepareOpenGL {
|
|
[super prepareOpenGL];
|
|
GLint swapInt = 1;
|
|
NSOpenGLContext* ctx = [_sapp_view_obj openGLContext];
|
|
[ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval];
|
|
[ctx makeCurrentContext];
|
|
}
|
|
- (void)drawRect:(NSRect)bound {
|
|
_sapp_macos_frame();
|
|
[[_sapp_view_obj openGLContext] flushBuffer];
|
|
}
|
|
#endif
|
|
|
|
- (BOOL)isOpaque {
|
|
return YES;
|
|
}
|
|
- (BOOL)canBecomeKey {
|
|
return YES;
|
|
}
|
|
- (BOOL)acceptsFirstResponder {
|
|
return YES;
|
|
}
|
|
- (void)updateTrackingAreas {
|
|
if (trackingArea != nil) {
|
|
[self removeTrackingArea:trackingArea];
|
|
trackingArea = nil;
|
|
}
|
|
const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
|
|
NSTrackingActiveInKeyWindow |
|
|
NSTrackingEnabledDuringMouseDrag |
|
|
NSTrackingCursorUpdate |
|
|
NSTrackingInVisibleRect |
|
|
NSTrackingAssumeInside;
|
|
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
|
|
[self addTrackingArea:trackingArea];
|
|
[super updateTrackingAreas];
|
|
}
|
|
- (void)mouseEntered:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)mouseExited:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)mouseDown:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)mouseUp:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)rightMouseDown:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)rightMouseUp:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)mouseMoved:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)mouseDragged:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)rightMouseDragged:(NSEvent*)event {
|
|
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)scrollWheel:(NSEvent*)event {
|
|
if (_sapp_events_enabled()) {
|
|
float dx = (float) event.scrollingDeltaX;
|
|
float dy = (float) event.scrollingDeltaY;
|
|
if (event.hasPreciseScrollingDeltas) {
|
|
dx *= 0.1;
|
|
dy *= 0.1;
|
|
}
|
|
if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
|
|
_sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags);
|
|
_sapp.event.mouse_x = _sapp.mouse_x;
|
|
_sapp.event.mouse_y = _sapp.mouse_y;
|
|
_sapp.event.scroll_x = dx;
|
|
_sapp.event.scroll_y = dy;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
- (void)keyDown:(NSEvent*)event {
|
|
if (_sapp_events_enabled()) {
|
|
const uint32_t mods = _sapp_macos_mod(event.modifierFlags);
|
|
/* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed,
|
|
as a workaround, to prevent key presses from sticking we'll send
|
|
a keyup event following right after the keydown if SUPER is also pressed
|
|
*/
|
|
const sapp_keycode key_code = _sapp_translate_key(event.keyCode);
|
|
_sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods);
|
|
if (0 != (mods & SAPP_MODIFIER_SUPER)) {
|
|
_sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods);
|
|
}
|
|
const NSString* chars = event.characters;
|
|
const NSUInteger len = chars.length;
|
|
if (len > 0) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
|
|
_sapp.event.modifiers = mods;
|
|
for (NSUInteger i = 0; i < len; i++) {
|
|
const unichar codepoint = [chars characterAtIndex:i];
|
|
if ((codepoint & 0xFF00) == 0xF700) {
|
|
continue;
|
|
}
|
|
_sapp.event.char_code = codepoint;
|
|
_sapp.event.key_repeat = event.isARepeat;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
/* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */
|
|
if (_sapp.clipboard_enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
- (void)keyUp:(NSEvent*)event {
|
|
_sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP,
|
|
_sapp_translate_key(event.keyCode),
|
|
event.isARepeat,
|
|
_sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
- (void)flagsChanged:(NSEvent*)event {
|
|
const uint32_t old_f = _sapp_macos_flags_changed_store;
|
|
const uint32_t new_f = event.modifierFlags;
|
|
_sapp_macos_flags_changed_store = new_f;
|
|
sapp_keycode key_code = SAPP_KEYCODE_INVALID;
|
|
bool down = false;
|
|
if ((new_f ^ old_f) & NSEventModifierFlagShift) {
|
|
key_code = SAPP_KEYCODE_LEFT_SHIFT;
|
|
down = 0 != (new_f & NSEventModifierFlagShift);
|
|
}
|
|
if ((new_f ^ old_f) & NSEventModifierFlagControl) {
|
|
key_code = SAPP_KEYCODE_LEFT_CONTROL;
|
|
down = 0 != (new_f & NSEventModifierFlagControl);
|
|
}
|
|
if ((new_f ^ old_f) & NSEventModifierFlagOption) {
|
|
key_code = SAPP_KEYCODE_LEFT_ALT;
|
|
down = 0 != (new_f & NSEventModifierFlagOption);
|
|
}
|
|
if ((new_f ^ old_f) & NSEventModifierFlagCommand) {
|
|
key_code = SAPP_KEYCODE_LEFT_SUPER;
|
|
down = 0 != (new_f & NSEventModifierFlagCommand);
|
|
}
|
|
if (key_code != SAPP_KEYCODE_INVALID) {
|
|
_sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP,
|
|
key_code,
|
|
false,
|
|
_sapp_macos_mod(event.modifierFlags));
|
|
}
|
|
}
|
|
- (void)cursorUpdate:(NSEvent*)event {
|
|
if (_sapp.desc.user_cursor) {
|
|
_sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR);
|
|
}
|
|
}
|
|
@end
|
|
|
|
void _sapp_macos_set_clipboard_string(const char* str) {
|
|
@autoreleasepool {
|
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
|
[pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
|
|
[pasteboard setString:@(str) forType:NSPasteboardTypeString];
|
|
}
|
|
}
|
|
|
|
const char* _sapp_macos_get_clipboard_string(void) {
|
|
SOKOL_ASSERT(_sapp.clipboard);
|
|
@autoreleasepool {
|
|
_sapp.clipboard[0] = 0;
|
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
|
if (![[pasteboard types] containsObject:NSPasteboardTypeString]) {
|
|
return _sapp.clipboard;
|
|
}
|
|
NSString* str = [pasteboard stringForType:NSPasteboardTypeString];
|
|
if (!str) {
|
|
return _sapp.clipboard;
|
|
}
|
|
_sapp_strcpy([str UTF8String], _sapp.clipboard, _sapp.clipboard_size);
|
|
}
|
|
return _sapp.clipboard;
|
|
}
|
|
|
|
#endif /* MacOS */
|
|
|
|
/*== iOS =====================================================================*/
|
|
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
|
#import <UIKit/UIKit.h>
|
|
#if defined(SOKOL_METAL)
|
|
#import <Metal/Metal.h>
|
|
#import <MetalKit/MetalKit.h>
|
|
#else
|
|
#import <GLKit/GLKit.h>
|
|
#include <OpenGLES/ES3/gl.h>
|
|
#include <OpenGLES/ES3/glext.h>
|
|
#endif
|
|
|
|
@interface _sapp_app_delegate : NSObject<UIApplicationDelegate>
|
|
@end
|
|
@interface _sapp_textfield_dlg : NSObject<UITextFieldDelegate>
|
|
- (void)keyboardWasShown:(NSNotification*)notif;
|
|
- (void)keyboardWillBeHidden:(NSNotification*)notif;
|
|
- (void)keyboardDidChangeFrame:(NSNotification*)notif;
|
|
@end
|
|
#if defined(SOKOL_METAL)
|
|
@interface _sapp_ios_mtk_view_dlg : NSObject<MTKViewDelegate>
|
|
@end
|
|
@interface _sapp_ios_view : MTKView;
|
|
@end
|
|
#else
|
|
@interface _sapp_ios_glk_view_dlg : NSObject<GLKViewDelegate>
|
|
@end
|
|
@interface _sapp_ios_view : GLKView
|
|
@end
|
|
#endif
|
|
|
|
static bool _sapp_ios_suspended;
|
|
static UIWindow* _sapp_ios_window_obj;
|
|
static _sapp_ios_view* _sapp_view_obj;
|
|
static UITextField* _sapp_ios_textfield_obj;
|
|
static _sapp_textfield_dlg* _sapp_ios_textfield_dlg_obj;
|
|
#if defined(SOKOL_METAL)
|
|
static _sapp_ios_mtk_view_dlg* _sapp_ios_mtk_view_dlg_obj;
|
|
static UIViewController<MTKViewDelegate>* _sapp_ios_view_ctrl_obj;
|
|
static id<MTLDevice> _sapp_mtl_device_obj;
|
|
#else
|
|
static EAGLContext* _sapp_ios_eagl_ctx_obj;
|
|
static _sapp_ios_glk_view_dlg* _sapp_ios_glk_view_dlg_obj;
|
|
static GLKViewController* _sapp_ios_view_ctrl_obj;
|
|
#endif
|
|
|
|
_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
|
|
_sapp_init_state(desc);
|
|
static int argc = 1;
|
|
static char* argv[] = { (char*)"sokol_app" };
|
|
UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class]));
|
|
_sapp_discard_state();
|
|
}
|
|
|
|
/* iOS entry function */
|
|
#if !defined(SOKOL_NO_ENTRY)
|
|
int main(int argc, char* argv[]) {
|
|
sapp_desc desc = sokol_main(argc, argv);
|
|
_sapp_run(&desc);
|
|
return 0;
|
|
}
|
|
#endif /* SOKOL_NO_ENTRY */
|
|
|
|
_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) {
|
|
CGRect screen_rect = UIScreen.mainScreen.bounds;
|
|
_sapp.window_width = (int) screen_rect.size.width;
|
|
_sapp.window_height = (int) screen_rect.size.height;
|
|
int cur_fb_width, cur_fb_height;
|
|
#if defined(SOKOL_METAL)
|
|
const CGSize fb_size = _sapp_view_obj.drawableSize;
|
|
cur_fb_width = (int) fb_size.width;
|
|
cur_fb_height = (int) fb_size.height;
|
|
#else
|
|
cur_fb_width = (int) _sapp_view_obj.drawableWidth;
|
|
cur_fb_height = (int) _sapp_view_obj.drawableHeight;
|
|
#endif
|
|
const bool dim_changed =
|
|
(_sapp.framebuffer_width != cur_fb_width) ||
|
|
(_sapp.framebuffer_height != cur_fb_height);
|
|
_sapp.framebuffer_width = cur_fb_width;
|
|
_sapp.framebuffer_height = cur_fb_height;
|
|
SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
|
|
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width;
|
|
if (dim_changed) {
|
|
_sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_ios_frame(void) {
|
|
_sapp_ios_update_dimensions();
|
|
_sapp_frame();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
|
|
/* if not happened yet, create an invisible text field */
|
|
if (nil == _sapp_ios_textfield_obj) {
|
|
_sapp_ios_textfield_dlg_obj = [[_sapp_textfield_dlg alloc] init];
|
|
_sapp_ios_textfield_obj = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
|
|
_sapp_ios_textfield_obj.keyboardType = UIKeyboardTypeDefault;
|
|
_sapp_ios_textfield_obj.returnKeyType = UIReturnKeyDefault;
|
|
_sapp_ios_textfield_obj.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
_sapp_ios_textfield_obj.autocorrectionType = UITextAutocorrectionTypeNo;
|
|
_sapp_ios_textfield_obj.spellCheckingType = UITextSpellCheckingTypeNo;
|
|
_sapp_ios_textfield_obj.hidden = YES;
|
|
_sapp_ios_textfield_obj.text = @"x";
|
|
_sapp_ios_textfield_obj.delegate = _sapp_ios_textfield_dlg_obj;
|
|
[_sapp_ios_view_ctrl_obj.view addSubview:_sapp_ios_textfield_obj];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj
|
|
selector:@selector(keyboardWasShown:)
|
|
name:UIKeyboardDidShowNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj
|
|
selector:@selector(keyboardWillBeHidden:)
|
|
name:UIKeyboardWillHideNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj
|
|
selector:@selector(keyboardDidChangeFrame:)
|
|
name:UIKeyboardDidChangeFrameNotification object:nil];
|
|
}
|
|
if (shown) {
|
|
/* setting the text field as first responder brings up the onscreen keyboard */
|
|
[_sapp_ios_textfield_obj becomeFirstResponder];
|
|
}
|
|
else {
|
|
[_sapp_ios_textfield_obj resignFirstResponder];
|
|
}
|
|
}
|
|
|
|
@implementation _sapp_app_delegate
|
|
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
|
CGRect screen_rect = UIScreen.mainScreen.bounds;
|
|
_sapp_ios_window_obj = [[UIWindow alloc] initWithFrame:screen_rect];
|
|
_sapp.window_width = screen_rect.size.width;
|
|
_sapp.window_height = screen_rect.size.height;
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp.framebuffer_width = 2 * _sapp.window_width;
|
|
_sapp.framebuffer_height = 2 * _sapp.window_height;
|
|
}
|
|
else {
|
|
_sapp.framebuffer_width = _sapp.window_width;
|
|
_sapp.framebuffer_height = _sapp.window_height;
|
|
}
|
|
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width;
|
|
#if defined(SOKOL_METAL)
|
|
_sapp_mtl_device_obj = MTLCreateSystemDefaultDevice();
|
|
_sapp_ios_mtk_view_dlg_obj = [[_sapp_ios_mtk_view_dlg alloc] init];
|
|
_sapp_view_obj = [[_sapp_ios_view alloc] init];
|
|
_sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval;
|
|
_sapp_view_obj.delegate = _sapp_ios_mtk_view_dlg_obj;
|
|
_sapp_view_obj.device = _sapp_mtl_device_obj;
|
|
_sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
_sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
|
_sapp_view_obj.sampleCount = _sapp.sample_count;
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp_view_obj.contentScaleFactor = 2.0;
|
|
}
|
|
else {
|
|
_sapp_view_obj.contentScaleFactor = 1.0;
|
|
}
|
|
_sapp_view_obj.userInteractionEnabled = YES;
|
|
_sapp_view_obj.multipleTouchEnabled = YES;
|
|
[_sapp_ios_window_obj addSubview:_sapp_view_obj];
|
|
_sapp_ios_view_ctrl_obj = [[UIViewController<MTKViewDelegate> alloc] init];
|
|
_sapp_ios_view_ctrl_obj.view = _sapp_view_obj;
|
|
_sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj;
|
|
#else
|
|
if (_sapp.desc.gl_force_gles2) {
|
|
_sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
_sapp.gles2_fallback = true;
|
|
}
|
|
else {
|
|
_sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
|
|
if (_sapp_ios_eagl_ctx_obj == nil) {
|
|
_sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
_sapp.gles2_fallback = true;
|
|
}
|
|
}
|
|
_sapp_ios_glk_view_dlg_obj = [[_sapp_ios_glk_view_dlg alloc] init];
|
|
_sapp_view_obj = [[_sapp_ios_view alloc] initWithFrame:screen_rect];
|
|
_sapp_view_obj.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
|
|
_sapp_view_obj.drawableDepthFormat = GLKViewDrawableDepthFormat24;
|
|
_sapp_view_obj.drawableStencilFormat = GLKViewDrawableStencilFormatNone;
|
|
_sapp_view_obj.drawableMultisample = GLKViewDrawableMultisampleNone; /* FIXME */
|
|
_sapp_view_obj.context = _sapp_ios_eagl_ctx_obj;
|
|
_sapp_view_obj.delegate = _sapp_ios_glk_view_dlg_obj;
|
|
_sapp_view_obj.enableSetNeedsDisplay = NO;
|
|
_sapp_view_obj.userInteractionEnabled = YES;
|
|
_sapp_view_obj.multipleTouchEnabled = YES;
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp_view_obj.contentScaleFactor = 2.0;
|
|
}
|
|
else {
|
|
_sapp_view_obj.contentScaleFactor = 1.0;
|
|
}
|
|
[_sapp_ios_window_obj addSubview:_sapp_view_obj];
|
|
_sapp_ios_view_ctrl_obj = [[GLKViewController alloc] init];
|
|
_sapp_ios_view_ctrl_obj.view = _sapp_view_obj;
|
|
_sapp_ios_view_ctrl_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval;
|
|
_sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj;
|
|
#endif
|
|
[_sapp_ios_window_obj makeKeyAndVisible];
|
|
|
|
_sapp.valid = true;
|
|
return YES;
|
|
}
|
|
|
|
- (void)applicationWillResignActive:(UIApplication *)application {
|
|
if (!_sapp_ios_suspended) {
|
|
_sapp_ios_suspended = true;
|
|
_sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED);
|
|
}
|
|
}
|
|
|
|
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
|
if (_sapp_ios_suspended) {
|
|
_sapp_ios_suspended = false;
|
|
_sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED);
|
|
}
|
|
}
|
|
@end
|
|
|
|
@implementation _sapp_textfield_dlg
|
|
- (void)keyboardWasShown:(NSNotification*)notif {
|
|
_sapp.onscreen_keyboard_shown = true;
|
|
/* query the keyboard's size, and modify the content view's size */
|
|
if (_sapp.desc.ios_keyboard_resizes_canvas) {
|
|
NSDictionary* info = notif.userInfo;
|
|
CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
|
|
CGRect view_frame = UIScreen.mainScreen.bounds;
|
|
view_frame.size.height -= kbd_h;
|
|
_sapp_view_obj.frame = view_frame;
|
|
}
|
|
}
|
|
- (void)keyboardWillBeHidden:(NSNotification*)notif {
|
|
_sapp.onscreen_keyboard_shown = false;
|
|
if (_sapp.desc.ios_keyboard_resizes_canvas) {
|
|
_sapp_view_obj.frame = UIScreen.mainScreen.bounds;
|
|
}
|
|
}
|
|
- (void)keyboardDidChangeFrame:(NSNotification*)notif {
|
|
/* this is for the case when the screen rotation changes while the keyboard is open */
|
|
if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) {
|
|
NSDictionary* info = notif.userInfo;
|
|
CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
|
|
CGRect view_frame = UIScreen.mainScreen.bounds;
|
|
view_frame.size.height -= kbd_h;
|
|
_sapp_view_obj.frame = view_frame;
|
|
}
|
|
}
|
|
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
|
|
if (_sapp_events_enabled()) {
|
|
const NSUInteger len = string.length;
|
|
if (len > 0) {
|
|
for (NSUInteger i = 0; i < len; i++) {
|
|
unichar c = [string characterAtIndex:i];
|
|
if (c >= 32) {
|
|
/* ignore surrogates for now */
|
|
if ((c < 0xD800) || (c > 0xDFFF)) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
|
|
_sapp.event.char_code = c;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
if (c <= 32) {
|
|
sapp_keycode k = SAPP_KEYCODE_INVALID;
|
|
switch (c) {
|
|
case 10: k = SAPP_KEYCODE_ENTER; break;
|
|
case 32: k = SAPP_KEYCODE_SPACE; break;
|
|
default: break;
|
|
}
|
|
if (k != SAPP_KEYCODE_INVALID) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
|
|
_sapp.event.key_code = k;
|
|
_sapp_call_event(&_sapp.event);
|
|
_sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
|
|
_sapp.event.key_code = k;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* this was a backspace */
|
|
_sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
|
|
_sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
|
|
_sapp_call_event(&_sapp.event);
|
|
_sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
|
|
_sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
@end
|
|
|
|
#if defined(SOKOL_METAL)
|
|
@implementation _sapp_ios_mtk_view_dlg
|
|
- (void)drawInMTKView:(MTKView*)view {
|
|
@autoreleasepool {
|
|
_sapp_ios_frame();
|
|
}
|
|
}
|
|
|
|
- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size {
|
|
/* this is required by the protocol, but we can't do anything useful here */
|
|
}
|
|
@end
|
|
#else
|
|
@implementation _sapp_ios_glk_view_dlg
|
|
- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
|
|
@autoreleasepool {
|
|
_sapp_ios_frame();
|
|
}
|
|
}
|
|
@end
|
|
#endif
|
|
|
|
_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>* touches, UIEvent* event) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
NSEnumerator* enumerator = event.allTouches.objectEnumerator;
|
|
UITouch* ios_touch;
|
|
while ((ios_touch = [enumerator nextObject])) {
|
|
if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) {
|
|
CGPoint ios_pos = [ios_touch locationInView:_sapp_view_obj];
|
|
sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++];
|
|
cur_point->identifier = (uintptr_t) ios_touch;
|
|
cur_point->pos_x = ios_pos.x * _sapp.dpi_scale;
|
|
cur_point->pos_y = ios_pos.y * _sapp.dpi_scale;
|
|
cur_point->changed = [touches containsObject:ios_touch];
|
|
}
|
|
}
|
|
if (_sapp.event.num_touches > 0) {
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
|
|
@implementation _sapp_ios_view
|
|
- (BOOL) isOpaque {
|
|
return YES;
|
|
}
|
|
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
|
|
_sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event);
|
|
}
|
|
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
|
|
_sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event);
|
|
}
|
|
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
|
|
_sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event);
|
|
}
|
|
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
|
|
_sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event);
|
|
}
|
|
@end
|
|
#endif /* TARGET_OS_IPHONE */
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
/*== EMSCRIPTEN ==============================================================*/
|
|
#if defined(__EMSCRIPTEN__)
|
|
#if defined(SOKOL_GLES3)
|
|
#include <GLES3/gl3.h>
|
|
#else
|
|
#ifndef GL_EXT_PROTOTYPES
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#endif
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#endif
|
|
#include <emscripten/emscripten.h>
|
|
#include <emscripten/html5.h>
|
|
|
|
static bool _sapp_emsc_input_created;
|
|
static bool _sapp_emsc_wants_show_keyboard;
|
|
static bool _sapp_emsc_wants_hide_keyboard;
|
|
|
|
/* this function is called from a JS event handler when the user hides
|
|
the onscreen keyboard pressing the 'dismiss keyboard key'
|
|
*/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) {
|
|
_sapp.onscreen_keyboard_shown = false;
|
|
}
|
|
#ifdef __cplusplus
|
|
} /* extern "C" */
|
|
#endif
|
|
|
|
/* Javascript helper functions for mobile virtual keyboard input */
|
|
EM_JS(void, sapp_js_create_textfield, (void), {
|
|
var _sapp_inp = document.createElement("input");
|
|
_sapp_inp.type = "text";
|
|
_sapp_inp.id = "_sokol_app_input_element";
|
|
_sapp_inp.autocapitalize = "none";
|
|
_sapp_inp.addEventListener("focusout", function(_sapp_event) {
|
|
__sapp_emsc_notify_keyboard_hidden()
|
|
|
|
});
|
|
document.body.append(_sapp_inp);
|
|
});
|
|
|
|
EM_JS(void, sapp_js_focus_textfield, (void), {
|
|
document.getElementById("_sokol_app_input_element").focus();
|
|
});
|
|
|
|
EM_JS(void, sapp_js_unfocus_textfield, (void), {
|
|
document.getElementById("_sokol_app_input_element").blur();
|
|
});
|
|
|
|
EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) {
|
|
if (_sapp.clipboard_enabled) {
|
|
_sapp_strcpy(str, _sapp.clipboard, _sapp.clipboard_size);
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */
|
|
EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) {
|
|
return _sapp.html5_ask_leave_site ? 1 : 0;
|
|
}
|
|
|
|
EM_JS(void, sapp_js_hook_beforeunload, (void), {
|
|
window.addEventListener('beforeunload', function(_sapp_event) {
|
|
if (__sapp_html5_get_ask_leave_site() != 0) {
|
|
_sapp_event.preventDefault();
|
|
_sapp_event.returnValue = ' ';
|
|
}
|
|
});
|
|
});
|
|
|
|
EM_JS(void, sapp_js_init_clipboard, (void), {
|
|
window.addEventListener('paste', function(event) {
|
|
var pasted_str = event.clipboardData.getData('text');
|
|
ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]);
|
|
});
|
|
});
|
|
|
|
EM_JS(void, sapp_js_write_clipboard, (const char* c_str), {
|
|
var str = UTF8ToString(c_str);
|
|
var ta = document.createElement('textarea');
|
|
ta.setAttribute('autocomplete', 'off');
|
|
ta.setAttribute('autocorrect', 'off');
|
|
ta.setAttribute('autocapitalize', 'off');
|
|
ta.setAttribute('spellcheck', 'false');
|
|
ta.style.left = -100 + 'px';
|
|
ta.style.top = -100 + 'px';
|
|
ta.style.height = 1;
|
|
ta.style.width = 1;
|
|
ta.value = str;
|
|
document.body.appendChild(ta);
|
|
ta.select();
|
|
document.execCommand('copy');
|
|
document.body.removeChild(ta);
|
|
});
|
|
|
|
_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) {
|
|
sapp_js_write_clipboard(str);
|
|
}
|
|
|
|
/* called from the emscripten event handler to update the keyboard visibility
|
|
state, this must happen from an JS input event handler, otherwise
|
|
the request will be ignored by the browser
|
|
*/
|
|
_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) {
|
|
if (_sapp_emsc_wants_show_keyboard) {
|
|
/* create input text field on demand */
|
|
if (!_sapp_emsc_input_created) {
|
|
_sapp_emsc_input_created = true;
|
|
sapp_js_create_textfield();
|
|
}
|
|
/* focus the text input field, this will bring up the keyboard */
|
|
_sapp.onscreen_keyboard_shown = true;
|
|
_sapp_emsc_wants_show_keyboard = false;
|
|
sapp_js_focus_textfield();
|
|
}
|
|
if (_sapp_emsc_wants_hide_keyboard) {
|
|
/* unfocus the text input field */
|
|
if (_sapp_emsc_input_created) {
|
|
_sapp.onscreen_keyboard_shown = false;
|
|
_sapp_emsc_wants_hide_keyboard = false;
|
|
sapp_js_unfocus_textfield();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* actually showing the onscreen keyboard must be initiated from a JS
|
|
input event handler, so we'll just keep track of the desired
|
|
state, and the actual state change will happen with the next input event
|
|
*/
|
|
_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) {
|
|
if (show) {
|
|
_sapp_emsc_wants_show_keyboard = true;
|
|
}
|
|
else {
|
|
_sapp_emsc_wants_hide_keyboard = true;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
|
|
double w, h;
|
|
emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h);
|
|
/* The above method might report zero when toggling HTML5 fullscreen,
|
|
in that case use the window's inner width reported by the
|
|
emscripten event. This works ok when toggling *into* fullscreen
|
|
but doesn't properly restore the previous canvas size when switching
|
|
back from fullscreen.
|
|
|
|
In general, due to the HTML5's fullscreen API's flaky nature it is
|
|
recommended to use 'soft fullscreen' (stretching the WebGL canvas
|
|
over the browser windows client rect) with a CSS definition like this:
|
|
|
|
position: absolute;
|
|
top: 0px;
|
|
left: 0px;
|
|
margin: 0px;
|
|
border: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
display: block;
|
|
*/
|
|
if (w < 1.0) {
|
|
w = ui_event->windowInnerWidth;
|
|
}
|
|
else {
|
|
_sapp.window_width = (int) w;
|
|
}
|
|
if (h < 1.0) {
|
|
h = ui_event->windowInnerHeight;
|
|
}
|
|
else {
|
|
_sapp.window_height = (int) h;
|
|
}
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp.dpi_scale = emscripten_get_device_pixel_ratio();
|
|
}
|
|
_sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
|
|
_sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
|
|
SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
|
|
emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _sapp.framebuffer_width, _sapp.framebuffer_height);
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_RESIZED);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) {
|
|
_SOKOL_UNUSED(time);
|
|
_SOKOL_UNUSED(userData);
|
|
_sapp_frame();
|
|
return EM_TRUE;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_context_cb(int emsc_type, const void* reserved, void* user_data) {
|
|
sapp_event_type type;
|
|
switch (emsc_type) {
|
|
case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break;
|
|
case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break;
|
|
default: type = SAPP_EVENTTYPE_INVALID; break;
|
|
}
|
|
if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) {
|
|
_sapp_init_event(type);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) {
|
|
_sapp.mouse_x = (emsc_event->targetX * _sapp.dpi_scale);
|
|
_sapp.mouse_y = (emsc_event->targetY * _sapp.dpi_scale);
|
|
if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) {
|
|
sapp_event_type type;
|
|
bool is_button_event = false;
|
|
switch (emsc_type) {
|
|
case EMSCRIPTEN_EVENT_MOUSEDOWN:
|
|
type = SAPP_EVENTTYPE_MOUSE_DOWN;
|
|
is_button_event = true;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_MOUSEUP:
|
|
type = SAPP_EVENTTYPE_MOUSE_UP;
|
|
is_button_event = true;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_MOUSEMOVE:
|
|
type = SAPP_EVENTTYPE_MOUSE_MOVE;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_MOUSEENTER:
|
|
type = SAPP_EVENTTYPE_MOUSE_ENTER;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_MOUSELEAVE:
|
|
type = SAPP_EVENTTYPE_MOUSE_LEAVE;
|
|
break;
|
|
default:
|
|
type = SAPP_EVENTTYPE_INVALID;
|
|
break;
|
|
}
|
|
if (type != SAPP_EVENTTYPE_INVALID) {
|
|
_sapp_init_event(type);
|
|
if (emsc_event->ctrlKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (emsc_event->shiftKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (emsc_event->altKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (emsc_event->metaKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
if (is_button_event) {
|
|
switch (emsc_event->button) {
|
|
case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break;
|
|
case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break;
|
|
case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break;
|
|
default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break;
|
|
}
|
|
}
|
|
else {
|
|
_sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
|
|
}
|
|
_sapp.event.mouse_x = _sapp.mouse_x;
|
|
_sapp.event.mouse_y = _sapp.mouse_y;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
_sapp_emsc_update_keyboard_state();
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
|
|
if (emsc_event->mouse.ctrlKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (emsc_event->mouse.shiftKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (emsc_event->mouse.altKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (emsc_event->mouse.metaKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
_sapp.event.scroll_x = -0.1 * (float)emsc_event->deltaX;
|
|
_sapp.event.scroll_y = -0.1 * (float)emsc_event->deltaY;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
_sapp_emsc_update_keyboard_state();
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) {
|
|
bool retval = true;
|
|
if (_sapp_events_enabled()) {
|
|
sapp_event_type type;
|
|
switch (emsc_type) {
|
|
case EMSCRIPTEN_EVENT_KEYDOWN:
|
|
type = SAPP_EVENTTYPE_KEY_DOWN;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_KEYUP:
|
|
type = SAPP_EVENTTYPE_KEY_UP;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_KEYPRESS:
|
|
type = SAPP_EVENTTYPE_CHAR;
|
|
break;
|
|
default:
|
|
type = SAPP_EVENTTYPE_INVALID;
|
|
break;
|
|
}
|
|
if (type != SAPP_EVENTTYPE_INVALID) {
|
|
bool send_keyup_followup = false;
|
|
_sapp_init_event(type);
|
|
_sapp.event.key_repeat = emsc_event->repeat;
|
|
if (emsc_event->ctrlKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (emsc_event->shiftKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (emsc_event->altKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (emsc_event->metaKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
if (type == SAPP_EVENTTYPE_CHAR) {
|
|
_sapp.event.char_code = emsc_event->charCode;
|
|
/* workaround to make Cmd+V work on Safari */
|
|
if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) {
|
|
retval = false;
|
|
}
|
|
}
|
|
else {
|
|
_sapp.event.key_code = _sapp_translate_key(emsc_event->keyCode);
|
|
/* Special hack for macOS: if the Super key is pressed, macOS doesn't
|
|
send keyUp events. As a workaround, to prevent keys from
|
|
"sticking", we'll send a keyup event following a keydown
|
|
when the SUPER key is pressed
|
|
*/
|
|
if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
|
|
(_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
|
|
(_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
|
|
(_sapp.event.modifiers & SAPP_MODIFIER_SUPER))
|
|
{
|
|
send_keyup_followup = true;
|
|
}
|
|
/* only forward a certain key ranges to the browser */
|
|
switch (_sapp.event.key_code) {
|
|
case SAPP_KEYCODE_WORLD_1:
|
|
case SAPP_KEYCODE_WORLD_2:
|
|
case SAPP_KEYCODE_ESCAPE:
|
|
case SAPP_KEYCODE_ENTER:
|
|
case SAPP_KEYCODE_TAB:
|
|
case SAPP_KEYCODE_BACKSPACE:
|
|
case SAPP_KEYCODE_INSERT:
|
|
case SAPP_KEYCODE_DELETE:
|
|
case SAPP_KEYCODE_RIGHT:
|
|
case SAPP_KEYCODE_LEFT:
|
|
case SAPP_KEYCODE_DOWN:
|
|
case SAPP_KEYCODE_UP:
|
|
case SAPP_KEYCODE_PAGE_UP:
|
|
case SAPP_KEYCODE_PAGE_DOWN:
|
|
case SAPP_KEYCODE_HOME:
|
|
case SAPP_KEYCODE_END:
|
|
case SAPP_KEYCODE_CAPS_LOCK:
|
|
case SAPP_KEYCODE_SCROLL_LOCK:
|
|
case SAPP_KEYCODE_NUM_LOCK:
|
|
case SAPP_KEYCODE_PRINT_SCREEN:
|
|
case SAPP_KEYCODE_PAUSE:
|
|
case SAPP_KEYCODE_F1:
|
|
case SAPP_KEYCODE_F2:
|
|
case SAPP_KEYCODE_F3:
|
|
case SAPP_KEYCODE_F4:
|
|
case SAPP_KEYCODE_F5:
|
|
case SAPP_KEYCODE_F6:
|
|
case SAPP_KEYCODE_F7:
|
|
case SAPP_KEYCODE_F8:
|
|
case SAPP_KEYCODE_F9:
|
|
case SAPP_KEYCODE_F10:
|
|
case SAPP_KEYCODE_F11:
|
|
case SAPP_KEYCODE_F12:
|
|
case SAPP_KEYCODE_F13:
|
|
case SAPP_KEYCODE_F14:
|
|
case SAPP_KEYCODE_F15:
|
|
case SAPP_KEYCODE_F16:
|
|
case SAPP_KEYCODE_F17:
|
|
case SAPP_KEYCODE_F18:
|
|
case SAPP_KEYCODE_F19:
|
|
case SAPP_KEYCODE_F20:
|
|
case SAPP_KEYCODE_F21:
|
|
case SAPP_KEYCODE_F22:
|
|
case SAPP_KEYCODE_F23:
|
|
case SAPP_KEYCODE_F24:
|
|
case SAPP_KEYCODE_F25:
|
|
case SAPP_KEYCODE_LEFT_SHIFT:
|
|
case SAPP_KEYCODE_LEFT_CONTROL:
|
|
case SAPP_KEYCODE_LEFT_ALT:
|
|
case SAPP_KEYCODE_LEFT_SUPER:
|
|
case SAPP_KEYCODE_RIGHT_SHIFT:
|
|
case SAPP_KEYCODE_RIGHT_CONTROL:
|
|
case SAPP_KEYCODE_RIGHT_ALT:
|
|
case SAPP_KEYCODE_RIGHT_SUPER:
|
|
case SAPP_KEYCODE_MENU:
|
|
/* consume the event */
|
|
break;
|
|
default:
|
|
/* forward key to browser */
|
|
retval = false;
|
|
break;
|
|
}
|
|
}
|
|
if (_sapp_call_event(&_sapp.event)) {
|
|
/* consume event via sapp_consume_event() */
|
|
retval = true;
|
|
}
|
|
if (send_keyup_followup) {
|
|
_sapp.event.type = SAPP_EVENTTYPE_KEY_UP;
|
|
if (_sapp_call_event(&_sapp.event)) {
|
|
retval = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_sapp_emsc_update_keyboard_state();
|
|
return retval;
|
|
}
|
|
|
|
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) {
|
|
bool retval = true;
|
|
if (_sapp_events_enabled()) {
|
|
sapp_event_type type;
|
|
switch (emsc_type) {
|
|
case EMSCRIPTEN_EVENT_TOUCHSTART:
|
|
type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_TOUCHMOVE:
|
|
type = SAPP_EVENTTYPE_TOUCHES_MOVED;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_TOUCHEND:
|
|
type = SAPP_EVENTTYPE_TOUCHES_ENDED;
|
|
break;
|
|
case EMSCRIPTEN_EVENT_TOUCHCANCEL:
|
|
type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
|
|
break;
|
|
default:
|
|
type = SAPP_EVENTTYPE_INVALID;
|
|
retval = false;
|
|
break;
|
|
}
|
|
if (type != SAPP_EVENTTYPE_INVALID) {
|
|
_sapp_init_event(type);
|
|
if (emsc_event->ctrlKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (emsc_event->shiftKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (emsc_event->altKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (emsc_event->metaKey) {
|
|
_sapp.event.modifiers |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
_sapp.event.num_touches = emsc_event->numTouches;
|
|
if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
|
|
_sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
|
|
}
|
|
for (int i = 0; i < _sapp.event.num_touches; i++) {
|
|
const EmscriptenTouchPoint* src = &emsc_event->touches[i];
|
|
sapp_touchpoint* dst = &_sapp.event.touches[i];
|
|
dst->identifier = src->identifier;
|
|
dst->pos_x = src->targetX * _sapp.dpi_scale;
|
|
dst->pos_y = src->targetY * _sapp.dpi_scale;
|
|
dst->changed = src->isChanged;
|
|
}
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
_sapp_emsc_update_keyboard_state();
|
|
return retval;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_emsc_init_keytable(void) {
|
|
_sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE;
|
|
_sapp.keycodes[9] = SAPP_KEYCODE_TAB;
|
|
_sapp.keycodes[13] = SAPP_KEYCODE_ENTER;
|
|
_sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT;
|
|
_sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL;
|
|
_sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT;
|
|
_sapp.keycodes[19] = SAPP_KEYCODE_PAUSE;
|
|
_sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE;
|
|
_sapp.keycodes[32] = SAPP_KEYCODE_SPACE;
|
|
_sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP;
|
|
_sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN;
|
|
_sapp.keycodes[35] = SAPP_KEYCODE_END;
|
|
_sapp.keycodes[36] = SAPP_KEYCODE_HOME;
|
|
_sapp.keycodes[37] = SAPP_KEYCODE_LEFT;
|
|
_sapp.keycodes[38] = SAPP_KEYCODE_UP;
|
|
_sapp.keycodes[39] = SAPP_KEYCODE_RIGHT;
|
|
_sapp.keycodes[40] = SAPP_KEYCODE_DOWN;
|
|
_sapp.keycodes[45] = SAPP_KEYCODE_INSERT;
|
|
_sapp.keycodes[46] = SAPP_KEYCODE_DELETE;
|
|
_sapp.keycodes[48] = SAPP_KEYCODE_0;
|
|
_sapp.keycodes[49] = SAPP_KEYCODE_1;
|
|
_sapp.keycodes[50] = SAPP_KEYCODE_2;
|
|
_sapp.keycodes[51] = SAPP_KEYCODE_3;
|
|
_sapp.keycodes[52] = SAPP_KEYCODE_4;
|
|
_sapp.keycodes[53] = SAPP_KEYCODE_5;
|
|
_sapp.keycodes[54] = SAPP_KEYCODE_6;
|
|
_sapp.keycodes[55] = SAPP_KEYCODE_7;
|
|
_sapp.keycodes[56] = SAPP_KEYCODE_8;
|
|
_sapp.keycodes[57] = SAPP_KEYCODE_9;
|
|
_sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON;
|
|
_sapp.keycodes[64] = SAPP_KEYCODE_EQUAL;
|
|
_sapp.keycodes[65] = SAPP_KEYCODE_A;
|
|
_sapp.keycodes[66] = SAPP_KEYCODE_B;
|
|
_sapp.keycodes[67] = SAPP_KEYCODE_C;
|
|
_sapp.keycodes[68] = SAPP_KEYCODE_D;
|
|
_sapp.keycodes[69] = SAPP_KEYCODE_E;
|
|
_sapp.keycodes[70] = SAPP_KEYCODE_F;
|
|
_sapp.keycodes[71] = SAPP_KEYCODE_G;
|
|
_sapp.keycodes[72] = SAPP_KEYCODE_H;
|
|
_sapp.keycodes[73] = SAPP_KEYCODE_I;
|
|
_sapp.keycodes[74] = SAPP_KEYCODE_J;
|
|
_sapp.keycodes[75] = SAPP_KEYCODE_K;
|
|
_sapp.keycodes[76] = SAPP_KEYCODE_L;
|
|
_sapp.keycodes[77] = SAPP_KEYCODE_M;
|
|
_sapp.keycodes[78] = SAPP_KEYCODE_N;
|
|
_sapp.keycodes[79] = SAPP_KEYCODE_O;
|
|
_sapp.keycodes[80] = SAPP_KEYCODE_P;
|
|
_sapp.keycodes[81] = SAPP_KEYCODE_Q;
|
|
_sapp.keycodes[82] = SAPP_KEYCODE_R;
|
|
_sapp.keycodes[83] = SAPP_KEYCODE_S;
|
|
_sapp.keycodes[84] = SAPP_KEYCODE_T;
|
|
_sapp.keycodes[85] = SAPP_KEYCODE_U;
|
|
_sapp.keycodes[86] = SAPP_KEYCODE_V;
|
|
_sapp.keycodes[87] = SAPP_KEYCODE_W;
|
|
_sapp.keycodes[88] = SAPP_KEYCODE_X;
|
|
_sapp.keycodes[89] = SAPP_KEYCODE_Y;
|
|
_sapp.keycodes[90] = SAPP_KEYCODE_Z;
|
|
_sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER;
|
|
_sapp.keycodes[93] = SAPP_KEYCODE_MENU;
|
|
_sapp.keycodes[96] = SAPP_KEYCODE_KP_0;
|
|
_sapp.keycodes[97] = SAPP_KEYCODE_KP_1;
|
|
_sapp.keycodes[98] = SAPP_KEYCODE_KP_2;
|
|
_sapp.keycodes[99] = SAPP_KEYCODE_KP_3;
|
|
_sapp.keycodes[100] = SAPP_KEYCODE_KP_4;
|
|
_sapp.keycodes[101] = SAPP_KEYCODE_KP_5;
|
|
_sapp.keycodes[102] = SAPP_KEYCODE_KP_6;
|
|
_sapp.keycodes[103] = SAPP_KEYCODE_KP_7;
|
|
_sapp.keycodes[104] = SAPP_KEYCODE_KP_8;
|
|
_sapp.keycodes[105] = SAPP_KEYCODE_KP_9;
|
|
_sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY;
|
|
_sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD;
|
|
_sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT;
|
|
_sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL;
|
|
_sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE;
|
|
_sapp.keycodes[112] = SAPP_KEYCODE_F1;
|
|
_sapp.keycodes[113] = SAPP_KEYCODE_F2;
|
|
_sapp.keycodes[114] = SAPP_KEYCODE_F3;
|
|
_sapp.keycodes[115] = SAPP_KEYCODE_F4;
|
|
_sapp.keycodes[116] = SAPP_KEYCODE_F5;
|
|
_sapp.keycodes[117] = SAPP_KEYCODE_F6;
|
|
_sapp.keycodes[118] = SAPP_KEYCODE_F7;
|
|
_sapp.keycodes[119] = SAPP_KEYCODE_F8;
|
|
_sapp.keycodes[120] = SAPP_KEYCODE_F9;
|
|
_sapp.keycodes[121] = SAPP_KEYCODE_F10;
|
|
_sapp.keycodes[122] = SAPP_KEYCODE_F11;
|
|
_sapp.keycodes[123] = SAPP_KEYCODE_F12;
|
|
_sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK;
|
|
_sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK;
|
|
_sapp.keycodes[173] = SAPP_KEYCODE_MINUS;
|
|
_sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON;
|
|
_sapp.keycodes[187] = SAPP_KEYCODE_EQUAL;
|
|
_sapp.keycodes[188] = SAPP_KEYCODE_COMMA;
|
|
_sapp.keycodes[189] = SAPP_KEYCODE_MINUS;
|
|
_sapp.keycodes[190] = SAPP_KEYCODE_PERIOD;
|
|
_sapp.keycodes[191] = SAPP_KEYCODE_SLASH;
|
|
_sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT;
|
|
_sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET;
|
|
_sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH;
|
|
_sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET;
|
|
_sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE;
|
|
_sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_emsc_init_clipboard(void) {
|
|
sapp_js_init_clipboard();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
|
|
_sapp_init_state(desc);
|
|
_sapp_emsc_init_keytable();
|
|
if (_sapp.clipboard_enabled) {
|
|
_sapp_emsc_init_clipboard();
|
|
}
|
|
double w, h;
|
|
if (_sapp.desc.html5_canvas_resize) {
|
|
w = (double) _sapp.desc.width;
|
|
h = (double) _sapp.desc.height;
|
|
}
|
|
else {
|
|
emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h);
|
|
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed);
|
|
}
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp.dpi_scale = emscripten_get_device_pixel_ratio();
|
|
}
|
|
_sapp.window_width = (int) w;
|
|
_sapp.window_height = (int) h;
|
|
_sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
|
|
_sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
|
|
emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _sapp.framebuffer_width, _sapp.framebuffer_height);
|
|
|
|
EmscriptenWebGLContextAttributes attrs;
|
|
emscripten_webgl_init_context_attributes(&attrs);
|
|
attrs.alpha = _sapp.desc.alpha;
|
|
attrs.depth = true;
|
|
attrs.stencil = true;
|
|
attrs.antialias = _sapp.sample_count > 1;
|
|
attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha;
|
|
attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer;
|
|
attrs.enableExtensionsByDefault = true;
|
|
#if defined(SOKOL_GLES3)
|
|
if (_sapp.desc.gl_force_gles2) {
|
|
attrs.majorVersion = 1;
|
|
_sapp.gles2_fallback = true;
|
|
}
|
|
else {
|
|
attrs.majorVersion = 2;
|
|
}
|
|
#endif
|
|
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs);
|
|
if (!ctx) {
|
|
attrs.majorVersion = 1;
|
|
ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs);
|
|
_sapp.gles2_fallback = true;
|
|
}
|
|
emscripten_webgl_make_context_current(ctx);
|
|
|
|
/* some WebGL extension are not enabled automatically by emscripten */
|
|
emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc");
|
|
|
|
_sapp.valid = true;
|
|
emscripten_set_mousedown_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb);
|
|
emscripten_set_mouseup_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb);
|
|
emscripten_set_mousemove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb);
|
|
emscripten_set_mouseenter_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb);
|
|
emscripten_set_mouseleave_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb);
|
|
emscripten_set_wheel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_wheel_cb);
|
|
emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
|
|
emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
|
|
emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
|
|
emscripten_set_touchstart_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb);
|
|
emscripten_set_touchmove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb);
|
|
emscripten_set_touchend_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb);
|
|
emscripten_set_touchcancel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb);
|
|
emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb);
|
|
emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb);
|
|
emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0);
|
|
|
|
sapp_js_hook_beforeunload();
|
|
|
|
// NOT A BUG: do not call _sapp_discard_state()
|
|
}
|
|
|
|
#if !defined(SOKOL_NO_ENTRY)
|
|
int main(int argc, char* argv[]) {
|
|
sapp_desc desc = sokol_main(argc, argv);
|
|
_sapp_run(&desc);
|
|
return 0;
|
|
}
|
|
#endif /* SOKOL_NO_ENTRY */
|
|
#endif /* __EMSCRIPTEN__ */
|
|
|
|
/*== MISC GL SUPPORT FUNCTIONS ================================================*/
|
|
#if defined(SOKOL_GLCORE33)
|
|
typedef struct {
|
|
int red_bits;
|
|
int green_bits;
|
|
int blue_bits;
|
|
int alpha_bits;
|
|
int depth_bits;
|
|
int stencil_bits;
|
|
int samples;
|
|
bool doublebuffer;
|
|
uintptr_t handle;
|
|
} _sapp_gl_fbconfig;
|
|
|
|
_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) {
|
|
memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig));
|
|
/* -1 means "don't care" */
|
|
fbconfig->red_bits = -1;
|
|
fbconfig->green_bits = -1;
|
|
fbconfig->blue_bits = -1;
|
|
fbconfig->alpha_bits = -1;
|
|
fbconfig->depth_bits = -1;
|
|
fbconfig->stencil_bits = -1;
|
|
fbconfig->samples = -1;
|
|
}
|
|
|
|
_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, unsigned int count) {
|
|
unsigned int i;
|
|
unsigned int missing, least_missing = 1000000;
|
|
unsigned int color_diff, least_color_diff = 10000000;
|
|
unsigned int extra_diff, least_extra_diff = 10000000;
|
|
const _sapp_gl_fbconfig* current;
|
|
const _sapp_gl_fbconfig* closest = NULL;
|
|
for (i = 0; i < count; i++) {
|
|
current = alternatives + i;
|
|
if (desired->doublebuffer != current->doublebuffer) {
|
|
continue;
|
|
}
|
|
missing = 0;
|
|
if (desired->alpha_bits > 0 && current->alpha_bits == 0) {
|
|
missing++;
|
|
}
|
|
if (desired->depth_bits > 0 && current->depth_bits == 0) {
|
|
missing++;
|
|
}
|
|
if (desired->stencil_bits > 0 && current->stencil_bits == 0) {
|
|
missing++;
|
|
}
|
|
if (desired->samples > 0 && current->samples == 0) {
|
|
/* Technically, several multisampling buffers could be
|
|
involved, but that's a lower level implementation detail and
|
|
not important to us here, so we count them as one
|
|
*/
|
|
missing++;
|
|
}
|
|
|
|
/* These polynomials make many small channel size differences matter
|
|
less than one large channel size difference
|
|
Calculate color channel size difference value
|
|
*/
|
|
color_diff = 0;
|
|
if (desired->red_bits != -1) {
|
|
color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
|
|
}
|
|
if (desired->green_bits != -1) {
|
|
color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
|
|
}
|
|
if (desired->blue_bits != -1) {
|
|
color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
|
|
}
|
|
|
|
/* Calculate non-color channel size difference value */
|
|
extra_diff = 0;
|
|
if (desired->alpha_bits != -1) {
|
|
extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
|
|
}
|
|
if (desired->depth_bits != -1) {
|
|
extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
|
|
}
|
|
if (desired->stencil_bits != -1) {
|
|
extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
|
|
}
|
|
if (desired->samples != -1) {
|
|
extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
|
|
}
|
|
|
|
/* Figure out if the current one is better than the best one found so far
|
|
Least number of missing buffers is the most important heuristic,
|
|
then color buffer size match and lastly size match for other buffers
|
|
*/
|
|
if (missing < least_missing) {
|
|
closest = current;
|
|
}
|
|
else if (missing == least_missing) {
|
|
if ((color_diff < least_color_diff) ||
|
|
(color_diff == least_color_diff && extra_diff < least_extra_diff))
|
|
{
|
|
closest = current;
|
|
}
|
|
}
|
|
if (current == closest) {
|
|
least_missing = missing;
|
|
least_color_diff = color_diff;
|
|
least_extra_diff = extra_diff;
|
|
}
|
|
}
|
|
return closest;
|
|
}
|
|
#endif
|
|
|
|
/*== WINDOWS ==================================================================*/
|
|
#if defined(_WIN32)
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <shellapi.h>
|
|
#pragma comment (lib, "Shell32.lib")
|
|
|
|
#if defined(SOKOL_D3D11)
|
|
#ifndef D3D11_NO_HELPERS
|
|
#define D3D11_NO_HELPERS
|
|
#endif
|
|
#ifndef CINTERFACE
|
|
#define CINTERFACE
|
|
#endif
|
|
#ifndef COBJMACROS
|
|
#define COBJMACROS
|
|
#endif
|
|
#include <windows.h>
|
|
#include <d3d11.h>
|
|
#include <dxgi.h>
|
|
#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
|
|
#pragma comment (lib, "WindowsApp.lib")
|
|
#else
|
|
#pragma comment (lib, "user32.lib")
|
|
#pragma comment (lib, "dxgi.lib")
|
|
#pragma comment (lib, "d3d11.lib")
|
|
#pragma comment (lib, "dxguid.lib")
|
|
#endif
|
|
#endif
|
|
|
|
/* see https://github.com/floooh/sokol/issues/138 */
|
|
#ifndef WM_MOUSEHWHEEL
|
|
#define WM_MOUSEHWHEEL (0x020E)
|
|
#endif
|
|
|
|
#ifndef DPI_ENUMS_DECLARED
|
|
typedef enum PROCESS_DPI_AWARENESS
|
|
{
|
|
PROCESS_DPI_UNAWARE = 0,
|
|
PROCESS_SYSTEM_DPI_AWARE = 1,
|
|
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
|
} PROCESS_DPI_AWARENESS;
|
|
typedef enum MONITOR_DPI_TYPE {
|
|
MDT_EFFECTIVE_DPI = 0,
|
|
MDT_ANGULAR_DPI = 1,
|
|
MDT_RAW_DPI = 2,
|
|
MDT_DEFAULT = MDT_EFFECTIVE_DPI
|
|
} MONITOR_DPI_TYPE;
|
|
#endif /*DPI_ENUMS_DECLARED*/
|
|
|
|
static HWND _sapp_win32_hwnd;
|
|
static HDC _sapp_win32_dc;
|
|
static bool _sapp_win32_in_create_window;
|
|
static bool _sapp_win32_dpi_aware;
|
|
static float _sapp_win32_content_scale;
|
|
static float _sapp_win32_window_scale;
|
|
static float _sapp_win32_mouse_scale;
|
|
static bool _sapp_win32_iconified;
|
|
typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void);
|
|
typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS);
|
|
typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
|
|
static SETPROCESSDPIAWARE_T _sapp_win32_setprocessdpiaware;
|
|
static SETPROCESSDPIAWARENESS_T _sapp_win32_setprocessdpiawareness;
|
|
static GETDPIFORMONITOR_T _sapp_win32_getdpiformonitor;
|
|
#if defined(SOKOL_D3D11)
|
|
static ID3D11Device* _sapp_d3d11_device;
|
|
static ID3D11DeviceContext* _sapp_d3d11_device_context;
|
|
static DXGI_SWAP_CHAIN_DESC _sapp_dxgi_swap_chain_desc;
|
|
static IDXGISwapChain* _sapp_dxgi_swap_chain;
|
|
static ID3D11Texture2D* _sapp_d3d11_rt;
|
|
static ID3D11RenderTargetView* _sapp_d3d11_rtv;
|
|
static ID3D11Texture2D* _sapp_d3d11_ds;
|
|
static ID3D11DepthStencilView* _sapp_d3d11_dsv;
|
|
#endif
|
|
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
|
|
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
|
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
|
#define WGL_PIXEL_TYPE_ARB 0x2013
|
|
#define WGL_TYPE_RGBA_ARB 0x202b
|
|
#define WGL_ACCELERATION_ARB 0x2003
|
|
#define WGL_NO_ACCELERATION_ARB 0x2025
|
|
#define WGL_RED_BITS_ARB 0x2015
|
|
#define WGL_RED_SHIFT_ARB 0x2016
|
|
#define WGL_GREEN_BITS_ARB 0x2017
|
|
#define WGL_GREEN_SHIFT_ARB 0x2018
|
|
#define WGL_BLUE_BITS_ARB 0x2019
|
|
#define WGL_BLUE_SHIFT_ARB 0x201a
|
|
#define WGL_ALPHA_BITS_ARB 0x201b
|
|
#define WGL_ALPHA_SHIFT_ARB 0x201c
|
|
#define WGL_ACCUM_BITS_ARB 0x201d
|
|
#define WGL_ACCUM_RED_BITS_ARB 0x201e
|
|
#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
|
|
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
|
|
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
|
|
#define WGL_DEPTH_BITS_ARB 0x2022
|
|
#define WGL_STENCIL_BITS_ARB 0x2023
|
|
#define WGL_AUX_BUFFERS_ARB 0x2024
|
|
#define WGL_STEREO_ARB 0x2012
|
|
#define WGL_DOUBLE_BUFFER_ARB 0x2011
|
|
#define WGL_SAMPLES_ARB 0x2042
|
|
#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
|
|
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
|
|
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
|
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
|
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
|
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
|
|
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
|
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
|
|
#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
|
|
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
|
#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261
|
|
#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
|
|
#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
|
|
#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
|
|
#define WGL_COLORSPACE_EXT 0x309d
|
|
#define WGL_COLORSPACE_SRGB_EXT 0x3089
|
|
#define ERROR_INVALID_VERSION_ARB 0x2095
|
|
#define ERROR_INVALID_PROFILE_ARB 0x2096
|
|
#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
|
|
typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
|
|
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*);
|
|
typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void);
|
|
typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);
|
|
typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*);
|
|
typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC);
|
|
typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC);
|
|
typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR);
|
|
typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void);
|
|
typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC);
|
|
static HINSTANCE _sapp_opengl32;
|
|
static HGLRC _sapp_gl_ctx;
|
|
static PFN_wglCreateContext _sapp_wglCreateContext;
|
|
static PFN_wglDeleteContext _sapp_wglDeleteContext;
|
|
static PFN_wglGetProcAddress _sapp_wglGetProcAddress;
|
|
static PFN_wglGetCurrentDC _sapp_wglGetCurrentDC;
|
|
static PFN_wglMakeCurrent _sapp_wglMakeCurrent;
|
|
static PFNWGLSWAPINTERVALEXTPROC _sapp_SwapIntervalEXT;
|
|
static PFNWGLGETPIXELFORMATATTRIBIVARBPROC _sapp_GetPixelFormatAttribivARB;
|
|
static PFNWGLGETEXTENSIONSSTRINGEXTPROC _sapp_GetExtensionsStringEXT;
|
|
static PFNWGLGETEXTENSIONSSTRINGARBPROC _sapp_GetExtensionsStringARB;
|
|
static PFNWGLCREATECONTEXTATTRIBSARBPROC _sapp_CreateContextAttribsARB;
|
|
static bool _sapp_ext_swap_control;
|
|
static bool _sapp_arb_multisample;
|
|
static bool _sapp_arb_pixel_format;
|
|
static bool _sapp_arb_create_context;
|
|
static bool _sapp_arb_create_context_profile;
|
|
static HWND _sapp_win32_msg_hwnd;
|
|
static HDC _sapp_win32_msg_dc;
|
|
|
|
/* NOTE: the optional GL loader only contains the GL constants and functions required for sokol_gfx.h, if you need
|
|
more, you'll need to use you own gl header-generator/loader
|
|
*/
|
|
#if !defined(SOKOL_WIN32_NO_GL_LOADER)
|
|
#if defined(SOKOL_GLCORE33)
|
|
#define __gl_h_ 1
|
|
#define __gl32_h_ 1
|
|
#define __gl31_h_ 1
|
|
#define __GL_H__ 1
|
|
#define __glext_h_ 1
|
|
#define __GLEXT_H_ 1
|
|
#define __gltypes_h_ 1
|
|
#define __glcorearb_h_ 1
|
|
#define __gl_glcorearb_h_ 1
|
|
#define GL_APIENTRY APIENTRY
|
|
|
|
typedef unsigned int GLenum;
|
|
typedef unsigned int GLuint;
|
|
typedef int GLsizei;
|
|
typedef char GLchar;
|
|
typedef ptrdiff_t GLintptr;
|
|
typedef ptrdiff_t GLsizeiptr;
|
|
typedef double GLclampd;
|
|
typedef unsigned short GLushort;
|
|
typedef unsigned char GLubyte;
|
|
typedef unsigned char GLboolean;
|
|
typedef uint64_t GLuint64;
|
|
typedef double GLdouble;
|
|
typedef unsigned short GLhalf;
|
|
typedef float GLclampf;
|
|
typedef unsigned int GLbitfield;
|
|
typedef signed char GLbyte;
|
|
typedef short GLshort;
|
|
typedef void GLvoid;
|
|
typedef int64_t GLint64;
|
|
typedef float GLfloat;
|
|
typedef struct __GLsync * GLsync;
|
|
typedef int GLint;
|
|
#define GL_INT_2_10_10_10_REV 0x8D9F
|
|
#define GL_R32F 0x822E
|
|
#define GL_PROGRAM_POINT_SIZE 0x8642
|
|
#define GL_STENCIL_ATTACHMENT 0x8D20
|
|
#define GL_DEPTH_ATTACHMENT 0x8D00
|
|
#define GL_COLOR_ATTACHMENT2 0x8CE2
|
|
#define GL_COLOR_ATTACHMENT0 0x8CE0
|
|
#define GL_R16F 0x822D
|
|
#define GL_COLOR_ATTACHMENT22 0x8CF6
|
|
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
|
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
|
|
#define GL_NUM_EXTENSIONS 0x821D
|
|
#define GL_INFO_LOG_LENGTH 0x8B84
|
|
#define GL_VERTEX_SHADER 0x8B31
|
|
#define GL_INCR 0x1E02
|
|
#define GL_DYNAMIC_DRAW 0x88E8
|
|
#define GL_STATIC_DRAW 0x88E4
|
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
|
|
#define GL_TEXTURE_CUBE_MAP 0x8513
|
|
#define GL_FUNC_SUBTRACT 0x800A
|
|
#define GL_FUNC_REVERSE_SUBTRACT 0x800B
|
|
#define GL_CONSTANT_COLOR 0x8001
|
|
#define GL_DECR_WRAP 0x8508
|
|
#define GL_R8 0x8229
|
|
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
|
|
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
|
|
#define GL_SHORT 0x1402
|
|
#define GL_DEPTH_TEST 0x0B71
|
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
|
|
#define GL_LINK_STATUS 0x8B82
|
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
|
|
#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
|
|
#define GL_RGBA16F 0x881A
|
|
#define GL_CONSTANT_ALPHA 0x8003
|
|
#define GL_READ_FRAMEBUFFER 0x8CA8
|
|
#define GL_TEXTURE0 0x84C0
|
|
#define GL_TEXTURE_MIN_LOD 0x813A
|
|
#define GL_CLAMP_TO_EDGE 0x812F
|
|
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
|
|
#define GL_TEXTURE_WRAP_R 0x8072
|
|
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
|
|
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
|
|
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
|
|
#define GL_SRC_ALPHA_SATURATE 0x0308
|
|
#define GL_STREAM_DRAW 0x88E0
|
|
#define GL_ONE 1
|
|
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
|
|
#define GL_RGB10_A2 0x8059
|
|
#define GL_RGBA8 0x8058
|
|
#define GL_COLOR_ATTACHMENT1 0x8CE1
|
|
#define GL_RGBA4 0x8056
|
|
#define GL_RGB8 0x8051
|
|
#define GL_ARRAY_BUFFER 0x8892
|
|
#define GL_STENCIL 0x1802
|
|
#define GL_TEXTURE_2D 0x0DE1
|
|
#define GL_DEPTH 0x1801
|
|
#define GL_FRONT 0x0404
|
|
#define GL_STENCIL_BUFFER_BIT 0x00000400
|
|
#define GL_REPEAT 0x2901
|
|
#define GL_RGBA 0x1908
|
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
|
|
#define GL_DECR 0x1E03
|
|
#define GL_FRAGMENT_SHADER 0x8B30
|
|
#define GL_FLOAT 0x1406
|
|
#define GL_TEXTURE_MAX_LOD 0x813B
|
|
#define GL_DEPTH_COMPONENT 0x1902
|
|
#define GL_ONE_MINUS_DST_ALPHA 0x0305
|
|
#define GL_COLOR 0x1800
|
|
#define GL_TEXTURE_2D_ARRAY 0x8C1A
|
|
#define GL_TRIANGLES 0x0004
|
|
#define GL_UNSIGNED_BYTE 0x1401
|
|
#define GL_TEXTURE_MAG_FILTER 0x2800
|
|
#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
|
|
#define GL_NONE 0
|
|
#define GL_SRC_COLOR 0x0300
|
|
#define GL_BYTE 0x1400
|
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
|
|
#define GL_LINE_STRIP 0x0003
|
|
#define GL_TEXTURE_3D 0x806F
|
|
#define GL_CW 0x0900
|
|
#define GL_LINEAR 0x2601
|
|
#define GL_RENDERBUFFER 0x8D41
|
|
#define GL_GEQUAL 0x0206
|
|
#define GL_COLOR_BUFFER_BIT 0x00004000
|
|
#define GL_RGBA32F 0x8814
|
|
#define GL_BLEND 0x0BE2
|
|
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
|
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
|
|
#define GL_TEXTURE_WRAP_T 0x2803
|
|
#define GL_TEXTURE_WRAP_S 0x2802
|
|
#define GL_TEXTURE_MIN_FILTER 0x2801
|
|
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
|
|
#define GL_EXTENSIONS 0x1F03
|
|
#define GL_NO_ERROR 0
|
|
#define GL_REPLACE 0x1E01
|
|
#define GL_KEEP 0x1E00
|
|
#define GL_CCW 0x0901
|
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
|
|
#define GL_RGB 0x1907
|
|
#define GL_TRIANGLE_STRIP 0x0005
|
|
#define GL_FALSE 0
|
|
#define GL_ZERO 0
|
|
#define GL_CULL_FACE 0x0B44
|
|
#define GL_INVERT 0x150A
|
|
#define GL_INT 0x1404
|
|
#define GL_UNSIGNED_INT 0x1405
|
|
#define GL_UNSIGNED_SHORT 0x1403
|
|
#define GL_NEAREST 0x2600
|
|
#define GL_SCISSOR_TEST 0x0C11
|
|
#define GL_LEQUAL 0x0203
|
|
#define GL_STENCIL_TEST 0x0B90
|
|
#define GL_DITHER 0x0BD0
|
|
#define GL_DEPTH_COMPONENT16 0x81A5
|
|
#define GL_EQUAL 0x0202
|
|
#define GL_FRAMEBUFFER 0x8D40
|
|
#define GL_RGB5 0x8050
|
|
#define GL_LINES 0x0001
|
|
#define GL_DEPTH_BUFFER_BIT 0x00000100
|
|
#define GL_SRC_ALPHA 0x0302
|
|
#define GL_INCR_WRAP 0x8507
|
|
#define GL_LESS 0x0201
|
|
#define GL_MULTISAMPLE 0x809D
|
|
#define GL_FRAMEBUFFER_BINDING 0x8CA6
|
|
#define GL_BACK 0x0405
|
|
#define GL_ALWAYS 0x0207
|
|
#define GL_FUNC_ADD 0x8006
|
|
#define GL_ONE_MINUS_DST_COLOR 0x0307
|
|
#define GL_NOTEQUAL 0x0205
|
|
#define GL_DST_COLOR 0x0306
|
|
#define GL_COMPILE_STATUS 0x8B81
|
|
#define GL_RED 0x1903
|
|
#define GL_COLOR_ATTACHMENT3 0x8CE3
|
|
#define GL_DST_ALPHA 0x0304
|
|
#define GL_RGB5_A1 0x8057
|
|
#define GL_GREATER 0x0204
|
|
#define GL_POLYGON_OFFSET_FILL 0x8037
|
|
#define GL_TRUE 1
|
|
#define GL_NEVER 0x0200
|
|
#define GL_POINTS 0x0000
|
|
#define GL_ONE_MINUS_SRC_COLOR 0x0301
|
|
#define GL_MIRRORED_REPEAT 0x8370
|
|
#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
|
|
#define GL_R11F_G11F_B10F 0x8C3A
|
|
#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
|
|
#define GL_RGBA32UI 0x8D70
|
|
#define GL_RGB32UI 0x8D71
|
|
#define GL_RGBA16UI 0x8D76
|
|
#define GL_RGB16UI 0x8D77
|
|
#define GL_RGBA8UI 0x8D7C
|
|
#define GL_RGB8UI 0x8D7D
|
|
#define GL_RGBA32I 0x8D82
|
|
#define GL_RGB32I 0x8D83
|
|
#define GL_RGBA16I 0x8D88
|
|
#define GL_RGB16I 0x8D89
|
|
#define GL_RGBA8I 0x8D8E
|
|
#define GL_RGB8I 0x8D8F
|
|
#define GL_RED_INTEGER 0x8D94
|
|
#define GL_RG 0x8227
|
|
#define GL_RG_INTEGER 0x8228
|
|
#define GL_R8 0x8229
|
|
#define GL_R16 0x822A
|
|
#define GL_RG8 0x822B
|
|
#define GL_RG16 0x822C
|
|
#define GL_R16F 0x822D
|
|
#define GL_R32F 0x822E
|
|
#define GL_RG16F 0x822F
|
|
#define GL_RG32F 0x8230
|
|
#define GL_R8I 0x8231
|
|
#define GL_R8UI 0x8232
|
|
#define GL_R16I 0x8233
|
|
#define GL_R16UI 0x8234
|
|
#define GL_R32I 0x8235
|
|
#define GL_R32UI 0x8236
|
|
#define GL_RG8I 0x8237
|
|
#define GL_RG8UI 0x8238
|
|
#define GL_RG16I 0x8239
|
|
#define GL_RG16UI 0x823A
|
|
#define GL_RG32I 0x823B
|
|
#define GL_RG32UI 0x823C
|
|
#define GL_RGBA_INTEGER 0x8D99
|
|
#define GL_R8_SNORM 0x8F94
|
|
#define GL_RG8_SNORM 0x8F95
|
|
#define GL_RGB8_SNORM 0x8F96
|
|
#define GL_RGBA8_SNORM 0x8F97
|
|
#define GL_R16_SNORM 0x8F98
|
|
#define GL_RG16_SNORM 0x8F99
|
|
#define GL_RGB16_SNORM 0x8F9A
|
|
#define GL_RGBA16_SNORM 0x8F9B
|
|
#define GL_RGBA16 0x805B
|
|
#define GL_MAX_TEXTURE_SIZE 0x0D33
|
|
#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
|
|
#define GL_MAX_3D_TEXTURE_SIZE 0x8073
|
|
#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
|
|
#define GL_MAX_VERTEX_ATTRIBS 0x8869
|
|
#define GL_CLAMP_TO_BORDER 0x812D
|
|
#define GL_TEXTURE_BORDER_COLOR 0x1004
|
|
|
|
typedef void (GL_APIENTRY *PFN_glBindVertexArray)(GLuint array);
|
|
static PFN_glBindVertexArray _sapp_glBindVertexArray;
|
|
typedef void (GL_APIENTRY *PFN_glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
|
|
static PFN_glFramebufferTextureLayer _sapp_glFramebufferTextureLayer;
|
|
typedef void (GL_APIENTRY *PFN_glGenFramebuffers)(GLsizei n, GLuint * framebuffers);
|
|
static PFN_glGenFramebuffers _sapp_glGenFramebuffers;
|
|
typedef void (GL_APIENTRY *PFN_glBindFramebuffer)(GLenum target, GLuint framebuffer);
|
|
static PFN_glBindFramebuffer _sapp_glBindFramebuffer;
|
|
typedef void (GL_APIENTRY *PFN_glBindRenderbuffer)(GLenum target, GLuint renderbuffer);
|
|
static PFN_glBindRenderbuffer _sapp_glBindRenderbuffer;
|
|
typedef const GLubyte * (GL_APIENTRY *PFN_glGetStringi)(GLenum name, GLuint index);
|
|
static PFN_glGetStringi _sapp_glGetStringi;
|
|
typedef void (GL_APIENTRY *PFN_glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
|
static PFN_glClearBufferfi _sapp_glClearBufferfi;
|
|
typedef void (GL_APIENTRY *PFN_glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat * value);
|
|
static PFN_glClearBufferfv _sapp_glClearBufferfv;
|
|
typedef void (GL_APIENTRY *PFN_glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint * value);
|
|
static PFN_glClearBufferuiv _sapp_glClearBufferuiv;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteRenderbuffers)(GLsizei n, const GLuint * renderbuffers);
|
|
static PFN_glDeleteRenderbuffers _sapp_glDeleteRenderbuffers;
|
|
typedef void (GL_APIENTRY *PFN_glUniform4fv)(GLint location, GLsizei count, const GLfloat * value);
|
|
static PFN_glUniform4fv _sapp_glUniform4fv;
|
|
typedef void (GL_APIENTRY *PFN_glUniform2fv)(GLint location, GLsizei count, const GLfloat * value);
|
|
static PFN_glUniform2fv _sapp_glUniform2fv;
|
|
typedef void (GL_APIENTRY *PFN_glUseProgram)(GLuint program);
|
|
static PFN_glUseProgram _sapp_glUseProgram;
|
|
typedef void (GL_APIENTRY *PFN_glShaderSource)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length);
|
|
static PFN_glShaderSource _sapp_glShaderSource;
|
|
typedef void (GL_APIENTRY *PFN_glLinkProgram)(GLuint program);
|
|
static PFN_glLinkProgram _sapp_glLinkProgram;
|
|
typedef GLint (GL_APIENTRY *PFN_glGetUniformLocation)(GLuint program, const GLchar * name);
|
|
static PFN_glGetUniformLocation _sapp_glGetUniformLocation;
|
|
typedef void (GL_APIENTRY *PFN_glGetShaderiv)(GLuint shader, GLenum pname, GLint * params);
|
|
static PFN_glGetShaderiv _sapp_glGetShaderiv;
|
|
typedef void (GL_APIENTRY *PFN_glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog);
|
|
static PFN_glGetProgramInfoLog _sapp_glGetProgramInfoLog;
|
|
typedef GLint (GL_APIENTRY *PFN_glGetAttribLocation)(GLuint program, const GLchar * name);
|
|
static PFN_glGetAttribLocation _sapp_glGetAttribLocation;
|
|
typedef void (GL_APIENTRY *PFN_glDisableVertexAttribArray)(GLuint index);
|
|
static PFN_glDisableVertexAttribArray _sapp_glDisableVertexAttribArray;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteShader)(GLuint shader);
|
|
static PFN_glDeleteShader _sapp_glDeleteShader;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteProgram)(GLuint program);
|
|
static PFN_glDeleteProgram _sapp_glDeleteProgram;
|
|
typedef void (GL_APIENTRY *PFN_glCompileShader)(GLuint shader);
|
|
static PFN_glCompileShader _sapp_glCompileShader;
|
|
typedef void (GL_APIENTRY *PFN_glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask);
|
|
static PFN_glStencilFuncSeparate _sapp_glStencilFuncSeparate;
|
|
typedef void (GL_APIENTRY *PFN_glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
|
|
static PFN_glStencilOpSeparate _sapp_glStencilOpSeparate;
|
|
typedef void (GL_APIENTRY *PFN_glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
|
static PFN_glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample;
|
|
typedef void (GL_APIENTRY *PFN_glDrawBuffers)(GLsizei n, const GLenum * bufs);
|
|
static PFN_glDrawBuffers _sapp_glDrawBuffers;
|
|
typedef void (GL_APIENTRY *PFN_glVertexAttribDivisor)(GLuint index, GLuint divisor);
|
|
static PFN_glVertexAttribDivisor _sapp_glVertexAttribDivisor;
|
|
typedef void (GL_APIENTRY *PFN_glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data);
|
|
static PFN_glBufferSubData _sapp_glBufferSubData;
|
|
typedef void (GL_APIENTRY *PFN_glGenBuffers)(GLsizei n, GLuint * buffers);
|
|
static PFN_glGenBuffers _sapp_glGenBuffers;
|
|
typedef GLenum (GL_APIENTRY *PFN_glCheckFramebufferStatus)(GLenum target);
|
|
static PFN_glCheckFramebufferStatus _sapp_glCheckFramebufferStatus;
|
|
typedef void (GL_APIENTRY *PFN_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
|
|
static PFN_glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer;
|
|
typedef void (GL_APIENTRY *PFN_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data);
|
|
static PFN_glCompressedTexImage2D _sapp_glCompressedTexImage2D;
|
|
typedef void (GL_APIENTRY *PFN_glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data);
|
|
static PFN_glCompressedTexImage3D _sapp_glCompressedTexImage3D;
|
|
typedef void (GL_APIENTRY *PFN_glActiveTexture)(GLenum texture);
|
|
static PFN_glActiveTexture _sapp_glActiveTexture;
|
|
typedef void (GL_APIENTRY *PFN_glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels);
|
|
static PFN_glTexSubImage3D _sapp_glTexSubImage3D;
|
|
typedef void (GL_APIENTRY *PFN_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);
|
|
static PFN_glUniformMatrix4fv _sapp_glUniformMatrix4fv;
|
|
typedef void (GL_APIENTRY *PFN_glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
|
|
static PFN_glRenderbufferStorage _sapp_glRenderbufferStorage;
|
|
typedef void (GL_APIENTRY *PFN_glGenTextures)(GLsizei n, GLuint * textures);
|
|
static PFN_glGenTextures _sapp_glGenTextures;
|
|
typedef void (GL_APIENTRY *PFN_glPolygonOffset)(GLfloat factor, GLfloat units);
|
|
static PFN_glPolygonOffset _sapp_glPolygonOffset;
|
|
typedef void (GL_APIENTRY *PFN_glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void * indices);
|
|
static PFN_glDrawElements _sapp_glDrawElements;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteFramebuffers)(GLsizei n, const GLuint * framebuffers);
|
|
static PFN_glDeleteFramebuffers _sapp_glDeleteFramebuffers;
|
|
typedef void (GL_APIENTRY *PFN_glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha);
|
|
static PFN_glBlendEquationSeparate _sapp_glBlendEquationSeparate;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteTextures)(GLsizei n, const GLuint * textures);
|
|
static PFN_glDeleteTextures _sapp_glDeleteTextures;
|
|
typedef void (GL_APIENTRY *PFN_glGetProgramiv)(GLuint program, GLenum pname, GLint * params);
|
|
static PFN_glGetProgramiv _sapp_glGetProgramiv;
|
|
typedef void (GL_APIENTRY *PFN_glBindTexture)(GLenum target, GLuint texture);
|
|
static PFN_glBindTexture _sapp_glBindTexture;
|
|
typedef void (GL_APIENTRY *PFN_glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels);
|
|
static PFN_glTexImage3D _sapp_glTexImage3D;
|
|
typedef GLuint (GL_APIENTRY *PFN_glCreateShader)(GLenum type);
|
|
static PFN_glCreateShader _sapp_glCreateShader;
|
|
typedef void (GL_APIENTRY *PFN_glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels);
|
|
static PFN_glTexSubImage2D _sapp_glTexSubImage2D;
|
|
typedef void (GL_APIENTRY *PFN_glClearDepth)(GLdouble depth);
|
|
static PFN_glClearDepth _sapp_glClearDepth;
|
|
typedef void (GL_APIENTRY *PFN_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
|
|
static PFN_glFramebufferTexture2D _sapp_glFramebufferTexture2D;
|
|
typedef GLuint (GL_APIENTRY *PFN_glCreateProgram)();
|
|
static PFN_glCreateProgram _sapp_glCreateProgram;
|
|
typedef void (GL_APIENTRY *PFN_glViewport)(GLint x, GLint y, GLsizei width, GLsizei height);
|
|
static PFN_glViewport _sapp_glViewport;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteBuffers)(GLsizei n, const GLuint * buffers);
|
|
static PFN_glDeleteBuffers _sapp_glDeleteBuffers;
|
|
typedef void (GL_APIENTRY *PFN_glDrawArrays)(GLenum mode, GLint first, GLsizei count);
|
|
static PFN_glDrawArrays _sapp_glDrawArrays;
|
|
typedef void (GL_APIENTRY *PFN_glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount);
|
|
static PFN_glDrawElementsInstanced _sapp_glDrawElementsInstanced;
|
|
typedef void (GL_APIENTRY *PFN_glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer);
|
|
static PFN_glVertexAttribPointer _sapp_glVertexAttribPointer;
|
|
typedef void (GL_APIENTRY *PFN_glUniform1i)(GLint location, GLint v0);
|
|
static PFN_glUniform1i _sapp_glUniform1i;
|
|
typedef void (GL_APIENTRY *PFN_glDisable)(GLenum cap);
|
|
static PFN_glDisable _sapp_glDisable;
|
|
typedef void (GL_APIENTRY *PFN_glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
|
|
static PFN_glColorMask _sapp_glColorMask;
|
|
typedef void (GL_APIENTRY *PFN_glBindBuffer)(GLenum target, GLuint buffer);
|
|
static PFN_glBindBuffer _sapp_glBindBuffer;
|
|
typedef void (GL_APIENTRY *PFN_glDeleteVertexArrays)(GLsizei n, const GLuint * arrays);
|
|
static PFN_glDeleteVertexArrays _sapp_glDeleteVertexArrays;
|
|
typedef void (GL_APIENTRY *PFN_glDepthMask)(GLboolean flag);
|
|
static PFN_glDepthMask _sapp_glDepthMask;
|
|
typedef void (GL_APIENTRY *PFN_glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
|
static PFN_glDrawArraysInstanced _sapp_glDrawArraysInstanced;
|
|
typedef void (GL_APIENTRY *PFN_glClearStencil)(GLint s);
|
|
static PFN_glClearStencil _sapp_glClearStencil;
|
|
typedef void (GL_APIENTRY *PFN_glScissor)(GLint x, GLint y, GLsizei width, GLsizei height);
|
|
static PFN_glScissor _sapp_glScissor;
|
|
typedef void (GL_APIENTRY *PFN_glUniform3fv)(GLint location, GLsizei count, const GLfloat * value);
|
|
static PFN_glUniform3fv _sapp_glUniform3fv;
|
|
typedef void (GL_APIENTRY *PFN_glGenRenderbuffers)(GLsizei n, GLuint * renderbuffers);
|
|
static PFN_glGenRenderbuffers _sapp_glGenRenderbuffers;
|
|
typedef void (GL_APIENTRY *PFN_glBufferData)(GLenum target, GLsizeiptr size, const void * data, GLenum usage);
|
|
static PFN_glBufferData _sapp_glBufferData;
|
|
typedef void (GL_APIENTRY *PFN_glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
|
|
static PFN_glBlendFuncSeparate _sapp_glBlendFuncSeparate;
|
|
typedef void (GL_APIENTRY *PFN_glTexParameteri)(GLenum target, GLenum pname, GLint param);
|
|
static PFN_glTexParameteri _sapp_glTexParameteri;
|
|
typedef void (GL_APIENTRY *PFN_glGetIntegerv)(GLenum pname, GLint * data);
|
|
static PFN_glGetIntegerv _sapp_glGetIntegerv;
|
|
typedef void (GL_APIENTRY *PFN_glEnable)(GLenum cap);
|
|
static PFN_glEnable _sapp_glEnable;
|
|
typedef void (GL_APIENTRY *PFN_glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
|
static PFN_glBlitFramebuffer _sapp_glBlitFramebuffer;
|
|
typedef void (GL_APIENTRY *PFN_glStencilMask)(GLuint mask);
|
|
static PFN_glStencilMask _sapp_glStencilMask;
|
|
typedef void (GL_APIENTRY *PFN_glAttachShader)(GLuint program, GLuint shader);
|
|
static PFN_glAttachShader _sapp_glAttachShader;
|
|
typedef GLenum (GL_APIENTRY *PFN_glGetError)();
|
|
static PFN_glGetError _sapp_glGetError;
|
|
typedef void (GL_APIENTRY *PFN_glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
|
static PFN_glClearColor _sapp_glClearColor;
|
|
typedef void (GL_APIENTRY *PFN_glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
|
static PFN_glBlendColor _sapp_glBlendColor;
|
|
typedef void (GL_APIENTRY *PFN_glTexParameterf)(GLenum target, GLenum pname, GLfloat param);
|
|
static PFN_glTexParameterf _sapp_glTexParameterf;
|
|
typedef void (GL_APIENTRY *PFN_glTexParameterfv)(GLenum target, GLenum pname, GLfloat* params);
|
|
static PFN_glTexParameterfv _sapp_glTexParameterfv;
|
|
typedef void (GL_APIENTRY *PFN_glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog);
|
|
static PFN_glGetShaderInfoLog _sapp_glGetShaderInfoLog;
|
|
typedef void (GL_APIENTRY *PFN_glDepthFunc)(GLenum func);
|
|
static PFN_glDepthFunc _sapp_glDepthFunc;
|
|
typedef void (GL_APIENTRY *PFN_glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass);
|
|
static PFN_glStencilOp _sapp_glStencilOp;
|
|
typedef void (GL_APIENTRY *PFN_glStencilFunc)(GLenum func, GLint ref, GLuint mask);
|
|
static PFN_glStencilFunc _sapp_glStencilFunc;
|
|
typedef void (GL_APIENTRY *PFN_glEnableVertexAttribArray)(GLuint index);
|
|
static PFN_glEnableVertexAttribArray _sapp_glEnableVertexAttribArray;
|
|
typedef void (GL_APIENTRY *PFN_glBlendFunc)(GLenum sfactor, GLenum dfactor);
|
|
static PFN_glBlendFunc _sapp_glBlendFunc;
|
|
typedef void (GL_APIENTRY *PFN_glUniform1fv)(GLint location, GLsizei count, const GLfloat * value);
|
|
static PFN_glUniform1fv _sapp_glUniform1fv;
|
|
typedef void (GL_APIENTRY *PFN_glReadBuffer)(GLenum src);
|
|
static PFN_glReadBuffer _sapp_glReadBuffer;
|
|
typedef void (GL_APIENTRY *PFN_glClear)(GLbitfield mask);
|
|
static PFN_glClear _sapp_glClear;
|
|
typedef void (GL_APIENTRY *PFN_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels);
|
|
static PFN_glTexImage2D _sapp_glTexImage2D;
|
|
typedef void (GL_APIENTRY *PFN_glGenVertexArrays)(GLsizei n, GLuint * arrays);
|
|
static PFN_glGenVertexArrays _sapp_glGenVertexArrays;
|
|
typedef void (GL_APIENTRY *PFN_glFrontFace)(GLenum mode);
|
|
static PFN_glFrontFace _sapp_glFrontFace;
|
|
typedef void (GL_APIENTRY *PFN_glCullFace)(GLenum mode);
|
|
static PFN_glCullFace _sapp_glCullFace;
|
|
|
|
_SOKOL_PRIVATE void* _sapp_win32_glgetprocaddr(const char* name) {
|
|
void* proc_addr = (void*) _sapp_wglGetProcAddress(name);
|
|
if (0 == proc_addr) {
|
|
proc_addr = (void*) GetProcAddress(_sapp_opengl32, name);
|
|
}
|
|
SOKOL_ASSERT(proc_addr);
|
|
return proc_addr;
|
|
}
|
|
|
|
#define _SAPP_GLPROC(name) _sapp_ ## name = (PFN_ ## name) _sapp_win32_glgetprocaddr(#name)
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_gl_loadfuncs(void) {
|
|
SOKOL_ASSERT(_sapp_wglGetProcAddress);
|
|
SOKOL_ASSERT(_sapp_opengl32);
|
|
_SAPP_GLPROC(glBindVertexArray);
|
|
_SAPP_GLPROC(glFramebufferTextureLayer);
|
|
_SAPP_GLPROC(glGenFramebuffers);
|
|
_SAPP_GLPROC(glBindFramebuffer);
|
|
_SAPP_GLPROC(glBindRenderbuffer);
|
|
_SAPP_GLPROC(glGetStringi);
|
|
_SAPP_GLPROC(glClearBufferfi);
|
|
_SAPP_GLPROC(glClearBufferfv);
|
|
_SAPP_GLPROC(glClearBufferuiv);
|
|
_SAPP_GLPROC(glDeleteRenderbuffers);
|
|
_SAPP_GLPROC(glUniform4fv);
|
|
_SAPP_GLPROC(glUniform2fv);
|
|
_SAPP_GLPROC(glUseProgram);
|
|
_SAPP_GLPROC(glShaderSource);
|
|
_SAPP_GLPROC(glLinkProgram);
|
|
_SAPP_GLPROC(glGetUniformLocation);
|
|
_SAPP_GLPROC(glGetShaderiv);
|
|
_SAPP_GLPROC(glGetProgramInfoLog);
|
|
_SAPP_GLPROC(glGetAttribLocation);
|
|
_SAPP_GLPROC(glDisableVertexAttribArray);
|
|
_SAPP_GLPROC(glDeleteShader);
|
|
_SAPP_GLPROC(glDeleteProgram);
|
|
_SAPP_GLPROC(glCompileShader);
|
|
_SAPP_GLPROC(glStencilFuncSeparate);
|
|
_SAPP_GLPROC(glStencilOpSeparate);
|
|
_SAPP_GLPROC(glRenderbufferStorageMultisample);
|
|
_SAPP_GLPROC(glDrawBuffers);
|
|
_SAPP_GLPROC(glVertexAttribDivisor);
|
|
_SAPP_GLPROC(glBufferSubData);
|
|
_SAPP_GLPROC(glGenBuffers);
|
|
_SAPP_GLPROC(glCheckFramebufferStatus);
|
|
_SAPP_GLPROC(glFramebufferRenderbuffer);
|
|
_SAPP_GLPROC(glCompressedTexImage2D);
|
|
_SAPP_GLPROC(glCompressedTexImage3D);
|
|
_SAPP_GLPROC(glActiveTexture);
|
|
_SAPP_GLPROC(glTexSubImage3D);
|
|
_SAPP_GLPROC(glUniformMatrix4fv);
|
|
_SAPP_GLPROC(glRenderbufferStorage);
|
|
_SAPP_GLPROC(glGenTextures);
|
|
_SAPP_GLPROC(glPolygonOffset);
|
|
_SAPP_GLPROC(glDrawElements);
|
|
_SAPP_GLPROC(glDeleteFramebuffers);
|
|
_SAPP_GLPROC(glBlendEquationSeparate);
|
|
_SAPP_GLPROC(glDeleteTextures);
|
|
_SAPP_GLPROC(glGetProgramiv);
|
|
_SAPP_GLPROC(glBindTexture);
|
|
_SAPP_GLPROC(glTexImage3D);
|
|
_SAPP_GLPROC(glCreateShader);
|
|
_SAPP_GLPROC(glTexSubImage2D);
|
|
_SAPP_GLPROC(glClearDepth);
|
|
_SAPP_GLPROC(glFramebufferTexture2D);
|
|
_SAPP_GLPROC(glCreateProgram);
|
|
_SAPP_GLPROC(glViewport);
|
|
_SAPP_GLPROC(glDeleteBuffers);
|
|
_SAPP_GLPROC(glDrawArrays);
|
|
_SAPP_GLPROC(glDrawElementsInstanced);
|
|
_SAPP_GLPROC(glVertexAttribPointer);
|
|
_SAPP_GLPROC(glUniform1i);
|
|
_SAPP_GLPROC(glDisable);
|
|
_SAPP_GLPROC(glColorMask);
|
|
_SAPP_GLPROC(glBindBuffer);
|
|
_SAPP_GLPROC(glDeleteVertexArrays);
|
|
_SAPP_GLPROC(glDepthMask);
|
|
_SAPP_GLPROC(glDrawArraysInstanced);
|
|
_SAPP_GLPROC(glClearStencil);
|
|
_SAPP_GLPROC(glScissor);
|
|
_SAPP_GLPROC(glUniform3fv);
|
|
_SAPP_GLPROC(glGenRenderbuffers);
|
|
_SAPP_GLPROC(glBufferData);
|
|
_SAPP_GLPROC(glBlendFuncSeparate);
|
|
_SAPP_GLPROC(glTexParameteri);
|
|
_SAPP_GLPROC(glGetIntegerv);
|
|
_SAPP_GLPROC(glEnable);
|
|
_SAPP_GLPROC(glBlitFramebuffer);
|
|
_SAPP_GLPROC(glStencilMask);
|
|
_SAPP_GLPROC(glAttachShader);
|
|
_SAPP_GLPROC(glGetError);
|
|
_SAPP_GLPROC(glClearColor);
|
|
_SAPP_GLPROC(glBlendColor);
|
|
_SAPP_GLPROC(glTexParameterf);
|
|
_SAPP_GLPROC(glTexParameterfv);
|
|
_SAPP_GLPROC(glGetShaderInfoLog);
|
|
_SAPP_GLPROC(glDepthFunc);
|
|
_SAPP_GLPROC(glStencilOp);
|
|
_SAPP_GLPROC(glStencilFunc);
|
|
_SAPP_GLPROC(glEnableVertexAttribArray);
|
|
_SAPP_GLPROC(glBlendFunc);
|
|
_SAPP_GLPROC(glUniform1fv);
|
|
_SAPP_GLPROC(glReadBuffer);
|
|
_SAPP_GLPROC(glClear);
|
|
_SAPP_GLPROC(glTexImage2D);
|
|
_SAPP_GLPROC(glGenVertexArrays);
|
|
_SAPP_GLPROC(glFrontFace);
|
|
_SAPP_GLPROC(glCullFace);
|
|
}
|
|
#define glBindVertexArray _sapp_glBindVertexArray
|
|
#define glFramebufferTextureLayer _sapp_glFramebufferTextureLayer
|
|
#define glGenFramebuffers _sapp_glGenFramebuffers
|
|
#define glBindFramebuffer _sapp_glBindFramebuffer
|
|
#define glBindRenderbuffer _sapp_glBindRenderbuffer
|
|
#define glGetStringi _sapp_glGetStringi
|
|
#define glClearBufferfi _sapp_glClearBufferfi
|
|
#define glClearBufferfv _sapp_glClearBufferfv
|
|
#define glClearBufferuiv _sapp_glClearBufferuiv
|
|
#define glDeleteRenderbuffers _sapp_glDeleteRenderbuffers
|
|
#define glUniform4fv _sapp_glUniform4fv
|
|
#define glUniform2fv _sapp_glUniform2fv
|
|
#define glUseProgram _sapp_glUseProgram
|
|
#define glShaderSource _sapp_glShaderSource
|
|
#define glLinkProgram _sapp_glLinkProgram
|
|
#define glGetUniformLocation _sapp_glGetUniformLocation
|
|
#define glGetShaderiv _sapp_glGetShaderiv
|
|
#define glGetProgramInfoLog _sapp_glGetProgramInfoLog
|
|
#define glGetAttribLocation _sapp_glGetAttribLocation
|
|
#define glDisableVertexAttribArray _sapp_glDisableVertexAttribArray
|
|
#define glDeleteShader _sapp_glDeleteShader
|
|
#define glDeleteProgram _sapp_glDeleteProgram
|
|
#define glCompileShader _sapp_glCompileShader
|
|
#define glStencilFuncSeparate _sapp_glStencilFuncSeparate
|
|
#define glStencilOpSeparate _sapp_glStencilOpSeparate
|
|
#define glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample
|
|
#define glDrawBuffers _sapp_glDrawBuffers
|
|
#define glVertexAttribDivisor _sapp_glVertexAttribDivisor
|
|
#define glBufferSubData _sapp_glBufferSubData
|
|
#define glGenBuffers _sapp_glGenBuffers
|
|
#define glCheckFramebufferStatus _sapp_glCheckFramebufferStatus
|
|
#define glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer
|
|
#define glCompressedTexImage2D _sapp_glCompressedTexImage2D
|
|
#define glCompressedTexImage3D _sapp_glCompressedTexImage3D
|
|
#define glActiveTexture _sapp_glActiveTexture
|
|
#define glTexSubImage3D _sapp_glTexSubImage3D
|
|
#define glUniformMatrix4fv _sapp_glUniformMatrix4fv
|
|
#define glRenderbufferStorage _sapp_glRenderbufferStorage
|
|
#define glGenTextures _sapp_glGenTextures
|
|
#define glPolygonOffset _sapp_glPolygonOffset
|
|
#define glDrawElements _sapp_glDrawElements
|
|
#define glDeleteFramebuffers _sapp_glDeleteFramebuffers
|
|
#define glBlendEquationSeparate _sapp_glBlendEquationSeparate
|
|
#define glDeleteTextures _sapp_glDeleteTextures
|
|
#define glGetProgramiv _sapp_glGetProgramiv
|
|
#define glBindTexture _sapp_glBindTexture
|
|
#define glTexImage3D _sapp_glTexImage3D
|
|
#define glCreateShader _sapp_glCreateShader
|
|
#define glTexSubImage2D _sapp_glTexSubImage2D
|
|
#define glClearDepth _sapp_glClearDepth
|
|
#define glFramebufferTexture2D _sapp_glFramebufferTexture2D
|
|
#define glCreateProgram _sapp_glCreateProgram
|
|
#define glViewport _sapp_glViewport
|
|
#define glDeleteBuffers _sapp_glDeleteBuffers
|
|
#define glDrawArrays _sapp_glDrawArrays
|
|
#define glDrawElementsInstanced _sapp_glDrawElementsInstanced
|
|
#define glVertexAttribPointer _sapp_glVertexAttribPointer
|
|
#define glUniform1i _sapp_glUniform1i
|
|
#define glDisable _sapp_glDisable
|
|
#define glColorMask _sapp_glColorMask
|
|
#define glBindBuffer _sapp_glBindBuffer
|
|
#define glDeleteVertexArrays _sapp_glDeleteVertexArrays
|
|
#define glDepthMask _sapp_glDepthMask
|
|
#define glDrawArraysInstanced _sapp_glDrawArraysInstanced
|
|
#define glClearStencil _sapp_glClearStencil
|
|
#define glScissor _sapp_glScissor
|
|
#define glUniform3fv _sapp_glUniform3fv
|
|
#define glGenRenderbuffers _sapp_glGenRenderbuffers
|
|
#define glBufferData _sapp_glBufferData
|
|
#define glBlendFuncSeparate _sapp_glBlendFuncSeparate
|
|
#define glTexParameteri _sapp_glTexParameteri
|
|
#define glGetIntegerv _sapp_glGetIntegerv
|
|
#define glEnable _sapp_glEnable
|
|
#define glBlitFramebuffer _sapp_glBlitFramebuffer
|
|
#define glStencilMask _sapp_glStencilMask
|
|
#define glAttachShader _sapp_glAttachShader
|
|
#define glGetError _sapp_glGetError
|
|
#define glClearColor _sapp_glClearColor
|
|
#define glBlendColor _sapp_glBlendColor
|
|
#define glTexParameterf _sapp_glTexParameterf
|
|
#define glTexParameterfv _sapp_glTexParameterfv
|
|
#define glGetShaderInfoLog _sapp_glGetShaderInfoLog
|
|
#define glDepthFunc _sapp_glDepthFunc
|
|
#define glStencilOp _sapp_glStencilOp
|
|
#define glStencilFunc _sapp_glStencilFunc
|
|
#define glEnableVertexAttribArray _sapp_glEnableVertexAttribArray
|
|
#define glBlendFunc _sapp_glBlendFunc
|
|
#define glUniform1fv _sapp_glUniform1fv
|
|
#define glReadBuffer _sapp_glReadBuffer
|
|
#define glClear _sapp_glClear
|
|
#define glTexImage2D _sapp_glTexImage2D
|
|
#define glGenVertexArrays _sapp_glGenVertexArrays
|
|
#define glFrontFace _sapp_glFrontFace
|
|
#define glCullFace _sapp_glCullFace
|
|
|
|
#endif /* SOKOL_WIN32_NO_GL_LOADER */
|
|
|
|
#endif /* SOKOL_GLCORE33 */
|
|
|
|
#if defined(SOKOL_D3D11)
|
|
#define _SAPP_SAFE_RELEASE(class, obj) if (obj) { class##_Release(obj); obj=0; }
|
|
_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) {
|
|
DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp_dxgi_swap_chain_desc;
|
|
sc_desc->BufferDesc.Width = _sapp.framebuffer_width;
|
|
sc_desc->BufferDesc.Height = _sapp.framebuffer_height;
|
|
sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
sc_desc->BufferDesc.RefreshRate.Numerator = 60;
|
|
sc_desc->BufferDesc.RefreshRate.Denominator = 1;
|
|
sc_desc->OutputWindow = _sapp_win32_hwnd;
|
|
sc_desc->Windowed = true;
|
|
sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
sc_desc->BufferCount = 1;
|
|
sc_desc->SampleDesc.Count = _sapp.sample_count;
|
|
sc_desc->SampleDesc.Quality = _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0;
|
|
sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
int create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
|
#if defined(SOKOL_DEBUG)
|
|
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
#endif
|
|
D3D_FEATURE_LEVEL feature_level;
|
|
HRESULT hr = D3D11CreateDeviceAndSwapChain(
|
|
NULL, /* pAdapter (use default) */
|
|
D3D_DRIVER_TYPE_HARDWARE, /* DriverType */
|
|
NULL, /* Software */
|
|
create_flags, /* Flags */
|
|
NULL, /* pFeatureLevels */
|
|
0, /* FeatureLevels */
|
|
D3D11_SDK_VERSION, /* SDKVersion */
|
|
sc_desc, /* pSwapChainDesc */
|
|
&_sapp_dxgi_swap_chain, /* ppSwapChain */
|
|
&_sapp_d3d11_device, /* ppDevice */
|
|
&feature_level, /* pFeatureLevel */
|
|
&_sapp_d3d11_device_context); /* ppImmediateContext */
|
|
_SOKOL_UNUSED(hr);
|
|
SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_dxgi_swap_chain && _sapp_d3d11_device && _sapp_d3d11_device_context);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) {
|
|
_SAPP_SAFE_RELEASE(IDXGISwapChain, _sapp_dxgi_swap_chain);
|
|
_SAPP_SAFE_RELEASE(ID3D11DeviceContext, _sapp_d3d11_device_context);
|
|
_SAPP_SAFE_RELEASE(ID3D11Device, _sapp_d3d11_device);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) {
|
|
HRESULT hr;
|
|
#ifdef __cplusplus
|
|
hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt);
|
|
#else
|
|
hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt);
|
|
#endif
|
|
SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rt);
|
|
hr = ID3D11Device_CreateRenderTargetView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_rt, NULL, &_sapp_d3d11_rtv);
|
|
SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rtv);
|
|
D3D11_TEXTURE2D_DESC ds_desc;
|
|
memset(&ds_desc, 0, sizeof(ds_desc));
|
|
ds_desc.Width = _sapp.framebuffer_width;
|
|
ds_desc.Height = _sapp.framebuffer_height;
|
|
ds_desc.MipLevels = 1;
|
|
ds_desc.ArraySize = 1;
|
|
ds_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
ds_desc.SampleDesc = _sapp_dxgi_swap_chain_desc.SampleDesc;
|
|
ds_desc.Usage = D3D11_USAGE_DEFAULT;
|
|
ds_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
|
hr = ID3D11Device_CreateTexture2D(_sapp_d3d11_device, &ds_desc, NULL, &_sapp_d3d11_ds);
|
|
SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_ds);
|
|
D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc;
|
|
memset(&dsv_desc, 0, sizeof(dsv_desc));
|
|
dsv_desc.Format = ds_desc.Format;
|
|
dsv_desc.ViewDimension = _sapp.sample_count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
|
|
hr = ID3D11Device_CreateDepthStencilView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_ds, &dsv_desc, &_sapp_d3d11_dsv);
|
|
SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_dsv);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) {
|
|
_SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_rt);
|
|
_SAPP_SAFE_RELEASE(ID3D11RenderTargetView, _sapp_d3d11_rtv);
|
|
_SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_ds);
|
|
_SAPP_SAFE_RELEASE(ID3D11DepthStencilView, _sapp_d3d11_dsv);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) {
|
|
if (_sapp_dxgi_swap_chain) {
|
|
_sapp_d3d11_destroy_default_render_target();
|
|
IDXGISwapChain_ResizeBuffers(_sapp_dxgi_swap_chain, 1, _sapp.framebuffer_width, _sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
|
|
_sapp_d3d11_create_default_render_target();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(SOKOL_GLCORE33)
|
|
_SOKOL_PRIVATE void _sapp_wgl_init(void) {
|
|
_sapp_opengl32 = LoadLibraryA("opengl32.dll");
|
|
if (!_sapp_opengl32) {
|
|
_sapp_fail("Failed to load opengl32.dll\n");
|
|
}
|
|
SOKOL_ASSERT(_sapp_opengl32);
|
|
_sapp_wglCreateContext = (PFN_wglCreateContext) GetProcAddress(_sapp_opengl32, "wglCreateContext");
|
|
SOKOL_ASSERT(_sapp_wglCreateContext);
|
|
_sapp_wglDeleteContext = (PFN_wglDeleteContext) GetProcAddress(_sapp_opengl32, "wglDeleteContext");
|
|
SOKOL_ASSERT(_sapp_wglDeleteContext);
|
|
_sapp_wglGetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_sapp_opengl32, "wglGetProcAddress");
|
|
SOKOL_ASSERT(_sapp_wglGetProcAddress);
|
|
_sapp_wglGetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_sapp_opengl32, "wglGetCurrentDC");
|
|
SOKOL_ASSERT(_sapp_wglGetCurrentDC);
|
|
_sapp_wglMakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_sapp_opengl32, "wglMakeCurrent");
|
|
SOKOL_ASSERT(_sapp_wglMakeCurrent);
|
|
|
|
_sapp_win32_msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW,
|
|
L"SOKOLAPP",
|
|
L"sokol-app message window",
|
|
WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
|
|
0, 0, 1, 1,
|
|
NULL, NULL,
|
|
GetModuleHandleW(NULL),
|
|
NULL);
|
|
if (!_sapp_win32_msg_hwnd) {
|
|
_sapp_fail("Win32: failed to create helper window!\n");
|
|
}
|
|
ShowWindow(_sapp_win32_msg_hwnd, SW_HIDE);
|
|
MSG msg;
|
|
while (PeekMessageW(&msg, _sapp_win32_msg_hwnd, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
_sapp_win32_msg_dc = GetDC(_sapp_win32_msg_hwnd);
|
|
if (!_sapp_win32_msg_dc) {
|
|
_sapp_fail("Win32: failed to obtain helper window DC!\n");
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) {
|
|
SOKOL_ASSERT(_sapp_opengl32 && _sapp_win32_msg_hwnd);
|
|
DestroyWindow(_sapp_win32_msg_hwnd); _sapp_win32_msg_hwnd = 0;
|
|
FreeLibrary(_sapp_opengl32); _sapp_opengl32 = 0;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) {
|
|
SOKOL_ASSERT(ext && extensions);
|
|
const char* start = extensions;
|
|
while (true) {
|
|
const char* where = strstr(start, ext);
|
|
if (!where) {
|
|
return false;
|
|
}
|
|
const char* terminator = where + strlen(ext);
|
|
if ((where == start) || (*(where - 1) == ' ')) {
|
|
if (*terminator == ' ' || *terminator == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
start = terminator;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) {
|
|
SOKOL_ASSERT(ext);
|
|
if (_sapp_GetExtensionsStringEXT) {
|
|
const char* extensions = _sapp_GetExtensionsStringEXT();
|
|
if (extensions) {
|
|
if (_sapp_wgl_has_ext(ext, extensions)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (_sapp_GetExtensionsStringARB) {
|
|
const char* extensions = _sapp_GetExtensionsStringARB(_sapp_wglGetCurrentDC());
|
|
if (extensions) {
|
|
if (_sapp_wgl_has_ext(ext, extensions)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) {
|
|
SOKOL_ASSERT(_sapp_win32_msg_dc);
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
memset(&pfd, 0, sizeof(pfd));
|
|
pfd.nSize = sizeof(pfd);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 24;
|
|
if (!SetPixelFormat(_sapp_win32_msg_dc, ChoosePixelFormat(_sapp_win32_msg_dc, &pfd), &pfd)) {
|
|
_sapp_fail("WGL: failed to set pixel format for dummy context\n");
|
|
}
|
|
HGLRC rc = _sapp_wglCreateContext(_sapp_win32_msg_dc);
|
|
if (!rc) {
|
|
_sapp_fail("WGL: Failed to create dummy context\n");
|
|
}
|
|
if (!_sapp_wglMakeCurrent(_sapp_win32_msg_dc, rc)) {
|
|
_sapp_fail("WGL: Failed to make context current\n");
|
|
}
|
|
_sapp_GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringEXT");
|
|
_sapp_GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringARB");
|
|
_sapp_CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) _sapp_wglGetProcAddress("wglCreateContextAttribsARB");
|
|
_sapp_SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) _sapp_wglGetProcAddress("wglSwapIntervalEXT");
|
|
_sapp_GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) _sapp_wglGetProcAddress("wglGetPixelFormatAttribivARB");
|
|
_sapp_arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample");
|
|
_sapp_arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context");
|
|
_sapp_arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile");
|
|
_sapp_ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control");
|
|
_sapp_arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format");
|
|
_sapp_wglMakeCurrent(_sapp_win32_msg_dc, 0);
|
|
_sapp_wglDeleteContext(rc);
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
|
|
SOKOL_ASSERT(_sapp_arb_pixel_format);
|
|
int value = 0;
|
|
if (!_sapp_GetPixelFormatAttribivARB(_sapp_win32_dc, pixel_format, 0, 1, &attrib, &value)) {
|
|
_sapp_fail("WGL: Failed to retrieve pixel format attribute\n");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
|
|
SOKOL_ASSERT(_sapp_win32_dc);
|
|
SOKOL_ASSERT(_sapp_arb_pixel_format);
|
|
const _sapp_gl_fbconfig* closest;
|
|
|
|
int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
|
|
_sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC(native_count, sizeof(_sapp_gl_fbconfig));
|
|
int usable_count = 0;
|
|
for (int i = 0; i < native_count; i++) {
|
|
const int n = i + 1;
|
|
_sapp_gl_fbconfig* u = usable_configs + usable_count;
|
|
_sapp_gl_init_fbconfig(u);
|
|
if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) {
|
|
continue;
|
|
}
|
|
if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) {
|
|
continue;
|
|
}
|
|
if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) {
|
|
continue;
|
|
}
|
|
u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB);
|
|
u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB);
|
|
u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB);
|
|
u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB);
|
|
u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB);
|
|
u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB);
|
|
if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) {
|
|
u->doublebuffer = true;
|
|
}
|
|
if (_sapp_arb_multisample) {
|
|
u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB);
|
|
}
|
|
u->handle = n;
|
|
usable_count++;
|
|
}
|
|
SOKOL_ASSERT(usable_count > 0);
|
|
_sapp_gl_fbconfig desired;
|
|
_sapp_gl_init_fbconfig(&desired);
|
|
desired.red_bits = 8;
|
|
desired.green_bits = 8;
|
|
desired.blue_bits = 8;
|
|
desired.alpha_bits = 8;
|
|
desired.depth_bits = 24;
|
|
desired.stencil_bits = 8;
|
|
desired.doublebuffer = true;
|
|
desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
|
|
closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
|
|
int pixel_format = 0;
|
|
if (closest) {
|
|
pixel_format = (int) closest->handle;
|
|
}
|
|
SOKOL_FREE(usable_configs);
|
|
return pixel_format;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_wgl_create_context(void) {
|
|
int pixel_format = _sapp_wgl_find_pixel_format();
|
|
if (0 == pixel_format) {
|
|
_sapp_fail("WGL: Didn't find matching pixel format.\n");
|
|
}
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
if (!DescribePixelFormat(_sapp_win32_dc, pixel_format, sizeof(pfd), &pfd)) {
|
|
_sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n");
|
|
}
|
|
if (!SetPixelFormat(_sapp_win32_dc, pixel_format, &pfd)) {
|
|
_sapp_fail("WGL: Failed to set selected pixel format!\n");
|
|
}
|
|
if (!_sapp_arb_create_context) {
|
|
_sapp_fail("WGL: ARB_create_context required!\n");
|
|
}
|
|
if (!_sapp_arb_create_context_profile) {
|
|
_sapp_fail("WGL: ARB_create_context_profile required!\n");
|
|
}
|
|
const int attrs[] = {
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0, 0
|
|
};
|
|
_sapp_gl_ctx = _sapp_CreateContextAttribsARB(_sapp_win32_dc, 0, attrs);
|
|
if (!_sapp_gl_ctx) {
|
|
const DWORD err = GetLastError();
|
|
if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) {
|
|
_sapp_fail("WGL: Driver does not support OpenGL version 3.3\n");
|
|
}
|
|
else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) {
|
|
_sapp_fail("WGL: Driver does not support the requested OpenGL profile");
|
|
}
|
|
else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) {
|
|
_sapp_fail("WGL: The share context is not compatible with the requested context");
|
|
}
|
|
else {
|
|
_sapp_fail("WGL: Failed to create OpenGL context");
|
|
}
|
|
}
|
|
_sapp_wglMakeCurrent(_sapp_win32_dc, _sapp_gl_ctx);
|
|
if (_sapp_ext_swap_control) {
|
|
/* FIXME: DwmIsCompositionEnabled() (see GLFW) */
|
|
_sapp_SwapIntervalEXT(_sapp.swap_interval);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) {
|
|
SOKOL_ASSERT(_sapp_gl_ctx);
|
|
_sapp_wglDeleteContext(_sapp_gl_ctx);
|
|
_sapp_gl_ctx = 0;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) {
|
|
SOKOL_ASSERT(_sapp_win32_dc);
|
|
/* FIXME: DwmIsCompositionEnabled? (see GLFW) */
|
|
SwapBuffers(_sapp_win32_dc);
|
|
}
|
|
#endif
|
|
|
|
_SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) {
|
|
SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
|
|
memset(dst, 0, dst_num_bytes);
|
|
const int dst_chars = dst_num_bytes / sizeof(wchar_t);
|
|
const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0);
|
|
if ((dst_needed > 0) && (dst_needed < dst_chars)) {
|
|
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars);
|
|
return true;
|
|
}
|
|
else {
|
|
/* input string doesn't fit into destination buffer */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) {
|
|
SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
|
|
memset(dst, 0, dst_num_bytes);
|
|
return 0 != WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool shown) {
|
|
ShowCursor((BOOL)shown);
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_win32_mouse_shown(void) {
|
|
CURSORINFO cursor_info;
|
|
memset(&cursor_info, 0, sizeof(CURSORINFO));
|
|
cursor_info.cbSize = sizeof(CURSORINFO);
|
|
GetCursorInfo(&cursor_info);
|
|
return (cursor_info.flags & CURSOR_SHOWING) != 0;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_init_keytable(void) {
|
|
/* same as GLFW */
|
|
_sapp.keycodes[0x00B] = SAPP_KEYCODE_0;
|
|
_sapp.keycodes[0x002] = SAPP_KEYCODE_1;
|
|
_sapp.keycodes[0x003] = SAPP_KEYCODE_2;
|
|
_sapp.keycodes[0x004] = SAPP_KEYCODE_3;
|
|
_sapp.keycodes[0x005] = SAPP_KEYCODE_4;
|
|
_sapp.keycodes[0x006] = SAPP_KEYCODE_5;
|
|
_sapp.keycodes[0x007] = SAPP_KEYCODE_6;
|
|
_sapp.keycodes[0x008] = SAPP_KEYCODE_7;
|
|
_sapp.keycodes[0x009] = SAPP_KEYCODE_8;
|
|
_sapp.keycodes[0x00A] = SAPP_KEYCODE_9;
|
|
_sapp.keycodes[0x01E] = SAPP_KEYCODE_A;
|
|
_sapp.keycodes[0x030] = SAPP_KEYCODE_B;
|
|
_sapp.keycodes[0x02E] = SAPP_KEYCODE_C;
|
|
_sapp.keycodes[0x020] = SAPP_KEYCODE_D;
|
|
_sapp.keycodes[0x012] = SAPP_KEYCODE_E;
|
|
_sapp.keycodes[0x021] = SAPP_KEYCODE_F;
|
|
_sapp.keycodes[0x022] = SAPP_KEYCODE_G;
|
|
_sapp.keycodes[0x023] = SAPP_KEYCODE_H;
|
|
_sapp.keycodes[0x017] = SAPP_KEYCODE_I;
|
|
_sapp.keycodes[0x024] = SAPP_KEYCODE_J;
|
|
_sapp.keycodes[0x025] = SAPP_KEYCODE_K;
|
|
_sapp.keycodes[0x026] = SAPP_KEYCODE_L;
|
|
_sapp.keycodes[0x032] = SAPP_KEYCODE_M;
|
|
_sapp.keycodes[0x031] = SAPP_KEYCODE_N;
|
|
_sapp.keycodes[0x018] = SAPP_KEYCODE_O;
|
|
_sapp.keycodes[0x019] = SAPP_KEYCODE_P;
|
|
_sapp.keycodes[0x010] = SAPP_KEYCODE_Q;
|
|
_sapp.keycodes[0x013] = SAPP_KEYCODE_R;
|
|
_sapp.keycodes[0x01F] = SAPP_KEYCODE_S;
|
|
_sapp.keycodes[0x014] = SAPP_KEYCODE_T;
|
|
_sapp.keycodes[0x016] = SAPP_KEYCODE_U;
|
|
_sapp.keycodes[0x02F] = SAPP_KEYCODE_V;
|
|
_sapp.keycodes[0x011] = SAPP_KEYCODE_W;
|
|
_sapp.keycodes[0x02D] = SAPP_KEYCODE_X;
|
|
_sapp.keycodes[0x015] = SAPP_KEYCODE_Y;
|
|
_sapp.keycodes[0x02C] = SAPP_KEYCODE_Z;
|
|
_sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE;
|
|
_sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH;
|
|
_sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA;
|
|
_sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL;
|
|
_sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT;
|
|
_sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET;
|
|
_sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS;
|
|
_sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD;
|
|
_sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET;
|
|
_sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON;
|
|
_sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH;
|
|
_sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2;
|
|
_sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE;
|
|
_sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE;
|
|
_sapp.keycodes[0x14F] = SAPP_KEYCODE_END;
|
|
_sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER;
|
|
_sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE;
|
|
_sapp.keycodes[0x147] = SAPP_KEYCODE_HOME;
|
|
_sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT;
|
|
_sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU;
|
|
_sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN;
|
|
_sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP;
|
|
_sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE;
|
|
_sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE;
|
|
_sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE;
|
|
_sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB;
|
|
_sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK;
|
|
_sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK;
|
|
_sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK;
|
|
_sapp.keycodes[0x03B] = SAPP_KEYCODE_F1;
|
|
_sapp.keycodes[0x03C] = SAPP_KEYCODE_F2;
|
|
_sapp.keycodes[0x03D] = SAPP_KEYCODE_F3;
|
|
_sapp.keycodes[0x03E] = SAPP_KEYCODE_F4;
|
|
_sapp.keycodes[0x03F] = SAPP_KEYCODE_F5;
|
|
_sapp.keycodes[0x040] = SAPP_KEYCODE_F6;
|
|
_sapp.keycodes[0x041] = SAPP_KEYCODE_F7;
|
|
_sapp.keycodes[0x042] = SAPP_KEYCODE_F8;
|
|
_sapp.keycodes[0x043] = SAPP_KEYCODE_F9;
|
|
_sapp.keycodes[0x044] = SAPP_KEYCODE_F10;
|
|
_sapp.keycodes[0x057] = SAPP_KEYCODE_F11;
|
|
_sapp.keycodes[0x058] = SAPP_KEYCODE_F12;
|
|
_sapp.keycodes[0x064] = SAPP_KEYCODE_F13;
|
|
_sapp.keycodes[0x065] = SAPP_KEYCODE_F14;
|
|
_sapp.keycodes[0x066] = SAPP_KEYCODE_F15;
|
|
_sapp.keycodes[0x067] = SAPP_KEYCODE_F16;
|
|
_sapp.keycodes[0x068] = SAPP_KEYCODE_F17;
|
|
_sapp.keycodes[0x069] = SAPP_KEYCODE_F18;
|
|
_sapp.keycodes[0x06A] = SAPP_KEYCODE_F19;
|
|
_sapp.keycodes[0x06B] = SAPP_KEYCODE_F20;
|
|
_sapp.keycodes[0x06C] = SAPP_KEYCODE_F21;
|
|
_sapp.keycodes[0x06D] = SAPP_KEYCODE_F22;
|
|
_sapp.keycodes[0x06E] = SAPP_KEYCODE_F23;
|
|
_sapp.keycodes[0x076] = SAPP_KEYCODE_F24;
|
|
_sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT;
|
|
_sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL;
|
|
_sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT;
|
|
_sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER;
|
|
_sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN;
|
|
_sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT;
|
|
_sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL;
|
|
_sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT;
|
|
_sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER;
|
|
_sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN;
|
|
_sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT;
|
|
_sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT;
|
|
_sapp.keycodes[0x148] = SAPP_KEYCODE_UP;
|
|
_sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0;
|
|
_sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1;
|
|
_sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2;
|
|
_sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3;
|
|
_sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4;
|
|
_sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5;
|
|
_sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6;
|
|
_sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7;
|
|
_sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8;
|
|
_sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9;
|
|
_sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD;
|
|
_sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL;
|
|
_sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE;
|
|
_sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER;
|
|
_sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY;
|
|
_sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT;
|
|
}
|
|
|
|
/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */
|
|
_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) {
|
|
RECT rect;
|
|
if (GetClientRect(_sapp_win32_hwnd, &rect)) {
|
|
_sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp_win32_window_scale);
|
|
_sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp_win32_window_scale);
|
|
const int fb_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale);
|
|
const int fb_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale);
|
|
if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) {
|
|
_sapp.framebuffer_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale);
|
|
_sapp.framebuffer_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale);
|
|
/* prevent a framebuffer size of 0 when window is minimized */
|
|
if (_sapp.framebuffer_width == 0) {
|
|
_sapp.framebuffer_width = 1;
|
|
}
|
|
if (_sapp.framebuffer_height == 0) {
|
|
_sapp.framebuffer_height = 1;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
_sapp.window_width = _sapp.window_height = 1;
|
|
_sapp.framebuffer_width = _sapp.framebuffer_height = 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) {
|
|
uint32_t mods = 0;
|
|
if (GetKeyState(VK_SHIFT) & (1<<31)) {
|
|
mods |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (GetKeyState(VK_CONTROL) & (1<<31)) {
|
|
mods |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (GetKeyState(VK_MENU) & (1<<31)) {
|
|
mods |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<31)) {
|
|
mods |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
return mods;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.modifiers = _sapp_win32_mods();
|
|
_sapp.event.mouse_button = btn;
|
|
_sapp.event.mouse_x = _sapp.mouse_x;
|
|
_sapp.event.mouse_y = _sapp.mouse_y;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
|
|
_sapp.event.modifiers = _sapp_win32_mods();
|
|
_sapp.event.scroll_x = -x / 30.0f;
|
|
_sapp.event.scroll_y = y / 30.0f;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) {
|
|
if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.modifiers = _sapp_win32_mods();
|
|
_sapp.event.key_code = _sapp.keycodes[vk];
|
|
_sapp.event.key_repeat = repeat;
|
|
_sapp_call_event(&_sapp.event);
|
|
/* check if a CLIPBOARD_PASTED event must be sent too */
|
|
if (_sapp.clipboard_enabled &&
|
|
(type == SAPP_EVENTTYPE_KEY_DOWN) &&
|
|
(_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
|
|
(_sapp.event.key_code == SAPP_KEYCODE_V))
|
|
{
|
|
_sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) {
|
|
if (_sapp_events_enabled() && (c >= 32)) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
|
|
_sapp.event.modifiers = _sapp_win32_mods();
|
|
_sapp.event.char_code = c;
|
|
_sapp.event.key_repeat = repeat;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
/* FIXME: refresh rendering during resize with a WM_TIMER event */
|
|
if (!_sapp_win32_in_create_window) {
|
|
switch (uMsg) {
|
|
case WM_CLOSE:
|
|
/* only give user a chance to intervene when sapp_quit() wasn't already called */
|
|
if (!_sapp.quit_ordered) {
|
|
/* if window should be closed and event handling is enabled, give user code
|
|
a change to intervene via sapp_cancel_quit()
|
|
*/
|
|
_sapp.quit_requested = true;
|
|
_sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
|
|
/* if user code hasn't intervened, quit the app */
|
|
if (_sapp.quit_requested) {
|
|
_sapp.quit_ordered = true;
|
|
}
|
|
}
|
|
if (_sapp.quit_ordered) {
|
|
PostQuitMessage(0);
|
|
}
|
|
return 0;
|
|
case WM_SYSCOMMAND:
|
|
switch (wParam & 0xFFF0) {
|
|
case SC_SCREENSAVE:
|
|
case SC_MONITORPOWER:
|
|
if (_sapp.desc.fullscreen) {
|
|
/* disable screen saver and blanking in fullscreen mode */
|
|
return 0;
|
|
}
|
|
break;
|
|
case SC_KEYMENU:
|
|
/* user trying to access menu via ALT */
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
case WM_SIZE:
|
|
{
|
|
const bool iconified = wParam == SIZE_MINIMIZED;
|
|
if (iconified != _sapp_win32_iconified) {
|
|
_sapp_win32_iconified = iconified;
|
|
if (iconified) {
|
|
_sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED);
|
|
}
|
|
else {
|
|
_sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_SETCURSOR:
|
|
if (_sapp.desc.user_cursor) {
|
|
if (LOWORD(lParam) == HTCLIENT) {
|
|
_sapp_win32_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR);
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT);
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT);
|
|
break;
|
|
case WM_MBUTTONDOWN:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE);
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT);
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT);
|
|
break;
|
|
case WM_MBUTTONUP:
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE);
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
_sapp.mouse_x = (float)GET_X_LPARAM(lParam) * _sapp_win32_mouse_scale;
|
|
_sapp.mouse_y = (float)GET_Y_LPARAM(lParam) * _sapp_win32_mouse_scale;
|
|
if (!_sapp.win32_mouse_tracked) {
|
|
_sapp.win32_mouse_tracked = true;
|
|
TRACKMOUSEEVENT tme;
|
|
memset(&tme, 0, sizeof(tme));
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = _sapp_win32_hwnd;
|
|
TrackMouseEvent(&tme);
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID);
|
|
}
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
|
|
break;
|
|
case WM_MOUSELEAVE:
|
|
_sapp.win32_mouse_tracked = false;
|
|
_sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID);
|
|
break;
|
|
case WM_MOUSEWHEEL:
|
|
_sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam)));
|
|
break;
|
|
case WM_MOUSEHWHEEL:
|
|
_sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f);
|
|
break;
|
|
case WM_CHAR:
|
|
_sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000));
|
|
break;
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
_sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000));
|
|
break;
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYUP:
|
|
_sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_create_window(void) {
|
|
WNDCLASSW wndclassw;
|
|
memset(&wndclassw, 0, sizeof(wndclassw));
|
|
wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc;
|
|
wndclassw.hInstance = GetModuleHandleW(NULL);
|
|
wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO);
|
|
wndclassw.lpszClassName = L"SOKOLAPP";
|
|
RegisterClassW(&wndclassw);
|
|
|
|
DWORD win_style;
|
|
const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
RECT rect = { 0, 0, 0, 0 };
|
|
if (_sapp.desc.fullscreen) {
|
|
win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
|
|
rect.right = GetSystemMetrics(SM_CXSCREEN);
|
|
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
else {
|
|
win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
|
|
rect.right = (int) ((float)_sapp.window_width * _sapp_win32_window_scale);
|
|
rect.bottom = (int) ((float)_sapp.window_height * _sapp_win32_window_scale);
|
|
}
|
|
AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
|
|
const int win_width = rect.right - rect.left;
|
|
const int win_height = rect.bottom - rect.top;
|
|
_sapp_win32_in_create_window = true;
|
|
_sapp_win32_hwnd = CreateWindowExW(
|
|
win_ex_style, /* dwExStyle */
|
|
L"SOKOLAPP", /* lpClassName */
|
|
_sapp.window_title_wide, /* lpWindowName */
|
|
win_style, /* dwStyle */
|
|
CW_USEDEFAULT, /* X */
|
|
CW_USEDEFAULT, /* Y */
|
|
win_width, /* nWidth */
|
|
win_height, /* nHeight */
|
|
NULL, /* hWndParent */
|
|
NULL, /* hMenu */
|
|
GetModuleHandle(NULL), /* hInstance */
|
|
NULL); /* lParam */
|
|
ShowWindow(_sapp_win32_hwnd, SW_SHOW);
|
|
_sapp_win32_in_create_window = false;
|
|
_sapp_win32_dc = GetDC(_sapp_win32_hwnd);
|
|
SOKOL_ASSERT(_sapp_win32_dc);
|
|
_sapp_win32_update_dimensions();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) {
|
|
DestroyWindow(_sapp_win32_hwnd); _sapp_win32_hwnd = 0;
|
|
UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL));
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) {
|
|
SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiaware);
|
|
SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiawareness);
|
|
SOKOL_ASSERT(0 == _sapp_win32_getdpiformonitor);
|
|
HINSTANCE user32 = LoadLibraryA("user32.dll");
|
|
if (user32) {
|
|
_sapp_win32_setprocessdpiaware = (SETPROCESSDPIAWARE_T) GetProcAddress(user32, "SetProcessDPIAware");
|
|
}
|
|
HINSTANCE shcore = LoadLibraryA("shcore.dll");
|
|
if (shcore) {
|
|
_sapp_win32_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T) GetProcAddress(shcore, "SetProcessDpiAwareness");
|
|
_sapp_win32_getdpiformonitor = (GETDPIFORMONITOR_T) GetProcAddress(shcore, "GetDpiForMonitor");
|
|
}
|
|
if (_sapp_win32_setprocessdpiawareness) {
|
|
/* if the app didn't request HighDPI rendering, let Windows do the upscaling */
|
|
PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE;
|
|
_sapp_win32_dpi_aware = true;
|
|
if (!_sapp.desc.high_dpi) {
|
|
process_dpi_awareness = PROCESS_DPI_UNAWARE;
|
|
_sapp_win32_dpi_aware = false;
|
|
}
|
|
_sapp_win32_setprocessdpiawareness(process_dpi_awareness);
|
|
}
|
|
else if (_sapp_win32_setprocessdpiaware) {
|
|
_sapp_win32_setprocessdpiaware();
|
|
_sapp_win32_dpi_aware = true;
|
|
}
|
|
/* get dpi scale factor for main monitor */
|
|
if (_sapp_win32_getdpiformonitor && _sapp_win32_dpi_aware) {
|
|
POINT pt = { 1, 1 };
|
|
HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
|
UINT dpix, dpiy;
|
|
HRESULT hr = _sapp_win32_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
|
|
_SOKOL_UNUSED(hr);
|
|
SOKOL_ASSERT(SUCCEEDED(hr));
|
|
/* clamp window scale to an integer factor */
|
|
_sapp_win32_window_scale = (float)dpix / 96.0f;
|
|
}
|
|
else {
|
|
_sapp_win32_window_scale = 1.0f;
|
|
}
|
|
if (_sapp.desc.high_dpi) {
|
|
_sapp_win32_content_scale = _sapp_win32_window_scale;
|
|
_sapp_win32_mouse_scale = 1.0f;
|
|
}
|
|
else {
|
|
_sapp_win32_content_scale = 1.0f;
|
|
_sapp_win32_mouse_scale = 1.0f / _sapp_win32_window_scale;
|
|
}
|
|
_sapp.dpi_scale = _sapp_win32_content_scale;
|
|
if (user32) {
|
|
FreeLibrary(user32);
|
|
}
|
|
if (shcore) {
|
|
FreeLibrary(shcore);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) {
|
|
SOKOL_ASSERT(str);
|
|
SOKOL_ASSERT(_sapp_win32_hwnd);
|
|
SOKOL_ASSERT(_sapp.clipboard_enabled && (_sapp.clipboard_size > 0));
|
|
|
|
wchar_t* wchar_buf = 0;
|
|
const int wchar_buf_size = _sapp.clipboard_size * sizeof(wchar_t);
|
|
HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size);
|
|
if (!object) {
|
|
goto error;
|
|
}
|
|
wchar_buf = (wchar_t*) GlobalLock(object);
|
|
if (!wchar_buf) {
|
|
goto error;
|
|
}
|
|
if (!_sapp_win32_utf8_to_wide(str, wchar_buf, wchar_buf_size)) {
|
|
goto error;
|
|
}
|
|
GlobalUnlock(wchar_buf);
|
|
wchar_buf = 0;
|
|
if (!OpenClipboard(_sapp_win32_hwnd)) {
|
|
goto error;
|
|
}
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_UNICODETEXT, object);
|
|
CloseClipboard();
|
|
return true;
|
|
|
|
error:
|
|
if (wchar_buf) {
|
|
GlobalUnlock(object);
|
|
}
|
|
if (object) {
|
|
GlobalFree(object);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) {
|
|
SOKOL_ASSERT(_sapp.clipboard_enabled && _sapp.clipboard);
|
|
SOKOL_ASSERT(_sapp_win32_hwnd);
|
|
if (!OpenClipboard(_sapp_win32_hwnd)) {
|
|
/* silently ignore any errors and just return the current
|
|
content of the local clipboard buffer
|
|
*/
|
|
return _sapp.clipboard;
|
|
}
|
|
HANDLE object = GetClipboardData(CF_UNICODETEXT);
|
|
if (!object) {
|
|
CloseClipboard();
|
|
return _sapp.clipboard;
|
|
}
|
|
const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object);
|
|
if (!wchar_buf) {
|
|
CloseClipboard();
|
|
return _sapp.clipboard;
|
|
}
|
|
_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard, _sapp.clipboard_size);
|
|
GlobalUnlock(object);
|
|
CloseClipboard();
|
|
return _sapp.clipboard;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
|
|
_sapp_init_state(desc);
|
|
_sapp_win32_init_keytable();
|
|
_sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
|
|
_sapp_win32_init_dpi();
|
|
_sapp_win32_create_window();
|
|
#if defined(SOKOL_D3D11)
|
|
_sapp_d3d11_create_device_and_swapchain();
|
|
_sapp_d3d11_create_default_render_target();
|
|
#endif
|
|
#if defined(SOKOL_GLCORE33)
|
|
_sapp_wgl_init();
|
|
_sapp_wgl_load_extensions();
|
|
_sapp_wgl_create_context();
|
|
#if !defined(SOKOL_WIN32_NO_GL_LOADER)
|
|
_sapp_win32_gl_loadfuncs();
|
|
#endif
|
|
#endif
|
|
_sapp.valid = true;
|
|
|
|
bool done = false;
|
|
while (!(done || _sapp.quit_ordered)) {
|
|
MSG msg;
|
|
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
if (WM_QUIT == msg.message) {
|
|
done = true;
|
|
continue;
|
|
}
|
|
else {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
_sapp_frame();
|
|
#if defined(SOKOL_D3D11)
|
|
IDXGISwapChain_Present(_sapp_dxgi_swap_chain, _sapp.swap_interval, 0);
|
|
if (IsIconic(_sapp_win32_hwnd)) {
|
|
Sleep(16 * _sapp.swap_interval);
|
|
}
|
|
#endif
|
|
#if defined(SOKOL_GLCORE33)
|
|
_sapp_wgl_swap_buffers();
|
|
#endif
|
|
/* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */
|
|
if (_sapp_win32_update_dimensions()) {
|
|
#if defined(SOKOL_D3D11)
|
|
_sapp_d3d11_resize_default_render_target();
|
|
#endif
|
|
_sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED);
|
|
}
|
|
if (_sapp.quit_requested) {
|
|
PostMessage(_sapp_win32_hwnd, WM_CLOSE, 0, 0);
|
|
}
|
|
}
|
|
_sapp_call_cleanup();
|
|
|
|
#if defined(SOKOL_D3D11)
|
|
_sapp_d3d11_destroy_default_render_target();
|
|
_sapp_d3d11_destroy_device_and_swapchain();
|
|
#else
|
|
_sapp_wgl_destroy_context();
|
|
_sapp_wgl_shutdown();
|
|
#endif
|
|
_sapp_win32_destroy_window();
|
|
_sapp_discard_state();
|
|
}
|
|
|
|
static char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) {
|
|
int argc = 0;
|
|
char** argv = 0;
|
|
char* args;
|
|
|
|
LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc);
|
|
if (w_argv == NULL) {
|
|
_sapp_fail("Win32: failed to parse command line");
|
|
} else {
|
|
size_t size = wcslen(w_command_line) * 4;
|
|
argv = (char**) SOKOL_CALLOC(1, (argc + 1) * sizeof(char*) + size);
|
|
args = (char*)&argv[argc + 1];
|
|
int n;
|
|
for (int i = 0; i < argc; ++i) {
|
|
n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL);
|
|
if (n == 0) {
|
|
_sapp_fail("Win32: failed to convert all arguments to utf8");
|
|
break;
|
|
}
|
|
argv[i] = args;
|
|
size -= n;
|
|
args += n;
|
|
}
|
|
LocalFree(w_argv);
|
|
}
|
|
*o_argc = argc;
|
|
return argv;
|
|
}
|
|
|
|
#if !defined(SOKOL_NO_ENTRY)
|
|
#if defined(SOKOL_WIN32_FORCE_MAIN)
|
|
int main(int argc, char* argv[]) {
|
|
sapp_desc desc = sokol_main(argc, argv);
|
|
_sapp_run(&desc);
|
|
return 0;
|
|
}
|
|
#else
|
|
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
|
|
_SOKOL_UNUSED(hInstance);
|
|
_SOKOL_UNUSED(hPrevInstance);
|
|
_SOKOL_UNUSED(lpCmdLine);
|
|
_SOKOL_UNUSED(nCmdShow);
|
|
int argc_utf8 = 0;
|
|
char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8);
|
|
sapp_desc desc = sokol_main(argc_utf8, argv_utf8);
|
|
_sapp_run(&desc);
|
|
SOKOL_FREE(argv_utf8);
|
|
return 0;
|
|
}
|
|
#endif /* SOKOL_WIN32_FORCE_MAIN */
|
|
#endif /* SOKOL_NO_ENTRY */
|
|
#undef _SAPP_SAFE_RELEASE
|
|
#endif /* WINDOWS */
|
|
|
|
/*== Android ================================================================*/
|
|
#if defined(__ANDROID__)
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <android/native_activity.h>
|
|
#include <android/looper.h>
|
|
|
|
#include <EGL/egl.h>
|
|
#if defined(SOKOL_GLES3)
|
|
#include <GLES3/gl3.h>
|
|
#else
|
|
#ifndef GL_EXT_PROTOTYPES
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#endif
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#endif
|
|
|
|
typedef struct {
|
|
pthread_t thread;
|
|
pthread_mutex_t mutex;
|
|
pthread_cond_t cond;
|
|
int read_from_main_fd;
|
|
int write_from_main_fd;
|
|
} _sapp_android_pt_t;
|
|
|
|
typedef struct {
|
|
ANativeWindow* window;
|
|
AInputQueue* input;
|
|
} _sapp_android_resources_t;
|
|
|
|
typedef enum {
|
|
_SOKOL_ANDROID_MSG_CREATE,
|
|
_SOKOL_ANDROID_MSG_RESUME,
|
|
_SOKOL_ANDROID_MSG_PAUSE,
|
|
_SOKOL_ANDROID_MSG_FOCUS,
|
|
_SOKOL_ANDROID_MSG_NO_FOCUS,
|
|
_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW,
|
|
_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE,
|
|
_SOKOL_ANDROID_MSG_DESTROY,
|
|
} _sapp_android_msg_t;
|
|
|
|
typedef struct {
|
|
ANativeActivity* activity;
|
|
_sapp_android_pt_t pt;
|
|
_sapp_android_resources_t pending;
|
|
_sapp_android_resources_t current;
|
|
ALooper* looper;
|
|
bool is_thread_started;
|
|
bool is_thread_stopping;
|
|
bool is_thread_stopped;
|
|
bool has_created;
|
|
bool has_resumed;
|
|
bool has_focus;
|
|
EGLConfig config;
|
|
EGLDisplay display;
|
|
EGLContext context;
|
|
EGLSurface surface;
|
|
} _sapp_android_state_t;
|
|
|
|
static _sapp_android_state_t _sapp_android_state;
|
|
|
|
/* android loop thread */
|
|
_SOKOL_PRIVATE bool _sapp_android_init_egl(void) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
SOKOL_ASSERT(state->display == EGL_NO_DISPLAY);
|
|
SOKOL_ASSERT(state->context == EGL_NO_CONTEXT);
|
|
|
|
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
if (display == EGL_NO_DISPLAY) {
|
|
return false;
|
|
}
|
|
if (eglInitialize(display, NULL, NULL) == EGL_FALSE) {
|
|
return false;
|
|
}
|
|
|
|
EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
|
|
const EGLint cfg_attributes[] = {
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_ALPHA_SIZE, alpha_size,
|
|
EGL_DEPTH_SIZE, 16,
|
|
EGL_STENCIL_SIZE, 0,
|
|
EGL_NONE,
|
|
};
|
|
EGLConfig available_cfgs[32];
|
|
EGLint cfg_count;
|
|
eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count);
|
|
SOKOL_ASSERT(cfg_count > 0);
|
|
SOKOL_ASSERT(cfg_count <= 32);
|
|
|
|
/* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */
|
|
EGLConfig config;
|
|
bool exact_cfg_found = false;
|
|
for (int i = 0; i < cfg_count; ++i) {
|
|
EGLConfig c = available_cfgs[i];
|
|
EGLint r, g, b, a, d;
|
|
if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE &&
|
|
eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE &&
|
|
eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE &&
|
|
eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE &&
|
|
eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE &&
|
|
r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) {
|
|
exact_cfg_found = true;
|
|
config = c;
|
|
break;
|
|
}
|
|
}
|
|
if (!exact_cfg_found) {
|
|
config = available_cfgs[0];
|
|
}
|
|
|
|
EGLint ctx_attributes[] = {
|
|
#if defined(SOKOL_GLES3)
|
|
EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3,
|
|
#else
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
#endif
|
|
EGL_NONE,
|
|
};
|
|
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes);
|
|
if (context == EGL_NO_CONTEXT) {
|
|
return false;
|
|
}
|
|
|
|
state->config = config;
|
|
state->display = display;
|
|
state->context = context;
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
if (state->display != EGL_NO_DISPLAY) {
|
|
eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
if (state->surface != EGL_NO_SURFACE) {
|
|
SOKOL_LOG("Destroying egl surface");
|
|
eglDestroySurface(state->display, state->surface);
|
|
state->surface = EGL_NO_SURFACE;
|
|
}
|
|
if (state->context != EGL_NO_CONTEXT) {
|
|
SOKOL_LOG("Destroying egl context");
|
|
eglDestroyContext(state->display, state->context);
|
|
state->context = EGL_NO_CONTEXT;
|
|
}
|
|
SOKOL_LOG("Terminating egl display");
|
|
eglTerminate(state->display);
|
|
state->display = EGL_NO_DISPLAY;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
SOKOL_ASSERT(state->display != EGL_NO_DISPLAY);
|
|
SOKOL_ASSERT(state->context != EGL_NO_CONTEXT);
|
|
SOKOL_ASSERT(state->surface == EGL_NO_SURFACE);
|
|
SOKOL_ASSERT(window);
|
|
|
|
/* TODO: set window flags */
|
|
/* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */
|
|
|
|
/* create egl surface and make it current */
|
|
EGLSurface surface = eglCreateWindowSurface(state->display, state->config, window, NULL);
|
|
if (surface == EGL_NO_SURFACE) {
|
|
return false;
|
|
}
|
|
if (eglMakeCurrent(state->display, surface, surface, state->context) == EGL_FALSE) {
|
|
return false;
|
|
}
|
|
state->surface = surface;
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
if (state->display == EGL_NO_DISPLAY) {
|
|
return;
|
|
}
|
|
eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
if (state->surface != EGL_NO_SURFACE) {
|
|
eglDestroySurface(state->display, state->surface);
|
|
state->surface = EGL_NO_SURFACE;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
SOKOL_LOG("event_cb()");
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
SOKOL_ASSERT(state->display != EGL_NO_DISPLAY);
|
|
SOKOL_ASSERT(state->context != EGL_NO_CONTEXT);
|
|
SOKOL_ASSERT(state->surface != EGL_NO_SURFACE);
|
|
SOKOL_ASSERT(window);
|
|
|
|
const int32_t win_w = ANativeWindow_getWidth(window);
|
|
const int32_t win_h = ANativeWindow_getHeight(window);
|
|
SOKOL_ASSERT(win_w >= 0 && win_h >= 0);
|
|
const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height);
|
|
_sapp.window_width = win_w;
|
|
_sapp.window_height = win_h;
|
|
if (win_changed || force_update) {
|
|
if (!_sapp.desc.high_dpi) {
|
|
const int32_t buf_w = win_w / 2;
|
|
const int32_t buf_h = win_h / 2;
|
|
EGLint format;
|
|
EGLBoolean egl_result = eglGetConfigAttrib(state->display, state->config, EGL_NATIVE_VISUAL_ID, &format);
|
|
SOKOL_ASSERT(egl_result == EGL_TRUE);
|
|
/* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions
|
|
as the ANativeWindow size results in weird display artefacts, that's
|
|
why it's only called when the buffer geometry is different from
|
|
the window size
|
|
*/
|
|
int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format);
|
|
SOKOL_ASSERT(result == 0);
|
|
}
|
|
}
|
|
|
|
/* query surface size */
|
|
EGLint fb_w, fb_h;
|
|
EGLBoolean egl_result_w = eglQuerySurface(state->display, state->surface, EGL_WIDTH, &fb_w);
|
|
EGLBoolean egl_result_h = eglQuerySurface(state->display, state->surface, EGL_HEIGHT, &fb_h);
|
|
SOKOL_ASSERT(egl_result_w == EGL_TRUE);
|
|
SOKOL_ASSERT(egl_result_h == EGL_TRUE);
|
|
const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height);
|
|
_sapp.framebuffer_width = fb_w;
|
|
_sapp.framebuffer_height = fb_h;
|
|
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
|
|
if (win_changed || fb_changed || force_update) {
|
|
if (!_sapp.first_frame) {
|
|
SOKOL_LOG("SAPP_EVENTTYPE_RESIZED");
|
|
_sapp_android_app_event(SAPP_EVENTTYPE_RESIZED);
|
|
}
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_cleanup(void) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
SOKOL_LOG("Cleaning up");
|
|
if (state->surface != EGL_NO_SURFACE) {
|
|
/* egl context is bound, cleanup gracefully */
|
|
if (_sapp.init_called && !_sapp.cleanup_called) {
|
|
SOKOL_LOG("cleanup_cb()");
|
|
_sapp_call_cleanup();
|
|
}
|
|
}
|
|
/* always try to cleanup by destroying egl context */
|
|
_sapp_android_cleanup_egl();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_shutdown(void) {
|
|
/* try to cleanup while we still have a surface and can call cleanup_cb() */
|
|
_sapp_android_cleanup();
|
|
/* request exit */
|
|
ANativeActivity_finish(_sapp_android_state.activity);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_frame(void) {
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
SOKOL_ASSERT(state->display != EGL_NO_DISPLAY);
|
|
SOKOL_ASSERT(state->context != EGL_NO_CONTEXT);
|
|
SOKOL_ASSERT(state->surface != EGL_NO_SURFACE);
|
|
_sapp_android_update_dimensions(state->current.window, false);
|
|
_sapp_frame();
|
|
eglSwapBuffers(state->display, _sapp_android_state.surface);
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
|
|
if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) {
|
|
return false;
|
|
}
|
|
if (!_sapp_events_enabled()) {
|
|
return false;
|
|
}
|
|
int32_t action_idx = AMotionEvent_getAction(e);
|
|
int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK;
|
|
sapp_event_type type = SAPP_EVENTTYPE_INVALID;
|
|
switch (action) {
|
|
case AMOTION_EVENT_ACTION_DOWN:
|
|
SOKOL_LOG("Touch: down");
|
|
case AMOTION_EVENT_ACTION_POINTER_DOWN:
|
|
SOKOL_LOG("Touch: ptr down");
|
|
type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
|
|
break;
|
|
case AMOTION_EVENT_ACTION_MOVE:
|
|
type = SAPP_EVENTTYPE_TOUCHES_MOVED;
|
|
break;
|
|
case AMOTION_EVENT_ACTION_UP:
|
|
SOKOL_LOG("Touch: up");
|
|
case AMOTION_EVENT_ACTION_POINTER_UP:
|
|
SOKOL_LOG("Touch: ptr up");
|
|
type = SAPP_EVENTTYPE_TOUCHES_ENDED;
|
|
break;
|
|
case AMOTION_EVENT_ACTION_CANCEL:
|
|
SOKOL_LOG("Touch: cancel");
|
|
type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (type == SAPP_EVENTTYPE_INVALID) {
|
|
return false;
|
|
}
|
|
int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
|
_sapp_init_event(type);
|
|
_sapp.event.num_touches = AMotionEvent_getPointerCount(e);
|
|
if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
|
|
_sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
|
|
}
|
|
for (int32_t i = 0; i < _sapp.event.num_touches; i++) {
|
|
sapp_touchpoint* dst = &_sapp.event.touches[i];
|
|
dst->identifier = AMotionEvent_getPointerId(e, i);
|
|
dst->pos_x = (AMotionEvent_getRawX(e, i) / _sapp.window_width) * _sapp.framebuffer_width;
|
|
dst->pos_y = (AMotionEvent_getRawY(e, i) / _sapp.window_height) * _sapp.framebuffer_height;
|
|
|
|
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN ||
|
|
action == AMOTION_EVENT_ACTION_POINTER_UP) {
|
|
dst->changed = i == idx;
|
|
} else {
|
|
dst->changed = true;
|
|
}
|
|
}
|
|
_sapp_call_event(&_sapp.event);
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) {
|
|
if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) {
|
|
return false;
|
|
}
|
|
if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) {
|
|
/* FIXME: this should be hooked into a "really quit?" mechanism
|
|
so the app can ask the user for confirmation, this is currently
|
|
generally missing in sokol_app.h
|
|
*/
|
|
_sapp_android_shutdown();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) {
|
|
if ((events & ALOOPER_EVENT_INPUT) == 0) {
|
|
SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event");
|
|
return 1;
|
|
}
|
|
_sapp_android_state_t* state = &_sapp_android_state;;
|
|
SOKOL_ASSERT(state->current.input);
|
|
AInputEvent* event = NULL;
|
|
while (AInputQueue_getEvent(state->current.input, &event) >= 0) {
|
|
if (AInputQueue_preDispatchEvent(state->current.input, event) != 0) {
|
|
continue;
|
|
}
|
|
int32_t handled = 0;
|
|
if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) {
|
|
handled = 1;
|
|
}
|
|
AInputQueue_finishEvent(state->current.input, event, handled);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) {
|
|
if ((events & ALOOPER_EVENT_INPUT) == 0) {
|
|
SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event");
|
|
return 1;
|
|
}
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
|
|
_sapp_android_msg_t msg;
|
|
if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) {
|
|
SOKOL_LOG("Could not write to read_from_main_fd");
|
|
return 1;
|
|
}
|
|
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
switch (msg) {
|
|
case _SOKOL_ANDROID_MSG_CREATE:
|
|
{
|
|
SOKOL_LOG("MSG_CREATE");
|
|
SOKOL_ASSERT(!_sapp.valid);
|
|
bool result = _sapp_android_init_egl();
|
|
SOKOL_ASSERT(result);
|
|
_sapp.valid = true;
|
|
state->has_created = true;
|
|
}
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_RESUME:
|
|
SOKOL_LOG("MSG_RESUME");
|
|
state->has_resumed = true;
|
|
_sapp_android_app_event(SAPP_EVENTTYPE_RESUMED);
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_PAUSE:
|
|
SOKOL_LOG("MSG_PAUSE");
|
|
state->has_resumed = false;
|
|
_sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED);
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_FOCUS:
|
|
SOKOL_LOG("MSG_FOCUS");
|
|
state->has_focus = true;
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_NO_FOCUS:
|
|
SOKOL_LOG("MSG_NO_FOCUS");
|
|
state->has_focus = false;
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW:
|
|
SOKOL_LOG("MSG_SET_NATIVE_WINDOW");
|
|
if (state->current.window != state->pending.window) {
|
|
if (state->current.window != NULL) {
|
|
_sapp_android_cleanup_egl_surface();
|
|
}
|
|
if (state->pending.window != NULL) {
|
|
SOKOL_LOG("Creating egl surface ...");
|
|
if (_sapp_android_init_egl_surface(state->pending.window)) {
|
|
SOKOL_LOG("... ok!");
|
|
_sapp_android_update_dimensions(state->pending.window, true);
|
|
} else {
|
|
SOKOL_LOG("... failed!");
|
|
_sapp_android_shutdown();
|
|
}
|
|
}
|
|
}
|
|
state->current.window = state->pending.window;
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE:
|
|
SOKOL_LOG("MSG_SET_INPUT_QUEUE");
|
|
if (state->current.input != state->pending.input) {
|
|
if (state->current.input != NULL) {
|
|
AInputQueue_detachLooper(state->current.input);
|
|
}
|
|
if (state->pending.input != NULL) {
|
|
AInputQueue_attachLooper(
|
|
state->pending.input,
|
|
state->looper,
|
|
ALOOPER_POLL_CALLBACK,
|
|
_sapp_android_input_cb,
|
|
NULL); /* data */
|
|
}
|
|
}
|
|
state->current.input = state->pending.input;
|
|
break;
|
|
case _SOKOL_ANDROID_MSG_DESTROY:
|
|
SOKOL_LOG("MSG_DESTROY");
|
|
_sapp_android_cleanup();
|
|
_sapp.valid = false;
|
|
state->is_thread_stopping = true;
|
|
break;
|
|
default:
|
|
SOKOL_LOG("Unknown msg type received");
|
|
break;
|
|
}
|
|
pthread_cond_broadcast(&state->pt.cond); /* signal "received" */
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
return 1;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_android_should_update(void) {
|
|
bool is_in_front = _sapp_android_state.has_resumed && _sapp_android_state.has_focus;
|
|
bool has_surface = _sapp_android_state.surface != EGL_NO_SURFACE;
|
|
return is_in_front && has_surface;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
/* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */
|
|
if (shown) {
|
|
SOKOL_LOG("Showing keyboard");
|
|
ANativeActivity_showSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED);
|
|
} else {
|
|
SOKOL_LOG("Hiding keyboard");
|
|
ANativeActivity_hideSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void* _sapp_android_loop(void* obj) {
|
|
SOKOL_LOG("Loop thread started");
|
|
_sapp_android_state_t* state = (_sapp_android_state_t*)obj;
|
|
|
|
state->looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/);
|
|
ALooper_addFd(state->looper,
|
|
state->pt.read_from_main_fd,
|
|
ALOOPER_POLL_CALLBACK,
|
|
ALOOPER_EVENT_INPUT,
|
|
_sapp_android_main_cb,
|
|
NULL); /* data */
|
|
|
|
/* signal start to main thread */
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
state->is_thread_started = true;
|
|
pthread_cond_broadcast(&state->pt.cond);
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
|
|
/* main loop */
|
|
while (!state->is_thread_stopping) {
|
|
/* sokol frame */
|
|
if (_sapp_android_should_update()) {
|
|
_sapp_android_frame();
|
|
}
|
|
|
|
/* process all events (or stop early if app is requested to quit) */
|
|
bool process_events = true;
|
|
while (process_events && !state->is_thread_stopping) {
|
|
bool block_until_event = !state->is_thread_stopping && !_sapp_android_should_update();
|
|
process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK;
|
|
}
|
|
}
|
|
|
|
/* cleanup thread */
|
|
if (state->current.input != NULL) {
|
|
AInputQueue_detachLooper(state->current.input);
|
|
}
|
|
|
|
/* the following causes heap corruption on exit, why??
|
|
ALooper_removeFd(state->looper, state->pt.read_from_main_fd);
|
|
ALooper_release(state->looper);*/
|
|
|
|
/* signal "destroyed" */
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
state->is_thread_stopped = true;
|
|
pthread_cond_broadcast(&state->pt.cond);
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
SOKOL_LOG("Loop thread done");
|
|
return NULL;
|
|
}
|
|
|
|
/* android main/ui thread */
|
|
_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_state_t* state, _sapp_android_msg_t msg) {
|
|
if (write(state->pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) {
|
|
SOKOL_LOG("Could not write to write_from_main_fd");
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onStart()");
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onResume()");
|
|
_sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_RESUME);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) {
|
|
SOKOL_LOG("NativeActivity onSaveInstanceState()");
|
|
*out_size = 0;
|
|
return NULL;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) {
|
|
SOKOL_LOG("NativeActivity onWindowFocusChanged()");
|
|
if (has_focus) {
|
|
_sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_FOCUS);
|
|
} else {
|
|
_sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_NO_FOCUS);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onPause()");
|
|
_sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_PAUSE);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onStop()");
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(_sapp_android_state_t* state, ANativeWindow* window) {
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
state->pending.window = window;
|
|
_sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW);
|
|
while (state->current.window != window) {
|
|
pthread_cond_wait(&state->pt.cond, &state->pt.mutex);
|
|
}
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) {
|
|
SOKOL_LOG("NativeActivity onNativeWindowCreated()");
|
|
_sapp_android_msg_set_native_window(&_sapp_android_state, window);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) {
|
|
SOKOL_LOG("NativeActivity onNativeWindowDestroyed()");
|
|
_sapp_android_msg_set_native_window(&_sapp_android_state, NULL);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(_sapp_android_state_t* state, AInputQueue* input) {
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
state->pending.input = input;
|
|
_sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE);
|
|
while (state->current.input != input) {
|
|
pthread_cond_wait(&state->pt.cond, &state->pt.mutex);
|
|
}
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) {
|
|
SOKOL_LOG("NativeActivity onInputQueueCreated()");
|
|
_sapp_android_msg_set_input_queue(&_sapp_android_state, queue);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) {
|
|
SOKOL_LOG("NativeActivity onInputQueueDestroyed()");
|
|
_sapp_android_msg_set_input_queue(&_sapp_android_state, NULL);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onConfigurationChanged()");
|
|
/* see android:configChanges in manifest */
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) {
|
|
SOKOL_LOG("NativeActivity onLowMemory()");
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) {
|
|
/*
|
|
* For some reason even an empty app using nativeactivity.h will crash (WIN DEATH)
|
|
* on my device (Moto X 2nd gen) when the app is removed from the task view
|
|
* (TaskStackView: onTaskViewDismissed).
|
|
*
|
|
* However, if ANativeActivity_finish() is explicitly called from for example
|
|
* _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity?
|
|
*/
|
|
SOKOL_LOG("NativeActivity onDestroy()");
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
|
|
/* send destroy msg */
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
_sapp_android_msg(state, _SOKOL_ANDROID_MSG_DESTROY);
|
|
while (!_sapp_android_state.is_thread_stopped) {
|
|
pthread_cond_wait(&state->pt.cond, &state->pt.mutex);
|
|
}
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
|
|
/* clean up main thread */
|
|
pthread_cond_destroy(&state->pt.cond);
|
|
pthread_mutex_destroy(&state->pt.mutex);
|
|
|
|
close(state->pt.read_from_main_fd);
|
|
close(state->pt.write_from_main_fd);
|
|
|
|
SOKOL_LOG("NativeActivity done");
|
|
|
|
/* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */
|
|
exit(0);
|
|
}
|
|
|
|
JNIEXPORT
|
|
void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) {
|
|
SOKOL_LOG("NativeActivity onCreate()");
|
|
|
|
sapp_desc desc = sokol_main(0, NULL);
|
|
_sapp_init_state(&desc);
|
|
|
|
/* start loop thread */
|
|
_sapp_android_state = (_sapp_android_state_t){0};
|
|
_sapp_android_state_t* state = &_sapp_android_state;
|
|
|
|
state->activity = activity;
|
|
|
|
int pipe_fd[2];
|
|
if (pipe(pipe_fd) != 0) {
|
|
SOKOL_LOG("Could not create thread pipe");
|
|
return;
|
|
}
|
|
state->pt.read_from_main_fd = pipe_fd[0];
|
|
state->pt.write_from_main_fd = pipe_fd[1];
|
|
|
|
pthread_mutex_init(&state->pt.mutex, NULL);
|
|
pthread_cond_init(&state->pt.cond, NULL);
|
|
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
pthread_create(&state->pt.thread, &attr, _sapp_android_loop, state);
|
|
pthread_attr_destroy(&attr);
|
|
|
|
/* wait until main loop has started */
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
while (!state->is_thread_started) {
|
|
pthread_cond_wait(&state->pt.cond, &state->pt.mutex);
|
|
}
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
|
|
/* send create msg */
|
|
pthread_mutex_lock(&state->pt.mutex);
|
|
_sapp_android_msg(state, _SOKOL_ANDROID_MSG_CREATE);
|
|
while (!state->has_created) {
|
|
pthread_cond_wait(&state->pt.cond, &state->pt.mutex);
|
|
}
|
|
pthread_mutex_unlock(&state->pt.mutex);
|
|
|
|
/* register for callbacks */
|
|
activity->instance = state;
|
|
activity->callbacks->onStart = _sapp_android_on_start;
|
|
activity->callbacks->onResume = _sapp_android_on_resume;
|
|
activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state;
|
|
activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed;
|
|
activity->callbacks->onPause = _sapp_android_on_pause;
|
|
activity->callbacks->onStop = _sapp_android_on_stop;
|
|
activity->callbacks->onDestroy = _sapp_android_on_destroy;
|
|
activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created;
|
|
/* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */
|
|
/* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */
|
|
activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed;
|
|
activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created;
|
|
activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed;
|
|
/* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */
|
|
activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed;
|
|
activity->callbacks->onLowMemory = _sapp_android_on_low_memory;
|
|
|
|
SOKOL_LOG("NativeActivity successfully created");
|
|
|
|
/* NOT A BUG: do NOT call sapp_discard_state() */
|
|
}
|
|
|
|
#endif /* Android */
|
|
|
|
/*== LINUX ==================================================================*/
|
|
#if (defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/XKBlib.h>
|
|
#include <X11/Xresource.h>
|
|
#include <X11/extensions/Xrandr.h>
|
|
#include <X11/Xmd.h> /* CARD32 */
|
|
#include <GL/gl.h>
|
|
#include <dlfcn.h> /* dlopen, dlsym, dlclose */
|
|
#include <limits.h> /* LONG_MAX */
|
|
|
|
#define GLX_VENDOR 1
|
|
#define GLX_RGBA_BIT 0x00000001
|
|
#define GLX_WINDOW_BIT 0x00000001
|
|
#define GLX_DRAWABLE_TYPE 0x8010
|
|
#define GLX_RENDER_TYPE 0x8011
|
|
#define GLX_RGBA_TYPE 0x8014
|
|
#define GLX_DOUBLEBUFFER 5
|
|
#define GLX_STEREO 6
|
|
#define GLX_AUX_BUFFERS 7
|
|
#define GLX_RED_SIZE 8
|
|
#define GLX_GREEN_SIZE 9
|
|
#define GLX_BLUE_SIZE 10
|
|
#define GLX_ALPHA_SIZE 11
|
|
#define GLX_DEPTH_SIZE 12
|
|
#define GLX_STENCIL_SIZE 13
|
|
#define GLX_ACCUM_RED_SIZE 14
|
|
#define GLX_ACCUM_GREEN_SIZE 15
|
|
#define GLX_ACCUM_BLUE_SIZE 16
|
|
#define GLX_ACCUM_ALPHA_SIZE 17
|
|
#define GLX_SAMPLES 0x186a1
|
|
#define GLX_VISUAL_ID 0x800b
|
|
|
|
#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
|
|
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
|
|
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
|
|
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
|
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
|
|
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
|
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
#define GLX_CONTEXT_FLAGS_ARB 0x2094
|
|
#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
|
|
#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
|
|
#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
|
#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261
|
|
#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
|
|
#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
|
|
#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
|
|
|
|
typedef XID GLXWindow;
|
|
typedef XID GLXDrawable;
|
|
typedef struct __GLXFBConfig* GLXFBConfig;
|
|
typedef struct __GLXcontext* GLXContext;
|
|
typedef void (*__GLXextproc)(void);
|
|
|
|
typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
|
|
typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
|
|
typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
|
|
typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
|
|
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);
|
|
typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);
|
|
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);
|
|
typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);
|
|
typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);
|
|
typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool);
|
|
typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName);
|
|
typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
|
|
typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
|
|
typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);
|
|
typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);
|
|
|
|
typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
|
|
typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);
|
|
|
|
static Display* _sapp_x11_display;
|
|
static int _sapp_x11_screen;
|
|
static Window _sapp_x11_root;
|
|
static Colormap _sapp_x11_colormap;
|
|
static Window _sapp_x11_window;
|
|
static float _sapp_x11_dpi;
|
|
static int _sapp_x11_window_state;
|
|
static unsigned char _sapp_x11_error_code;
|
|
static void* _sapp_glx_libgl;
|
|
static int _sapp_glx_major;
|
|
static int _sapp_glx_minor;
|
|
static int _sapp_glx_eventbase;
|
|
static int _sapp_glx_errorbase;
|
|
static GLXContext _sapp_glx_ctx;
|
|
static GLXWindow _sapp_glx_window;
|
|
static Atom _sapp_x11_UTF8_STRING;
|
|
static Atom _sapp_x11_WM_PROTOCOLS;
|
|
static Atom _sapp_x11_WM_DELETE_WINDOW;
|
|
static Atom _sapp_x11_WM_STATE;
|
|
static Atom _sapp_x11_NET_WM_NAME;
|
|
static Atom _sapp_x11_NET_WM_ICON_NAME;
|
|
// GLX 1.3 functions
|
|
static PFNGLXGETFBCONFIGSPROC _sapp_glx_GetFBConfigs;
|
|
static PFNGLXGETFBCONFIGATTRIBPROC _sapp_glx_GetFBConfigAttrib;
|
|
static PFNGLXGETCLIENTSTRINGPROC _sapp_glx_GetClientString;
|
|
static PFNGLXQUERYEXTENSIONPROC _sapp_glx_QueryExtension;
|
|
static PFNGLXQUERYVERSIONPROC _sapp_glx_QueryVersion;
|
|
static PFNGLXDESTROYCONTEXTPROC _sapp_glx_DestroyContext;
|
|
static PFNGLXMAKECURRENTPROC _sapp_glx_MakeCurrent;
|
|
static PFNGLXSWAPBUFFERSPROC _sapp_glx_SwapBuffers;
|
|
static PFNGLXQUERYEXTENSIONSSTRINGPROC _sapp_glx_QueryExtensionsString;
|
|
static PFNGLXCREATENEWCONTEXTPROC _sapp_glx_CreateNewContext;
|
|
static PFNGLXGETVISUALFROMFBCONFIGPROC _sapp_glx_GetVisualFromFBConfig;
|
|
static PFNGLXCREATEWINDOWPROC _sapp_glx_CreateWindow;
|
|
static PFNGLXDESTROYWINDOWPROC _sapp_glx_DestroyWindow;
|
|
|
|
// GLX 1.4 and extension functions
|
|
static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddress;
|
|
static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddressARB;
|
|
static PFNGLXSWAPINTERVALEXTPROC _sapp_glx_SwapIntervalEXT;
|
|
static PFNGLXSWAPINTERVALMESAPROC _sapp_glx_SwapIntervalMESA;
|
|
static PFNGLXCREATECONTEXTATTRIBSARBPROC _sapp_glx_CreateContextAttribsARB;
|
|
static bool _sapp_glx_EXT_swap_control;
|
|
static bool _sapp_glx_MESA_swap_control;
|
|
static bool _sapp_glx_ARB_multisample;
|
|
static bool _sapp_glx_ARB_framebuffer_sRGB;
|
|
static bool _sapp_glx_EXT_framebuffer_sRGB;
|
|
static bool _sapp_glx_ARB_create_context;
|
|
static bool _sapp_glx_ARB_create_context_profile;
|
|
|
|
/* see GLFW's xkb_unicode.c */
|
|
static const struct _sapp_x11_codepair {
|
|
uint16_t keysym;
|
|
uint16_t ucs;
|
|
} _sapp_x11_keysymtab[] = {
|
|
{ 0x01a1, 0x0104 },
|
|
{ 0x01a2, 0x02d8 },
|
|
{ 0x01a3, 0x0141 },
|
|
{ 0x01a5, 0x013d },
|
|
{ 0x01a6, 0x015a },
|
|
{ 0x01a9, 0x0160 },
|
|
{ 0x01aa, 0x015e },
|
|
{ 0x01ab, 0x0164 },
|
|
{ 0x01ac, 0x0179 },
|
|
{ 0x01ae, 0x017d },
|
|
{ 0x01af, 0x017b },
|
|
{ 0x01b1, 0x0105 },
|
|
{ 0x01b2, 0x02db },
|
|
{ 0x01b3, 0x0142 },
|
|
{ 0x01b5, 0x013e },
|
|
{ 0x01b6, 0x015b },
|
|
{ 0x01b7, 0x02c7 },
|
|
{ 0x01b9, 0x0161 },
|
|
{ 0x01ba, 0x015f },
|
|
{ 0x01bb, 0x0165 },
|
|
{ 0x01bc, 0x017a },
|
|
{ 0x01bd, 0x02dd },
|
|
{ 0x01be, 0x017e },
|
|
{ 0x01bf, 0x017c },
|
|
{ 0x01c0, 0x0154 },
|
|
{ 0x01c3, 0x0102 },
|
|
{ 0x01c5, 0x0139 },
|
|
{ 0x01c6, 0x0106 },
|
|
{ 0x01c8, 0x010c },
|
|
{ 0x01ca, 0x0118 },
|
|
{ 0x01cc, 0x011a },
|
|
{ 0x01cf, 0x010e },
|
|
{ 0x01d0, 0x0110 },
|
|
{ 0x01d1, 0x0143 },
|
|
{ 0x01d2, 0x0147 },
|
|
{ 0x01d5, 0x0150 },
|
|
{ 0x01d8, 0x0158 },
|
|
{ 0x01d9, 0x016e },
|
|
{ 0x01db, 0x0170 },
|
|
{ 0x01de, 0x0162 },
|
|
{ 0x01e0, 0x0155 },
|
|
{ 0x01e3, 0x0103 },
|
|
{ 0x01e5, 0x013a },
|
|
{ 0x01e6, 0x0107 },
|
|
{ 0x01e8, 0x010d },
|
|
{ 0x01ea, 0x0119 },
|
|
{ 0x01ec, 0x011b },
|
|
{ 0x01ef, 0x010f },
|
|
{ 0x01f0, 0x0111 },
|
|
{ 0x01f1, 0x0144 },
|
|
{ 0x01f2, 0x0148 },
|
|
{ 0x01f5, 0x0151 },
|
|
{ 0x01f8, 0x0159 },
|
|
{ 0x01f9, 0x016f },
|
|
{ 0x01fb, 0x0171 },
|
|
{ 0x01fe, 0x0163 },
|
|
{ 0x01ff, 0x02d9 },
|
|
{ 0x02a1, 0x0126 },
|
|
{ 0x02a6, 0x0124 },
|
|
{ 0x02a9, 0x0130 },
|
|
{ 0x02ab, 0x011e },
|
|
{ 0x02ac, 0x0134 },
|
|
{ 0x02b1, 0x0127 },
|
|
{ 0x02b6, 0x0125 },
|
|
{ 0x02b9, 0x0131 },
|
|
{ 0x02bb, 0x011f },
|
|
{ 0x02bc, 0x0135 },
|
|
{ 0x02c5, 0x010a },
|
|
{ 0x02c6, 0x0108 },
|
|
{ 0x02d5, 0x0120 },
|
|
{ 0x02d8, 0x011c },
|
|
{ 0x02dd, 0x016c },
|
|
{ 0x02de, 0x015c },
|
|
{ 0x02e5, 0x010b },
|
|
{ 0x02e6, 0x0109 },
|
|
{ 0x02f5, 0x0121 },
|
|
{ 0x02f8, 0x011d },
|
|
{ 0x02fd, 0x016d },
|
|
{ 0x02fe, 0x015d },
|
|
{ 0x03a2, 0x0138 },
|
|
{ 0x03a3, 0x0156 },
|
|
{ 0x03a5, 0x0128 },
|
|
{ 0x03a6, 0x013b },
|
|
{ 0x03aa, 0x0112 },
|
|
{ 0x03ab, 0x0122 },
|
|
{ 0x03ac, 0x0166 },
|
|
{ 0x03b3, 0x0157 },
|
|
{ 0x03b5, 0x0129 },
|
|
{ 0x03b6, 0x013c },
|
|
{ 0x03ba, 0x0113 },
|
|
{ 0x03bb, 0x0123 },
|
|
{ 0x03bc, 0x0167 },
|
|
{ 0x03bd, 0x014a },
|
|
{ 0x03bf, 0x014b },
|
|
{ 0x03c0, 0x0100 },
|
|
{ 0x03c7, 0x012e },
|
|
{ 0x03cc, 0x0116 },
|
|
{ 0x03cf, 0x012a },
|
|
{ 0x03d1, 0x0145 },
|
|
{ 0x03d2, 0x014c },
|
|
{ 0x03d3, 0x0136 },
|
|
{ 0x03d9, 0x0172 },
|
|
{ 0x03dd, 0x0168 },
|
|
{ 0x03de, 0x016a },
|
|
{ 0x03e0, 0x0101 },
|
|
{ 0x03e7, 0x012f },
|
|
{ 0x03ec, 0x0117 },
|
|
{ 0x03ef, 0x012b },
|
|
{ 0x03f1, 0x0146 },
|
|
{ 0x03f2, 0x014d },
|
|
{ 0x03f3, 0x0137 },
|
|
{ 0x03f9, 0x0173 },
|
|
{ 0x03fd, 0x0169 },
|
|
{ 0x03fe, 0x016b },
|
|
{ 0x047e, 0x203e },
|
|
{ 0x04a1, 0x3002 },
|
|
{ 0x04a2, 0x300c },
|
|
{ 0x04a3, 0x300d },
|
|
{ 0x04a4, 0x3001 },
|
|
{ 0x04a5, 0x30fb },
|
|
{ 0x04a6, 0x30f2 },
|
|
{ 0x04a7, 0x30a1 },
|
|
{ 0x04a8, 0x30a3 },
|
|
{ 0x04a9, 0x30a5 },
|
|
{ 0x04aa, 0x30a7 },
|
|
{ 0x04ab, 0x30a9 },
|
|
{ 0x04ac, 0x30e3 },
|
|
{ 0x04ad, 0x30e5 },
|
|
{ 0x04ae, 0x30e7 },
|
|
{ 0x04af, 0x30c3 },
|
|
{ 0x04b0, 0x30fc },
|
|
{ 0x04b1, 0x30a2 },
|
|
{ 0x04b2, 0x30a4 },
|
|
{ 0x04b3, 0x30a6 },
|
|
{ 0x04b4, 0x30a8 },
|
|
{ 0x04b5, 0x30aa },
|
|
{ 0x04b6, 0x30ab },
|
|
{ 0x04b7, 0x30ad },
|
|
{ 0x04b8, 0x30af },
|
|
{ 0x04b9, 0x30b1 },
|
|
{ 0x04ba, 0x30b3 },
|
|
{ 0x04bb, 0x30b5 },
|
|
{ 0x04bc, 0x30b7 },
|
|
{ 0x04bd, 0x30b9 },
|
|
{ 0x04be, 0x30bb },
|
|
{ 0x04bf, 0x30bd },
|
|
{ 0x04c0, 0x30bf },
|
|
{ 0x04c1, 0x30c1 },
|
|
{ 0x04c2, 0x30c4 },
|
|
{ 0x04c3, 0x30c6 },
|
|
{ 0x04c4, 0x30c8 },
|
|
{ 0x04c5, 0x30ca },
|
|
{ 0x04c6, 0x30cb },
|
|
{ 0x04c7, 0x30cc },
|
|
{ 0x04c8, 0x30cd },
|
|
{ 0x04c9, 0x30ce },
|
|
{ 0x04ca, 0x30cf },
|
|
{ 0x04cb, 0x30d2 },
|
|
{ 0x04cc, 0x30d5 },
|
|
{ 0x04cd, 0x30d8 },
|
|
{ 0x04ce, 0x30db },
|
|
{ 0x04cf, 0x30de },
|
|
{ 0x04d0, 0x30df },
|
|
{ 0x04d1, 0x30e0 },
|
|
{ 0x04d2, 0x30e1 },
|
|
{ 0x04d3, 0x30e2 },
|
|
{ 0x04d4, 0x30e4 },
|
|
{ 0x04d5, 0x30e6 },
|
|
{ 0x04d6, 0x30e8 },
|
|
{ 0x04d7, 0x30e9 },
|
|
{ 0x04d8, 0x30ea },
|
|
{ 0x04d9, 0x30eb },
|
|
{ 0x04da, 0x30ec },
|
|
{ 0x04db, 0x30ed },
|
|
{ 0x04dc, 0x30ef },
|
|
{ 0x04dd, 0x30f3 },
|
|
{ 0x04de, 0x309b },
|
|
{ 0x04df, 0x309c },
|
|
{ 0x05ac, 0x060c },
|
|
{ 0x05bb, 0x061b },
|
|
{ 0x05bf, 0x061f },
|
|
{ 0x05c1, 0x0621 },
|
|
{ 0x05c2, 0x0622 },
|
|
{ 0x05c3, 0x0623 },
|
|
{ 0x05c4, 0x0624 },
|
|
{ 0x05c5, 0x0625 },
|
|
{ 0x05c6, 0x0626 },
|
|
{ 0x05c7, 0x0627 },
|
|
{ 0x05c8, 0x0628 },
|
|
{ 0x05c9, 0x0629 },
|
|
{ 0x05ca, 0x062a },
|
|
{ 0x05cb, 0x062b },
|
|
{ 0x05cc, 0x062c },
|
|
{ 0x05cd, 0x062d },
|
|
{ 0x05ce, 0x062e },
|
|
{ 0x05cf, 0x062f },
|
|
{ 0x05d0, 0x0630 },
|
|
{ 0x05d1, 0x0631 },
|
|
{ 0x05d2, 0x0632 },
|
|
{ 0x05d3, 0x0633 },
|
|
{ 0x05d4, 0x0634 },
|
|
{ 0x05d5, 0x0635 },
|
|
{ 0x05d6, 0x0636 },
|
|
{ 0x05d7, 0x0637 },
|
|
{ 0x05d8, 0x0638 },
|
|
{ 0x05d9, 0x0639 },
|
|
{ 0x05da, 0x063a },
|
|
{ 0x05e0, 0x0640 },
|
|
{ 0x05e1, 0x0641 },
|
|
{ 0x05e2, 0x0642 },
|
|
{ 0x05e3, 0x0643 },
|
|
{ 0x05e4, 0x0644 },
|
|
{ 0x05e5, 0x0645 },
|
|
{ 0x05e6, 0x0646 },
|
|
{ 0x05e7, 0x0647 },
|
|
{ 0x05e8, 0x0648 },
|
|
{ 0x05e9, 0x0649 },
|
|
{ 0x05ea, 0x064a },
|
|
{ 0x05eb, 0x064b },
|
|
{ 0x05ec, 0x064c },
|
|
{ 0x05ed, 0x064d },
|
|
{ 0x05ee, 0x064e },
|
|
{ 0x05ef, 0x064f },
|
|
{ 0x05f0, 0x0650 },
|
|
{ 0x05f1, 0x0651 },
|
|
{ 0x05f2, 0x0652 },
|
|
{ 0x06a1, 0x0452 },
|
|
{ 0x06a2, 0x0453 },
|
|
{ 0x06a3, 0x0451 },
|
|
{ 0x06a4, 0x0454 },
|
|
{ 0x06a5, 0x0455 },
|
|
{ 0x06a6, 0x0456 },
|
|
{ 0x06a7, 0x0457 },
|
|
{ 0x06a8, 0x0458 },
|
|
{ 0x06a9, 0x0459 },
|
|
{ 0x06aa, 0x045a },
|
|
{ 0x06ab, 0x045b },
|
|
{ 0x06ac, 0x045c },
|
|
{ 0x06ae, 0x045e },
|
|
{ 0x06af, 0x045f },
|
|
{ 0x06b0, 0x2116 },
|
|
{ 0x06b1, 0x0402 },
|
|
{ 0x06b2, 0x0403 },
|
|
{ 0x06b3, 0x0401 },
|
|
{ 0x06b4, 0x0404 },
|
|
{ 0x06b5, 0x0405 },
|
|
{ 0x06b6, 0x0406 },
|
|
{ 0x06b7, 0x0407 },
|
|
{ 0x06b8, 0x0408 },
|
|
{ 0x06b9, 0x0409 },
|
|
{ 0x06ba, 0x040a },
|
|
{ 0x06bb, 0x040b },
|
|
{ 0x06bc, 0x040c },
|
|
{ 0x06be, 0x040e },
|
|
{ 0x06bf, 0x040f },
|
|
{ 0x06c0, 0x044e },
|
|
{ 0x06c1, 0x0430 },
|
|
{ 0x06c2, 0x0431 },
|
|
{ 0x06c3, 0x0446 },
|
|
{ 0x06c4, 0x0434 },
|
|
{ 0x06c5, 0x0435 },
|
|
{ 0x06c6, 0x0444 },
|
|
{ 0x06c7, 0x0433 },
|
|
{ 0x06c8, 0x0445 },
|
|
{ 0x06c9, 0x0438 },
|
|
{ 0x06ca, 0x0439 },
|
|
{ 0x06cb, 0x043a },
|
|
{ 0x06cc, 0x043b },
|
|
{ 0x06cd, 0x043c },
|
|
{ 0x06ce, 0x043d },
|
|
{ 0x06cf, 0x043e },
|
|
{ 0x06d0, 0x043f },
|
|
{ 0x06d1, 0x044f },
|
|
{ 0x06d2, 0x0440 },
|
|
{ 0x06d3, 0x0441 },
|
|
{ 0x06d4, 0x0442 },
|
|
{ 0x06d5, 0x0443 },
|
|
{ 0x06d6, 0x0436 },
|
|
{ 0x06d7, 0x0432 },
|
|
{ 0x06d8, 0x044c },
|
|
{ 0x06d9, 0x044b },
|
|
{ 0x06da, 0x0437 },
|
|
{ 0x06db, 0x0448 },
|
|
{ 0x06dc, 0x044d },
|
|
{ 0x06dd, 0x0449 },
|
|
{ 0x06de, 0x0447 },
|
|
{ 0x06df, 0x044a },
|
|
{ 0x06e0, 0x042e },
|
|
{ 0x06e1, 0x0410 },
|
|
{ 0x06e2, 0x0411 },
|
|
{ 0x06e3, 0x0426 },
|
|
{ 0x06e4, 0x0414 },
|
|
{ 0x06e5, 0x0415 },
|
|
{ 0x06e6, 0x0424 },
|
|
{ 0x06e7, 0x0413 },
|
|
{ 0x06e8, 0x0425 },
|
|
{ 0x06e9, 0x0418 },
|
|
{ 0x06ea, 0x0419 },
|
|
{ 0x06eb, 0x041a },
|
|
{ 0x06ec, 0x041b },
|
|
{ 0x06ed, 0x041c },
|
|
{ 0x06ee, 0x041d },
|
|
{ 0x06ef, 0x041e },
|
|
{ 0x06f0, 0x041f },
|
|
{ 0x06f1, 0x042f },
|
|
{ 0x06f2, 0x0420 },
|
|
{ 0x06f3, 0x0421 },
|
|
{ 0x06f4, 0x0422 },
|
|
{ 0x06f5, 0x0423 },
|
|
{ 0x06f6, 0x0416 },
|
|
{ 0x06f7, 0x0412 },
|
|
{ 0x06f8, 0x042c },
|
|
{ 0x06f9, 0x042b },
|
|
{ 0x06fa, 0x0417 },
|
|
{ 0x06fb, 0x0428 },
|
|
{ 0x06fc, 0x042d },
|
|
{ 0x06fd, 0x0429 },
|
|
{ 0x06fe, 0x0427 },
|
|
{ 0x06ff, 0x042a },
|
|
{ 0x07a1, 0x0386 },
|
|
{ 0x07a2, 0x0388 },
|
|
{ 0x07a3, 0x0389 },
|
|
{ 0x07a4, 0x038a },
|
|
{ 0x07a5, 0x03aa },
|
|
{ 0x07a7, 0x038c },
|
|
{ 0x07a8, 0x038e },
|
|
{ 0x07a9, 0x03ab },
|
|
{ 0x07ab, 0x038f },
|
|
{ 0x07ae, 0x0385 },
|
|
{ 0x07af, 0x2015 },
|
|
{ 0x07b1, 0x03ac },
|
|
{ 0x07b2, 0x03ad },
|
|
{ 0x07b3, 0x03ae },
|
|
{ 0x07b4, 0x03af },
|
|
{ 0x07b5, 0x03ca },
|
|
{ 0x07b6, 0x0390 },
|
|
{ 0x07b7, 0x03cc },
|
|
{ 0x07b8, 0x03cd },
|
|
{ 0x07b9, 0x03cb },
|
|
{ 0x07ba, 0x03b0 },
|
|
{ 0x07bb, 0x03ce },
|
|
{ 0x07c1, 0x0391 },
|
|
{ 0x07c2, 0x0392 },
|
|
{ 0x07c3, 0x0393 },
|
|
{ 0x07c4, 0x0394 },
|
|
{ 0x07c5, 0x0395 },
|
|
{ 0x07c6, 0x0396 },
|
|
{ 0x07c7, 0x0397 },
|
|
{ 0x07c8, 0x0398 },
|
|
{ 0x07c9, 0x0399 },
|
|
{ 0x07ca, 0x039a },
|
|
{ 0x07cb, 0x039b },
|
|
{ 0x07cc, 0x039c },
|
|
{ 0x07cd, 0x039d },
|
|
{ 0x07ce, 0x039e },
|
|
{ 0x07cf, 0x039f },
|
|
{ 0x07d0, 0x03a0 },
|
|
{ 0x07d1, 0x03a1 },
|
|
{ 0x07d2, 0x03a3 },
|
|
{ 0x07d4, 0x03a4 },
|
|
{ 0x07d5, 0x03a5 },
|
|
{ 0x07d6, 0x03a6 },
|
|
{ 0x07d7, 0x03a7 },
|
|
{ 0x07d8, 0x03a8 },
|
|
{ 0x07d9, 0x03a9 },
|
|
{ 0x07e1, 0x03b1 },
|
|
{ 0x07e2, 0x03b2 },
|
|
{ 0x07e3, 0x03b3 },
|
|
{ 0x07e4, 0x03b4 },
|
|
{ 0x07e5, 0x03b5 },
|
|
{ 0x07e6, 0x03b6 },
|
|
{ 0x07e7, 0x03b7 },
|
|
{ 0x07e8, 0x03b8 },
|
|
{ 0x07e9, 0x03b9 },
|
|
{ 0x07ea, 0x03ba },
|
|
{ 0x07eb, 0x03bb },
|
|
{ 0x07ec, 0x03bc },
|
|
{ 0x07ed, 0x03bd },
|
|
{ 0x07ee, 0x03be },
|
|
{ 0x07ef, 0x03bf },
|
|
{ 0x07f0, 0x03c0 },
|
|
{ 0x07f1, 0x03c1 },
|
|
{ 0x07f2, 0x03c3 },
|
|
{ 0x07f3, 0x03c2 },
|
|
{ 0x07f4, 0x03c4 },
|
|
{ 0x07f5, 0x03c5 },
|
|
{ 0x07f6, 0x03c6 },
|
|
{ 0x07f7, 0x03c7 },
|
|
{ 0x07f8, 0x03c8 },
|
|
{ 0x07f9, 0x03c9 },
|
|
{ 0x08a1, 0x23b7 },
|
|
{ 0x08a2, 0x250c },
|
|
{ 0x08a3, 0x2500 },
|
|
{ 0x08a4, 0x2320 },
|
|
{ 0x08a5, 0x2321 },
|
|
{ 0x08a6, 0x2502 },
|
|
{ 0x08a7, 0x23a1 },
|
|
{ 0x08a8, 0x23a3 },
|
|
{ 0x08a9, 0x23a4 },
|
|
{ 0x08aa, 0x23a6 },
|
|
{ 0x08ab, 0x239b },
|
|
{ 0x08ac, 0x239d },
|
|
{ 0x08ad, 0x239e },
|
|
{ 0x08ae, 0x23a0 },
|
|
{ 0x08af, 0x23a8 },
|
|
{ 0x08b0, 0x23ac },
|
|
{ 0x08bc, 0x2264 },
|
|
{ 0x08bd, 0x2260 },
|
|
{ 0x08be, 0x2265 },
|
|
{ 0x08bf, 0x222b },
|
|
{ 0x08c0, 0x2234 },
|
|
{ 0x08c1, 0x221d },
|
|
{ 0x08c2, 0x221e },
|
|
{ 0x08c5, 0x2207 },
|
|
{ 0x08c8, 0x223c },
|
|
{ 0x08c9, 0x2243 },
|
|
{ 0x08cd, 0x21d4 },
|
|
{ 0x08ce, 0x21d2 },
|
|
{ 0x08cf, 0x2261 },
|
|
{ 0x08d6, 0x221a },
|
|
{ 0x08da, 0x2282 },
|
|
{ 0x08db, 0x2283 },
|
|
{ 0x08dc, 0x2229 },
|
|
{ 0x08dd, 0x222a },
|
|
{ 0x08de, 0x2227 },
|
|
{ 0x08df, 0x2228 },
|
|
{ 0x08ef, 0x2202 },
|
|
{ 0x08f6, 0x0192 },
|
|
{ 0x08fb, 0x2190 },
|
|
{ 0x08fc, 0x2191 },
|
|
{ 0x08fd, 0x2192 },
|
|
{ 0x08fe, 0x2193 },
|
|
{ 0x09e0, 0x25c6 },
|
|
{ 0x09e1, 0x2592 },
|
|
{ 0x09e2, 0x2409 },
|
|
{ 0x09e3, 0x240c },
|
|
{ 0x09e4, 0x240d },
|
|
{ 0x09e5, 0x240a },
|
|
{ 0x09e8, 0x2424 },
|
|
{ 0x09e9, 0x240b },
|
|
{ 0x09ea, 0x2518 },
|
|
{ 0x09eb, 0x2510 },
|
|
{ 0x09ec, 0x250c },
|
|
{ 0x09ed, 0x2514 },
|
|
{ 0x09ee, 0x253c },
|
|
{ 0x09ef, 0x23ba },
|
|
{ 0x09f0, 0x23bb },
|
|
{ 0x09f1, 0x2500 },
|
|
{ 0x09f2, 0x23bc },
|
|
{ 0x09f3, 0x23bd },
|
|
{ 0x09f4, 0x251c },
|
|
{ 0x09f5, 0x2524 },
|
|
{ 0x09f6, 0x2534 },
|
|
{ 0x09f7, 0x252c },
|
|
{ 0x09f8, 0x2502 },
|
|
{ 0x0aa1, 0x2003 },
|
|
{ 0x0aa2, 0x2002 },
|
|
{ 0x0aa3, 0x2004 },
|
|
{ 0x0aa4, 0x2005 },
|
|
{ 0x0aa5, 0x2007 },
|
|
{ 0x0aa6, 0x2008 },
|
|
{ 0x0aa7, 0x2009 },
|
|
{ 0x0aa8, 0x200a },
|
|
{ 0x0aa9, 0x2014 },
|
|
{ 0x0aaa, 0x2013 },
|
|
{ 0x0aae, 0x2026 },
|
|
{ 0x0aaf, 0x2025 },
|
|
{ 0x0ab0, 0x2153 },
|
|
{ 0x0ab1, 0x2154 },
|
|
{ 0x0ab2, 0x2155 },
|
|
{ 0x0ab3, 0x2156 },
|
|
{ 0x0ab4, 0x2157 },
|
|
{ 0x0ab5, 0x2158 },
|
|
{ 0x0ab6, 0x2159 },
|
|
{ 0x0ab7, 0x215a },
|
|
{ 0x0ab8, 0x2105 },
|
|
{ 0x0abb, 0x2012 },
|
|
{ 0x0abc, 0x2329 },
|
|
{ 0x0abe, 0x232a },
|
|
{ 0x0ac3, 0x215b },
|
|
{ 0x0ac4, 0x215c },
|
|
{ 0x0ac5, 0x215d },
|
|
{ 0x0ac6, 0x215e },
|
|
{ 0x0ac9, 0x2122 },
|
|
{ 0x0aca, 0x2613 },
|
|
{ 0x0acc, 0x25c1 },
|
|
{ 0x0acd, 0x25b7 },
|
|
{ 0x0ace, 0x25cb },
|
|
{ 0x0acf, 0x25af },
|
|
{ 0x0ad0, 0x2018 },
|
|
{ 0x0ad1, 0x2019 },
|
|
{ 0x0ad2, 0x201c },
|
|
{ 0x0ad3, 0x201d },
|
|
{ 0x0ad4, 0x211e },
|
|
{ 0x0ad6, 0x2032 },
|
|
{ 0x0ad7, 0x2033 },
|
|
{ 0x0ad9, 0x271d },
|
|
{ 0x0adb, 0x25ac },
|
|
{ 0x0adc, 0x25c0 },
|
|
{ 0x0add, 0x25b6 },
|
|
{ 0x0ade, 0x25cf },
|
|
{ 0x0adf, 0x25ae },
|
|
{ 0x0ae0, 0x25e6 },
|
|
{ 0x0ae1, 0x25ab },
|
|
{ 0x0ae2, 0x25ad },
|
|
{ 0x0ae3, 0x25b3 },
|
|
{ 0x0ae4, 0x25bd },
|
|
{ 0x0ae5, 0x2606 },
|
|
{ 0x0ae6, 0x2022 },
|
|
{ 0x0ae7, 0x25aa },
|
|
{ 0x0ae8, 0x25b2 },
|
|
{ 0x0ae9, 0x25bc },
|
|
{ 0x0aea, 0x261c },
|
|
{ 0x0aeb, 0x261e },
|
|
{ 0x0aec, 0x2663 },
|
|
{ 0x0aed, 0x2666 },
|
|
{ 0x0aee, 0x2665 },
|
|
{ 0x0af0, 0x2720 },
|
|
{ 0x0af1, 0x2020 },
|
|
{ 0x0af2, 0x2021 },
|
|
{ 0x0af3, 0x2713 },
|
|
{ 0x0af4, 0x2717 },
|
|
{ 0x0af5, 0x266f },
|
|
{ 0x0af6, 0x266d },
|
|
{ 0x0af7, 0x2642 },
|
|
{ 0x0af8, 0x2640 },
|
|
{ 0x0af9, 0x260e },
|
|
{ 0x0afa, 0x2315 },
|
|
{ 0x0afb, 0x2117 },
|
|
{ 0x0afc, 0x2038 },
|
|
{ 0x0afd, 0x201a },
|
|
{ 0x0afe, 0x201e },
|
|
{ 0x0ba3, 0x003c },
|
|
{ 0x0ba6, 0x003e },
|
|
{ 0x0ba8, 0x2228 },
|
|
{ 0x0ba9, 0x2227 },
|
|
{ 0x0bc0, 0x00af },
|
|
{ 0x0bc2, 0x22a5 },
|
|
{ 0x0bc3, 0x2229 },
|
|
{ 0x0bc4, 0x230a },
|
|
{ 0x0bc6, 0x005f },
|
|
{ 0x0bca, 0x2218 },
|
|
{ 0x0bcc, 0x2395 },
|
|
{ 0x0bce, 0x22a4 },
|
|
{ 0x0bcf, 0x25cb },
|
|
{ 0x0bd3, 0x2308 },
|
|
{ 0x0bd6, 0x222a },
|
|
{ 0x0bd8, 0x2283 },
|
|
{ 0x0bda, 0x2282 },
|
|
{ 0x0bdc, 0x22a2 },
|
|
{ 0x0bfc, 0x22a3 },
|
|
{ 0x0cdf, 0x2017 },
|
|
{ 0x0ce0, 0x05d0 },
|
|
{ 0x0ce1, 0x05d1 },
|
|
{ 0x0ce2, 0x05d2 },
|
|
{ 0x0ce3, 0x05d3 },
|
|
{ 0x0ce4, 0x05d4 },
|
|
{ 0x0ce5, 0x05d5 },
|
|
{ 0x0ce6, 0x05d6 },
|
|
{ 0x0ce7, 0x05d7 },
|
|
{ 0x0ce8, 0x05d8 },
|
|
{ 0x0ce9, 0x05d9 },
|
|
{ 0x0cea, 0x05da },
|
|
{ 0x0ceb, 0x05db },
|
|
{ 0x0cec, 0x05dc },
|
|
{ 0x0ced, 0x05dd },
|
|
{ 0x0cee, 0x05de },
|
|
{ 0x0cef, 0x05df },
|
|
{ 0x0cf0, 0x05e0 },
|
|
{ 0x0cf1, 0x05e1 },
|
|
{ 0x0cf2, 0x05e2 },
|
|
{ 0x0cf3, 0x05e3 },
|
|
{ 0x0cf4, 0x05e4 },
|
|
{ 0x0cf5, 0x05e5 },
|
|
{ 0x0cf6, 0x05e6 },
|
|
{ 0x0cf7, 0x05e7 },
|
|
{ 0x0cf8, 0x05e8 },
|
|
{ 0x0cf9, 0x05e9 },
|
|
{ 0x0cfa, 0x05ea },
|
|
{ 0x0da1, 0x0e01 },
|
|
{ 0x0da2, 0x0e02 },
|
|
{ 0x0da3, 0x0e03 },
|
|
{ 0x0da4, 0x0e04 },
|
|
{ 0x0da5, 0x0e05 },
|
|
{ 0x0da6, 0x0e06 },
|
|
{ 0x0da7, 0x0e07 },
|
|
{ 0x0da8, 0x0e08 },
|
|
{ 0x0da9, 0x0e09 },
|
|
{ 0x0daa, 0x0e0a },
|
|
{ 0x0dab, 0x0e0b },
|
|
{ 0x0dac, 0x0e0c },
|
|
{ 0x0dad, 0x0e0d },
|
|
{ 0x0dae, 0x0e0e },
|
|
{ 0x0daf, 0x0e0f },
|
|
{ 0x0db0, 0x0e10 },
|
|
{ 0x0db1, 0x0e11 },
|
|
{ 0x0db2, 0x0e12 },
|
|
{ 0x0db3, 0x0e13 },
|
|
{ 0x0db4, 0x0e14 },
|
|
{ 0x0db5, 0x0e15 },
|
|
{ 0x0db6, 0x0e16 },
|
|
{ 0x0db7, 0x0e17 },
|
|
{ 0x0db8, 0x0e18 },
|
|
{ 0x0db9, 0x0e19 },
|
|
{ 0x0dba, 0x0e1a },
|
|
{ 0x0dbb, 0x0e1b },
|
|
{ 0x0dbc, 0x0e1c },
|
|
{ 0x0dbd, 0x0e1d },
|
|
{ 0x0dbe, 0x0e1e },
|
|
{ 0x0dbf, 0x0e1f },
|
|
{ 0x0dc0, 0x0e20 },
|
|
{ 0x0dc1, 0x0e21 },
|
|
{ 0x0dc2, 0x0e22 },
|
|
{ 0x0dc3, 0x0e23 },
|
|
{ 0x0dc4, 0x0e24 },
|
|
{ 0x0dc5, 0x0e25 },
|
|
{ 0x0dc6, 0x0e26 },
|
|
{ 0x0dc7, 0x0e27 },
|
|
{ 0x0dc8, 0x0e28 },
|
|
{ 0x0dc9, 0x0e29 },
|
|
{ 0x0dca, 0x0e2a },
|
|
{ 0x0dcb, 0x0e2b },
|
|
{ 0x0dcc, 0x0e2c },
|
|
{ 0x0dcd, 0x0e2d },
|
|
{ 0x0dce, 0x0e2e },
|
|
{ 0x0dcf, 0x0e2f },
|
|
{ 0x0dd0, 0x0e30 },
|
|
{ 0x0dd1, 0x0e31 },
|
|
{ 0x0dd2, 0x0e32 },
|
|
{ 0x0dd3, 0x0e33 },
|
|
{ 0x0dd4, 0x0e34 },
|
|
{ 0x0dd5, 0x0e35 },
|
|
{ 0x0dd6, 0x0e36 },
|
|
{ 0x0dd7, 0x0e37 },
|
|
{ 0x0dd8, 0x0e38 },
|
|
{ 0x0dd9, 0x0e39 },
|
|
{ 0x0dda, 0x0e3a },
|
|
{ 0x0ddf, 0x0e3f },
|
|
{ 0x0de0, 0x0e40 },
|
|
{ 0x0de1, 0x0e41 },
|
|
{ 0x0de2, 0x0e42 },
|
|
{ 0x0de3, 0x0e43 },
|
|
{ 0x0de4, 0x0e44 },
|
|
{ 0x0de5, 0x0e45 },
|
|
{ 0x0de6, 0x0e46 },
|
|
{ 0x0de7, 0x0e47 },
|
|
{ 0x0de8, 0x0e48 },
|
|
{ 0x0de9, 0x0e49 },
|
|
{ 0x0dea, 0x0e4a },
|
|
{ 0x0deb, 0x0e4b },
|
|
{ 0x0dec, 0x0e4c },
|
|
{ 0x0ded, 0x0e4d },
|
|
{ 0x0df0, 0x0e50 },
|
|
{ 0x0df1, 0x0e51 },
|
|
{ 0x0df2, 0x0e52 },
|
|
{ 0x0df3, 0x0e53 },
|
|
{ 0x0df4, 0x0e54 },
|
|
{ 0x0df5, 0x0e55 },
|
|
{ 0x0df6, 0x0e56 },
|
|
{ 0x0df7, 0x0e57 },
|
|
{ 0x0df8, 0x0e58 },
|
|
{ 0x0df9, 0x0e59 },
|
|
{ 0x0ea1, 0x3131 },
|
|
{ 0x0ea2, 0x3132 },
|
|
{ 0x0ea3, 0x3133 },
|
|
{ 0x0ea4, 0x3134 },
|
|
{ 0x0ea5, 0x3135 },
|
|
{ 0x0ea6, 0x3136 },
|
|
{ 0x0ea7, 0x3137 },
|
|
{ 0x0ea8, 0x3138 },
|
|
{ 0x0ea9, 0x3139 },
|
|
{ 0x0eaa, 0x313a },
|
|
{ 0x0eab, 0x313b },
|
|
{ 0x0eac, 0x313c },
|
|
{ 0x0ead, 0x313d },
|
|
{ 0x0eae, 0x313e },
|
|
{ 0x0eaf, 0x313f },
|
|
{ 0x0eb0, 0x3140 },
|
|
{ 0x0eb1, 0x3141 },
|
|
{ 0x0eb2, 0x3142 },
|
|
{ 0x0eb3, 0x3143 },
|
|
{ 0x0eb4, 0x3144 },
|
|
{ 0x0eb5, 0x3145 },
|
|
{ 0x0eb6, 0x3146 },
|
|
{ 0x0eb7, 0x3147 },
|
|
{ 0x0eb8, 0x3148 },
|
|
{ 0x0eb9, 0x3149 },
|
|
{ 0x0eba, 0x314a },
|
|
{ 0x0ebb, 0x314b },
|
|
{ 0x0ebc, 0x314c },
|
|
{ 0x0ebd, 0x314d },
|
|
{ 0x0ebe, 0x314e },
|
|
{ 0x0ebf, 0x314f },
|
|
{ 0x0ec0, 0x3150 },
|
|
{ 0x0ec1, 0x3151 },
|
|
{ 0x0ec2, 0x3152 },
|
|
{ 0x0ec3, 0x3153 },
|
|
{ 0x0ec4, 0x3154 },
|
|
{ 0x0ec5, 0x3155 },
|
|
{ 0x0ec6, 0x3156 },
|
|
{ 0x0ec7, 0x3157 },
|
|
{ 0x0ec8, 0x3158 },
|
|
{ 0x0ec9, 0x3159 },
|
|
{ 0x0eca, 0x315a },
|
|
{ 0x0ecb, 0x315b },
|
|
{ 0x0ecc, 0x315c },
|
|
{ 0x0ecd, 0x315d },
|
|
{ 0x0ece, 0x315e },
|
|
{ 0x0ecf, 0x315f },
|
|
{ 0x0ed0, 0x3160 },
|
|
{ 0x0ed1, 0x3161 },
|
|
{ 0x0ed2, 0x3162 },
|
|
{ 0x0ed3, 0x3163 },
|
|
{ 0x0ed4, 0x11a8 },
|
|
{ 0x0ed5, 0x11a9 },
|
|
{ 0x0ed6, 0x11aa },
|
|
{ 0x0ed7, 0x11ab },
|
|
{ 0x0ed8, 0x11ac },
|
|
{ 0x0ed9, 0x11ad },
|
|
{ 0x0eda, 0x11ae },
|
|
{ 0x0edb, 0x11af },
|
|
{ 0x0edc, 0x11b0 },
|
|
{ 0x0edd, 0x11b1 },
|
|
{ 0x0ede, 0x11b2 },
|
|
{ 0x0edf, 0x11b3 },
|
|
{ 0x0ee0, 0x11b4 },
|
|
{ 0x0ee1, 0x11b5 },
|
|
{ 0x0ee2, 0x11b6 },
|
|
{ 0x0ee3, 0x11b7 },
|
|
{ 0x0ee4, 0x11b8 },
|
|
{ 0x0ee5, 0x11b9 },
|
|
{ 0x0ee6, 0x11ba },
|
|
{ 0x0ee7, 0x11bb },
|
|
{ 0x0ee8, 0x11bc },
|
|
{ 0x0ee9, 0x11bd },
|
|
{ 0x0eea, 0x11be },
|
|
{ 0x0eeb, 0x11bf },
|
|
{ 0x0eec, 0x11c0 },
|
|
{ 0x0eed, 0x11c1 },
|
|
{ 0x0eee, 0x11c2 },
|
|
{ 0x0eef, 0x316d },
|
|
{ 0x0ef0, 0x3171 },
|
|
{ 0x0ef1, 0x3178 },
|
|
{ 0x0ef2, 0x317f },
|
|
{ 0x0ef3, 0x3181 },
|
|
{ 0x0ef4, 0x3184 },
|
|
{ 0x0ef5, 0x3186 },
|
|
{ 0x0ef6, 0x318d },
|
|
{ 0x0ef7, 0x318e },
|
|
{ 0x0ef8, 0x11eb },
|
|
{ 0x0ef9, 0x11f0 },
|
|
{ 0x0efa, 0x11f9 },
|
|
{ 0x0eff, 0x20a9 },
|
|
{ 0x13a4, 0x20ac },
|
|
{ 0x13bc, 0x0152 },
|
|
{ 0x13bd, 0x0153 },
|
|
{ 0x13be, 0x0178 },
|
|
{ 0x20ac, 0x20ac },
|
|
{ 0xfe50, '`' },
|
|
{ 0xfe51, 0x00b4 },
|
|
{ 0xfe52, '^' },
|
|
{ 0xfe53, '~' },
|
|
{ 0xfe54, 0x00af },
|
|
{ 0xfe55, 0x02d8 },
|
|
{ 0xfe56, 0x02d9 },
|
|
{ 0xfe57, 0x00a8 },
|
|
{ 0xfe58, 0x02da },
|
|
{ 0xfe59, 0x02dd },
|
|
{ 0xfe5a, 0x02c7 },
|
|
{ 0xfe5b, 0x00b8 },
|
|
{ 0xfe5c, 0x02db },
|
|
{ 0xfe5d, 0x037a },
|
|
{ 0xfe5e, 0x309b },
|
|
{ 0xfe5f, 0x309c },
|
|
{ 0xfe63, '/' },
|
|
{ 0xfe64, 0x02bc },
|
|
{ 0xfe65, 0x02bd },
|
|
{ 0xfe66, 0x02f5 },
|
|
{ 0xfe67, 0x02f3 },
|
|
{ 0xfe68, 0x02cd },
|
|
{ 0xfe69, 0xa788 },
|
|
{ 0xfe6a, 0x02f7 },
|
|
{ 0xfe6e, ',' },
|
|
{ 0xfe6f, 0x00a4 },
|
|
{ 0xfe80, 'a' }, /* XK_dead_a */
|
|
{ 0xfe81, 'A' }, /* XK_dead_A */
|
|
{ 0xfe82, 'e' }, /* XK_dead_e */
|
|
{ 0xfe83, 'E' }, /* XK_dead_E */
|
|
{ 0xfe84, 'i' }, /* XK_dead_i */
|
|
{ 0xfe85, 'I' }, /* XK_dead_I */
|
|
{ 0xfe86, 'o' }, /* XK_dead_o */
|
|
{ 0xfe87, 'O' }, /* XK_dead_O */
|
|
{ 0xfe88, 'u' }, /* XK_dead_u */
|
|
{ 0xfe89, 'U' }, /* XK_dead_U */
|
|
{ 0xfe8a, 0x0259 },
|
|
{ 0xfe8b, 0x018f },
|
|
{ 0xfe8c, 0x00b5 },
|
|
{ 0xfe90, '_' },
|
|
{ 0xfe91, 0x02c8 },
|
|
{ 0xfe92, 0x02cc },
|
|
{ 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
|
|
{ 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
|
|
{ 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
|
|
{ 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
|
|
{ 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
|
|
{ 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
|
|
{ 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
|
|
{ 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
|
|
{ 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
|
|
{ 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
|
|
{ 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
|
|
{ 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
|
|
{ 0xffab /*XKB_KEY_KP_Add*/, '+' },
|
|
{ 0xffac /*XKB_KEY_KP_Separator*/, ',' },
|
|
{ 0xffad /*XKB_KEY_KP_Subtract*/, '-' },
|
|
{ 0xffae /*XKB_KEY_KP_Decimal*/, '.' },
|
|
{ 0xffaf /*XKB_KEY_KP_Divide*/, '/' },
|
|
{ 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 },
|
|
{ 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 },
|
|
{ 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 },
|
|
{ 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 },
|
|
{ 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 },
|
|
{ 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 },
|
|
{ 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
|
|
{ 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
|
|
{ 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
|
|
{ 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
|
|
{ 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
|
|
};
|
|
|
|
_SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) {
|
|
_sapp_x11_error_code = event->error_code;
|
|
return 0;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) {
|
|
_sapp_x11_error_code = Success;
|
|
XSetErrorHandler(_sapp_x11_error_handler);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) {
|
|
XSync(_sapp_x11_display, False);
|
|
XSetErrorHandler(NULL);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_init_extensions(void) {
|
|
_sapp_x11_UTF8_STRING = XInternAtom(_sapp_x11_display, "UTF8_STRING", False);
|
|
_sapp_x11_WM_PROTOCOLS = XInternAtom(_sapp_x11_display, "WM_PROTOCOLS", False);
|
|
_sapp_x11_WM_DELETE_WINDOW = XInternAtom(_sapp_x11_display, "WM_DELETE_WINDOW", False);
|
|
_sapp_x11_WM_STATE = XInternAtom(_sapp_x11_display, "WM_STATE", False);
|
|
_sapp_x11_NET_WM_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_NAME", False);
|
|
_sapp_x11_NET_WM_ICON_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_ICON_NAME", False);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) {
|
|
/* from GLFW:
|
|
|
|
NOTE: Default to the display-wide DPI as we don't currently have a policy
|
|
for which monitor a window is considered to be on
|
|
_sapp_x11_dpi = DisplayWidth(_sapp_x11_display, _sapp_x11_screen) *
|
|
25.4f / DisplayWidthMM(_sapp_x11_display, _sapp_x11_screen);
|
|
|
|
NOTE: Basing the scale on Xft.dpi where available should provide the most
|
|
consistent user experience (matches Qt, Gtk, etc), although not
|
|
always the most accurate one
|
|
*/
|
|
char* rms = XResourceManagerString(_sapp_x11_display);
|
|
if (rms) {
|
|
XrmDatabase db = XrmGetStringDatabase(rms);
|
|
if (db) {
|
|
XrmValue value;
|
|
char* type = NULL;
|
|
if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) {
|
|
if (type && strcmp(type, "String") == 0) {
|
|
_sapp_x11_dpi = atof(value.addr);
|
|
}
|
|
}
|
|
XrmDestroyDatabase(db);
|
|
}
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) {
|
|
SOKOL_ASSERT(ext);
|
|
const char* start = extensions;
|
|
while (true) {
|
|
const char* where = strstr(start, ext);
|
|
if (!where) {
|
|
return false;
|
|
}
|
|
const char* terminator = where + strlen(ext);
|
|
if ((where == start) || (*(where - 1) == ' ')) {
|
|
if (*terminator == ' ' || *terminator == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
start = terminator;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) {
|
|
if (extensions) {
|
|
return _sapp_glx_has_ext(ext, extensions);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname)
|
|
{
|
|
if (_sapp_glx_GetProcAddress) {
|
|
return (void*) _sapp_glx_GetProcAddress((const GLubyte*) procname);
|
|
}
|
|
else if (_sapp_glx_GetProcAddressARB) {
|
|
return (void*) _sapp_glx_GetProcAddressARB((const GLubyte*) procname);
|
|
}
|
|
else {
|
|
return dlsym(_sapp_glx_libgl, procname);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_init() {
|
|
const char* sonames[] = { "libGL.so.1", "libGL.so", 0 };
|
|
for (int i = 0; sonames[i]; i++) {
|
|
_sapp_glx_libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL);
|
|
if (_sapp_glx_libgl) {
|
|
break;
|
|
}
|
|
}
|
|
if (!_sapp_glx_libgl) {
|
|
_sapp_fail("GLX: failed to load libGL");
|
|
}
|
|
_sapp_glx_GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigs");
|
|
_sapp_glx_GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigAttrib");
|
|
_sapp_glx_GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp_glx_libgl, "glXGetClientString");
|
|
_sapp_glx_QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryExtension");
|
|
_sapp_glx_QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryVersion");
|
|
_sapp_glx_DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXDestroyContext");
|
|
_sapp_glx_MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp_glx_libgl, "glXMakeCurrent");
|
|
_sapp_glx_SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp_glx_libgl, "glXSwapBuffers");
|
|
_sapp_glx_QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp_glx_libgl, "glXQueryExtensionsString");
|
|
_sapp_glx_CreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXCreateNewContext");
|
|
_sapp_glx_CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp_glx_libgl, "glXCreateWindow");
|
|
_sapp_glx_DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp_glx_libgl, "glXDestroyWindow");
|
|
_sapp_glx_GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddress");
|
|
_sapp_glx_GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddressARB");
|
|
_sapp_glx_GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp_glx_libgl, "glXGetVisualFromFBConfig");
|
|
if (!_sapp_glx_GetFBConfigs ||
|
|
!_sapp_glx_GetFBConfigAttrib ||
|
|
!_sapp_glx_GetClientString ||
|
|
!_sapp_glx_QueryExtension ||
|
|
!_sapp_glx_QueryVersion ||
|
|
!_sapp_glx_DestroyContext ||
|
|
!_sapp_glx_MakeCurrent ||
|
|
!_sapp_glx_SwapBuffers ||
|
|
!_sapp_glx_QueryExtensionsString ||
|
|
!_sapp_glx_CreateNewContext ||
|
|
!_sapp_glx_CreateWindow ||
|
|
!_sapp_glx_DestroyWindow ||
|
|
!_sapp_glx_GetProcAddress ||
|
|
!_sapp_glx_GetProcAddressARB ||
|
|
!_sapp_glx_GetVisualFromFBConfig)
|
|
{
|
|
_sapp_fail("GLX: failed to load required entry points");
|
|
}
|
|
|
|
if (!_sapp_glx_QueryExtension(_sapp_x11_display,
|
|
&_sapp_glx_errorbase,
|
|
&_sapp_glx_eventbase))
|
|
{
|
|
_sapp_fail("GLX: GLX extension not found");
|
|
}
|
|
if (!_sapp_glx_QueryVersion(_sapp_x11_display, &_sapp_glx_major, &_sapp_glx_minor)) {
|
|
_sapp_fail("GLX: Failed to query GLX version");
|
|
}
|
|
if (_sapp_glx_major == 1 && _sapp_glx_minor < 3) {
|
|
_sapp_fail("GLX: GLX version 1.3 is required");
|
|
}
|
|
const char* exts = _sapp_glx_QueryExtensionsString(_sapp_x11_display, _sapp_x11_screen);
|
|
if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) {
|
|
_sapp_glx_SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT");
|
|
_sapp_glx_EXT_swap_control = 0 != _sapp_glx_SwapIntervalEXT;
|
|
}
|
|
if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) {
|
|
_sapp_glx_SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA");
|
|
_sapp_glx_MESA_swap_control = 0 != _sapp_glx_SwapIntervalMESA;
|
|
}
|
|
_sapp_glx_ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts);
|
|
_sapp_glx_ARB_framebuffer_sRGB = _sapp_glx_extsupported("GLX_ARB_framebuffer_sRGB", exts);
|
|
_sapp_glx_EXT_framebuffer_sRGB = _sapp_glx_extsupported("GLX_EXT_framebuffer_sRGB", exts);
|
|
if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) {
|
|
_sapp_glx_CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB");
|
|
_sapp_glx_ARB_create_context = 0 != _sapp_glx_CreateContextAttribsARB;
|
|
}
|
|
_sapp_glx_ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts);
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) {
|
|
int value;
|
|
_sapp_glx_GetFBConfigAttrib(_sapp_x11_display, fbconfig, attrib, &value);
|
|
return value;
|
|
}
|
|
|
|
_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() {
|
|
GLXFBConfig* native_configs;
|
|
_sapp_gl_fbconfig* usable_configs;
|
|
const _sapp_gl_fbconfig* closest;
|
|
int i, native_count, usable_count;
|
|
const char* vendor;
|
|
bool trust_window_bit = true;
|
|
|
|
/* HACK: This is a (hopefully temporary) workaround for Chromium
|
|
(VirtualBox GL) not setting the window bit on any GLXFBConfigs
|
|
*/
|
|
vendor = _sapp_glx_GetClientString(_sapp_x11_display, GLX_VENDOR);
|
|
if (vendor && strcmp(vendor, "Chromium") == 0) {
|
|
trust_window_bit = false;
|
|
}
|
|
|
|
native_configs = _sapp_glx_GetFBConfigs(_sapp_x11_display, _sapp_x11_screen, &native_count);
|
|
if (!native_configs || !native_count) {
|
|
_sapp_fail("GLX: No GLXFBConfigs returned");
|
|
}
|
|
|
|
usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC(native_count, sizeof(_sapp_gl_fbconfig));
|
|
usable_count = 0;
|
|
for (i = 0; i < native_count; i++) {
|
|
const GLXFBConfig n = native_configs[i];
|
|
_sapp_gl_fbconfig* u = usable_configs + usable_count;
|
|
_sapp_gl_init_fbconfig(u);
|
|
|
|
/* Only consider RGBA GLXFBConfigs */
|
|
if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) {
|
|
continue;
|
|
}
|
|
/* Only consider window GLXFBConfigs */
|
|
if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) {
|
|
if (trust_window_bit) {
|
|
continue;
|
|
}
|
|
}
|
|
u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE);
|
|
u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE);
|
|
u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE);
|
|
u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE);
|
|
u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE);
|
|
u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE);
|
|
if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) {
|
|
u->doublebuffer = true;
|
|
}
|
|
if (_sapp_glx_ARB_multisample) {
|
|
u->samples = _sapp_glx_attrib(n, GLX_SAMPLES);
|
|
}
|
|
u->handle = (uintptr_t) n;
|
|
usable_count++;
|
|
}
|
|
_sapp_gl_fbconfig desired;
|
|
_sapp_gl_init_fbconfig(&desired);
|
|
desired.red_bits = 8;
|
|
desired.green_bits = 8;
|
|
desired.blue_bits = 8;
|
|
desired.alpha_bits = 8;
|
|
desired.depth_bits = 24;
|
|
desired.stencil_bits = 8;
|
|
desired.doublebuffer = true;
|
|
desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
|
|
closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
|
|
GLXFBConfig result = 0;
|
|
if (closest) {
|
|
result = (GLXFBConfig) closest->handle;
|
|
}
|
|
XFree(native_configs);
|
|
SOKOL_FREE(usable_configs);
|
|
return result;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) {
|
|
GLXFBConfig native = _sapp_glx_choosefbconfig();
|
|
if (0 == native) {
|
|
_sapp_fail("GLX: Failed to find a suitable GLXFBConfig");
|
|
}
|
|
XVisualInfo* result = _sapp_glx_GetVisualFromFBConfig(_sapp_x11_display, native);
|
|
if (!result) {
|
|
_sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig");
|
|
}
|
|
*visual = result->visual;
|
|
*depth = result->depth;
|
|
XFree(result);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_create_context(void) {
|
|
GLXFBConfig native = _sapp_glx_choosefbconfig();
|
|
if (0 == native){
|
|
_sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)");
|
|
}
|
|
if (!(_sapp_glx_ARB_create_context && _sapp_glx_ARB_create_context_profile)) {
|
|
_sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required");
|
|
}
|
|
_sapp_x11_grab_error_handler();
|
|
const int attribs[] = {
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
0, 0
|
|
};
|
|
_sapp_glx_ctx = _sapp_glx_CreateContextAttribsARB(_sapp_x11_display, native, NULL, True, attribs);
|
|
if (!_sapp_glx_ctx) {
|
|
_sapp_fail("GLX: failed to create GL context");
|
|
}
|
|
_sapp_x11_release_error_handler();
|
|
_sapp_glx_window = _sapp_glx_CreateWindow(_sapp_x11_display, native, _sapp_x11_window, NULL);
|
|
if (!_sapp_glx_window) {
|
|
_sapp_fail("GLX: failed to create window");
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_destroy_context(void) {
|
|
if (_sapp_glx_window) {
|
|
_sapp_glx_DestroyWindow(_sapp_x11_display, _sapp_glx_window);
|
|
_sapp_glx_window = 0;
|
|
}
|
|
if (_sapp_glx_ctx) {
|
|
_sapp_glx_DestroyContext(_sapp_x11_display, _sapp_glx_ctx);
|
|
_sapp_glx_ctx = 0;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_make_current(void) {
|
|
_sapp_glx_MakeCurrent(_sapp_x11_display, _sapp_glx_window, _sapp_glx_ctx);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) {
|
|
_sapp_glx_SwapBuffers(_sapp_x11_display, _sapp_glx_window);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) {
|
|
_sapp_glx_make_current();
|
|
if (_sapp_glx_EXT_swap_control) {
|
|
_sapp_glx_SwapIntervalEXT(_sapp_x11_display, _sapp_glx_window, interval);
|
|
}
|
|
else if (_sapp_glx_MESA_swap_control) {
|
|
_sapp_glx_SwapIntervalMESA(interval);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) {
|
|
Xutf8SetWMProperties(_sapp_x11_display,
|
|
_sapp_x11_window,
|
|
_sapp.window_title, _sapp.window_title,
|
|
NULL, 0, NULL, NULL, NULL);
|
|
XChangeProperty(_sapp_x11_display, _sapp_x11_window,
|
|
_sapp_x11_NET_WM_NAME, _sapp_x11_UTF8_STRING, 8,
|
|
PropModeReplace,
|
|
(unsigned char*)_sapp.window_title,
|
|
strlen(_sapp.window_title));
|
|
XChangeProperty(_sapp_x11_display, _sapp_x11_window,
|
|
_sapp_x11_NET_WM_ICON_NAME, _sapp_x11_UTF8_STRING, 8,
|
|
PropModeReplace,
|
|
(unsigned char*)_sapp.window_title,
|
|
strlen(_sapp.window_title));
|
|
XFlush(_sapp_x11_display);
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) {
|
|
XWindowAttributes attribs;
|
|
XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &attribs);
|
|
_sapp.window_width = attribs.width;
|
|
_sapp.window_height = attribs.height;
|
|
_sapp.framebuffer_width = _sapp.window_width;
|
|
_sapp.framebuffer_height = _sapp.framebuffer_height;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) {
|
|
_sapp_x11_colormap = XCreateColormap(_sapp_x11_display, _sapp_x11_root, visual, AllocNone);
|
|
XSetWindowAttributes wa;
|
|
memset(&wa, 0, sizeof(wa));
|
|
const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask;
|
|
wa.colormap = _sapp_x11_colormap;
|
|
wa.border_pixel = 0;
|
|
wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
|
|
PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
|
|
ExposureMask | FocusChangeMask | VisibilityChangeMask |
|
|
EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
|
|
_sapp_x11_grab_error_handler();
|
|
_sapp_x11_window = XCreateWindow(_sapp_x11_display,
|
|
_sapp_x11_root,
|
|
0, 0,
|
|
_sapp.window_width,
|
|
_sapp.window_height,
|
|
0, /* border width */
|
|
depth, /* color depth */
|
|
InputOutput,
|
|
visual,
|
|
wamask,
|
|
&wa);
|
|
_sapp_x11_release_error_handler();
|
|
if (!_sapp_x11_window) {
|
|
_sapp_fail("X11: Failed to create window");
|
|
}
|
|
|
|
Atom protocols[] = {
|
|
_sapp_x11_WM_DELETE_WINDOW
|
|
};
|
|
XSetWMProtocols(_sapp_x11_display, _sapp_x11_window, protocols, 1);
|
|
|
|
XSizeHints* hints = XAllocSizeHints();
|
|
hints->flags |= PWinGravity;
|
|
hints->win_gravity = StaticGravity;
|
|
XSetWMNormalHints(_sapp_x11_display, _sapp_x11_window, hints);
|
|
XFree(hints);
|
|
|
|
_sapp_x11_update_window_title();
|
|
_sapp_x11_query_window_size();
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_destroy_window(void) {
|
|
if (_sapp_x11_window) {
|
|
XUnmapWindow(_sapp_x11_display, _sapp_x11_window);
|
|
XDestroyWindow(_sapp_x11_display, _sapp_x11_window);
|
|
_sapp_x11_window = 0;
|
|
}
|
|
if (_sapp_x11_colormap) {
|
|
XFreeColormap(_sapp_x11_display, _sapp_x11_colormap);
|
|
_sapp_x11_colormap = 0;
|
|
}
|
|
XFlush(_sapp_x11_display);
|
|
}
|
|
|
|
_SOKOL_PRIVATE bool _sapp_x11_window_visible(void) {
|
|
XWindowAttributes wa;
|
|
XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &wa);
|
|
return wa.map_state == IsViewable;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_show_window(void) {
|
|
if (!_sapp_x11_window_visible()) {
|
|
XMapWindow(_sapp_x11_display, _sapp_x11_window);
|
|
XRaiseWindow(_sapp_x11_display, _sapp_x11_window);
|
|
XFlush(_sapp_x11_display);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_hide_window(void) {
|
|
XUnmapWindow(_sapp_x11_display, _sapp_x11_window);
|
|
XFlush(_sapp_x11_display);
|
|
}
|
|
|
|
_SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Atom property, Atom type, unsigned char** value) {
|
|
Atom actualType;
|
|
int actualFormat;
|
|
unsigned long itemCount, bytesAfter;
|
|
XGetWindowProperty(_sapp_x11_display,
|
|
_sapp_x11_window,
|
|
property,
|
|
0,
|
|
LONG_MAX,
|
|
False,
|
|
type,
|
|
&actualType,
|
|
&actualFormat,
|
|
&itemCount,
|
|
&bytesAfter,
|
|
value);
|
|
return itemCount;
|
|
}
|
|
|
|
_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) {
|
|
int result = WithdrawnState;
|
|
struct {
|
|
CARD32 state;
|
|
Window icon;
|
|
} *state = NULL;
|
|
|
|
if (_sapp_x11_get_window_property(_sapp_x11_WM_STATE, _sapp_x11_WM_STATE, (unsigned char**)&state) >= 2) {
|
|
result = state->state;
|
|
}
|
|
if (state) {
|
|
XFree(state);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
_SOKOL_PRIVATE uint32_t _sapp_x11_mod(int x11_mods) {
|
|
uint32_t mods = 0;
|
|
if (x11_mods & ShiftMask) {
|
|
mods |= SAPP_MODIFIER_SHIFT;
|
|
}
|
|
if (x11_mods & ControlMask) {
|
|
mods |= SAPP_MODIFIER_CTRL;
|
|
}
|
|
if (x11_mods & Mod1Mask) {
|
|
mods |= SAPP_MODIFIER_ALT;
|
|
}
|
|
if (x11_mods & Mod4Mask) {
|
|
mods |= SAPP_MODIFIER_SUPER;
|
|
}
|
|
return mods;
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) {
|
|
switch (event->xbutton.button) {
|
|
case Button1: return SAPP_MOUSEBUTTON_LEFT;
|
|
case Button2: return SAPP_MOUSEBUTTON_MIDDLE;
|
|
case Button3: return SAPP_MOUSEBUTTON_RIGHT;
|
|
default: return SAPP_MOUSEBUTTON_INVALID;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.mouse_button = btn;
|
|
_sapp.event.modifiers = mods;
|
|
_sapp.event.mouse_x = _sapp.mouse_x;
|
|
_sapp.event.mouse_y = _sapp.mouse_y;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
|
|
_sapp.event.modifiers = mods;
|
|
_sapp.event.scroll_x = x;
|
|
_sapp.event.scroll_y = y;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(type);
|
|
_sapp.event.key_code = key;
|
|
_sapp.event.key_repeat = repeat;
|
|
_sapp.event.modifiers = mods;
|
|
_sapp_call_event(&_sapp.event);
|
|
/* check if a CLIPBOARD_PASTED event must be sent too */
|
|
if (_sapp.clipboard_enabled &&
|
|
(type == SAPP_EVENTTYPE_KEY_DOWN) &&
|
|
(_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
|
|
(_sapp.event.key_code == SAPP_KEYCODE_V))
|
|
{
|
|
_sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) {
|
|
if (_sapp_events_enabled()) {
|
|
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
|
|
_sapp.event.char_code = chr;
|
|
_sapp.event.key_repeat = repeat;
|
|
_sapp.event.modifiers = mods;
|
|
_sapp_call_event(&_sapp.event);
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) {
|
|
int dummy;
|
|
KeySym* keysyms = XGetKeyboardMapping(_sapp_x11_display, scancode, 1, &dummy);
|
|
SOKOL_ASSERT(keysyms);
|
|
KeySym keysym = keysyms[0];
|
|
XFree(keysyms);
|
|
switch (keysym) {
|
|
case XK_Escape: return SAPP_KEYCODE_ESCAPE;
|
|
case XK_Tab: return SAPP_KEYCODE_TAB;
|
|
case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT;
|
|
case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT;
|
|
case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL;
|
|
case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL;
|
|
case XK_Meta_L:
|
|
case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT;
|
|
case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */
|
|
case XK_ISO_Level3_Shift: /* AltGr on at least some machines */
|
|
case XK_Meta_R:
|
|
case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT;
|
|
case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER;
|
|
case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER;
|
|
case XK_Menu: return SAPP_KEYCODE_MENU;
|
|
case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK;
|
|
case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK;
|
|
case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN;
|
|
case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK;
|
|
case XK_Pause: return SAPP_KEYCODE_PAUSE;
|
|
case XK_Delete: return SAPP_KEYCODE_DELETE;
|
|
case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE;
|
|
case XK_Return: return SAPP_KEYCODE_ENTER;
|
|
case XK_Home: return SAPP_KEYCODE_HOME;
|
|
case XK_End: return SAPP_KEYCODE_END;
|
|
case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP;
|
|
case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN;
|
|
case XK_Insert: return SAPP_KEYCODE_INSERT;
|
|
case XK_Left: return SAPP_KEYCODE_LEFT;
|
|
case XK_Right: return SAPP_KEYCODE_RIGHT;
|
|
case XK_Down: return SAPP_KEYCODE_DOWN;
|
|
case XK_Up: return SAPP_KEYCODE_UP;
|
|
case XK_F1: return SAPP_KEYCODE_F1;
|
|
case XK_F2: return SAPP_KEYCODE_F2;
|
|
case XK_F3: return SAPP_KEYCODE_F3;
|
|
case XK_F4: return SAPP_KEYCODE_F4;
|
|
case XK_F5: return SAPP_KEYCODE_F5;
|
|
case XK_F6: return SAPP_KEYCODE_F6;
|
|
case XK_F7: return SAPP_KEYCODE_F7;
|
|
case XK_F8: return SAPP_KEYCODE_F8;
|
|
case XK_F9: return SAPP_KEYCODE_F9;
|
|
case XK_F10: return SAPP_KEYCODE_F10;
|
|
case XK_F11: return SAPP_KEYCODE_F11;
|
|
case XK_F12: return SAPP_KEYCODE_F12;
|
|
case XK_F13: return SAPP_KEYCODE_F13;
|
|
case XK_F14: return SAPP_KEYCODE_F14;
|
|
case XK_F15: return SAPP_KEYCODE_F15;
|
|
case XK_F16: return SAPP_KEYCODE_F16;
|
|
case XK_F17: return SAPP_KEYCODE_F17;
|
|
case XK_F18: return SAPP_KEYCODE_F18;
|
|
case XK_F19: return SAPP_KEYCODE_F19;
|
|
case XK_F20: return SAPP_KEYCODE_F20;
|
|
case XK_F21: return SAPP_KEYCODE_F21;
|
|
case XK_F22: return SAPP_KEYCODE_F22;
|
|
case XK_F23: return SAPP_KEYCODE_F23;
|
|
case XK_F24: return SAPP_KEYCODE_F24;
|
|
case XK_F25: return SAPP_KEYCODE_F25;
|
|
|
|
case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE;
|
|
case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY;
|
|
case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT;
|
|
case XK_KP_Add: return SAPP_KEYCODE_KP_ADD;
|
|
|
|
case XK_KP_Insert: return SAPP_KEYCODE_KP_0;
|
|
case XK_KP_End: return SAPP_KEYCODE_KP_1;
|
|
case XK_KP_Down: return SAPP_KEYCODE_KP_2;
|
|
case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3;
|
|
case XK_KP_Left: return SAPP_KEYCODE_KP_4;
|
|
case XK_KP_Begin: return SAPP_KEYCODE_KP_5;
|
|
case XK_KP_Right: return SAPP_KEYCODE_KP_6;
|
|
case XK_KP_Home: return SAPP_KEYCODE_KP_7;
|
|
case XK_KP_Up: return SAPP_KEYCODE_KP_8;
|
|
case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9;
|
|
case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL;
|
|
case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL;
|
|
case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER;
|
|
|
|
case XK_a: return SAPP_KEYCODE_A;
|
|
case XK_b: return SAPP_KEYCODE_B;
|
|
case XK_c: return SAPP_KEYCODE_C;
|
|
case XK_d: return SAPP_KEYCODE_D;
|
|
case XK_e: return SAPP_KEYCODE_E;
|
|
case XK_f: return SAPP_KEYCODE_F;
|
|
case XK_g: return SAPP_KEYCODE_G;
|
|
case XK_h: return SAPP_KEYCODE_H;
|
|
case XK_i: return SAPP_KEYCODE_I;
|
|
case XK_j: return SAPP_KEYCODE_J;
|
|
case XK_k: return SAPP_KEYCODE_K;
|
|
case XK_l: return SAPP_KEYCODE_L;
|
|
case XK_m: return SAPP_KEYCODE_M;
|
|
case XK_n: return SAPP_KEYCODE_N;
|
|
case XK_o: return SAPP_KEYCODE_O;
|
|
case XK_p: return SAPP_KEYCODE_P;
|
|
case XK_q: return SAPP_KEYCODE_Q;
|
|
case XK_r: return SAPP_KEYCODE_R;
|
|
case XK_s: return SAPP_KEYCODE_S;
|
|
case XK_t: return SAPP_KEYCODE_T;
|
|
case XK_u: return SAPP_KEYCODE_U;
|
|
case XK_v: return SAPP_KEYCODE_V;
|
|
case XK_w: return SAPP_KEYCODE_W;
|
|
case XK_x: return SAPP_KEYCODE_X;
|
|
case XK_y: return SAPP_KEYCODE_Y;
|
|
case XK_z: return SAPP_KEYCODE_Z;
|
|
case XK_1: return SAPP_KEYCODE_1;
|
|
case XK_2: return SAPP_KEYCODE_2;
|
|
case XK_3: return SAPP_KEYCODE_3;
|
|
case XK_4: return SAPP_KEYCODE_4;
|
|
case XK_5: return SAPP_KEYCODE_5;
|
|
case XK_6: return SAPP_KEYCODE_6;
|
|
case XK_7: return SAPP_KEYCODE_7;
|
|
case XK_8: return SAPP_KEYCODE_8;
|
|
case XK_9: return SAPP_KEYCODE_9;
|
|
case XK_0: return SAPP_KEYCODE_0;
|
|
case XK_space: return SAPP_KEYCODE_SPACE;
|
|
case XK_minus: return SAPP_KEYCODE_MINUS;
|
|
case XK_equal: return SAPP_KEYCODE_EQUAL;
|
|
case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET;
|
|
case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET;
|
|
case XK_backslash: return SAPP_KEYCODE_BACKSLASH;
|
|
case XK_semicolon: return SAPP_KEYCODE_SEMICOLON;
|
|
case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE;
|
|
case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT;
|
|
case XK_comma: return SAPP_KEYCODE_COMMA;
|
|
case XK_period: return SAPP_KEYCODE_PERIOD;
|
|
case XK_slash: return SAPP_KEYCODE_SLASH;
|
|
case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */
|
|
default: return SAPP_KEYCODE_INVALID;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) {
|
|
int min = 0;
|
|
int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1;
|
|
int mid;
|
|
|
|
/* First check for Latin-1 characters (1:1 mapping) */
|
|
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
|
|
(keysym >= 0x00a0 && keysym <= 0x00ff))
|
|
{
|
|
return keysym;
|
|
}
|
|
|
|
/* Also check for directly encoded 24-bit UCS characters */
|
|
if ((keysym & 0xff000000) == 0x01000000) {
|
|
return keysym & 0x00ffffff;
|
|
}
|
|
|
|
/* Binary search in table */
|
|
while (max >= min) {
|
|
mid = (min + max) / 2;
|
|
if (_sapp_x11_keysymtab[mid].keysym < keysym) {
|
|
min = mid + 1;
|
|
}
|
|
else if (_sapp_x11_keysymtab[mid].keysym > keysym) {
|
|
max = mid - 1;
|
|
}
|
|
else {
|
|
return _sapp_x11_keysymtab[mid].ucs;
|
|
}
|
|
}
|
|
|
|
/* No matching Unicode value found */
|
|
return -1;
|
|
}
|
|
|
|
// XLib manual says keycodes are in the range [8, 255] inclusive.
|
|
// https://tronche.com/gui/x/xlib/input/keyboard-encoding.html
|
|
static bool _sapp_x11_keycodes[256];
|
|
|
|
_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
|
|
switch (event->type) {
|
|
case KeyPress:
|
|
{
|
|
int keycode = event->xkey.keycode;
|
|
const sapp_keycode key = _sapp_x11_translate_key(keycode);
|
|
bool repeat = _sapp_x11_keycodes[keycode & 0xFF];
|
|
_sapp_x11_keycodes[keycode & 0xFF] = true;
|
|
const uint32_t mods = _sapp_x11_mod(event->xkey.state);
|
|
if (key != SAPP_KEYCODE_INVALID) {
|
|
_sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods);
|
|
}
|
|
KeySym keysym;
|
|
XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
|
|
int32_t chr = _sapp_x11_keysym_to_unicode(keysym);
|
|
if (chr > 0) {
|
|
_sapp_x11_char_event((uint32_t)chr, repeat, mods);
|
|
}
|
|
}
|
|
break;
|
|
case KeyRelease:
|
|
{
|
|
int keycode = event->xkey.keycode;
|
|
const sapp_keycode key = _sapp_x11_translate_key(keycode);
|
|
_sapp_x11_keycodes[keycode & 0xFF] = false;
|
|
if (key != SAPP_KEYCODE_INVALID) {
|
|
const uint32_t mods = _sapp_x11_mod(event->xkey.state);
|
|
_sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods);
|
|
}
|
|
}
|
|
break;
|
|
case ButtonPress:
|
|
{
|
|
const sapp_mousebutton btn = _sapp_x11_translate_button(event);
|
|
const uint32_t mods = _sapp_x11_mod(event->xbutton.state);
|
|
if (btn != SAPP_MOUSEBUTTON_INVALID) {
|
|
_sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods);
|
|
}
|
|
else {
|
|
/* might be a scroll event */
|
|
switch (event->xbutton.button) {
|
|
case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break;
|
|
case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break;
|
|
case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break;
|
|
case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ButtonRelease:
|
|
{
|
|
const sapp_mousebutton btn = _sapp_x11_translate_button(event);
|
|
if (btn != SAPP_MOUSEBUTTON_INVALID) {
|
|
_sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, _sapp_x11_mod(event->xbutton.state));
|
|
}
|
|
}
|
|
break;
|
|
case EnterNotify:
|
|
_sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state));
|
|
break;
|
|
case LeaveNotify:
|
|
_sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state));
|
|
break;
|
|
case MotionNotify:
|
|
_sapp.mouse_x = event->xmotion.x;
|
|
_sapp.mouse_y = event->xmotion.y;
|
|
_sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state));
|
|
break;
|
|
case ConfigureNotify:
|
|
if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) {
|
|
_sapp.window_width = event->xconfigure.width;
|
|
_sapp.window_height = event->xconfigure.height;
|
|
_sapp.framebuffer_width = _sapp.window_width;
|
|
_sapp.framebuffer_height = _sapp.window_height;
|
|
_sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED);
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
if (event->xproperty.state == PropertyNewValue) {
|
|
if (event->xproperty.atom == _sapp_x11_WM_STATE) {
|
|
const int state = _sapp_x11_get_window_state();
|
|
if (state != _sapp_x11_window_state) {
|
|
_sapp_x11_window_state = state;
|
|
if (state == IconicState) {
|
|
_sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED);
|
|
}
|
|
else if (state == NormalState) {
|
|
_sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ClientMessage:
|
|
if (event->xclient.message_type == _sapp_x11_WM_PROTOCOLS) {
|
|
const Atom protocol = event->xclient.data.l[0];
|
|
if (protocol == _sapp_x11_WM_DELETE_WINDOW) {
|
|
_sapp.quit_requested = true;
|
|
}
|
|
}
|
|
break;
|
|
case DestroyNotify:
|
|
break;
|
|
}
|
|
}
|
|
|
|
_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
|
|
_sapp_init_state(desc);
|
|
_sapp_x11_window_state = NormalState;
|
|
|
|
XInitThreads();
|
|
XrmInitialize();
|
|
_sapp_x11_display = XOpenDisplay(NULL);
|
|
if (!_sapp_x11_display) {
|
|
_sapp_fail("XOpenDisplay() failed!\n");
|
|
}
|
|
_sapp_x11_screen = DefaultScreen(_sapp_x11_display);
|
|
_sapp_x11_root = DefaultRootWindow(_sapp_x11_display);
|
|
XkbSetDetectableAutoRepeat(_sapp_x11_display, true, NULL);
|
|
_sapp_x11_query_system_dpi();
|
|
_sapp.dpi_scale = _sapp_x11_dpi / 96.0f;
|
|
_sapp_x11_init_extensions();
|
|
_sapp_glx_init();
|
|
Visual* visual = 0;
|
|
int depth = 0;
|
|
_sapp_glx_choose_visual(&visual, &depth);
|
|
_sapp_x11_create_window(visual, depth);
|
|
_sapp_glx_create_context();
|
|
_sapp.valid = true;
|
|
_sapp_x11_show_window();
|
|
_sapp_glx_swapinterval(_sapp.swap_interval);
|
|
XFlush(_sapp_x11_display);
|
|
while (!_sapp.quit_ordered) {
|
|
_sapp_glx_make_current();
|
|
int count = XPending(_sapp_x11_display);
|
|
while (count--) {
|
|
XEvent event;
|
|
XNextEvent(_sapp_x11_display, &event);
|
|
_sapp_x11_process_event(&event);
|
|
}
|
|
_sapp_frame();
|
|
_sapp_glx_swap_buffers();
|
|
XFlush(_sapp_x11_display);
|
|
/* handle quit-requested, either from window or from sapp_request_quit() */
|
|
if (_sapp.quit_requested && !_sapp.quit_ordered) {
|
|
/* give user code a chance to intervene */
|
|
_sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
|
|
/* if user code hasn't intervened, quit the app */
|
|
if (_sapp.quit_requested) {
|
|
_sapp.quit_ordered = true;
|
|
}
|
|
}
|
|
}
|
|
_sapp_call_cleanup();
|
|
_sapp_glx_destroy_context();
|
|
_sapp_x11_destroy_window();
|
|
XCloseDisplay(_sapp_x11_display);
|
|
_sapp_discard_state();
|
|
}
|
|
|
|
#if !defined(SOKOL_NO_ENTRY)
|
|
int main(int argc, char* argv[]) {
|
|
sapp_desc desc = sokol_main(argc, argv);
|
|
_sapp_run(&desc);
|
|
return 0;
|
|
}
|
|
#endif /* SOKOL_NO_ENTRY */
|
|
#endif /* LINUX */
|
|
|
|
/*== PUBLIC API FUNCTIONS ====================================================*/
|
|
#if defined(SOKOL_NO_ENTRY)
|
|
SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) {
|
|
SOKOL_ASSERT(desc);
|
|
_sapp_run(desc);
|
|
return 0;
|
|
}
|
|
|
|
/* this is just a stub so the linker doesn't complain */
|
|
sapp_desc sokol_main(int argc, char* argv[]) {
|
|
_SOKOL_UNUSED(argc);
|
|
_SOKOL_UNUSED(argv);
|
|
sapp_desc desc;
|
|
memset(&desc, 0, sizeof(desc));
|
|
return desc;
|
|
}
|
|
#else
|
|
/* likewise, in normal mode, sapp_run() is just an empty stub */
|
|
SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) {
|
|
_SOKOL_UNUSED(desc);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
SOKOL_API_IMPL bool sapp_isvalid(void) {
|
|
return _sapp.valid;
|
|
}
|
|
|
|
SOKOL_API_IMPL void* sapp_userdata(void) {
|
|
return _sapp.desc.user_data;
|
|
}
|
|
|
|
SOKOL_API_IMPL sapp_desc sapp_query_desc(void) {
|
|
return _sapp.desc;
|
|
}
|
|
|
|
SOKOL_API_IMPL uint64_t sapp_frame_count(void) {
|
|
return _sapp.frame_count;
|
|
}
|
|
|
|
SOKOL_API_IMPL int sapp_width(void) {
|
|
return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1;
|
|
}
|
|
|
|
SOKOL_API_IMPL int sapp_height(void) {
|
|
return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1;
|
|
}
|
|
|
|
SOKOL_API_IMPL bool sapp_high_dpi(void) {
|
|
return _sapp.desc.high_dpi && (_sapp.dpi_scale > 1.5f);
|
|
}
|
|
|
|
SOKOL_API_IMPL float sapp_dpi_scale(void) {
|
|
return _sapp.dpi_scale;
|
|
}
|
|
|
|
SOKOL_API_IMPL bool sapp_gles2(void) {
|
|
return _sapp.gles2_fallback;
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_show_keyboard(bool shown) {
|
|
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
|
_sapp_ios_show_keyboard(shown);
|
|
#elif defined(__EMSCRIPTEN__)
|
|
_sapp_emsc_show_keyboard(shown);
|
|
#elif defined(__ANDROID__)
|
|
_sapp_android_show_keyboard(shown);
|
|
#else
|
|
_SOKOL_UNUSED(shown);
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL bool sapp_keyboard_shown(void) {
|
|
return _sapp.onscreen_keyboard_shown;
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_show_mouse(bool shown) {
|
|
#if defined(_WIN32)
|
|
_sapp_win32_show_mouse(shown);
|
|
#else
|
|
_SOKOL_UNUSED(shown);
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL bool sapp_mouse_shown(void) {
|
|
#if defined(_WIN32)
|
|
return _sapp_win32_mouse_shown();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_request_quit(void) {
|
|
_sapp.quit_requested = true;
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_cancel_quit(void) {
|
|
_sapp.quit_requested = false;
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_quit(void) {
|
|
_sapp.quit_ordered = true;
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_consume_event(void) {
|
|
_sapp.event_consumed = true;
|
|
}
|
|
|
|
/* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */
|
|
SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) {
|
|
if (!_sapp.clipboard_enabled) {
|
|
return;
|
|
}
|
|
SOKOL_ASSERT(str);
|
|
#if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
|
|
_sapp_macos_set_clipboard_string(str);
|
|
#elif defined(__EMSCRIPTEN__)
|
|
_sapp_emsc_set_clipboard_string(str);
|
|
#elif defined(_WIN32)
|
|
_sapp_win32_set_clipboard_string(str);
|
|
#else
|
|
/* not implemented */
|
|
#endif
|
|
_sapp_strcpy(str, _sapp.clipboard, _sapp.clipboard_size);
|
|
}
|
|
|
|
SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) {
|
|
if (!_sapp.clipboard_enabled) {
|
|
return "";
|
|
}
|
|
#if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
|
|
return _sapp_macos_get_clipboard_string();
|
|
#elif defined(__EMSCRIPTEN__)
|
|
return _sapp.clipboard;
|
|
#elif defined(_WIN32)
|
|
return _sapp_win32_get_clipboard_string();
|
|
#else
|
|
/* not implemented */
|
|
return _sapp.clipboard;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_metal_get_device(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_METAL)
|
|
const void* obj = (__bridge const void*) _sapp_mtl_device_obj;
|
|
SOKOL_ASSERT(obj);
|
|
return obj;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_METAL)
|
|
const void* obj = (__bridge const void*) [_sapp_view_obj currentRenderPassDescriptor];
|
|
SOKOL_ASSERT(obj);
|
|
return obj;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_METAL)
|
|
const void* obj = (__bridge const void*) [_sapp_view_obj currentDrawable];
|
|
SOKOL_ASSERT(obj);
|
|
return obj;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_macos_get_window(void) {
|
|
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
|
const void* obj = (__bridge const void*) _sapp_macos_window_obj;
|
|
SOKOL_ASSERT(obj);
|
|
return obj;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_ios_get_window(void) {
|
|
#if defined(__APPLE__) && TARGET_OS_IPHONE
|
|
const void* obj = (__bridge const void*) _sapp_ios_window_obj;
|
|
SOKOL_ASSERT(obj);
|
|
return obj;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_D3D11)
|
|
return _sapp_d3d11_device;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_D3D11)
|
|
return _sapp_d3d11_device_context;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_D3D11)
|
|
return _sapp_d3d11_rtv;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(SOKOL_D3D11)
|
|
return _sapp_d3d11_dsv;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(_WIN32)
|
|
return _sapp_win32_hwnd;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) {
|
|
SOKOL_ASSERT(_sapp.valid);
|
|
#if defined(__ANDROID__)
|
|
return (void*)_sapp_android_state.activity;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) {
|
|
_sapp.html5_ask_leave_site = ask;
|
|
}
|
|
|
|
#undef _sapp_def
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif /* SOKOL_IMPL */
|