aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarett Bass <garettbass@me.com>2021-02-25 19:00:05 -0800
committerGarett Bass <garettbass@me.com>2021-02-25 19:00:05 -0800
commitd84f79130dc6d13262f429e98fe9dc60b5320665 (patch)
treed010e7c190f741d0d435fd3da963197fd86f6247
parent9a880ce42ed055cdacc9d117dad7d3ed02ea7d28 (diff)
parentfeab36e67fde490bdd08d5e174f524b52aad5cd0 (diff)
Merge branch 'master' of https://github.com/floooh/sokol
-rw-r--r--.github/workflows/main.yml3
-rw-r--r--CHANGELOG.md28
-rw-r--r--README.md2
-rw-r--r--sokol_app.h140
-rw-r--r--sokol_audio.h94
-rw-r--r--sokol_gfx.h6
-rw-r--r--util/sokol_imgui.h120
-rw-r--r--util/sokol_shape.h6
8 files changed, 343 insertions, 56 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4fd79e3a..9670e317 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -185,6 +185,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
+ - uses: actions/setup-java@v1
+ with:
+ java-version: '8'
- name: prepare
run: |
mkdir workspace
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97780a30..d3c9bea2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,30 @@
## Updates
-> NOTE: this list will usually only be updated with changes that affect the public APIs
-
-- **13-Fev-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h)
+- **22-Feb-2021**: Mouse input latency in sokol_app.h's macOS backend has been
+ quite significantly reduced, please see the detailed explanation [in this
+ PR](https://github.com/floooh/sokol/pull/483). Many thanks to @randrew for
+ the PR!
+
+- **19-Feb-2021**: sokol_app.h learned some Windows-specific config options
+to redirect stdout/stderr to the parent terminal or a separate console
+window, and allow outputting UTF-8 encoded text. For details, search for
+"WINDOWS CONSOLE OUTPUT" in
+[sokol_app.h](https://github.com/floooh/sokol/blob/master/sokol_app.h). Many
+thanks to @garettbass for the initial PR!
+
+- **17-Feb-2021**: When compiled for iOS, the sokol_audio.h CoreAudio backend now
+uses the **AVAudioSession** class to activate and deactivate audio output as needed.
+This fixes sokol_audio.h for iPhones (so far, sokol_audio.h accidentally only worked
+for iPads). Please see [this issue](https://github.com/floooh/sokol/issues/431) for details.
+A somewhat unfortunate side effect of this fix is that sokol_audio.h must now be compiled
+as Objective-C when targetting iOS, also note that a new framework must be linked: ```AVFoundation```.
+Many thanks to @oviano for providing the PR!
+
+- **14-Feb-2021**: The Dear ImGui rendering backend in [sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) has been rewritten to only do a single
+buffer-update per frame each for vertex- and index-data. This addresses performance-problems
+with sg_append_buffer() in the GL backend on some platforms (see [this issue](https://github.com/floooh/sokol/issues/399) for details.
+
+- **13-Feb-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h)
has been added which implements a rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear)
on top of sokol_gfx.h. Also see the new sample [nuklear-sapp](https://floooh.github.io/sokol-html5/nuklear-sapp.html).
Many thanks to **@wmerrifield** for the PR!
diff --git a/README.md b/README.md
index 5cf768e5..f2e742ac 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Simple
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
cross-platform libraries for C and C++, written in C.
-[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**12-Feb-2021**: new utility header **sokol_nuklear.h**)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**22-Feb-2021** sokol_app.h: mouse latency reduction on macOS)
## Examples and Related Projects
diff --git a/sokol_app.h b/sokol_app.h
index d0e62318..7c9d67bf 100644
--- a/sokol_app.h
+++ b/sokol_app.h
@@ -828,6 +828,36 @@
NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
+ WINDOWS CONSOLE OUTPUT
+ ======================
+ On Windows, regular windowed applications don't show any stdout/stderr text
+ output, which can be a bit of a hassle for printf() debugging or generally
+ logging text to the console. Also, console output by default uses a local
+ codepage setting and thus international UTF-8 encoded text is printed
+ as garbage.
+
+ To help with these issues, sokol_app.h can be configured at startup
+ via the following Windows-specific sapp_desc flags:
+
+ sapp_desc.win32_console_utf8 (default: false)
+ When set to true, the output console codepage will be switched
+ to UTF-8 (and restored to the original codepage on exit)
+
+ sapp_desc.win32_console_attach (default: false)
+ When set to true, stdout and stderr will be attached to the
+ console of the parent process (if the parent process actually
+ has a console). This means that if the application was started
+ in a command line window, stdout and stderr output will be printed
+ to the terminal, just like a regular command line program. But if
+ the application is started via double-click, it will behave like
+ a regular UI application, and stdout/stderr will not be visible.
+
+ sapp_desc.win32_console_create (default: false)
+ When set to true, a new console window will be created and
+ stdout/stderr will be redirected to that console window. It
+ doesn't matter if the application is started from the command
+ line or via double-click.
+
TEMP NOTE DUMP
==============
- onscreen keyboard support on Android requires Java :(, should we even bother?
@@ -1120,13 +1150,17 @@ typedef struct sapp_desc {
int max_dropped_files; /* max number of dropped files to process (default: 1) */
int max_dropped_file_path_length; /* max length in bytes of a dropped UTF-8 file path (default: 2048) */
+ /* backend-specific options */
+ bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */
+ bool win32_console_utf8; /* if true, set the output console codepage to UTF-8 */
+ bool win32_console_create; /* if true, attach stdout/stderr to a new console window */
+ bool win32_console_attach; /* if true, attach stdout/stderr to parent process */
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;
/* HTML5 specific: request and response structs for
@@ -1454,6 +1488,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#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) */
+ #pragma warning(disable:4996) /* 'freopen': This function or variable may be unsafe. */
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
@@ -1464,9 +1499,14 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
- #if !defined(SOKOL_WIN32_FORCE_MAIN)
- #pragma comment (linker, "/subsystem:windows")
+ #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem
+ #if defined(SOKOL_WIN32_FORCE_MAIN)
+ #pragma comment (linker, "/subsystem:console")
+ #else
+ #pragma comment (linker, "/subsystem:windows")
+ #endif
#endif
+ #include <stdio.h> /* freopen() */
#pragma comment (lib, "kernel32")
#pragma comment (lib, "user32")
@@ -1694,6 +1734,7 @@ typedef struct {
typedef struct {
HWND hwnd;
HDC dc;
+ UINT orig_codepage;
LONG mouse_locked_x, mouse_locked_y;
bool is_win10_or_greater;
bool in_create_window;
@@ -2917,9 +2958,9 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) {
[_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]];
}
-_SOKOL_PRIVATE void _sapp_macos_update_mouse(void) {
+_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) {
if (!_sapp.mouse.locked) {
- const NSPoint mouse_pos = [_sapp.macos.window mouseLocationOutsideOfEventStream];
+ const NSPoint mouse_pos = event.locationInWindow;
float new_x = mouse_pos.x * _sapp.dpi_scale;
float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1;
/* don't update dx/dy in the very first update */
@@ -2961,19 +3002,16 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) {
stack with calls to sapp_show_mouse()
*/
if (_sapp.mouse.locked) {
- [NSEvent setMouseCoalescingEnabled:NO];
CGAssociateMouseAndMouseCursorPosition(NO);
CGDisplayHideCursor(kCGDirectMainDisplay);
}
else {
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(YES);
- [NSEvent setMouseCoalescingEnabled:YES];
}
}
_SOKOL_PRIVATE void _sapp_macos_frame(void) {
- _sapp_macos_update_mouse();
_sapp_frame();
if (_sapp.quit_requested || _sapp.quit_ordered) {
[_sapp.macos.window performClose:nil];
@@ -3083,6 +3121,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
[_sapp.macos.window makeKeyAndOrderFront:nil];
_sapp_macos_update_dimensions();
+ [NSEvent setMouseCoalescingEnabled:NO];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender {
@@ -3156,7 +3195,9 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
backing:(NSBackingStoreType)backingStoreType
defer:(BOOL)flag {
if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) {
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
+ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
+ #endif
}
return self;
}
@@ -3170,6 +3211,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
+ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
NSPasteboard *pboard = [sender draggingPasteboard];
if ([pboard.types containsObject:NSPasteboardTypeFileURL]) {
_sapp_clear_drop_buffer();
@@ -3195,6 +3237,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
return YES;
}
+ #endif
return NO;
}
@end
@@ -3227,8 +3270,44 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
#endif
+_SOKOL_PRIVATE void _sapp_macos_poll_input_events() {
+ const NSEventMask mask = NSEventMaskLeftMouseDown |
+ NSEventMaskLeftMouseUp|
+ NSEventMaskRightMouseDown |
+ NSEventMaskRightMouseUp |
+ NSEventMaskMouseMoved |
+ NSEventMaskLeftMouseDragged |
+ NSEventMaskRightMouseDragged |
+ NSEventMaskMouseEntered |
+ NSEventMaskMouseExited |
+ NSEventMaskKeyDown |
+ NSEventMaskKeyUp |
+ NSEventMaskCursorUpdate |
+ NSEventMaskScrollWheel |
+ NSEventMaskTabletPoint |
+ NSEventMaskTabletProximity |
+ NSEventMaskOtherMouseDown |
+ NSEventMaskOtherMouseUp |
+ NSEventMaskOtherMouseDragged |
+ NSEventMaskPressure |
+ NSEventMaskDirectTouch;
+ @autoreleasepool {
+ for (;;) {
+ // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend,
+ // see: https://github.com/floooh/sokol/issues/486
+ NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES];
+ if (event == nil) {
+ break;
+ }
+ [NSApp sendEvent:event];
+ }
+ }
+}
+
- (void)drawRect:(NSRect)rect {
_SOKOL_UNUSED(rect);
+ /* Catch any last-moment input events */
+ _sapp_macos_poll_input_events();
_sapp_macos_frame();
#if !defined(SOKOL_METAL)
[[_sapp.macos.view openGLContext] flushBuffer];
@@ -3260,6 +3339,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
[super updateTrackingAreas];
}
- (void)mouseEntered:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
/* don't send mouse enter/leave while dragging (so that it behaves the same as
on Windows while SetCapture is active
*/
@@ -3268,39 +3348,47 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
}
- (void)mouseExited:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (0 == _sapp.macos.mouse_buttons) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
}
}
- (void)mouseDown:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT);
}
- (void)mouseUp:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT);
}
- (void)rightMouseDown:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT);
}
- (void)rightMouseUp:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT);
}
- (void)otherMouseDown:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (2 == event.buttonNumber) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE);
}
}
- (void)otherMouseUp:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (2 == event.buttonNumber) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
_sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE);
}
}
- (void)otherMouseDragged:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (2 == event.buttonNumber) {
if (_sapp.mouse.locked) {
_sapp.mouse.dx = [event deltaX];
@@ -3310,6 +3398,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
}
}
- (void)mouseMoved:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (_sapp.mouse.locked) {
_sapp.mouse.dx = [event deltaX];
_sapp.mouse.dy = [event deltaY];
@@ -3317,6 +3406,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
}
- (void)mouseDragged:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (_sapp.mouse.locked) {
_sapp.mouse.dx = [event deltaX];
_sapp.mouse.dy = [event deltaY];
@@ -3324,6 +3414,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
}
- (void)rightMouseDragged:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (_sapp.mouse.locked) {
_sapp.mouse.dx = [event deltaX];
_sapp.mouse.dy = [event deltaY];
@@ -3331,6 +3422,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
_sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
}
- (void)scrollWheel:(NSEvent*)event {
+ _sapp_macos_update_mouse(event);
if (_sapp_events_enabled()) {
float dx = (float) event.scrollingDeltaX;
float dy = (float) event.scrollingDeltaY;
@@ -3574,7 +3666,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
_sapp.ios.view.device = _sapp.ios.mtl_device;
_sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
_sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
- _sapp.ios.view.sampleCount = _sapp.sample_count;
+ _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count;
if (_sapp.desc.high_dpi) {
_sapp.ios.view.contentScaleFactor = 2.0;
}
@@ -6090,6 +6182,32 @@ _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) {
UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL));
}
+_SOKOL_PRIVATE void _sapp_win32_init_console(void) {
+ if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) {
+ BOOL con_valid = FALSE;
+ if (_sapp.desc.win32_console_create) {
+ con_valid = AllocConsole();
+ }
+ else if (_sapp.desc.win32_console_attach) {
+ con_valid = AttachConsole(ATTACH_PARENT_PROCESS);
+ }
+ if (con_valid) {
+ freopen("CON", "w", stdout);
+ freopen("CON", "w", stderr);
+ }
+ }
+ if (_sapp.desc.win32_console_utf8) {
+ _sapp.win32.orig_codepage = GetConsoleOutputCP();
+ SetConsoleOutputCP(CP_UTF8);
+ }
+}
+
+_SOKOL_PRIVATE void _sapp_win32_restore_console(void) {
+ if (_sapp.desc.win32_console_utf8) {
+ SetConsoleOutputCP(_sapp.win32.orig_codepage);
+ }
+}
+
_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) {
typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void);
@@ -6240,6 +6358,7 @@ _SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) {
_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
_sapp_init_state(desc);
+ _sapp_win32_init_console();
_sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater();
_sapp_win32_uwp_init_keytable();
_sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
@@ -6303,6 +6422,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
_sapp_wgl_shutdown();
#endif
_sapp_win32_destroy_window();
+ _sapp_win32_restore_console();
_sapp_discard_state();
}
diff --git a/sokol_audio.h b/sokol_audio.h
index 5b22913d..b1a9571f 100644
--- a/sokol_audio.h
+++ b/sokol_audio.h
@@ -41,7 +41,8 @@
- Windows: WASAPI
- Linux: ALSA (link with asound)
- - macOS/iOS: CoreAudio (link with AudioToolbox)
+ - macOS: CoreAudio (link with AudioToolbox)
+ - iOS: CoreAudio+AVAudioSession (link with AudioToolbox and AVFoundation)
- emscripten: WebAudio with ScriptProcessorNode
- Android: OpenSLES (link with OpenSLES)
@@ -307,9 +308,13 @@
THE COREAUDIO BACKEND
=====================
The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined).
- Since the CoreAudio API is implemented in C (not Objective-C) the
+ Since the CoreAudio API is implemented in C (not Objective-C) on macOS the
implementation part of Sokol Audio can be included into a C source file.
+ However on iOS, Sokol Audio must be compiled as Objective-C due to it's
+ reliance on the AVAudioSession object. The iOS code path support both
+ being compiled with or without ARC (Automatic Reference Counting).
+
For thread synchronisation, the CoreAudio backend will use the
pthread_mutex_* functions.
@@ -509,7 +514,16 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
#if defined(SOKOL_DUMMY_BACKEND)
// No audio API needed for SOKOL_DUMMY_BACKEND
#elif defined(__APPLE__)
+ #include <TargetConditionals.h>
#include <AudioToolbox/AudioToolbox.h>
+ #if TARGET_OS_IOS
+ #if !defined(__cplusplus)
+ #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
+ #error "sokol_audio.h on iOS requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
+ #endif
+ #endif
+ #include <AVFoundation/AVFoundation.h>
+ #endif
#elif (defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
@@ -597,6 +611,9 @@ typedef struct {
typedef struct {
AudioQueueRef ca_audio_queue;
+ #if TARGET_OS_IOS
+ id ca_interruption_handler;
+ #endif
} _saudio_backend_t;
/*=== ALSA BACKEND DECLARATIONS ==============================================*/
@@ -972,6 +989,57 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { };
/*=== COREAUDIO BACKEND IMPLEMENTATION =======================================*/
#elif defined(__APPLE__)
+#if TARGET_OS_IOS
+#if __has_feature(objc_arc)
+#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; }
+#else
+#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; }
+#endif
+
+@interface _saudio_interruption_handler : NSObject { }
+@end
+
+@implementation _saudio_interruption_handler
+-(id)init {
+ self = [super init];
+ AVAudioSession* session = [AVAudioSession sharedInstance];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session];
+ return self;
+}
+
+-(void)dealloc {
+ [self remove_handler];
+ #if !__has_feature(objc_arc)
+ [super dealloc];
+ #endif
+}
+
+-(void)remove_handler {
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil];
+}
+
+-(void)handle_interruption:(NSNotification*)notification {
+ AVAudioSession* session = [AVAudioSession sharedInstance];
+ SOKOL_ASSERT(session);
+ NSDictionary* dict = notification.userInfo;
+ SOKOL_ASSERT(dict);
+ NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
+ switch (type) {
+ case AVAudioSessionInterruptionTypeBegan:
+ AudioQueuePause(_saudio.backend.ca_audio_queue);
+ [session setActive:false error:nil];
+ break;
+ case AVAudioSessionInterruptionTypeEnded:
+ [session setActive:true error:nil];
+ AudioQueueStart(_saudio.backend.ca_audio_queue, NULL);
+ break;
+ default:
+ break;
+ }
+}
+@end
+#endif
+
/* NOTE: the buffer data callback is called on a separate thread! */
_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer) {
_SOKOL_UNUSED(user_data);
@@ -994,6 +1062,17 @@ _SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, AudioQueueRef qu
_SOKOL_PRIVATE bool _saudio_backend_init(void) {
SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue);
+ #if TARGET_OS_IOS
+ /* activate audio session */
+ AVAudioSession* session = [AVAudioSession sharedInstance];
+ SOKOL_ASSERT(session != nil);
+ [session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
+ [session setActive:true error:nil];
+
+ /* create interruption handler */
+ _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init];
+ #endif
+
/* create an audio queue with fp32 samples */
AudioStreamBasicDescription fmt;
memset(&fmt, 0, sizeof(fmt));
@@ -1033,6 +1112,17 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
AudioQueueStop(_saudio.backend.ca_audio_queue, true);
AudioQueueDispose(_saudio.backend.ca_audio_queue, false);
_saudio.backend.ca_audio_queue = NULL;
+ #if TARGET_OS_IOS
+ /* remove interruption handler */
+ if (_saudio.backend.ca_interruption_handler != nil) {
+ [_saudio.backend.ca_interruption_handler remove_handler];
+ _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler);
+ }
+ /* deactivate audio session */
+ AVAudioSession* session = [AVAudioSession sharedInstance];
+ SOKOL_ASSERT(session);
+ [session setActive:false error:nil];;
+ #endif
}
/*=== ALSA BACKEND IMPLEMENTATION ============================================*/
diff --git a/sokol_gfx.h b/sokol_gfx.h
index d9a9e4da..87276666 100644
--- a/sokol_gfx.h
+++ b/sokol_gfx.h
@@ -99,17 +99,17 @@
--- start rendering to the default frame buffer with:
- sg_begin_default_pass(const sg_pass_action* actions, int width, int height)
+ sg_begin_default_pass(const sg_pass_action* action, int width, int height)
...or alternatively with:
- sg_begin_default_passf(const sg_pass_action* actions, float width, float height)
+ sg_begin_default_passf(const sg_pass_action* action, float width, float height)
...which takes the framebuffer width and height as float values.
--- or start rendering to an offscreen framebuffer with:
- sg_begin_pass(sg_pass pass, const sg_pass_action* actions)
+ sg_begin_pass(sg_pass pass, const sg_pass_action* action)
--- set the pipeline state for the next draw call with:
diff --git a/util/sokol_imgui.h b/util/sokol_imgui.h
index 3440e802..c0b94589 100644
--- a/util/sokol_imgui.h
+++ b/util/sokol_imgui.h
@@ -42,6 +42,8 @@
to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
+ SOKOL_MALLOC(s) - your own malloc function (default: malloc(s))
+ SOKOL_FREE(p) - your own free function (default: free(p))
SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
@@ -286,6 +288,11 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc)
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
+#ifndef SOKOL_MALLOC
+ #include <stdlib.h>
+ #define SOKOL_MALLOC(s) malloc(s)
+ #define SOKOL_FREE(p) free(p)
+#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
@@ -312,6 +319,10 @@ typedef struct {
sg_shader shd;
sg_pipeline pip;
bool is_osx; // return true if running on OSX (or HTML5 OSX), needed for copy/paste
+
+ sg_range vertices;
+ sg_range indices;
+
#if !defined(SOKOL_IMGUI_NO_SOKOL_APP)
bool btn_down[SAPP_MAX_MOUSEBUTTONS];
bool btn_up[SAPP_MAX_MOUSEBUTTONS];
@@ -1617,6 +1628,15 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
since sokol_gfx.h will do its own default-value handling
*/
+ /* allocate an intermediate vertex- and index-buffer */
+ SOKOL_ASSERT(_simgui.desc.max_vertices > 0);
+ _simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert);
+ _simgui.vertices.ptr = SOKOL_MALLOC(_simgui.vertices.size);
+ SOKOL_ASSERT(_simgui.vertices.ptr);
+ _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx);
+ _simgui.indices.ptr = SOKOL_MALLOC(_simgui.indices.size);
+ SOKOL_ASSERT(_simgui.indices.ptr);
+
/* initialize Dear ImGui */
#if defined(__cplusplus)
ImGui::CreateContext();
@@ -1672,7 +1692,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
sg_buffer_desc vb_desc;
memset(&vb_desc, 0, sizeof(vb_desc));
vb_desc.usage = SG_USAGE_STREAM;
- vb_desc.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert);
+ vb_desc.size = _simgui.vertices.size;
vb_desc.label = "sokol-imgui-vertices";
_simgui.vbuf = sg_make_buffer(&vb_desc);
@@ -1680,7 +1700,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
memset(&ib_desc, 0, sizeof(ib_desc));
ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
ib_desc.usage = SG_USAGE_STREAM;
- ib_desc.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(uint16_t);
+ ib_desc.size = _simgui.indices.size;
ib_desc.label = "sokol-imgui-indices";
_simgui.ibuf = sg_make_buffer(&ib_desc);
@@ -1814,6 +1834,10 @@ SOKOL_API_IMPL void simgui_shutdown(void) {
sg_destroy_buffer(_simgui.ibuf);
sg_destroy_buffer(_simgui.vbuf);
sg_pop_debug_group();
+ SOKOL_ASSERT(_simgui.vertices.ptr);
+ SOKOL_FREE((void*)_simgui.vertices.ptr);
+ SOKOL_ASSERT(_simgui.indices.ptr);
+ SOKOL_FREE((void*)_simgui.indices.ptr);
}
#if !defined(SOKOL_IMGUI_NO_SOKOL_APP)
@@ -1887,10 +1911,58 @@ SOKOL_API_IMPL void simgui_render(void) {
if (draw_data->CmdListsCount == 0) {
return;
}
+ /* copy vertices and indices into an intermediate buffer so that
+ they can be updated with a single sg_update_buffer() call each
+ (sg_append_buffer() has performance problems on some GL platforms),
+ also keep track of valid number of command lists in case of a
+ buffer overflow
+ */
+ size_t all_vtx_size = 0;
+ size_t all_idx_size = 0;
+ int cmd_list_count = 0;
+ for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++, cmd_list_count++) {
+ ImDrawList* cl = draw_data->CmdLists[cl_index];
+ #if defined(__cplusplus)
+ const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
+ const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
+ const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front();
+ const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front();
+ #else
+ const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
+ const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
+ const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data;
+ const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data;
+ #endif
+
+ /* check for buffer overflow */
+ if (((all_vtx_size + vtx_size) > _simgui.vertices.size) ||
+ ((all_idx_size + idx_size) > _simgui.indices.size))
+ {
+ break;
+ }
+
+ /* copy vertices and indices into common buffers */
+ void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size);
+ void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size);
+ memcpy(dst_vtx_ptr, vtx_ptr, vtx_size);
+ memcpy(dst_idx_ptr, idx_ptr, idx_size);
+ all_vtx_size += vtx_size;
+ all_idx_size += idx_size;
+ }
+ if (0 == cmd_list_count) {
+ return;
+ }
- /* render the ImGui command list */
+ /* update the sokol-gfx vertex- and index-buffer */
sg_push_debug_group("sokol-imgui");
+ sg_range vtx_data = _simgui.vertices;
+ vtx_data.size = all_vtx_size;
+ sg_range idx_data = _simgui.indices;
+ idx_data.size = all_idx_size;
+ sg_update_buffer(_simgui.vbuf, &vtx_data);
+ sg_update_buffer(_simgui.ibuf, &idx_data);
+ /* render the ImGui command list */
const float dpi_scale = _simgui.desc.dpi_scale;
const int fb_width = (int) (io->DisplaySize.x * dpi_scale);
const int fb_height = (int) (io->DisplaySize.y * dpi_scale);
@@ -1911,38 +1983,9 @@ SOKOL_API_IMPL void simgui_render(void) {
bind.fs_images[0].id = (uint32_t)(uintptr_t)tex_id;
int vb_offset = 0;
int ib_offset = 0;
- for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) {
- ImDrawList* cl = draw_data->CmdLists[cl_index];
+ for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) {
+ const ImDrawList* cl = draw_data->CmdLists[cl_index];
- /* append vertices and indices to buffers, record start offsets in draw state */
- #if defined(__cplusplus)
- const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
- const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
- const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front();
- const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front();
- #else
- const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
- const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
- const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data;
- const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data;
- #endif
- if (vtx_ptr) {
- const sg_range vtx_range = { vtx_ptr, vtx_size };
- vb_offset = sg_append_buffer(bind.vertex_buffers[0], &vtx_range);
- }
- if (idx_ptr) {
- const sg_range idx_range = { idx_ptr, idx_size };
- ib_offset = sg_append_buffer(bind.index_buffer, &idx_range);
- }
- /* don't render anything if the buffer is in overflow state (this is also
- checked internally in sokol_gfx, draw calls that attempt to draw with
- overflowed buffers will be silently dropped)
- */
- if (sg_query_buffer_overflow(bind.vertex_buffers[0]) ||
- sg_query_buffer_overflow(bind.index_buffer))
- {
- break;
- }
bind.vertex_buffer_offsets[0] = vb_offset;
bind.index_buffer_offset = ib_offset;
sg_apply_bindings(&bind);
@@ -1981,6 +2024,15 @@ SOKOL_API_IMPL void simgui_render(void) {
}
base_element += (int)pcmd->ElemCount;
}
+ #if defined(__cplusplus)
+ const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
+ const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
+ #else
+ const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
+ const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
+ #endif
+ vb_offset += (int)vtx_size;
+ ib_offset += (int)idx_size;
}
sg_apply_viewport(0, 0, fb_width, fb_height, true);
sg_apply_scissor_rect(0, 0, fb_width, fb_height, true);
diff --git a/util/sokol_shape.h b/util/sokol_shape.h
index be2a9c6c..195f6232 100644
--- a/util/sokol_shape.h
+++ b/util/sokol_shape.h
@@ -36,7 +36,7 @@
================
sokol_shape.h creates vertices and indices for simple shapes and
builds structs which can be plugged into sokol-gfx resource
- creation descriptor structs.
+ creation functions:
The following shape types are supported:
@@ -154,7 +154,7 @@
You can also provide additional creation parameters, like a common vertex
color, a debug-helper to randomize colors, tell the shape builder function
to merge the new shape with the previous shape into the same draw-element-range,
- or a 4x4 transform matrix to move, rotate and scale the generated matrices:
+ or a 4x4 transform matrix to move, rotate and scale the generated vertices:
```c
sshape_buffer_t buf = ...;
@@ -179,7 +179,7 @@
assert(buf.valid);
```
- The following helper functions are offered to build a packed
+ The following helper functions can be used to build a packed
color value or to convert from external matrix types:
```c