diff --git a/sokol_app.h b/sokol_app.h index 0bdc30ba7..5b97aaebd 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -911,6 +911,7 @@ typedef enum sapp_event_type { SAPP_EVENTTYPE_QUIT_REQUESTED, SAPP_EVENTTYPE_CLIPBOARD_PASTED, SAPP_EVENTTYPE_FILES_DROPPED, + SAPP_EVENTTYPE_WINDOW_CLOSED, _SAPP_EVENTTYPE_NUM, _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF } sapp_event_type; @@ -1061,7 +1062,11 @@ enum { SAPP_MODIFIER_SUPER = (1<<3) }; +/* window handle typedef - simple uint32 id handle, sokol_gfx resource style */ +typedef struct sapp_window { uint32_t id; } sapp_window; + typedef struct sapp_event { + sapp_window window; uint64_t frame_count; sapp_event_type type; sapp_keycode key_code; @@ -1083,6 +1088,24 @@ typedef struct sapp_event { int framebuffer_height; } sapp_event; +typedef struct sapp_window_desc { + 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 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 high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */ + bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */ + bool enable_dragndrop; /* enable file dropping (drag'n'drop), default is false */ + 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) */ + bool enable_clipboard; /* enable clipboard access, default is false */ + int clipboard_size; /* max size of clipboard content in bytes */ + sapp_window parent_window; /* if 0, main_window is used as a parent */ +} sapp_window_desc; + typedef struct sapp_desc { void (*init_cb)(void); /* these are the user-provided callbacks without user data */ void (*frame_cb)(void); @@ -1097,21 +1120,24 @@ typedef struct sapp_desc { void (*event_userdata_cb)(const sapp_event*, void*); void (*fail_userdata_cb)(const char*, void*); + /* DISCUSS: this currently is an almost exact copy of sapp_window_desc, but changing this into window struct here would be a breaking change */ 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 high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */ 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 */ bool enable_dragndrop; /* enable file dropping (drag'n'drop), default is false */ 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) */ + bool enable_clipboard; /* enable clipboard access, default is false */ + int clipboard_size; /* max size of clipboard content in bytes */ + sapp_window_desc window; + 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 */ @@ -1119,6 +1145,8 @@ typedef struct sapp_desc { 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 */ + + uint32_t window_pool_size; /* max number of simultaneous windows this app can create */ } sapp_desc; /* HTML5 specific: request and response structs for @@ -1156,15 +1184,21 @@ extern sapp_desc sokol_main(int argc, char* argv[]); /* returns true after sokol-app has been initialized */ SOKOL_APP_API_DECL bool sapp_isvalid(void); /* returns the current framebuffer width in pixels */ -SOKOL_APP_API_DECL int sapp_width(void); +SOKOL_APP_API_DECL int sapp_width(); +/* returns the current framebuffer width in pixels for specified window */ +SOKOL_APP_API_DECL int sapp_window_width(sapp_window window_id); /* returns the current framebuffer height in pixels */ -SOKOL_APP_API_DECL int sapp_height(void); +SOKOL_APP_API_DECL int sapp_height(); +/* returns the current framebuffer height in pixels for specified window */ +SOKOL_APP_API_DECL int sapp_window_height(sapp_window window_id); /* get default framebuffer color pixel format */ SOKOL_APP_API_DECL int sapp_color_format(void); /* get default framebuffer depth pixel format */ SOKOL_APP_API_DECL int sapp_depth_format(void); /* get default framebuffer sample count */ SOKOL_APP_API_DECL int sapp_sample_count(void); +/* get window default framebuffer sample count */ +SOKOL_APP_API_DECL int sapp_window_sample_count(sapp_window window_id); /* returns true when high_dpi was requested and actually running in a high-dpi scenario */ SOKOL_APP_API_DECL bool sapp_high_dpi(void); /* returns the dpi scaling factor (window pixels to framebuffer pixels) */ @@ -1173,18 +1207,30 @@ SOKOL_APP_API_DECL float sapp_dpi_scale(void); SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); /* return true if the mobile device onscreen keyboard is currently shown */ SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); -/* query fullscreen mode */ -SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); -/* toggle fullscreen mode */ -SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); -/* show or hide the mouse cursor */ +/* query fullscreen mode for default window */ +SOKOL_APP_API_DECL bool sapp_is_fullscreen(); +/* toggle fullscreen mode for default window */ +SOKOL_APP_API_DECL void sapp_toggle_fullscreen(); +/* query fullscreen mode for a window */ +SOKOL_APP_API_DECL bool sapp_window_is_fullscreen(sapp_window window_id); +/* toggle fullscreen mode for a window*/ +SOKOL_APP_API_DECL void sapp_window_toggle_fullscreen(sapp_window window_id); +/* show or hide the mouse cursor for the main window */ SOKOL_APP_API_DECL void sapp_show_mouse(bool show); -/* show or hide the mouse cursor */ +/* show or hide the mouse cursor for the specified window */ +SOKOL_APP_API_DECL void sapp_window_show_mouse(sapp_window, bool show); +/* is the mouse cursor shown or hidden for the main window */ SOKOL_APP_API_DECL bool sapp_mouse_shown(); -/* enable/disable mouse-pointer-lock mode */ +/* is the mouse cursor shown or hidden for the specified window */ +SOKOL_APP_API_DECL bool sapp_window_mouse_shown(sapp_window window_id); +/* enable/disable mouse-pointer-lock mode for main window */ SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); -/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ +/* enable/disable mouse-pointer-lock mode for specified window */ +SOKOL_APP_API_DECL void sapp_window_lock_mouse(sapp_window window_id, bool lock); +/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) for main window*/ SOKOL_APP_API_DECL bool sapp_mouse_locked(void); +/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) for specified window */ +SOKOL_APP_API_DECL bool sapp_window_mouse_locked(sapp_window); /* return the userdata pointer optionally provided in sapp_desc */ SOKOL_APP_API_DECL void* sapp_userdata(void); /* return a copy of the sapp_desc structure */ @@ -1199,22 +1245,44 @@ SOKOL_APP_API_DECL void sapp_quit(void); SOKOL_APP_API_DECL void sapp_consume_event(void); /* get the current frame counter (for comparison with sapp_event.frame_count) */ SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); -/* write string into clipboard */ +/* write string into main window clipboard */ SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); -/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ +/* write string into window clipboard */ +SOKOL_APP_API_DECL void sapp_window_set_clipboard_string(sapp_window window_id, const char* str); +/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) for main window*/ SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); -/* set the window title (only on desktop platforms) */ -SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); +/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) for specific window */ +SOKOL_APP_API_DECL const char* sapp_window_get_clipboard_string(sapp_window window_id); /* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); +/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) for a specific window */ +SOKOL_APP_API_DECL int sapp_window_get_num_dropped_files(sapp_window window_id); /* gets the dropped file paths */ SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); +/* gets the dropped file paths for window */ +SOKOL_APP_API_DECL const char* sapp_window_get_dropped_file_path(sapp_window window_id, int index); + +/* returns this applications default/main window */ +SOKOL_APP_API_DECL sapp_window sapp_main_window(); +/* create a new child window */ +SOKOL_APP_API_DECL sapp_window sapp_create_window(const sapp_window_desc* desc); +/* destroy a window */ +SOKOL_APP_API_DECL void sapp_destroy_window(sapp_window window); +/* set the window title (only on desktop platforms) of main_window */ +SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); +/* set the window title (only on desktop platforms) of specified window */ +SOKOL_APP_API_DECL void sapp_window_set_window_title(sapp_window window, const char* str); +/* returns true if window is still valid */ +SOKOL_APP_API_DECL bool sapp_window_isvalid(sapp_window window); + /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ SOKOL_APP_API_DECL int sapp_run(const sapp_desc* desc); /* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ SOKOL_APP_API_DECL bool sapp_gles2(void); +/* GL: make window gl context current */ +SOKOL_APP_API_DECL void sapp_gl_make_context_current(sapp_window window); /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); @@ -1227,10 +1295,16 @@ SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_req SOKOL_APP_API_DECL const void* sapp_metal_get_device(void); /* Metal: get bridged pointer to this frame's renderpass descriptor */ SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); +/* Metal: get bridged pointer to this frame's renderpass descriptor of specified window */ +SOKOL_APP_API_DECL const void* sapp_metal_window_get_renderpass_descriptor(sapp_window window); /* Metal: get bridged pointer to current drawable */ SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void); -/* macOS: get bridged pointer to macOS NSWindow */ +/* Metal: get bridged pointer for drawable of specified window */ +SOKOL_APP_API_DECL const void* sapp_metal_window_get_drawable(sapp_window window); +/* macOS: get bridged pointer to macOS NSWindow for the main window */ SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); +/* macOS: get bridged pointer to macOS NSWindow for specified window */ +SOKOL_APP_API_DECL const void* sapp_macos_window_get_window(sapp_window window_id); /* iOS: get bridged pointer to iOS UIWindow */ SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); @@ -1240,10 +1314,16 @@ SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void); SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void); /* D3D11: get pointer to ID3D11RenderTargetView object */ SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void); +/* D3D11: get pointer to ID3D11RenderTargetView object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_window_get_render_target_view(sapp_window window_id); /* D3D11: get pointer to ID3D11DepthStencilView */ SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); -/* Win32: get the HWND window handle */ +/* D3D11: get pointer to ID3D11DepthStencilView */ +SOKOL_APP_API_DECL const void* sapp_d3d11_window_get_depth_stencil_view(sapp_window window_id); +/* Win32: get the HWND window handle of the main window */ SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); +/* Win32: get the HWND window handle of the specified window */ +SOKOL_APP_API_DECL const void* sapp_win32_window_get_hwnd(sapp_window window_id); /* WebGPU: get WGPUDevice handle */ SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void); @@ -1361,7 +1441,7 @@ inline int sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE) +#if !defined(SOKOL_CALLOC) || !defined(SOKOL_MALLOC) || !defined(SOKOL_FREE) #include #endif #if !defined(SOKOL_CALLOC) @@ -1370,6 +1450,9 @@ inline int sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #if !defined(SOKOL_FREE) #define SOKOL_FREE(p) free(p) #endif +#if !defined(SOKOL_MALLOC) + #define SOKOL_MALLOC(s) malloc(s) +#endif #ifndef SOKOL_LOG #ifdef SOKOL_DEBUG #if defined(__ANDROID__) @@ -1548,31 +1631,58 @@ inline int sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } @interface _sapp_macos_app_delegate : NSObject @end @interface _sapp_macos_window : NSWindow +{ + struct _sapp_window_t* window; +} +- (instancetype)initWithContentRect:(NSRect)contentRect + sappWindow:(struct _sapp_window_t*)initWindow + styleMask:(NSWindowStyleMask)style + backing:(NSBackingStoreType)backingStoreType + defer:(BOOL)flag; @end @interface _sapp_macos_window_delegate : NSObject +{ + struct _sapp_window_t* window; +} +- (instancetype)initWithSappWindow:(struct _sapp_window_t*)initWindow; @end #if defined(SOKOL_METAL) @interface _sapp_macos_view : MTKView + { + struct _sapp_window_t* window; + } + - (instancetype)initWithSappWindow:(struct _sapp_window_t*)initWindow; @end #elif defined(SOKOL_GLCORE33) - @interface _sapp_macos_view : NSOpenGLView - - (void)timerFired:(id)sender; + @interface _sapp_macos_view : NSView + { + struct _sapp_window_t* window; + NSTrackingArea* trackingArea; + } + - (instancetype)initWithSappWindow:(struct _sapp_window_t*)initWindow + initWithFrame:(NSRect)frameRect; @end #endif // SOKOL_GLCORE33 typedef struct { - uint32_t flags_changed_store; uint8_t mouse_buttons; - NSWindow* window; - NSTrackingArea* tracking_area; _sapp_macos_app_delegate* app_dlg; - _sapp_macos_window_delegate* win_dlg; - _sapp_macos_view* view; #if defined(SOKOL_METAL) id mtl_device; #endif } _sapp_macos_t; +typedef struct { + uint32_t flags_changed_store; + NSWindow* window; + NSTrackingArea* tracking_area; + _sapp_macos_window_delegate* win_dlg; + _sapp_macos_view* view; + #if defined(SOKOL_GLCORE33) + NSOpenGLContext* gl_ctx; + #endif +} _sapp_macos_window_t; + #endif // _SAPP_MACOS /*== IOS DECLARATIONS ========================================================*/ @@ -1643,6 +1753,10 @@ typedef struct { typedef struct { ID3D11Device* device; ID3D11DeviceContext* device_context; + IDXGIFactory* dxgi_factory; +} _sapp_d3d11_t; + +typedef struct { ID3D11Texture2D* rt; ID3D11RenderTargetView* rtv; ID3D11Texture2D* msaa_rt; @@ -1651,12 +1765,15 @@ typedef struct { ID3D11DepthStencilView* dsv; DXGI_SWAP_CHAIN_DESC swap_chain_desc; IDXGISwapChain* swap_chain; -} _sapp_d3d11_t; +} _sapp_d3d11_window_t; + #endif /*== WIN32 DECLARATIONS ======================================================*/ #if defined(_SAPP_WIN32) +#define _SAPP_WINDOW_PROP "SAPP_WINDOW_PROP" + #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { @@ -1680,13 +1797,7 @@ typedef struct { } _sapp_win32_dpi_t; typedef struct { - HWND hwnd; - HDC dc; - LONG mouse_locked_x, mouse_locked_y; bool is_win10_or_greater; - bool in_create_window; - bool iconified; - bool mouse_tracked; uint8_t mouse_capture_mask; _sapp_win32_dpi_t dpi; bool raw_input_mousepos_valid; @@ -1695,6 +1806,15 @@ typedef struct { uint8_t raw_input_data[256]; } _sapp_win32_t; +typedef struct { + HWND hwnd; + HDC dc; + bool iconified; + bool in_create_window; + bool mouse_tracked; + LONG mouse_locked_x, mouse_locked_y; +} _sapp_win32_window_t; + #if defined(SOKOL_GLCORE33) #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 @@ -1733,7 +1853,6 @@ typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); typedef struct { HINSTANCE opengl32; - HGLRC gl_ctx; PFN_wglCreateContext CreateContext; PFN_wglDeleteContext DeleteContext; PFN_wglGetProcAddress GetProcAddress; @@ -1752,6 +1871,10 @@ typedef struct { HWND msg_hwnd; HDC msg_dc; } _sapp_wgl_t; + +typedef struct { + HGLRC gl_ctx; +} _sapp_wgl_window_t; #endif // SOKOL_GLCORE33 #endif // _SAPP_WIN32 @@ -1899,10 +2022,7 @@ typedef struct { Display* display; int screen; Window root; - Colormap colormap; - Window window; Cursor hidden_cursor; - int window_state; float dpi; unsigned char error_code; Atom UTF8_STRING; @@ -1913,18 +2033,28 @@ typedef struct { Atom NET_WM_ICON_NAME; Atom NET_WM_STATE; Atom NET_WM_STATE_FULLSCREEN; + XContext context; _sapp_xi_t xi; _sapp_xdnd_t xdnd; } _sapp_x11_t; +typedef struct { + Colormap colormap; + Window window; + int window_state; +} _sapp_x11_window_t; + +typedef struct { + GLXContext ctx; + GLXWindow window; +} _sapp_glx_window_t; + typedef struct { void* libgl; int major; int minor; int event_base; int error_base; - GLXContext ctx; - GLXWindow window; // GLX 1.3 functions PFNGLXGETFBCONFIGSPROC GetFBConfigs; @@ -2004,10 +2134,58 @@ typedef struct { bool pos_valid; } _sapp_mouse_t; +/* this *MUST* remain 0 */ +enum { + _SAPP_SLOT_SHIFT = 16, + _SAPP_SLOT_MASK = (1<<_SAPP_SLOT_SHIFT)-1, + _SAPP_MAX_POOL_SIZE = (1<<_SAPP_SLOT_SHIFT), +}; + +#define _SAPP_INVALID_SLOT_INDEX (0) +#define SAPP_INVALID_ID (0) + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sapp_pool_t; + +typedef struct _sapp_window_t { + uint32_t id; + bool fullscreen; + bool user_cursor; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; + int swap_interval; + int sample_count; + char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ + wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ + _sapp_drop_t drop; + _sapp_clipboard_t clipboard; + _sapp_mouse_t mouse; + #if defined(_SAPP_WIN32) + _sapp_win32_window_t win32; + #if defined(SOKOL_GLCORE33) + _sapp_wgl_window_t wgl; + #elif defined(SOKOL_D3D11) + _sapp_d3d11_window_t d3d11; + #endif + #elif defined(_SAPP_UWP) + _sapp_d3d11_window_t d3d11; + #elif defined(_SAPP_LINUX) + _sapp_x11_window_t x11; + _sapp_glx_window_t glx; + #elif defined(_SAPP_MACOS) + _sapp_macos_window_t macos; + #endif +} _sapp_window_t; + typedef struct { sapp_desc desc; bool valid; - bool fullscreen; bool gles2_fallback; bool first_frame; bool init_called; @@ -2017,18 +2195,12 @@ typedef struct { bool event_consumed; bool html5_ask_leave_site; bool onscreen_keyboard_shown; - int window_width; - int window_height; - int framebuffer_width; - int framebuffer_height; - int sample_count; - int swap_interval; float dpi_scale; uint64_t frame_count; sapp_event event; - _sapp_mouse_t mouse; - _sapp_clipboard_t clipboard; - _sapp_drop_t drop; + _sapp_pool_t window_pool; + _sapp_window_t* windows; + _sapp_window_t* main_window; #if defined(_SAPP_MACOS) _sapp_macos_t macos; #elif defined(_SAPP_IOS) @@ -2054,12 +2226,12 @@ typedef struct { _sapp_glx_t glx; #endif char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; - char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ - wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ + sapp_keycode keycodes[SAPP_MAX_KEYCODES]; } _sapp_t; static _sapp_t _sapp; + /*=== OPTIONAL MINI GL LOADER FOR WIN32/WGL ==================================*/ #if defined(_SAPP_WIN32) && defined(SOKOL_GLCORE33) && !defined(SOKOL_WIN32_NO_GL_LOADER) #define __gl_h_ 1 @@ -2487,12 +2659,12 @@ _SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { } } -_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { - SOKOL_ASSERT(_sapp.drop.buffer); - SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); - int offset = index * _sapp.drop.max_path_length; - SOKOL_ASSERT(offset < _sapp.drop.buf_size); - return &_sapp.drop.buffer[offset]; +_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(_sapp_window_t* window, int index) { + SOKOL_ASSERT(window->drop.buffer); + SOKOL_ASSERT((index >= 0) && (index <= window->drop.max_files)); + int offset = index * window->drop.max_path_length; + SOKOL_ASSERT(offset < window->drop.buf_size); + return &window->drop.buffer[offset]; } /* Copy a string into a fixed size buffer with guaranteed zero- @@ -2525,78 +2697,257 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { } } -_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { - sapp_desc desc = *in_desc; +_SOKOL_PRIVATE sapp_window_desc _sapp_window_desc_defaults(const sapp_window_desc* in_desc) { + sapp_window_desc desc = *in_desc; desc.width = _sapp_def(desc.width, 640); desc.height = _sapp_def(desc.height, 480); desc.sample_count = _sapp_def(desc.sample_count, 1); desc.swap_interval = _sapp_def(desc.swap_interval, 1); - desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); - desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); + desc.window_title = _sapp_def(desc.window_title, "sokol_app"); + desc.fullscreen = _sapp_def(desc.fullscreen, false); + desc.high_dpi = _sapp_def(desc.high_dpi, false); desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1); desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048); - desc.window_title = _sapp_def(desc.window_title, "sokol_app"); + desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); + return desc; +} + +_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { + sapp_desc desc = *in_desc; + + /* for compatibility we copy these values into the window struct */ + desc.window.width = in_desc->width; + desc.window.height = in_desc->height; + desc.window.sample_count = in_desc->sample_count; + desc.window.swap_interval = in_desc->swap_interval; + desc.window.window_title = in_desc->window_title; + desc.window.fullscreen = in_desc->fullscreen; + desc.window.high_dpi = in_desc->high_dpi; + desc.window.max_dropped_files = in_desc->max_dropped_files; + desc.window.max_dropped_file_path_length = in_desc->max_dropped_file_path_length; + desc.window.clipboard_size = in_desc->clipboard_size; + desc.window.alpha = in_desc->alpha; + desc.window.enable_clipboard = in_desc->enable_clipboard; + desc.window.enable_dragndrop = in_desc->enable_dragndrop; + desc.window.user_cursor = in_desc->user_cursor; + + desc.window = _sapp_window_desc_defaults(&in_desc->window); + desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); + desc.window_pool_size = _sapp_def(desc.window_pool_size, 1); return desc; } +/*== pool init/alloc functions ==========================*/ + +_SOKOL_PRIVATE void _sapp_init_pool(_sapp_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; + pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + memset(pool->gen_ctrs, 0, gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int)*num); + SOKOL_ASSERT(pool->free_queue); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sapp_discard_pool(_sapp_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_FREE(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + SOKOL_FREE(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sapp_pool_alloc_index(_sapp_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SAPP_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sapp_pool_free_index(_sapp_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SAPP_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sapp_setup_pools() { + // Sanity check that we don't expect >1 windows on anything but desktop + #if !defined(_SAPP_WIN32) && !defined(_SAPP_MACOS) && !defined(_SAPP_LINUX) + // DISCUSS: better place for this validation? + SOKOL_ASSERT(_sapp.desc.window_pool_size == 1); + #endif + + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((_sapp.desc.window_pool_size > 0) && (_sapp.desc.window_pool_size < _SAPP_MAX_POOL_SIZE)); + _sapp_init_pool(&_sapp.window_pool, _sapp.desc.window_pool_size); + size_t window_pool_byte_size = sizeof(_sapp_window_t) * _sapp.window_pool.size; + _sapp.windows = (_sapp_window_t*) SOKOL_MALLOC(window_pool_byte_size); + SOKOL_ASSERT(_sapp.windows); + memset(_sapp.windows, 0, window_pool_byte_size); +} + +_SOKOL_PRIVATE void _sapp_discard_pools() { + SOKOL_FREE(_sapp.windows); + _sapp.windows = 0; + + _sapp_discard_pool(&_sapp.window_pool); +} + +/* extract slot index from id */ +_SOKOL_PRIVATE int _sapp_slot_index(uint32_t id) { + int slot_index = (int) (id & _SAPP_SLOT_MASK); + SOKOL_ASSERT(_SAPP_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* create sapp_window handle from id */ +_SOKOL_PRIVATE sapp_window _sapp_window(uint32_t id) { + SOKOL_ASSERT(id != SAPP_INVALID_ID); + sapp_window window_id; + window_id.id = id; + return window_id; +} + +/* returns pointer to resource by id without matching id check */ +_SOKOL_PRIVATE _sapp_window_t* _sapp_window_at(uint32_t window_id) { + SOKOL_ASSERT((SAPP_INVALID_ID != window_id)); + int slot_index = _sapp_slot_index(window_id); + SOKOL_ASSERT((slot_index > _SAPP_INVALID_SLOT_INDEX) && (slot_index < _sapp.window_pool.size)); + return &_sapp.windows[slot_index]; +} + +/* returns pointer to resource with matching id check, may return 0 */ +_SOKOL_PRIVATE _sapp_window_t* _sapp_lookup_window(uint32_t window_id) { + if (SAPP_INVALID_ID != window_id) { + _sapp_window_t* window = _sapp_window_at(window_id); + if (window->id == window_id) { + return window; + } + } + return 0; +} + +_SOKOL_PRIVATE sapp_window _sapp_alloc_window(void) { + _sapp_pool_t* const pool = &_sapp.window_pool; + SOKOL_ASSERT(pool->gen_ctrs && pool->free_queue); + + sapp_window window_id; + + int slot_index = _sapp_pool_alloc_index(pool); + if (_SAPP_INVALID_SLOT_INDEX != slot_index) { + SOKOL_ASSERT(_sapp.windows[slot_index].id == SAPP_INVALID_ID); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + uint32_t id = (ctr<<_SAPP_SLOT_SHIFT)|(slot_index & _SAPP_SLOT_MASK); + _sapp.windows[slot_index].id = id; + window_id.id = id; + } + else { + /* pool is exhausted */ + window_id.id = SAPP_INVALID_ID; + } + return window_id; +} + +_SOKOL_PRIVATE void _sapp_reset_window(_sapp_window_t* window) { + SOKOL_ASSERT(window); + memset(window, 0, sizeof(_sapp_window_t)); +} + +_SOKOL_PRIVATE _sapp_window_t* _sapp_init_window(sapp_window window_id, const sapp_window_desc* desc){ + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID && desc); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + + window->window_width = desc->width; + window->window_height = desc->height; + window->framebuffer_width = desc->width; + window->framebuffer_height = desc->height; + window->fullscreen = desc->fullscreen; + window->user_cursor = desc->user_cursor; + + _sapp_strcpy(desc->window_title, window->window_title, sizeof(window->window_title)); + // TODO: do we need the below still? Potentially if we keep a desc in the window_t + // _sapp.desc.window.window_title = _sapp.window_title; + + window->swap_interval = desc->swap_interval; + window->sample_count = desc->sample_count; + window->mouse.shown = true; + + window->drop.enabled = desc->enable_dragndrop; + if (window->drop.enabled) { + window->drop.max_files = desc->max_dropped_files; + window->drop.max_path_length = desc->max_dropped_file_path_length; + window->drop.buf_size = window->drop.max_files * window->drop.max_path_length; + window->drop.buffer = (char*) SOKOL_CALLOC(1, window->drop.buf_size); + } + + window->clipboard.enabled = desc->enable_clipboard; + if (window->clipboard.enabled) { + window->clipboard.buf_size = desc->clipboard_size; + window->clipboard.buffer = (char*) SOKOL_CALLOC(1, window->clipboard.buf_size); + } + + return window; +} + _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { _SAPP_CLEAR(_sapp_t, _sapp); _sapp.desc = _sapp_desc_defaults(desc); _sapp.first_frame = true; - _sapp.window_width = _sapp.desc.width; - _sapp.window_height = _sapp.desc.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - _sapp.sample_count = _sapp.desc.sample_count; - _sapp.swap_interval = _sapp.desc.swap_interval; _sapp.html5_canvas_selector[0] = '#'; _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; - _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; - if (_sapp.clipboard.enabled) { - _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; - _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, _sapp.clipboard.buf_size); - } - _sapp.drop.enabled = _sapp.desc.enable_dragndrop; - if (_sapp.drop.enabled) { - _sapp.drop.max_files = _sapp.desc.max_dropped_files; - _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; - _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; - _sapp.drop.buffer = (char*) SOKOL_CALLOC(1, _sapp.drop.buf_size); - } - _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); - _sapp.desc.window_title = _sapp.window_title; _sapp.dpi_scale = 1.0f; - _sapp.fullscreen = _sapp.desc.fullscreen; - _sapp.mouse.shown = true; } _SOKOL_PRIVATE void _sapp_discard_state(void) { - if (_sapp.clipboard.enabled) { - SOKOL_ASSERT(_sapp.clipboard.buffer); - SOKOL_FREE((void*)_sapp.clipboard.buffer); - } - if (_sapp.drop.enabled) { - SOKOL_ASSERT(_sapp.drop.buffer); - SOKOL_FREE((void*)_sapp.drop.buffer); - } + _sapp_discard_pools(); _SAPP_CLEAR(_sapp_t, _sapp); } -_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_init_event(_sapp_window_t* window, sapp_event_type type) { memset(&_sapp.event, 0, sizeof(_sapp.event)); + _sapp.event.window.id = window->id; _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; - _sapp.event.mouse_x = _sapp.mouse.x; - _sapp.event.mouse_y = _sapp.mouse.y; - _sapp.event.mouse_dx = _sapp.mouse.dx; - _sapp.event.mouse_dy = _sapp.mouse.dy; + _sapp.event.window_width = window->window_width; + _sapp.event.window_height = window->window_height; + _sapp.event.framebuffer_width = window->framebuffer_width; + _sapp.event.framebuffer_height = window->framebuffer_height; + _sapp.event.mouse_x = window->mouse.x; + _sapp.event.mouse_y = window->mouse.y; + _sapp.event.mouse_dx = window->mouse.dx; + _sapp.event.mouse_dy = window->mouse.dy; } _SOKOL_PRIVATE bool _sapp_events_enabled(void) { @@ -2613,10 +2964,10 @@ _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { } } -_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { - if (_sapp.drop.enabled) { - SOKOL_ASSERT(_sapp.drop.buffer); - memset(_sapp.drop.buffer, 0, _sapp.drop.buf_size); +_SOKOL_PRIVATE void _sapp_clear_drop_buffer(_sapp_window_t* window) { + if (window->drop.enabled) { + SOKOL_ASSERT(window->drop.buffer); + memset(window->drop.buffer, 0, window->drop.buf_size); } } @@ -2757,18 +3108,37 @@ _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { // NOTE: it's safe to call [release] on a nil object - _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); - _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); - _SAPP_OBJC_RELEASE(_sapp.macos.view); + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + _sapp_window_t* window = &_sapp.windows[i]; + sapp_destroy_window(_sapp_window(window->id)); + } + } #if defined(SOKOL_METAL) _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); - #endif - _SAPP_OBJC_RELEASE(_sapp.macos.window); + #endif } +void _sapp_macos_process_window_events(void){ + NSEvent* event; + + do { + event = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: nil + inMode: NSDefaultRunLoopMode + dequeue: YES]; + + if(event != NULL) + [NSApp sendEvent:event]; + } + while (event != NULL); +} + +_SOKOL_PRIVATE void _sapp_macos_frame(void); _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { _sapp_init_state(desc); + _sapp_setup_pools(); _sapp_macos_init_keytable(); [NSApplication sharedApplication]; NSApp.activationPolicy = NSApplicationActivationPolicyRegular; @@ -2776,8 +3146,29 @@ _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { NSApp.delegate = _sapp.macos.app_dlg; [NSApp activateIgnoringOtherApps:YES]; [NSApp run]; - // NOTE: [NSApp run] never returns, instead cleanup code - // must be put into applicationWillTerminate + + while(!_sapp.quit_ordered){ + _sapp_macos_frame(); + + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + _sapp_window_t* window = &_sapp.windows[i]; + + #if defined(SOKOL_METAL) + [window->macos.view draw]; + #else + [window->macos.gl_ctx update]; + [window->macos.gl_ctx flushBuffer]; + #endif + } + } + + _sapp_macos_process_window_events(); + } + + _sapp_call_cleanup(); + _sapp_macos_discard_state(); + _sapp_discard_state(); } /* MacOS entry function */ @@ -2806,18 +3197,18 @@ _SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) { return m; } -_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { +_SOKOL_PRIVATE void _sapp_macos_mouse_event(_sapp_window_t* window, sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp.event.mouse_button = btn; _sapp.event.modifiers = mod; _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { +_SOKOL_PRIVATE void _sapp_macos_key_event(_sapp_window_t* window, sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp.event.key_code = key; _sapp.event.key_repeat = repeat; _sapp.event.modifiers = mod; @@ -2825,54 +3216,54 @@ _SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key } } -_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_macos_app_event(_sapp_window_t* window, sapp_event_type type) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { +_SOKOL_PRIVATE void _sapp_macos_update_dimensions(_sapp_window_t* window) { #if defined(SOKOL_METAL) - const NSRect fb_rect = [_sapp.macos.view bounds]; - _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale; - _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale; + const NSRect fb_rect = [window->macos.view bounds]; + window->framebuffer_width = fb_rect.size.width * _sapp.dpi_scale; + window->framebuffer_height = fb_rect.size.height * _sapp.dpi_scale; #elif defined(SOKOL_GLCORE33) - const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]]; - _sapp.framebuffer_width = fb_rect.size.width; - _sapp.framebuffer_height = fb_rect.size.height; + const NSRect fb_rect = [window->macos.view convertRectToBacking:[window->macos.view frame]]; + window->framebuffer_width = fb_rect.size.width; + window->framebuffer_height = fb_rect.size.height; #endif - const NSRect bounds = [_sapp.macos.view bounds]; - _sapp.window_width = bounds.size.width; - _sapp.window_height = bounds.size.height; - if (_sapp.framebuffer_width == 0) { - _sapp.framebuffer_width = 1; + const NSRect bounds = [window->macos.view bounds]; + window->window_width = bounds.size.width; + window->window_height = bounds.size.height; + if (window->framebuffer_width == 0) { + window->framebuffer_width = 1; } - if (_sapp.framebuffer_height == 0) { - _sapp.framebuffer_height = 1; + if (window->framebuffer_height == 0) { + window->framebuffer_height = 1; } - if (_sapp.window_width == 0) { - _sapp.window_width = 1; + if (window->window_width == 0) { + window->window_width = 1; } - if (_sapp.window_height == 0) { - _sapp.window_height = 1; + if (window->window_height == 0) { + window->window_height = 1; } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; + _sapp.dpi_scale = (float)window->framebuffer_width / (float)window->window_width; /* also make sure the MTKView drawable size is uptodate */ #if defined(SOKOL_METAL) - CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; - _sapp.macos.view.drawableSize = drawable_size; + CGSize drawable_size = { (CGFloat) window->framebuffer_width, (CGFloat) window->framebuffer_height }; + window->macos.view.drawableSize = drawable_size; #endif } -_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { +_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(_sapp_window_t* window) { /* NOTE: the _sapp.fullscreen flag is also notified by the windowDidEnterFullscreen / windowDidExitFullscreen event handlers */ - _sapp.fullscreen = !_sapp.fullscreen; - [_sapp.macos.window toggleFullScreen:nil]; + window->fullscreen = !window->fullscreen; + [window->macos.window toggleFullScreen:nil]; } _SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { @@ -2883,40 +3274,40 @@ _SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { } } -_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.buffer); +_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(_sapp_window_t* window) { + SOKOL_ASSERT(window->clipboard.buffer); @autoreleasepool { - _sapp.clipboard.buffer[0] = 0; + window->clipboard.buffer[0] = 0; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; if (!str) { - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } - _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy([str UTF8String], window->clipboard.buffer, window->clipboard.buf_size); } - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } -_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { - [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; +_SOKOL_PRIVATE void _sapp_macos_update_window_title(_sapp_window_t* window) { + [window->macos.window setTitle: [NSString stringWithUTF8String:window->window_title]]; } -_SOKOL_PRIVATE void _sapp_macos_update_mouse(void) { - if (!_sapp.mouse.locked) { - const NSPoint mouse_pos = [_sapp.macos.window mouseLocationOutsideOfEventStream]; +_SOKOL_PRIVATE void _sapp_macos_update_mouse(_sapp_window_t* window) { + if (!window->mouse.locked) { + const NSPoint mouse_pos = [window->macos.window mouseLocationOutsideOfEventStream]; float new_x = mouse_pos.x * _sapp.dpi_scale; - float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; + float new_y = window->framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; /* don't update dx/dy in the very first update */ - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; + if (window->mouse.pos_valid) { + window->mouse.dx = new_x - window->mouse.x; + window->mouse.dy = new_y - window->mouse.y; } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; + window->mouse.x = new_x; + window->mouse.y = new_y; + window->mouse.pos_valid = true; } } @@ -2930,13 +3321,13 @@ _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { } } -_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { +_SOKOL_PRIVATE void _sapp_macos_lock_mouse(_sapp_window_t* window, bool lock) { + if (lock == window->mouse.locked) { return; } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; + window->mouse.dx = 0.0f; + window->mouse.dy = 0.0f; + window->mouse.locked = lock; /* NOTE that this code doesn't warp the mouse cursor to the window center as everybody else does it. This lead to a spike in the @@ -2947,7 +3338,7 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { NOTE also that the hide/show of the mouse cursor should properly stack with calls to sapp_show_mouse() */ - if (_sapp.mouse.locked) { + if (window->mouse.locked) { [NSEvent setMouseCoalescingEnabled:NO]; CGAssociateMouseAndMouseCursorPosition(NO); CGDisplayHideCursor(kCGDirectMainDisplay); @@ -2960,61 +3351,73 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { } _SOKOL_PRIVATE void _sapp_macos_frame(void) { - _sapp_macos_update_mouse(); + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + _sapp_window_t* window = &_sapp.windows[i]; + _sapp_macos_update_mouse(window); + } + } + /* always make the main window the current context before frame() cb */ + #if defined(SOKOL_GLCORE33) + [_sapp.main_window->macos.gl_ctx makeCurrentContext]; + #endif + _sapp_frame(); - if (_sapp.quit_requested || _sapp.quit_ordered) { - [_sapp.macos.window performClose:nil]; + if (_sapp.quit_requested) { + /* triggers a shouldClose event but doesn't actually close the window, see windowShouldClose */ + [_sapp.main_window->macos.window performClose:nil]; } } -@implementation _sapp_macos_app_delegate -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { - _SOKOL_UNUSED(aNotification); - if (_sapp.fullscreen) { +_SOKOL_PRIVATE void _sapp_macos_create_window(_sapp_window_t* window, const sapp_window_desc* desc){ + if (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; + window->window_width = screen_rect.size.width; + window->window_height = screen_rect.size.height; + if (desc->high_dpi) { + window->framebuffer_width = 2 * window->window_width; + window->framebuffer_height = 2 * window->window_height; } else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; + window->framebuffer_width = window->window_width; + window->framebuffer_height = window->window_height; } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + _sapp.dpi_scale = (float)window->framebuffer_width / (float) window->window_width; } const NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; - NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); - _sapp.macos.window = [[_sapp_macos_window alloc] + NSRect window_rect = NSMakeRect(0, 0, window->window_width, window->window_height); + window->macos.window = [[_sapp_macos_window alloc] initWithContentRect:window_rect + sappWindow:window styleMask:style backing:NSBackingStoreBuffered defer:NO]; - _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate - _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; - _sapp.macos.window.acceptsMouseMovedEvents = YES; - _sapp.macos.window.restorable = YES; - - _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; - _sapp.macos.window.delegate = _sapp.macos.win_dlg; + window->macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate + window->macos.window.title = [NSString stringWithUTF8String:window->window_title]; + window->macos.window.acceptsMouseMovedEvents = YES; + // NOTE: The user would need to provide serialization of window manually for this to be useful. + window->macos.window.restorable = NO; + + window->macos.win_dlg = [[_sapp_macos_window_delegate alloc] initWithSappWindow:window]; + [window->macos.window setDelegate:window->macos.win_dlg]; #if defined(SOKOL_METAL) - _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); - _sapp.macos.view = [[_sapp_macos_view alloc] init]; - [_sapp.macos.view updateTrackingAreas]; - _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp.macos.view.device = _sapp.macos.mtl_device; - _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; - _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp.macos.view.sampleCount = _sapp.sample_count; - _sapp.macos.view.autoResizeDrawable = false; - _sapp.macos.window.contentView = _sapp.macos.view; - [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; - _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; + window->macos.view = [[_sapp_macos_view alloc] initWithSappWindow:window]; + [window->macos.view updateTrackingAreas]; + window->macos.view.preferredFramesPerSecond = 60 / window->swap_interval; + window->macos.view.paused = true; + window->macos.view.enableSetNeedsDisplay = false; + window->macos.view.device = _sapp.macos.mtl_device; + window->macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + window->macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + window->macos.view.sampleCount = window->sample_count; + window->macos.view.autoResizeDrawable = false; + window->macos.window.contentView = window->macos.view; + [window->macos.window makeFirstResponder:window->macos.view]; + window->macos.view.layer.magnificationFilter = kCAFilterNearest; #elif defined(SOKOL_GLCORE33) NSOpenGLPixelFormatAttribute attrs[32]; int i = 0; @@ -3025,10 +3428,10 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; - if (_sapp.sample_count > 1) { + if (window->sample_count > 1) { attrs[i++] = NSOpenGLPFAMultisample; attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; - attrs[i++] = NSOpenGLPFASamples; attrs[i++] = _sapp.sample_count; + attrs[i++] = NSOpenGLPFASamples; attrs[i++] = window->sample_count; } else { attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; @@ -3037,39 +3440,70 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; SOKOL_ASSERT(glpixelformat_obj != nil); - _sapp.macos.view = [[_sapp_macos_view alloc] - initWithFrame:window_rect - pixelFormat:glpixelformat_obj]; - _SAPP_OBJC_RELEASE(glpixelformat_obj); - [_sapp.macos.view updateTrackingAreas]; - if (_sapp.desc.high_dpi) { - [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; + window->macos.view = [[_sapp_macos_view alloc] + initWithSappWindow:window + initWithFrame:window_rect]; + [window->macos.view updateTrackingAreas]; + if (desc->high_dpi) { + [window->macos.view setWantsBestResolutionOpenGLSurface:YES]; } else { - [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; + [window->macos.view setWantsBestResolutionOpenGLSurface:NO]; } - _sapp.macos.window.contentView = _sapp.macos.view; - [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + [window->macos.window setContentView:window->macos.view]; + [window->macos.window makeFirstResponder:window->macos.view]; + + window->macos.gl_ctx = [[NSOpenGLContext alloc] initWithFormat:glpixelformat_obj shareContext:nil]; + SOKOL_ASSERT(window->macos.gl_ctx != nil); + + _SAPP_OBJC_RELEASE(glpixelformat_obj); - NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 - target:_sapp.macos.view - selector:@selector(timerFired:) - userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; - timer_obj = nil; + [window->macos.gl_ctx setView:window->macos.view]; #endif - _sapp.valid = true; - if (_sapp.fullscreen) { + + if (window->fullscreen) { /* on GL, this already toggles a rendered frame, so set the valid flag before */ - [_sapp.macos.window toggleFullScreen:self]; + // TODO: is the sender here required?? Set to nil (was self) + [window->macos.window toggleFullScreen:nil]; } else { - [_sapp.macos.window center]; + [window->macos.window center]; } - [_sapp.macos.window makeKeyAndOrderFront:nil]; - _sapp_macos_update_dimensions(); + [window->macos.window makeKeyAndOrderFront:nil]; + _sapp_macos_update_dimensions(window); +} + +_SOKOL_PRIVATE void _sapp_macos_destroy_window(_sapp_window_t* window) { + _SAPP_OBJC_RELEASE(window->macos.tracking_area); + _SAPP_OBJC_RELEASE(window->macos.win_dlg); + _SAPP_OBJC_RELEASE(window->macos.view); + #if defined(SOKOL_GLCORE33) + _SAPP_OBJC_RELEASE(window->macos.gl_ctx); + #endif + + [window->macos.window close]; + _SAPP_OBJC_RELEASE(window->macos.window); + + /* DISCUSS: glfw does a NSAutoreleasePool drain at this point, should we add this? */ +} + +@implementation _sapp_macos_app_delegate +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { + _SOKOL_UNUSED(aNotification); + + #if defined(SOKOL_METAL) + _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); + #endif + + sapp_window window_id = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(window_id.id); + + // TODO: I moved a fullscreen check into create_window, which means the fullscreen event won't arrive due to + // sapp being invalid. Problematic? User knows it's a fullscreen at startup. + _sapp.valid = true; + + [NSApp stop:nil]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { @@ -3086,63 +3520,85 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @end @implementation _sapp_macos_window_delegate +- (instancetype)initWithSappWindow:(_sapp_window_t* )initWindow { + self = [super init]; + if(self != nil){ + window = initWindow; + } + + return self; +} + - (BOOL)windowShouldClose:(id)sender { _SOKOL_UNUSED(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; + /* NOTE: We only give the user a chance to cancel the main window, should be changed maybe? */ + if(window == _sapp.main_window){ + /* 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(window, SAPP_EVENTTYPE_QUIT_REQUESTED); + /* user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } } - } - if (_sapp.quit_ordered) { - return YES; + if(_sapp.quit_ordered){ + /* close all existing windows */ + for (int i = 0; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + sapp_destroy_window(_sapp_window(_sapp.windows[i].id)); + } + } + } + return NO; } else { + _sapp_macos_app_event(window, SAPP_EVENTTYPE_WINDOW_CLOSED); + sapp_destroy_window(_sapp_window(window->id)); return NO; } } - (void)windowDidResize:(NSNotification*)notification { _SOKOL_UNUSED(notification); - _sapp_macos_update_dimensions(); + _sapp_macos_update_dimensions(window); if (!_sapp.first_frame) { - _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_macos_app_event(window, SAPP_EVENTTYPE_RESIZED); } } - (void)windowDidMiniaturize:(NSNotification*)notification { _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_macos_app_event(window, SAPP_EVENTTYPE_ICONIFIED); } - (void)windowDidDeminiaturize:(NSNotification*)notification { _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_macos_app_event(window, SAPP_EVENTTYPE_RESTORED); } - (void)windowDidEnterFullScreen:(NSNotification*)notification { _SOKOL_UNUSED(notification); - _sapp.fullscreen = true; + window->fullscreen = true; } - (void)windowDidExitFullScreen:(NSNotification*)notification { _SOKOL_UNUSED(notification); - _sapp.fullscreen = false; + window->fullscreen = false; } @end @implementation _sapp_macos_window - (instancetype)initWithContentRect:(NSRect)contentRect + sappWindow:(struct _sapp_window_t*)initWindow styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag { if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { + window = initWindow; [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; } return self; @@ -3159,12 +3615,12 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { - (BOOL)performDragOperation:(id)sender { NSPasteboard *pboard = [sender draggingPasteboard]; if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count; + _sapp_clear_drop_buffer(window); + window->drop.num_files = ((int)pboard.pasteboardItems.count > window->drop.max_files) ? window->drop.max_files : pboard.pasteboardItems.count; bool drop_failed = false; - for (int i = 0; i < _sapp.drop.num_files; i++) { + for (int i = 0; i < window->drop.num_files; i++) { NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[i] stringForType:NSPasteboardTypeFileURL]]; - if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(window, i), window->drop.max_path_length)) { SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); drop_failed = true; break; @@ -3172,13 +3628,13 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } if (!drop_failed) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_init_event(window, SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } } else { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; + _sapp_clear_drop_buffer(window); + window->drop.num_files = 0; } return YES; } @@ -3187,7 +3643,35 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @end @implementation _sapp_macos_view -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_METAL) +- (instancetype)initWithSappWindow:(_sapp_window_t* )initWindow +{ + self = [super init]; + if(self != nil){ + window = initWindow; + } + + return self; +} +#elif defined(SOKOL_GLCORE33) +- (instancetype)initWithSappWindow:(struct _sapp_window_t*)initWindow + initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + if(self != nil){ + window = initWindow; + trackingArea = nil; + } + + return self; +} + +- (void)dealloc +{ + [trackingArea release]; + [super dealloc]; +} + /* NOTE: this is a hack/fix when the initial window size has been clipped by macOS because it didn't fit on the screen, in that case the frame size of the window is reported wrong if low-dpi rendering @@ -3198,28 +3682,19 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { the correct dimensions. */ - (void)reshape { - _sapp_macos_update_dimensions(); - [super reshape]; -} -- (void)timerFired:(id)sender { - _SOKOL_UNUSED(sender); - [self setNeedsDisplay:YES]; + _sapp_macos_update_dimensions(window); } + - (void)prepareOpenGL { - [super prepareOpenGL]; GLint swapInt = 1; - NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; - [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; - [ctx makeCurrentContext]; + [window->macos.gl_ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; + [window->macos.gl_ctx makeCurrentContext]; } #endif +// TODO: check if this is needed - (void)drawRect:(NSRect)rect { _SOKOL_UNUSED(rect); - _sapp_macos_frame(); - #if !defined(SOKOL_METAL) - [[_sapp.macos.view openGLContext] flushBuffer]; - #endif } - (BOOL)isOpaque { @@ -3232,9 +3707,9 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { return YES; } - (void)updateTrackingAreas { - if (_sapp.macos.tracking_area != nil) { - [self removeTrackingArea:_sapp.macos.tracking_area]; - _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + if (window->macos.tracking_area != nil) { + [self removeTrackingArea:window->macos.tracking_area]; + _SAPP_OBJC_RELEASE(window->macos.tracking_area); } const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | @@ -3242,8 +3717,8 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { NSTrackingCursorUpdate | NSTrackingInVisibleRect | NSTrackingAssumeInside; - _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; - [self addTrackingArea:_sapp.macos.tracking_area]; + window->macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; + [self addTrackingArea:window->macos.tracking_area]; [super updateTrackingAreas]; } - (void)mouseEntered:(NSEvent*)event { @@ -3251,71 +3726,71 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { on Windows while SetCapture is active */ if (0 == _sapp.macos.mouse_buttons) { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } } - (void)mouseExited:(NSEvent*)event { if (0 == _sapp.macos.mouse_buttons) { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, 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)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons |= (1<mouse.locked) { + window->mouse.dx = [event deltaX]; + window->mouse.dy = [event deltaY]; } - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); } } - (void)mouseMoved:(NSEvent*)event { - if (_sapp.mouse.locked) { - _sapp.mouse.dx = [event deltaX]; - _sapp.mouse.dy = [event deltaY]; + if (window->mouse.locked) { + window->mouse.dx = [event deltaX]; + window->mouse.dy = [event deltaY]; } - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)mouseDragged:(NSEvent*)event { - if (_sapp.mouse.locked) { - _sapp.mouse.dx = [event deltaX]; - _sapp.mouse.dy = [event deltaY]; + if (window->mouse.locked) { + window->mouse.dx = [event deltaX]; + window->mouse.dy = [event deltaY]; } - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)rightMouseDragged:(NSEvent*)event { - if (_sapp.mouse.locked) { - _sapp.mouse.dx = [event deltaX]; - _sapp.mouse.dy = [event deltaY]; + if (window->mouse.locked) { + window->mouse.dx = [event deltaX]; + window->mouse.dy = [event deltaY]; } - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); + _sapp_macos_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } - (void)scrollWheel:(NSEvent*)event { if (_sapp_events_enabled()) { @@ -3326,7 +3801,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { dy *= 0.1; } if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp_init_event(window, SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags); _sapp.event.scroll_x = dx; _sapp.event.scroll_y = dy; @@ -3342,14 +3817,14 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { 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); + _sapp_macos_key_event(window, 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); + _sapp_macos_key_event(window, 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_init_event(window, SAPP_EVENTTYPE_CHAR); _sapp.event.modifiers = mods; for (NSUInteger i = 0; i < len; i++) { const unichar codepoint = [chars characterAtIndex:i]; @@ -3362,22 +3837,22 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } } /* 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); + if (window->clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { + _sapp_init_event(window, SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); } } } - (void)keyUp:(NSEvent*)event { - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, + _sapp_macos_key_event(window, 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 old_f = window->macos.flags_changed_store; const uint32_t new_f = event.modifierFlags; - _sapp.macos.flags_changed_store = new_f; + window->macos.flags_changed_store = new_f; sapp_keycode key_code = SAPP_KEYCODE_INVALID; bool down = false; if ((new_f ^ old_f) & NSEventModifierFlagShift) { @@ -3397,7 +3872,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { down = 0 != (new_f & NSEventModifierFlagCommand); } if (key_code != SAPP_KEYCODE_INVALID) { - _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, + _sapp_macos_key_event(window, down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, key_code, false, _sapp_macos_mod(event.modifierFlags)); @@ -3405,8 +3880,8 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } - (void)cursorUpdate:(NSEvent*)event { _SOKOL_UNUSED(event); - if (_sapp.desc.user_cursor) { - _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); + if (window->user_cursor) { + _sapp_macos_app_event(window, SAPP_EVENTTYPE_UPDATE_CURSOR); } } @end @@ -3433,6 +3908,7 @@ _SOKOL_PRIVATE void _sapp_ios_discard_state(void) { _SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { _sapp_init_state(desc); + _sapp_setup_pools(); static int argc = 1; static char* argv[] = { (char*)"sokol_app" }; UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); @@ -3449,14 +3925,14 @@ int main(int argc, char* argv[]) { _SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); NSEnumerator* enumerator = event.allTouches.objectEnumerator; UITouch* ios_touch; while ((ios_touch = [enumerator nextObject])) { @@ -3477,8 +3953,8 @@ _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet _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; + _sapp.main_window->window_width = (int) screen_rect.size.width; + _sapp.main_window->window_height = (int) screen_rect.size.height; int cur_fb_width, cur_fb_height; #if defined(SOKOL_METAL) const CGSize fb_size = _sapp.ios.view.drawableSize; @@ -3489,12 +3965,12 @@ _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { cur_fb_height = (int) _sapp.ios.view.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; + (_sapp.main_window->framebuffer_width != cur_fb_width) || + (_sapp.main_window->framebuffer_height != cur_fb_height); + _sapp.main_window->framebuffer_width = cur_fb_width; + _sapp.main_window->framebuffer_height = cur_fb_height; + SOKOL_ASSERT((_sapp.main_window->framebuffer_width > 0) && (_sapp.main_window->framebuffer_height > 0)); + _sapp.dpi_scale = (float)_sapp.main_window->framebuffer_width / (float) _sapp.main_window->window_width; if (dim_changed && !_sapp.first_frame) { _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); } @@ -3541,28 +4017,32 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { @implementation _sapp_app_delegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + + sapp_window window_id = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(window_id.id); + CGRect screen_rect = UIScreen.mainScreen.bounds; _sapp.ios.window = [[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; + _sapp.main_window->window_width = screen_rect.size.width; + _sapp.main_window->window_height = screen_rect.size.height; + if (_sapp.desc.window.high_dpi) { + _sapp.main_window->framebuffer_width = 2 * _sapp.main_window->window_width; + _sapp.main_window->framebuffer_height = 2 * _sapp.main_window->window_height; } else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; + _sapp.main_window->framebuffer_width = _sapp.main_window->window_width; + _sapp.main_window->framebuffer_height = _sapp.main_window->window_height; } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + _sapp.dpi_scale = (float)_sapp.main_window->framebuffer_width / (float) _sapp.main_window->window_width; #if defined(SOKOL_METAL) _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); _sapp.ios.view = [[_sapp_ios_view alloc] init]; - _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.main_window->swap_interval; _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; - if (_sapp.desc.high_dpi) { + _sapp.ios.view.sampleCount = _sapp.main_window->sample_count; + if (_sapp.desc.window.high_dpi) { _sapp.ios.view.contentScaleFactor = 2.0; } else { @@ -3595,7 +4075,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { _sapp.ios.view.enableSetNeedsDisplay = NO; _sapp.ios.view.userInteractionEnabled = YES; _sapp.ios.view.multipleTouchEnabled = YES; - if (_sapp.desc.high_dpi) { + if (_sapp.desc.window.high_dpi) { _sapp.ios.view.contentScaleFactor = 2.0; } else { @@ -3603,7 +4083,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { } _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; _sapp.ios.view_ctrl.view = _sapp.ios.view; - _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.main_window->swap_interval; _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; #endif [_sapp.ios.window makeKeyAndVisible]; @@ -3675,7 +4155,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { if (c >= 32) { /* ignore surrogates for now */ if ((c < 0xD800) || (c > 0xDFFF)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_CHAR); _sapp.event.char_code = c; _sapp_call_event(&_sapp.event); } @@ -3688,10 +4168,10 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { default: break; } if (k != SAPP_KEYCODE_INVALID) { - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_KEY_DOWN); _sapp.event.key_code = k; _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_KEY_UP); _sapp.event.key_code = k; _sapp_call_event(&_sapp.event); } @@ -3700,10 +4180,10 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { } else { /* this was a backspace */ - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_KEY_DOWN); _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_KEY_UP); _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; _sapp_call_event(&_sapp.event); } @@ -3752,10 +4232,10 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { } EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { - if (_sapp.clipboard.enabled) { - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + if (_sapp.main_window->clipboard.enabled) { + _sapp_strcpy(str, _sapp.main_window->clipboard.buffer, _sapp.main_window->clipboard.buf_size); if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); } } @@ -3767,53 +4247,53 @@ EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { } EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { - if (!_sapp.drop.enabled) { + if (!_sapp.main_window->drop.enabled) { return; } if (num < 0) { num = 0; } - if (num > _sapp.drop.max_files) { - num = _sapp.drop.max_files; + if (num > _sapp.main_window->drop.max_files) { + num = _sapp.main_window->drop.max_files; } - _sapp.drop.num_files = num; - _sapp_clear_drop_buffer(); + _sapp.main_window->drop.num_files = num; + _sapp_clear_drop_buffer(_sapp.main_window); } EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { /* NOTE: name is only the filename part, not a path */ - if (!_sapp.drop.enabled) { + if (!_sapp.main_window->drop.enabled) { return; } if (0 == name) { return; } - SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); - if ((i < 0) || (i >= _sapp.drop.num_files)) { + SOKOL_ASSERT(_sapp.main_window->drop.num_files <= _sapp.main_window->drop.max_files); + if ((i < 0) || (i >= _sapp.main_window->drop.num_files)) { return; } - if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(_sapp.main_window, i), _sapp.main_window->drop.max_path_length)) { SOKOL_LOG("sokol_app.h: dropped file path too long!\n"); - _sapp.drop.num_files = 0; + _sapp.main_window->drop.num_files = 0; } } EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { - if (!_sapp.drop.enabled) { + if (!_sapp.main_window->drop.enabled) { return; } - if (0 == _sapp.drop.num_files) { + if (0 == _sapp.main_window->drop.num_files) { /* there was an error copying the filenames */ - _sapp_clear_drop_buffer(); + _sapp_clear_drop_buffer(_sapp.main_window); return; } if (_sapp_events_enabled()) { - _sapp.mouse.x = (float)x * _sapp.dpi_scale; - _sapp.mouse.y = (float)y * _sapp.dpi_scale; - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.main_window->mouse.x = (float)x * _sapp.dpi_scale; + _sapp.main_window->mouse.y = (float)y * _sapp.dpi_scale; + _sapp.main_window->mouse.dx = 0.0f; + _sapp.main_window->mouse.dy = 0.0f; + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } } @@ -4031,7 +4511,7 @@ EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), { _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(emsc_type); _SOKOL_UNUSED(user_data); - _sapp.mouse.locked = emsc_event->isActive; + _sapp.main_window->mouse.locked = emsc_event->isActive; return EM_TRUE; } @@ -4039,7 +4519,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* _SOKOL_UNUSED(emsc_type); _SOKOL_UNUSED(reserved); _SOKOL_UNUSED(user_data); - _sapp.mouse.locked = false; + _sapp.main_window->mouse.locked = false; _sapp.emsc.mouse_lock_requested = false; return true; } @@ -4114,28 +4594,28 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU w = ui_event->windowInnerWidth; } else { - _sapp.window_width = (int) w; + _sapp.main_window->window_width = (int) w; } if (h < 1.0) { h = ui_event->windowInnerHeight; } else { - _sapp.window_height = (int) h; + _sapp.main_window->window_height = (int) h; } - if (_sapp.desc.high_dpi) { + if (_sapp.desc.window.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_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + _sapp.main_window->framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.main_window->framebuffer_height = (int) (h * _sapp.dpi_scale); + SOKOL_ASSERT((_sapp.main_window->framebuffer_width > 0) && (_sapp.main_window->framebuffer_height > 0)); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.main_window->framebuffer_width, _sapp.main_window->framebuffer_height); #if defined(SOKOL_WGPU) /* on WebGPU: recreate size-dependent rendering surfaces */ _sapp_emsc_wgpu_surfaces_discard(); _sapp_emsc_wgpu_surfaces_create(); #endif if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_RESIZED); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_RESIZED); _sapp_call_event(&_sapp.event); } return true; @@ -4143,20 +4623,20 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(user_data); - if (_sapp.mouse.locked) { - _sapp.mouse.dx = (float) emsc_event->movementX; - _sapp.mouse.dy = (float) emsc_event->movementY; + if (_sapp.main_window->mouse.locked) { + _sapp.main_window->mouse.dx = (float) emsc_event->movementX; + _sapp.main_window->mouse.dy = (float) emsc_event->movementY; } else { float new_x = emsc_event->targetX * _sapp.dpi_scale; float new_y = emsc_event->targetY * _sapp.dpi_scale; - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; + if (_sapp.main_window->mouse.pos_valid) { + _sapp.main_window->mouse.dx = new_x - _sapp.main_window->mouse.x; + _sapp.main_window->mouse.dy = new_y - _sapp.main_window->mouse.y; } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; + _sapp.main_window->mouse.x = new_x; + _sapp.main_window->mouse.y = new_y; + _sapp.main_window->mouse.pos_valid = true; } if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { sapp_event_type type; @@ -4184,7 +4664,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE break; } if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } @@ -4223,7 +4703,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelE _SOKOL_UNUSED(emsc_type); _SOKOL_UNUSED(user_data); if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_MOUSE_SCROLL); if (emsc_event->mouse.ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } @@ -4274,7 +4754,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } if (type != SAPP_EVENTTYPE_INVALID) { bool send_keyup_followup = false; - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp.event.key_repeat = emsc_event->repeat; if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; @@ -4362,8 +4842,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard 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_ALT: case SAPP_KEYCODE_RIGHT_SUPER: case SAPP_KEYCODE_MENU: /* consume the event */ @@ -4415,7 +4894,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchE break; } if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } @@ -4562,7 +5041,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* re default: type = SAPP_EVENTTYPE_INVALID; break; } if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp_call_event(&_sapp.event); } return true; @@ -4571,10 +5050,10 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* re _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { EmscriptenWebGLContextAttributes attrs; emscripten_webgl_init_context_attributes(&attrs); - attrs.alpha = _sapp.desc.alpha; + attrs.alpha = _sapp.desc.window.alpha; attrs.depth = true; attrs.stencil = true; - attrs.antialias = _sapp.sample_count > 1; + attrs.antialias = _sapp.main_window->sample_count > 1; attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; attrs.enableExtensionsByDefault = true; @@ -4656,28 +5135,28 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { memset(&ds_desc, 0, sizeof(ds_desc)); ds_desc.usage = WGPUTextureUsage_OutputAttachment; ds_desc.dimension = WGPUTextureDimension_2D; - ds_desc.size.width = (uint32_t) _sapp.framebuffer_width; - ds_desc.size.height = (uint32_t) _sapp.framebuffer_height; + ds_desc.size.width = (uint32_t) _sapp.main_window->framebuffer_width; + ds_desc.size.height = (uint32_t) _sapp.main_window->framebuffer_height; ds_desc.size.depth = 1; ds_desc.arrayLayerCount = 1; ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8; ds_desc.mipLevelCount = 1; - ds_desc.sampleCount = _sapp.sample_count; + ds_desc.sampleCount = _sapp.main_window->sample_count; _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc); _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0); - if (_sapp.sample_count > 1) { + if (_sapp.main_window->sample_count > 1) { WGPUTextureDescriptor msaa_desc; memset(&msaa_desc, 0, sizeof(msaa_desc)); msaa_desc.usage = WGPUTextureUsage_OutputAttachment; msaa_desc.dimension = WGPUTextureDimension_2D; - msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width; - msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height; + msaa_desc.size.width = (uint32_t) _sapp.main_window->framebuffer_width; + msaa_desc.size.height = (uint32_t) _sapp.main_window->framebuffer_height; msaa_desc.size.depth = 1; msaa_desc.arrayLayerCount = 1; msaa_desc.format = _sapp.emsc.wgpu.render_format; msaa_desc.mipLevelCount = 1; - msaa_desc.sampleCount = _sapp.sample_count; + msaa_desc.sampleCount = _sapp.main_window->sample_count; _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc); _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0); } @@ -4727,10 +5206,10 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); sapp_js_add_beforeunload_listener(); - if (_sapp.clipboard.enabled) { + if (_sapp.main_window->clipboard.enabled) { sapp_js_add_clipboard_listener(); } - if (_sapp.drop.enabled) { + if (_sapp.main_window->drop.enabled) { sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) @@ -4756,10 +5235,10 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); sapp_js_remove_beforeunload_listener(); - if (_sapp.clipboard.enabled) { + if (_sapp.main_window->clipboard.enabled) { sapp_js_remove_clipboard_listener(); } - if (_sapp.drop.enabled) { + if (_sapp.main_window->drop.enabled) { sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) @@ -4800,7 +5279,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { /* quit-handling */ if (_sapp.quit_requested) { - _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_QUIT_REQUESTED); _sapp_call_event(&_sapp.event); if (_sapp.quit_requested) { _sapp.quit_ordered = true; @@ -4817,25 +5296,28 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { _sapp_init_state(desc); + _sapp_setup_pools(); sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]); _sapp_emsc_keytable_init(); double w, h; if (_sapp.desc.html5_canvas_resize) { - w = (double) _sapp.desc.width; - h = (double) _sapp.desc.height; + w = (double) _sapp.desc.window.width; + h = (double) _sapp.desc.window.height; } else { emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); } - if (_sapp.desc.high_dpi) { + if (_sapp.desc.window.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_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + sapp_window main_window_id = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(main_window_id.id); + _sapp.main_window->window_width = (int) w; + _sapp.main_window->window_height = (int) h; + _sapp.main_window->framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.main_window->framebuffer_height = (int) (h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.main_window->framebuffer_width, _sapp.main_window->framebuffer_height); #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) _sapp_emsc_webgl_init(); #elif defined(SOKOL_WGPU) @@ -4988,9 +5470,9 @@ _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, } } -_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(_sapp_window_t* window, sapp_event_type type) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp_call_event(&_sapp.event); } } @@ -5187,14 +5669,55 @@ static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval #endif } -_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { - DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; - sc_desc->BufferDesc.Width = _sapp.framebuffer_width; - sc_desc->BufferDesc.Height = _sapp.framebuffer_height; +static inline HRESULT _sapp_dxgi_CreateSwapChain(IDXGIFactory* self, ID3D11Device* device, DXGI_SWAP_CHAIN_DESC* sc_desc, IDXGISwapChain** swap_chain) { + #if defined(__cplusplus) + return self->CreateSwapChain(device, sc_desc, swap_chain); + #else + return self->lpVtbl->CreateSwapChain(self, (IUnknown*)device, sc_desc, swap_chain); + #endif +} + +static inline HRESULT _sapp_dxgi_CreateDXGIFactory(IDXGIFactory** dxgi_factory) { + #if defined(__cplusplus) + return CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)dxgi_factory); + #else + return CreateDXGIFactory(&IID_IDXGIFactory, (void**)&_sapp.d3d11.dxgi_factory); + #endif +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_device(void) { + 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 = D3D11CreateDevice( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + NULL, /* pFeatureLevels */ + 0, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + &_sapp.d3d11.device, /* ppDevice */ + &feature_level, /* pFeatureLevel */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.device && _sapp.d3d11.device_context); + + // We need a dxgi factory to create multiple swapchains + hr = _sapp_dxgi_CreateDXGIFactory(&_sapp.d3d11.dxgi_factory); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dxgi_factory); +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_swapchain(_sapp_window_t* window) { + DXGI_SWAP_CHAIN_DESC* sc_desc = &window->d3d11.swap_chain_desc; + sc_desc->BufferDesc.Width = window->framebuffer_width; + sc_desc->BufferDesc.Height = window->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->OutputWindow = window->win32.hwnd; sc_desc->Windowed = true; if (_sapp.win32.is_win10_or_greater) { sc_desc->BufferCount = 2; @@ -5207,109 +5730,102 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { sc_desc->SampleDesc.Count = 1; sc_desc->SampleDesc.Quality = 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.d3d11.swap_chain, /* ppSwapChain */ - &_sapp.d3d11.device, /* ppDevice */ - &feature_level, /* pFeatureLevel */ - &_sapp.d3d11.device_context); /* ppImmediateContext */ - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); + + HRESULT hr = _sapp_dxgi_CreateSwapChain( + _sapp.d3d11.dxgi_factory, /* dxgi_factory */ + _sapp.d3d11.device, /* *pDevice */ + sc_desc, /* Swapchain Desc */ + &window->d3d11.swap_chain /* ppSwapChain */ + ); + + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.swap_chain); } -_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { - _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); +_SOKOL_PRIVATE void _sapp_d3d11_destroy_device() { _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); _SAPP_SAFE_RELEASE(_sapp.d3d11.device); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dxgi_factory); +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_swapchain(_sapp_window_t* window) { + _SAPP_SAFE_RELEASE(window->d3d11.swap_chain); } -_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { - SOKOL_ASSERT(0 == _sapp.d3d11.rt); - SOKOL_ASSERT(0 == _sapp.d3d11.rtv); - SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); - SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); - SOKOL_ASSERT(0 == _sapp.d3d11.ds); - SOKOL_ASSERT(0 == _sapp.d3d11.dsv); +_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(_sapp_window_t* window) { + SOKOL_ASSERT(0 == window->d3d11.rt); + SOKOL_ASSERT(0 == window->d3d11.rtv); + SOKOL_ASSERT(0 == window->d3d11.msaa_rt); + SOKOL_ASSERT(0 == window->d3d11.msaa_rtv); + SOKOL_ASSERT(0 == window->d3d11.ds); + SOKOL_ASSERT(0 == window->d3d11.dsv); HRESULT hr; /* view for the swapchain-created framebuffer */ #ifdef __cplusplus - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); + hr = _sapp_dxgi_GetBuffer(window->d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&window->d3d11.rt); #else - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); + hr = _sapp_dxgi_GetBuffer(window->d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&window->d3d11.rt); #endif - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); - hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)window->d3d11.rt, NULL, &window->d3d11.rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.rtv); /* common desc for MSAA and depth-stencil texture */ D3D11_TEXTURE2D_DESC tex_desc; memset(&tex_desc, 0, sizeof(tex_desc)); - tex_desc.Width = _sapp.framebuffer_width; - tex_desc.Height = _sapp.framebuffer_height; + tex_desc.Width = window->framebuffer_width; + tex_desc.Height = window->framebuffer_height; tex_desc.MipLevels = 1; tex_desc.ArraySize = 1; tex_desc.Usage = D3D11_USAGE_DEFAULT; tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - tex_desc.SampleDesc.Count = _sapp.sample_count; - tex_desc.SampleDesc.Quality = _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; + tex_desc.SampleDesc.Count = window->sample_count; + tex_desc.SampleDesc.Quality = window->sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; /* create MSAA texture and view if antialiasing requested */ - if (_sapp.sample_count > 1) { + if (window->sample_count > 1) { tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); - hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &window->d3d11.msaa_rt); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.msaa_rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)window->d3d11.msaa_rt, NULL, &window->d3d11.msaa_rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.msaa_rtv); } /* texture and view for the depth-stencil-surface */ tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); - hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &window->d3d11.ds); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.ds); + hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)window->d3d11.ds, NULL, &window->d3d11.dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && window->d3d11.dsv); } -_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { - _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); - _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); - _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); - _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); - _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); - _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); +_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(_sapp_window_t* window) { + _SAPP_SAFE_RELEASE(window->d3d11.rt); + _SAPP_SAFE_RELEASE(window->d3d11.rtv); + _SAPP_SAFE_RELEASE(window->d3d11.msaa_rt); + _SAPP_SAFE_RELEASE(window->d3d11.msaa_rtv); + _SAPP_SAFE_RELEASE(window->d3d11.ds); + _SAPP_SAFE_RELEASE(window->d3d11.dsv); } -_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { - if (_sapp.d3d11.swap_chain) { - _sapp_d3d11_destroy_default_render_target(); - _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, _sapp.framebuffer_width, _sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); - _sapp_d3d11_create_default_render_target(); +_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(_sapp_window_t* window) { + if (window->d3d11.swap_chain) { + _sapp_d3d11_destroy_default_render_target(window); + _sapp_dxgi_ResizeBuffers(window->d3d11.swap_chain, window->d3d11.swap_chain_desc.BufferCount, window->framebuffer_width, window->framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + _sapp_d3d11_create_default_render_target(window); } } -_SOKOL_PRIVATE void _sapp_d3d11_present(void) { +_SOKOL_PRIVATE void _sapp_d3d11_present(_sapp_window_t* window) { /* do MSAA resolve if needed */ - if (_sapp.sample_count > 1) { - SOKOL_ASSERT(_sapp.d3d11.rt); - SOKOL_ASSERT(_sapp.d3d11.msaa_rt); - _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); + if (window->sample_count > 1) { + SOKOL_ASSERT(window->d3d11.rt); + SOKOL_ASSERT(window->d3d11.msaa_rt); + _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)window->d3d11.rt, 0, (ID3D11Resource*)window->d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); } - _sapp_dxgi_Present(_sapp.d3d11.swap_chain, _sapp.swap_interval, 0); + _sapp_dxgi_Present(window->d3d11.swap_chain, window->swap_interval, 0); } #endif /* SOKOL_D3D11 */ @@ -5430,51 +5946,60 @@ _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); + + /* + Moved to here from _sapp_win32_run, since we need an active context for this to work. I have heard before that technically these should be loaded + on the same context as their usage, but never experienced any problems in practice. + */ + #if !defined(SOKOL_WIN32_NO_GL_LOADER) + _sapp_win32_gl_loadfuncs(); + #endif + _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); _sapp.wgl.DeleteContext(rc); } -_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { +_SOKOL_PRIVATE int _sapp_wgl_attrib(_sapp_window_t* window, int pixel_format, int attrib) { SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); int value = 0; - if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { + if (!_sapp.wgl.GetPixelFormatAttribivARB(window->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_PRIVATE int _sapp_wgl_find_pixel_format(_sapp_window_t* window) { + SOKOL_ASSERT(window->win32.dc); SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); const _sapp_gl_fbconfig* closest; - int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); + int native_count = _sapp_wgl_attrib(window, 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)) { + if (!_sapp_wgl_attrib(window, n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(window, n, WGL_DRAW_TO_WINDOW_ARB)) { continue; } - if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { + if (_sapp_wgl_attrib(window, n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { continue; } - if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { + if (_sapp_wgl_attrib(window, 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->red_bits = _sapp_wgl_attrib(window, n, WGL_RED_BITS_ARB); + u->green_bits = _sapp_wgl_attrib(window, n, WGL_GREEN_BITS_ARB); + u->blue_bits = _sapp_wgl_attrib(window, n, WGL_BLUE_BITS_ARB); + u->alpha_bits = _sapp_wgl_attrib(window, n, WGL_ALPHA_BITS_ARB); + u->depth_bits = _sapp_wgl_attrib(window, n, WGL_DEPTH_BITS_ARB); + u->stencil_bits = _sapp_wgl_attrib(window, n, WGL_STENCIL_BITS_ARB); + if (_sapp_wgl_attrib(window, n, WGL_DOUBLE_BUFFER_ARB)) { u->doublebuffer = true; } if (_sapp.wgl.arb_multisample) { - u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); + u->samples = _sapp_wgl_attrib(window, n, WGL_SAMPLES_ARB); } u->handle = n; usable_count++; @@ -5489,7 +6014,7 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { desired.depth_bits = 24; desired.stencil_bits = 8; desired.doublebuffer = true; - desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + desired.samples = window->sample_count > 1 ? window->sample_count : 0; closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); int pixel_format = 0; if (closest) { @@ -5499,16 +6024,16 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { return pixel_format; } -_SOKOL_PRIVATE void _sapp_wgl_create_context(void) { - int pixel_format = _sapp_wgl_find_pixel_format(); +_SOKOL_PRIVATE void _sapp_wgl_create_context(_sapp_window_t* window) { + int pixel_format = _sapp_wgl_find_pixel_format(window); 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)) { + if (!DescribePixelFormat(window->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)) { + if (!SetPixelFormat(window->win32.dc, pixel_format, &pfd)) { _sapp_fail("WGL: Failed to set selected pixel format!\n"); } if (!_sapp.wgl.arb_create_context) { @@ -5524,8 +6049,8 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0, 0 }; - _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); - if (!_sapp.wgl.gl_ctx) { + window->wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(window->win32.dc, 0, attrs); + if (!window->wgl.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"); @@ -5540,23 +6065,23 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { _sapp_fail("WGL: Failed to create OpenGL context"); } } - _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); + _sapp.wgl.MakeCurrent(window->win32.dc, window->wgl.gl_ctx); if (_sapp.wgl.ext_swap_control) { /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ - _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); + _sapp.wgl.SwapIntervalEXT(window->swap_interval); } } -_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { - SOKOL_ASSERT(_sapp.wgl.gl_ctx); - _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); - _sapp.wgl.gl_ctx = 0; +_SOKOL_PRIVATE void _sapp_wgl_destroy_context(_sapp_window_t* window) { + SOKOL_ASSERT(window->wgl.gl_ctx); + _sapp.wgl.DeleteContext(window->wgl.gl_ctx); + window->wgl.gl_ctx = 0; } -_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { - SOKOL_ASSERT(_sapp.win32.dc); +_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(_sapp_window_t* window) { + SOKOL_ASSERT(window->win32.dc); /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ - SwapBuffers(_sapp.win32.dc); + SwapBuffers(window->win32.dc); } #endif /* SOKOL_GLCORE33 */ @@ -5573,8 +6098,8 @@ _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int } } -_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { - HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); +_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(_sapp_window_t* window) { + HMONITOR monitor = MonitorFromWindow(window->win32.hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFO minfo; memset(&minfo, 0, sizeof(minfo)); minfo.cbSize = sizeof(MONITORINFO); @@ -5587,11 +6112,12 @@ _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { DWORD win_style; RECT rect = { 0, 0, 0, 0 }; - _sapp.fullscreen = !_sapp.fullscreen; - if (!_sapp.fullscreen) { + // TODO: Update to window specific desc, we currently don't store the window desc inside the window, needed? + window->fullscreen = !window->fullscreen; + if (!window->fullscreen) { win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale); + rect.right = (int) ((float)_sapp.desc.window.width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.desc.window.height * _sapp.win32.dpi.window_scale); } else { win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; @@ -5601,13 +6127,13 @@ _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); int win_width = rect.right - rect.left; int win_height = rect.bottom - rect.top; - if (!_sapp.fullscreen) { + if (!window->fullscreen) { rect.left = (monitor_w - win_width) / 2; rect.top = (monitor_h - win_height) / 2; } - SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); - SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); + SetWindowLongPtr(window->win32.hwnd, GWL_STYLE, win_style); + SetWindowPos(window->win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); } _SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { @@ -5615,9 +6141,10 @@ _SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { ShowCursor((BOOL)visible); } -_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { +_SOKOL_PRIVATE void _sapp_win32_capture_mouse(_sapp_window_t* window, uint8_t btn_mask) { + // TODO: window specific capture flag, what exactly happens here? Don't fully understand the code yet. if (0 == _sapp.win32.mouse_capture_mask) { - SetCapture(_sapp.win32.hwnd); + SetCapture(window->win32.hwnd); } _sapp.win32.mouse_capture_mask |= btn_mask; } @@ -5631,31 +6158,31 @@ _SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { } } -_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { +_SOKOL_PRIVATE void _sapp_win32_lock_mouse(_sapp_window_t* window, bool lock) { + if (lock == window->mouse.locked) { return; } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; + window->mouse.dx = 0.0f; + window->mouse.dy = 0.0f; + window->mouse.locked = lock; _sapp_win32_release_mouse(0xFF); - if (_sapp.mouse.locked) { + if (window->mouse.locked) { /* store the current mouse position, so it can be restored when unlocked */ POINT pos; BOOL res = GetCursorPos(&pos); SOKOL_ASSERT(res); _SOKOL_UNUSED(res); - _sapp.win32.mouse_locked_x = pos.x; - _sapp.win32.mouse_locked_y = pos.y; + window->win32.mouse_locked_x = pos.x; + window->win32.mouse_locked_y = pos.y; /* while the mouse is locked, make the mouse cursor invisible and confine the mouse movement to a small rectangle inside our window (so that we dont miss any mouse up events) */ RECT client_rect = { - _sapp.win32.mouse_locked_x, - _sapp.win32.mouse_locked_y, - _sapp.win32.mouse_locked_x, - _sapp.win32.mouse_locked_y + window->win32.mouse_locked_x, + window->win32.mouse_locked_y, + window->win32.mouse_locked_x, + window->win32.mouse_locked_y }; ClipCursor(&client_rect); @@ -5664,10 +6191,10 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ const RAWINPUTDEVICE rid = { - 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC - 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE - 0, // dwFlags - _sapp.win32.hwnd // hwndTarget + 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC + 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE + 0, // dwFlags + window->win32.hwnd // hwndTarget }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); @@ -5689,35 +6216,36 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { ShowCursor(TRUE); /* restore the 'pre-locked' mouse position */ - BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); + BOOL res = SetCursorPos(window->win32.mouse_locked_x, window->win32.mouse_locked_y); SOKOL_ASSERT(res); _SOKOL_UNUSED(res); } } /* 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) { +_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(_sapp_window_t* window) { RECT rect; - if (GetClientRect(_sapp.win32.hwnd, &rect)) { - _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); - _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); - const int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale); - const int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale); - if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { - _sapp.framebuffer_width = fb_width; - _sapp.framebuffer_height = fb_height; + if (GetClientRect(window->win32.hwnd, &rect)) { + // DISCUSS: should dpi scale be per window? I don't even know if that's possible + window->window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); + window->window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); + const int fb_width = (int)((float)window->window_width * _sapp.win32.dpi.content_scale); + const int fb_height = (int)((float)window->window_height * _sapp.win32.dpi.content_scale); + if ((fb_width != window->framebuffer_width) || (fb_height != window->framebuffer_height)) { + window->framebuffer_width = fb_width; + window->framebuffer_height = fb_height; /* prevent a framebuffer size of 0 when window is minimized */ - if (_sapp.framebuffer_width == 0) { - _sapp.framebuffer_width = 1; + if (window->framebuffer_width == 0) { + window->framebuffer_width = 1; } - if (_sapp.framebuffer_height == 0) { - _sapp.framebuffer_height = 1; + if (window->framebuffer_height == 0) { + window->framebuffer_height = 1; } return true; } } else { - _sapp.window_width = _sapp.window_height = 1; - _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + window->window_width = window->window_height = 1; + window->framebuffer_width = window->framebuffer_height = 1; } return false; } @@ -5739,18 +6267,18 @@ _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { return mods; } -_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { +_SOKOL_PRIVATE void _sapp_win32_mouse_event(_sapp_window_t* window, sapp_event_type type, sapp_mousebutton btn) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.mouse_button = btn; _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { +_SOKOL_PRIVATE void _sapp_win32_scroll_event(_sapp_window_t* window, float x, float y) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp_init_event(window, SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.scroll_x = -x / 30.0f; _sapp.event.scroll_y = y / 30.0f; @@ -5758,28 +6286,28 @@ _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { } } -_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { +_SOKOL_PRIVATE void _sapp_win32_key_event(_sapp_window_t* window, sapp_event_type type, int vk, bool repeat) { if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { - _sapp_init_event(type); + _sapp_init_event(window, 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 && + if (window->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_init_event(window, SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); } } } -_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { +_SOKOL_PRIVATE void _sapp_win32_char_event(_sapp_window_t* window, uint32_t c, bool repeat) { if (_sapp_events_enabled() && (c >= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp_init_event(window, SAPP_EVENTTYPE_CHAR); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.char_code = c; _sapp.event.key_repeat = repeat; @@ -5787,19 +6315,19 @@ _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { } } -_SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { - if (!_sapp.drop.enabled) { +_SOKOL_PRIVATE void _sapp_win32_files_dropped(_sapp_window_t* window, HDROP hdrop) { + if (!window->drop.enabled) { return; } - _sapp_clear_drop_buffer(); + _sapp_clear_drop_buffer(window); bool drop_failed = false; const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); - _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; - for (int i = 0; i < _sapp.drop.num_files; i++) { + window->drop.num_files = (count > window->drop.max_files) ? window->drop.max_files : count; + for (int i = 0; i < window->drop.num_files; i++) { const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR)); DragQueryFileW(hdrop, i, buffer, num_chars); - if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr(window, i), window->drop.max_path_length)) { SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); drop_failed = true; } @@ -5808,41 +6336,49 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { DragFinish(hdrop); if (!drop_failed) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_init_event(window, SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } } else { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; + _sapp_clear_drop_buffer(window); + window->drop.num_files = 0; } } _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (!_sapp.win32.in_create_window) { + _sapp_window_t* window = (_sapp_window_t*)GetPropA(hWnd, _SAPP_WINDOW_PROP); + if (window && !window->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_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - /* if user code hasn't intervened, quit the app */ - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; + if(window == _sapp.main_window){ + /* 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_uwp_app_event(window, 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); } } - if (_sapp.quit_ordered) { - PostQuitMessage(0); + else { + // DISCUSS: wo we want to give the user a per window opportunity to cancel closure requests? + _sapp_win32_uwp_app_event(window, SAPP_EVENTTYPE_WINDOW_CLOSED); + sapp_destroy_window(_sapp_window(window->id)); } return 0; case WM_SYSCOMMAND: switch (wParam & 0xFFF0) { case SC_SCREENSAVE: case SC_MONITORPOWER: - if (_sapp.fullscreen) { + if (window->fullscreen) { /* disable screen saver and blanking in fullscreen mode */ return 0; } @@ -5857,77 +6393,77 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM case WM_SIZE: { const bool iconified = wParam == SIZE_MINIMIZED; - if (iconified != _sapp.win32.iconified) { - _sapp.win32.iconified = iconified; + if (iconified != window->win32.iconified) { + window->win32.iconified = iconified; if (iconified) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_win32_uwp_app_event(window, SAPP_EVENTTYPE_ICONIFIED); } else { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_win32_uwp_app_event(window, SAPP_EVENTTYPE_RESTORED); } } } break; case WM_SETCURSOR: - if (_sapp.desc.user_cursor) { + if (window->user_cursor) { if (LOWORD(lParam) == HTCLIENT) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); + _sapp_win32_uwp_app_event(window, SAPP_EVENTTYPE_UPDATE_CURSOR); return 1; } } break; case WM_LBUTTONDOWN: - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); - _sapp_win32_capture_mouse(1<mouse.locked) { const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; /* don't update dx/dy in the very first event */ - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; + if (window->mouse.pos_valid) { + window->mouse.dx = new_x - window->mouse.x; + window->mouse.dy = new_y - window->mouse.y; } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - if (!_sapp.win32.mouse_tracked) { - _sapp.win32.mouse_tracked = true; + window->mouse.x = new_x; + window->mouse.y = new_y; + window->mouse.pos_valid = true; + if (!window->win32.mouse_tracked) { + window->win32.mouse_tracked = true; TRACKMOUSEEVENT tme; memset(&tme, 0, sizeof(tme)); tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; - tme.hwndTrack = _sapp.win32.hwnd; + tme.hwndTrack = window->win32.hwnd; TrackMouseEvent(&tme); - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); + _sapp_win32_mouse_event(window, SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); } - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + _sapp_win32_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); } break; case WM_INPUT: /* raw mouse input during mouse-lock */ - if (_sapp.mouse.locked) { + if (window->mouse.locked) { HRAWINPUT ri = (HRAWINPUT) lParam; UINT size = sizeof(_sapp.win32.raw_input_data); if (-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) { @@ -5944,8 +6480,8 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM if (_sapp.win32.raw_input_mousepos_valid) { LONG new_x = raw_mouse_data->data.mouse.lLastX; LONG new_y = raw_mouse_data->data.mouse.lLastY; - _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); - _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); + window->mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); + window->mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); _sapp.win32.raw_input_mousepos_x = new_x; _sapp.win32.raw_input_mousepos_y = new_y; _sapp.win32.raw_input_mousepos_valid = true; @@ -5953,63 +6489,65 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM } else { /* mouse reports movement delta (this seems to be the common case) */ - _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; - _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; + window->mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; + window->mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; } - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + _sapp_win32_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); } break; case WM_MOUSELEAVE: - if (!_sapp.mouse.locked) { - _sapp.win32.mouse_tracked = false; - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + if (!window->mouse.locked) { + window->win32.mouse_tracked = false; + _sapp_win32_mouse_event(window, SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); } break; case WM_MOUSEWHEEL: - _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); + _sapp_win32_scroll_event(window, 0.0f, (float)((SHORT)HIWORD(wParam))); break; case WM_MOUSEHWHEEL: - _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); + _sapp_win32_scroll_event(window, (float)((SHORT)HIWORD(wParam)), 0.0f); break; case WM_CHAR: - _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); + _sapp_win32_char_event(window, (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)); + _sapp_win32_key_event(window, 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); + _sapp_win32_key_event(window, SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); break; case WM_ENTERSIZEMOVE: - SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); + SetTimer(window->win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); break; case WM_EXITSIZEMOVE: - KillTimer(_sapp.win32.hwnd, 1); + KillTimer(window->win32.hwnd, 1); break; case WM_TIMER: + // TODO: I think we may have to consider current context here, since in the run situation of update, we ensure that the main context is active. + // This also currently pauses ALL windows, not just the one being resized. _sapp_frame(); #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); + _sapp_d3d11_present(window); #endif #if defined(SOKOL_GLCORE33) - _sapp_wgl_swap_buffers(); + _sapp_wgl_swap_buffers(window); #endif /* NOTE: resizing the swap-chain during resize leads to a substantial memory spike (hundreds of megabytes for a few seconds). if (_sapp_win32_update_dimensions()) { #if defined(SOKOL_D3D11) - _sapp_d3d11_resize_default_render_target(); + _sapp_d3d11_resize_default_render_target(window); #endif _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); } */ break; case WM_DROPFILES: - _sapp_win32_files_dropped((HDROP)wParam); + _sapp_win32_files_dropped(window, (HDROP)wParam); break; default: break; @@ -6018,59 +6556,53 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM 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); - +_SOKOL_PRIVATE void _sapp_win32_create_window(_sapp_window_t* window, const sapp_window_desc* desc) { DWORD win_style; const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; RECT rect = { 0, 0, 0, 0 }; - if (_sapp.fullscreen) { + if (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.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); + rect.right = (int) ((float)desc->width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)desc->height * _sapp.win32.dpi.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(); - - DragAcceptFiles(_sapp.win32.hwnd, 1); -} - -_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { - DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; - UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); + window->win32.in_create_window = true; + + _sapp_window_t* parent = _sapp_lookup_window(desc->parent_window.id); + + window->win32.hwnd = CreateWindowExW( + win_ex_style, /* dwExStyle */ + L"SOKOLAPP", /* lpClassName */ + window->window_title_wide, /* lpWindowName */ + win_style, /* dwStyle */ + CW_USEDEFAULT, /* X */ + CW_USEDEFAULT, /* Y */ + win_width, /* nWidth */ + win_height, /* nHeight */ + parent ? parent->win32.hwnd : NULL, /* hWndParent */ + NULL, /* hMenu */ + GetModuleHandle(NULL), /* hInstance */ + NULL); /* lParam */ + SetPropA(window->win32.hwnd, _SAPP_WINDOW_PROP, window); + ShowWindow(window->win32.hwnd, SW_SHOW); + window->win32.in_create_window = false; + window->win32.dc = GetDC(window->win32.hwnd); + SOKOL_ASSERT(window->win32.dc); + _sapp_win32_update_dimensions(window); + + DragAcceptFiles(window->win32.hwnd, 1); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_window(_sapp_window_t* window) { + DestroyWindow(window->win32.hwnd); + window->win32.hwnd = 0; } _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { @@ -6095,7 +6627,7 @@ _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { /* 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) { + if (!_sapp.desc.window.high_dpi) { process_dpi_awareness = PROCESS_DPI_UNAWARE; _sapp.win32.dpi.aware = false; } @@ -6119,7 +6651,7 @@ _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { else { _sapp.win32.dpi.window_scale = 1.0f; } - if (_sapp.desc.high_dpi) { + if (_sapp.desc.window.high_dpi) { _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; _sapp.win32.dpi.mouse_scale = 1.0f; } @@ -6136,13 +6668,13 @@ _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { } } -_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { +_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(_sapp_window_t* window, const char* str) { SOKOL_ASSERT(str); - SOKOL_ASSERT(_sapp.win32.hwnd); - SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + SOKOL_ASSERT(window->win32.hwnd); + SOKOL_ASSERT(window->clipboard.enabled && (window->clipboard.buf_size > 0)); wchar_t* wchar_buf = 0; - const int wchar_buf_size = _sapp.clipboard.buf_size * sizeof(wchar_t); + const int wchar_buf_size = window->clipboard.buf_size * sizeof(wchar_t); HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); if (!object) { goto error; @@ -6156,7 +6688,7 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { } GlobalUnlock(wchar_buf); wchar_buf = 0; - if (!OpenClipboard(_sapp.win32.hwnd)) { + if (!OpenClipboard(window->win32.hwnd)) { goto error; } EmptyClipboard(); @@ -6174,36 +6706,36 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { return false; } -_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); - SOKOL_ASSERT(_sapp.win32.hwnd); - if (!OpenClipboard(_sapp.win32.hwnd)) { +_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(_sapp_window_t* window) { + SOKOL_ASSERT(window->clipboard.enabled && window->clipboard.buffer); + SOKOL_ASSERT(window->win32.hwnd); + if (!OpenClipboard(window->win32.hwnd)) { /* silently ignore any errors and just return the current content of the local clipboard buffer */ - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } HANDLE object = GetClipboardData(CF_UNICODETEXT); if (!object) { CloseClipboard(); - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); if (!wchar_buf) { CloseClipboard(); - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } - if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { + if (!_sapp_win32_wide_to_utf8(wchar_buf, window->clipboard.buffer, window->clipboard.buf_size)) { SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); } GlobalUnlock(object); CloseClipboard(); - return _sapp.clipboard.buffer; + return window->clipboard.buffer; } -_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); +_SOKOL_PRIVATE void _sapp_win32_update_window_title(_sapp_window_t* window) { + _sapp_win32_uwp_utf8_to_wide(window->window_title, window->window_title_wide, sizeof(window->window_title_wide)); + SetWindowTextW(window->win32.hwnd, window->window_title_wide); } /* don't laugh, but this seems to be the easiest and most robust @@ -6223,23 +6755,30 @@ _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_setup_pools(); _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)); _sapp_win32_init_dpi(); - _sapp_win32_create_window(); + + 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); + #if defined(SOKOL_D3D11) - _sapp_d3d11_create_device_and_swapchain(); - _sapp_d3d11_create_default_render_target(); - #endif - #if defined(SOKOL_GLCORE33) + _sapp_d3d11_create_device(); + #elif 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_window main_window_id = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(main_window_id.id); _sapp.valid = true; bool done = false; @@ -6255,37 +6794,52 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { DispatchMessage(&msg); } } + sapp_gl_make_context_current(_sapp_window(_sapp.main_window->id)); _sapp_frame(); - #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); - if (IsIconic(_sapp.win32.hwnd)) { - Sleep(16 * _sapp.swap_interval); + + // DISCUSS: Not ideal, since we're checking for valid ids, but probably still better than keeping an additional linked list? + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + _sapp_window_t* window = &_sapp.windows[i]; + + #if defined(SOKOL_D3D11) + _sapp_d3d11_present(window); + if (IsIconic(window->win32.hwnd)) { + Sleep(16 * window->swap_interval); + } + #elif defined(SOKOL_GLCORE33) + _sapp_wgl_swap_buffers(window); + #endif + + /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ + if (_sapp_win32_update_dimensions(window)) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(window); + #endif + _sapp_win32_uwp_app_event(window, SAPP_EVENTTYPE_RESIZED); + } } - #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_uwp_app_event(SAPP_EVENTTYPE_RESIZED); - } + } + if (_sapp.quit_requested) { - PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); + PostMessage(_sapp.main_window->win32.hwnd, WM_CLOSE, 0, 0); } } _sapp_call_cleanup(); + UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); + + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + // TODO: Is there a better way to handle this instead of creating a tmp object? + sapp_destroy_window(_sapp_window(_sapp.windows[i].id)); + } + } #if defined(SOKOL_D3D11) - _sapp_d3d11_destroy_default_render_target(); - _sapp_d3d11_destroy_device_and_swapchain(); - #else - _sapp_wgl_destroy_context(); + _sapp_d3d11_destroy_device(); + #elif defined(SOKOL_GLCORE33) _sapp_wgl_shutdown(); #endif - _sapp_win32_destroy_window(); _sapp_discard_state(); } @@ -6353,7 +6907,7 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ // Helper functions _SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; - if (_sapp.desc.high_dpi) { + if (_sapp.desc.window.high_dpi) { _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; } @@ -6397,7 +6951,7 @@ _SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow cons _SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton btn, winrt::Windows::UI::Core::CoreWindow const& sender_window) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp.event.modifiers = _sapp_uwp_mods(sender_window); _sapp.event.mouse_button = btn; _sapp_call_event(&_sapp.event); @@ -6406,7 +6960,7 @@ _SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton _SOKOL_PRIVATE void _sapp_uwp_scroll_event(float delta, bool horizontal, winrt::Windows::UI::Core::CoreWindow const& sender_window) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = _sapp_uwp_mods(sender_window); _sapp.event.scroll_x = horizontal ? (-delta / 30.0f) : 0.0f; _sapp.event.scroll_y = horizontal ? 0.0f : (delta / 30.0f); @@ -6463,18 +7017,18 @@ _SOKOL_PRIVATE void _sapp_uwp_key_event(sapp_event_type type, winrt::Windows::UI auto key_status = args.KeyStatus(); uint32_t ext_scan_code = key_status.ScanCode | (key_status.IsExtendedKey ? 0x100 : 0); if (_sapp_events_enabled() && (ext_scan_code < SAPP_MAX_KEYCODES)) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp.event.modifiers = _sapp_uwp_mods(sender_window); _sapp.event.key_code = _sapp.keycodes[ext_scan_code]; _sapp.event.key_repeat = type == SAPP_EVENTTYPE_KEY_UP ? false : key_status.WasKeyDown; _sapp_call_event(&_sapp.event); /* check if a CLIPBOARD_PASTED event must be sent too */ - if (_sapp.clipboard.enabled && + if (_sapp.main_window->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_init_event(_sapp.main_window, SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); } } @@ -6482,7 +7036,7 @@ _SOKOL_PRIVATE void _sapp_uwp_key_event(sapp_event_type type, winrt::Windows::UI _SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows::UI::Core::CoreWindow const& sender_window) { if (_sapp_events_enabled() && (c >= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp_init_event(_sapp.main_window, SAPP_EVENTTYPE_CHAR); _sapp.event.modifiers = _sapp_uwp_mods(sender_window); _sapp.event.char_code = c; _sapp.event.key_repeat = repeat; @@ -6492,14 +7046,14 @@ _SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows _SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - _sapp.fullscreen = appView.IsFullScreenMode(); - if (!_sapp.fullscreen) { + _sapp.main_window->fullscreen = appView.IsFullScreenMode(); + if (!_sapp.main_window->fullscreen) { appView.TryEnterFullScreenMode(); } else { appView.ExitFullScreenMode(); } - _sapp.fullscreen = appView.IsFullScreenMode(); + _sapp.main_window->fullscreen = appView.IsFullScreenMode(); } namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ @@ -6727,12 +7281,12 @@ void DeviceResources::CreateDeviceResources() { void DeviceResources::CreateWindowSizeDependentResources() { // Cleanup Sokol Context (these are non-owning raw pointers) - _sapp.d3d11.rt = nullptr; - _sapp.d3d11.rtv = nullptr; - _sapp.d3d11.msaa_rt = nullptr; - _sapp.d3d11.msaa_rtv = nullptr; - _sapp.d3d11.ds = nullptr; - _sapp.d3d11.dsv = nullptr; + _sapp.main_window->d3d11.rt = nullptr; + _sapp.main_window->d3d11.rtv = nullptr; + _sapp.main_window->d3d11.msaa_rt = nullptr; + _sapp.main_window->d3d11.msaa_rtv = nullptr; + _sapp.main_window->d3d11.ds = nullptr; + _sapp.main_window->d3d11.dsv = nullptr; // Clear the previous window size specific context. ID3D11RenderTargetView* nullViews[] = { nullptr }; @@ -6812,8 +7366,8 @@ void DeviceResources::CreateWindowSizeDependentResources() { winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); // Setup Sokol Context - winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); - _sapp.d3d11.swap_chain = m_swapChain.as().detach(); + winrt::check_hresult(swapChain->GetDesc(&_sapp.main_window->d3d11.swap_chain_desc)); + _sapp.main_window->d3d11.swap_chain = m_swapChain.as().detach(); } // Set the proper orientation for the swap chain, and generate 2D and @@ -6845,7 +7399,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); // Create MSAA texture and view if needed - if (_sapp.sample_count > 1) { + if (_sapp.main_window->sample_count > 1) { CD3D11_TEXTURE2D_DESC1 msaaTexDesc( DXGI_FORMAT_B8G8R8A8_UNORM, lround(m_d3dRenderTargetSize.Width), @@ -6855,8 +7409,8 @@ void DeviceResources::CreateWindowSizeDependentResources() { D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, 0, // cpuAccessFlags - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 + _sapp.main_window->sample_count, + _sapp.main_window->sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 ); winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); @@ -6872,8 +7426,8 @@ void DeviceResources::CreateWindowSizeDependentResources() { D3D11_BIND_DEPTH_STENCIL, D3D11_USAGE_DEFAULT, 0, // cpuAccessFlag - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 + _sapp.main_window->sample_count, + _sapp.main_window->sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 ); winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); @@ -6881,19 +7435,19 @@ void DeviceResources::CreateWindowSizeDependentResources() { winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); // Set sokol window and framebuffer sizes - _sapp.window_width = (int) m_logicalSize.Width; - _sapp.window_height = (int) m_logicalSize.Height; - _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); - _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); + _sapp.main_window->window_width = (int) m_logicalSize.Width; + _sapp.main_window->window_height = (int) m_logicalSize.Height; + _sapp.main_window->framebuffer_width = lround(m_d3dRenderTargetSize.Width); + _sapp.main_window->framebuffer_height = lround(m_d3dRenderTargetSize.Height); // Setup Sokol Context - _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); - _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); - _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); - _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); - if (_sapp.sample_count > 1) { - _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); - _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); + _sapp.main_window->d3d11.rt = m_d3dRenderTarget.as().get(); + _sapp.main_window->d3d11.rtv = m_d3dRenderTargetView.as().get(); + _sapp.main_window->d3d11.ds = m_d3dDepthStencil.as().get(); + _sapp.main_window->d3d11.dsv = m_d3dDepthStencilView.get(); + if (_sapp.main_window->sample_count > 1) { + _sapp.main_window->d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); + _sapp.main_window->d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); } // Sokol app is now valid @@ -7020,7 +7574,7 @@ void DeviceResources::Trim() { void DeviceResources::Present() { // MSAA resolve if needed - if (_sapp.sample_count > 1) { + if (_sapp.main_window->sample_count > 1) { m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); } @@ -7194,44 +7748,44 @@ void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationVie _SOKOL_UNUSED(args); _SOKOL_UNUSED(applicationView); auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height); + auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.window.width, (float)_sapp.desc.window.height); appView.SetPreferredMinSize(targetSize); appView.TryResizeView(targetSize); // Disabling this since it can only append the title to the app name (Title - Appname). // There's no way of just setting a string to be the window title. - //appView.Title(_sapp.window_title_wide); + //appView.Title(_sapp.main_window->window_title_wide); // Run() won't start until the CoreWindow is activated. winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); - if (_sapp.desc.fullscreen) { + if (_sapp.desc.window.fullscreen) { appView.TryEnterFullScreenMode(); } - _sapp.fullscreen = appView.IsFullScreenMode(); + _sapp.main_window->fullscreen = appView.IsFullScreenMode(); } void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { _SOKOL_UNUSED(sender); _SOKOL_UNUSED(args); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); + _sapp_win32_uwp_app_event(_sapp.main_window, SAPP_EVENTTYPE_SUSPENDED); } void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { _SOKOL_UNUSED(args); _SOKOL_UNUSED(sender); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); + _sapp_win32_uwp_app_event(_sapp.main_window, SAPP_EVENTTYPE_RESUMED); } void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { _SOKOL_UNUSED(args); m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_uwp_app_event(_sapp.main_window, SAPP_EVENTTYPE_RESIZED); } void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { _SOKOL_UNUSED(sender); m_windowVisible = args.Visible(); - _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); + _sapp_win32_uwp_app_event(_sapp.main_window, m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); } void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { @@ -7279,13 +7833,13 @@ void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, win const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); // don't update dx/dy in the very first event - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; + if (_sapp.main_window->mouse.pos_valid) { + _sapp.main_window->mouse.dx = new_x - _sapp.main_window->mouse.x; + _sapp.main_window->mouse.dy = new_y - _sapp.main_window->mouse.y; } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; + _sapp.main_window->mouse.x = new_x; + _sapp.main_window->mouse.y = new_y; + _sapp.main_window->mouse.pos_valid = true; if (!_sapp.uwp.mouse_tracked) { _sapp.uwp.mouse_tracked = true; _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); @@ -7305,14 +7859,14 @@ void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation con // NOTE: UNTESTED _SOKOL_UNUSED(args); m_deviceResources->SetDpi(sender.LogicalDpi()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_uwp_app_event(_sapp.main_window, SAPP_EVENTTYPE_RESIZED); } void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { // NOTE: UNTESTED _SOKOL_UNUSED(args); m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_uwp_app_event(_sapp.main_window, SAPP_EVENTTYPE_RESIZED); } void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { @@ -7326,8 +7880,11 @@ void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::Displa _SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { _sapp_init_state(desc); + _sapp_setup_pools(); + sapp_window window_id = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(_sapp.main_window); _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); } @@ -7364,7 +7921,7 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { return false; } - EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + EGLint alpha_size = _sapp.desc.window.alpha ? 8 : 0; const EGLint cfg_attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, @@ -7474,7 +8031,7 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); SOKOL_LOG("event_cb()"); _sapp_call_event(&_sapp.event); } @@ -7489,11 +8046,11 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool 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; + const bool win_changed = (win_w != _sapp.main_window->window_width) || (win_h != _sapp.main_window->window_height); + _sapp.main_window->window_width = win_w; + _sapp.main_window->window_height = win_h; if (win_changed || force_update) { - if (!_sapp.desc.high_dpi) { + if (!_sapp.desc.window.high_dpi) { const int32_t buf_w = win_w / 2; const int32_t buf_h = win_h / 2; EGLint format; @@ -7515,10 +8072,10 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.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; + const bool fb_changed = (fb_w != _sapp.main_window->framebuffer_width) || (fb_h != _sapp.main_window->framebuffer_height); + _sapp.main_window->framebuffer_width = fb_w; + _sapp.main_window->framebuffer_height = fb_h; + _sapp.dpi_scale = (float)_sapp.main_window->framebuffer_width / (float)_sapp.main_window->window_width; if (win_changed || fb_changed || force_update) { if (!_sapp.first_frame) { SOKOL_LOG("SAPP_EVENTTYPE_RESIZED"); @@ -7593,7 +8150,7 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { return false; } int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - _sapp_init_event(type); + _sapp_init_event(_sapp.main_window, type); _sapp.event.num_touches = AMotionEvent_getPointerCount(e); if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; @@ -7601,8 +8158,8 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { 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; + dst->pos_x = (AMotionEvent_getRawX(e, i) / _sapp.main_window->window_width) * _sapp.main_window->framebuffer_width; + dst->pos_y = (AMotionEvent_getRawY(e, i) / _sapp.main_window->window_height) * _sapp.main_window->framebuffer_height; if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -8863,18 +9420,18 @@ _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); - if (_sapp.drop.enabled) { - _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); - _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); - _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); - _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); - _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); - _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); - _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); - _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); - _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); - _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); - } + // Note: not optional anymore, since we can't predict whether a future window will enable drag and drop + _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); + _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); + _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); + _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); + _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); + _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); + _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); + _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); + _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); + _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); + /* check Xi extension for raw mouse input */ if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { @@ -9031,7 +9588,7 @@ _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { return value; } -_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(_sapp_window_t* window) { GLXFBConfig* native_configs; _sapp_gl_fbconfig* usable_configs; const _sapp_gl_fbconfig* closest; @@ -9093,7 +9650,7 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { desired.depth_bits = 24; desired.stencil_bits = 8; desired.doublebuffer = true; - desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + desired.samples = window->sample_count > 1 ? window->sample_count : 0; closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); GLXFBConfig result = 0; if (closest) { @@ -9104,8 +9661,8 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { return result; } -_SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { - GLXFBConfig native = _sapp_glx_choosefbconfig(); +_SOKOL_PRIVATE void _sapp_glx_choose_visual(_sapp_window_t* window, Visual** visual, int* depth) { + GLXFBConfig native = _sapp_glx_choosefbconfig(window); if (0 == native) { _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); } @@ -9118,8 +9675,8 @@ _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { XFree(result); } -_SOKOL_PRIVATE void _sapp_glx_create_context(void) { - GLXFBConfig native = _sapp_glx_choosefbconfig(); +_SOKOL_PRIVATE void _sapp_glx_create_context(_sapp_window_t* window) { + GLXFBConfig native = _sapp_glx_choosefbconfig(window); if (0 == native){ _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); } @@ -9134,52 +9691,54 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { 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) { + window->glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); + if (!window->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) { + window->glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, window->x11.window, NULL); + if (!window->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; +_SOKOL_PRIVATE void _sapp_glx_destroy_context(_sapp_window_t* window) { + if (window->glx.window) { + _sapp.glx.DestroyWindow(_sapp.x11.display, window->glx.window); + window->glx.window = 0; } - if (_sapp.glx.ctx) { - _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); - _sapp.glx.ctx = 0; + if (window->glx.ctx) { + _sapp.glx.DestroyContext(_sapp.x11.display, window->glx.ctx); + window->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_make_current(_sapp_window_t* window) { + if(!_sapp.glx.MakeCurrent(_sapp.x11.display, window->glx.window, window->glx.ctx)){ + SOKOL_LOG("Failed to make context current"); + } } -_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { - _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); +_SOKOL_PRIVATE void _sapp_glx_swap_buffers(_sapp_window_t* window) { + _sapp.glx.SwapBuffers(_sapp.x11.display, window->glx.window); } -_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { - _sapp_glx_make_current(); +_SOKOL_PRIVATE void _sapp_glx_swapinterval(_sapp_window_t* window, int interval) { + _sapp_glx_make_current(window); if (_sapp.glx.EXT_swap_control) { - _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); + _sapp.glx.SwapIntervalEXT(_sapp.x11.display, window->glx.window, interval); } else if (_sapp.glx.MESA_swap_control) { _sapp.glx.SwapIntervalMESA(interval); } } -_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { +_SOKOL_PRIVATE void _sapp_x11_send_event(_sapp_window_t* window, Atom type, int a, int b, int c, int d, int e) { XEvent event; memset(&event, 0, sizeof(event)); event.type = ClientMessage; - event.xclient.window = _sapp.x11.window; + event.xclient.window = window->x11.window; event.xclient.format = 32; event.xclient.message_type = type; event.xclient.data.l[0] = a; @@ -9194,28 +9753,30 @@ _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, &event); } -_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { +_SOKOL_PRIVATE void _sapp_x11_query_window_size(_sapp_window_t* window) { 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.window_height; + XGetWindowAttributes(_sapp.x11.display, window->x11.window, &attribs); + window->window_width = attribs.width; + window->window_height = attribs.height; + window->framebuffer_width = window->window_width; + window->framebuffer_height = window->window_height; } -_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { +_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(_sapp_window_t* window, bool enable) { /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { if (enable) { const int _NET_WM_STATE_ADD = 1; - _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _sapp_x11_send_event(window, + _sapp.x11.NET_WM_STATE, _NET_WM_STATE_ADD, _sapp.x11.NET_WM_STATE_FULLSCREEN, 0, 1, 0); } else { const int _NET_WM_STATE_REMOVE = 0; - _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _sapp_x11_send_event(window, + _sapp.x11.NET_WM_STATE, _NET_WM_STATE_REMOVE, _sapp.x11.NET_WM_STATE_FULLSCREEN, 0, 1, 0); @@ -9238,29 +9799,29 @@ _SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { XcursorImageDestroy(img); } -_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { - _sapp.fullscreen = !_sapp.fullscreen; - _sapp_x11_set_fullscreen(_sapp.fullscreen); - _sapp_x11_query_window_size(); +_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(_sapp_window_t* window) { + window->fullscreen = !window->fullscreen; + _sapp_x11_set_fullscreen(window, window->fullscreen); + _sapp_x11_query_window_size(window); } -_SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) { +_SOKOL_PRIVATE void _sapp_x11_show_mouse(_sapp_window_t* window, bool show) { if (show) { - XUndefineCursor(_sapp.x11.display, _sapp.x11.window); + XUndefineCursor(_sapp.x11.display, window->x11.window); } else { - XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); + XDefineCursor(_sapp.x11.display, window->x11.window, _sapp.x11.hidden_cursor); } } -_SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { +_SOKOL_PRIVATE void _sapp_x11_lock_mouse(_sapp_window_t* window, bool lock) { + if (lock == window->mouse.locked) { return; } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; - if (_sapp.mouse.locked) { + window->mouse.dx = 0.0f; + window->mouse.dy = 0.0f; + window->mouse.locked = lock; + if (window->mouse.locked) { if (_sapp.x11.xi.available) { XIEventMask em; unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro @@ -9271,12 +9832,12 @@ _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); } XGrabPointer(_sapp.x11.display, // display - _sapp.x11.window, // grab_window + window->x11.window, // grab_window True, // owner_events ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask GrabModeAsync, // pointer_mode GrabModeAsync, // keyboard_mode - _sapp.x11.window, // confine_to + window->x11.window, // confine_to _sapp.x11.hidden_cursor, // cursor CurrentTime); // time } @@ -9289,47 +9850,68 @@ _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { em.mask = mask; XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); } - XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); + XWarpPointer(_sapp.x11.display, None, window->x11.window, 0, 0, 0, 0, (int) window->mouse.x, window->mouse.y); XUngrabPointer(_sapp.x11.display, CurrentTime); } XFlush(_sapp.x11.display); } -_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { +_SOKOL_PRIVATE void _sapp_x11_update_window_title(_sapp_window_t* window) { Xutf8SetWMProperties(_sapp.x11.display, - _sapp.x11.window, - _sapp.window_title, _sapp.window_title, + window->x11.window, + window->window_title, window->window_title, NULL, 0, NULL, NULL, NULL); - XChangeProperty(_sapp.x11.display, _sapp.x11.window, + XChangeProperty(_sapp.x11.display, window->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, + (unsigned char*)window->window_title, + strlen(window->window_title)); + XChangeProperty(_sapp.x11.display, window->x11.window, _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, PropModeReplace, - (unsigned char*)_sapp.window_title, - strlen(_sapp.window_title)); + (unsigned char*)window->window_title, + strlen(window->window_title)); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE bool _sapp_x11_window_visible(_sapp_window_t* window) { + XWindowAttributes wa; + XGetWindowAttributes(_sapp.x11.display, window->x11.window, &wa); + return wa.map_state == IsViewable; +} + +_SOKOL_PRIVATE void _sapp_x11_show_window(_sapp_window_t* window) { + if (!_sapp_x11_window_visible(window)) { + XMapWindow(_sapp.x11.display, window->x11.window); + XRaiseWindow(_sapp.x11.display, window->x11.window); + XFlush(_sapp.x11.display); + } +} + +_SOKOL_PRIVATE void _sapp_x11_hide_window(_sapp_window_t* window) { + XUnmapWindow(_sapp.x11.display, window->x11.window); XFlush(_sapp.x11.display); } -_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { - _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); +_SOKOL_PRIVATE void _sapp_x11_create_window(_sapp_window_t* window, Visual* visual, int depth) { + window->x11.window_state = NormalState; + window->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.colormap = window->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, + window->x11.window = XCreateWindow(_sapp.x11.display, _sapp.x11.root, 0, 0, - _sapp.window_width, - _sapp.window_height, + window->window_width, + window->window_height, 0, /* border width */ depth, /* color depth */ InputOutput, @@ -9337,58 +9919,50 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { wamask, &wa); _sapp_x11_release_error_handler(); - if (!_sapp.x11.window) { + if (!window->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); + XSetWMProtocols(_sapp.x11.display, window->x11.window, protocols, 1); XSizeHints* hints = XAllocSizeHints(); hints->flags |= PWinGravity; hints->win_gravity = StaticGravity; - XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); + XSetWMNormalHints(_sapp.x11.display, window->x11.window, hints); XFree(hints); /* announce support for drag'n'drop */ - if (_sapp.drop.enabled) { + if (window->drop.enabled) { const Atom version = _SAPP_X11_XDND_VERSION; - XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); + XChangeProperty(_sapp.x11.display, window->x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); } - _sapp_x11_update_window_title(); -} + _sapp_x11_update_window_title(window); -_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; + /* associate window XID with _sapp_window_t* for later retrieval during event processing */ + XSaveContext(_sapp.x11.display, window->x11.window,_sapp.x11.context, (XPointer) window); + + _sapp_x11_show_window(window); + if (window->fullscreen) { + _sapp_x11_set_fullscreen(window, true); } + _sapp_x11_query_window_size(window); + _sapp_glx_swapinterval(window, window->swap_interval); 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_destroy_window(_sapp_window_t* window) { + if (window->x11.window) { + XUnmapWindow(_sapp.x11.display, window->x11.window); + XDestroyWindow(_sapp.x11.display, window->x11.window); + window->x11.window = 0; + } + if (window->x11.colormap) { + XFreeColormap(_sapp.x11.display, window->x11.colormap); + window->x11.colormap = 0; } -} - -_SOKOL_PRIVATE void _sapp_x11_hide_window(void) { - XUnmapWindow(_sapp.x11.display, _sapp.x11.window); XFlush(_sapp.x11.display); } @@ -9411,14 +9985,14 @@ _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom p return itemCount; } -_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { +_SOKOL_PRIVATE int _sapp_x11_get_window_state(_sapp_window_t* window) { int result = WithdrawnState; struct { CARD32 state; Window icon; } *state = NULL; - if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { + if (_sapp_x11_get_window_property(window->x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { result = state->state; } if (state) { @@ -9444,9 +10018,9 @@ _SOKOL_PRIVATE uint32_t _sapp_x11_mod(int x11_mods) { return mods; } -_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_x11_app_event(_sapp_window_t* window, sapp_event_type type) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp_call_event(&_sapp.event); } } @@ -9460,18 +10034,18 @@ _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) } } -_SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { +_SOKOL_PRIVATE void _sapp_x11_mouse_event(_sapp_window_t* window, sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, type); _sapp.event.mouse_button = btn; _sapp.event.modifiers = mods; _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { +_SOKOL_PRIVATE void _sapp_x11_scroll_event(_sapp_window_t* window, float x, float y, uint32_t mods) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp_init_event(window, SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = mods; _sapp.event.scroll_x = x; _sapp.event.scroll_y = y; @@ -9479,28 +10053,28 @@ _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { } } -_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { +_SOKOL_PRIVATE void _sapp_x11_key_event(_sapp_window_t* window, sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { if (_sapp_events_enabled()) { - _sapp_init_event(type); + _sapp_init_event(window, 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 && + if (window->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_init_event(window, SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); } } } -_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { +_SOKOL_PRIVATE void _sapp_x11_char_event(_sapp_window_t* window, uint32_t chr, bool repeat, uint32_t mods) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp_init_event(window, SAPP_EVENTTYPE_CHAR); _sapp.event.char_code = chr; _sapp.event.key_repeat = repeat; _sapp.event.modifiers = mods; @@ -9680,12 +10254,12 @@ _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { return -1; } -_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { +_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(_sapp_window_t* window, const char* src) { SOKOL_ASSERT(src); - SOKOL_ASSERT(_sapp.drop.buffer); + SOKOL_ASSERT(window->drop.buffer); - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; + _sapp_clear_drop_buffer(window); + window->drop.num_files = 0; /* src is (potentially percent-encoded) string made of one or multiple paths @@ -9694,8 +10268,8 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { bool err = false; int src_count = 0; char src_chr = 0; - char* dst_ptr = _sapp.drop.buffer; - const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 + char* dst_ptr = window->drop.buffer; + const char* dst_end_ptr = dst_ptr + (window->drop.max_path_length - 1); // room for terminating 0 while (0 != (src_chr = *src++)) { src_count++; char dst_chr = 0; @@ -9720,13 +10294,13 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { else if (src_chr == '\n') { src_chr = 0; src_count = 0; - _sapp.drop.num_files++; + window->drop.num_files++; // too many files is not an error - if (_sapp.drop.num_files >= _sapp.drop.max_files) { + if (window->drop.num_files >= window->drop.max_files) { break; } - dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; - dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); + dst_ptr = window->drop.buffer + window->drop.num_files * window->drop.max_path_length; + dst_end_ptr = dst_ptr + (window->drop.max_path_length - 1); } else if ((src_chr == '%') && src[0] && src[1]) { // a percent-encoded byte (most like UTF-8 multibyte sequence) @@ -9750,8 +10324,8 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { } } if (err) { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; + _sapp_clear_drop_buffer(window); + window->drop.num_files = 0; return false; } else { @@ -9763,11 +10337,21 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { // 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) { +_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { + _sapp_window_t* window = NULL; + if (XFindContext(_sapp.x11.display, + event->xany.window, + _sapp.x11.context, + (XPointer*) &window) != 0) + { + /* This is an event for a window that has already been destroyed */ + return; + } + Bool filtered = XFilterEvent(event, None); switch (event->type) { case GenericEvent: - if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (window->mouse.locked && _sapp.x11.xi.available) { if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { if (XGetEventData(_sapp.x11.display, &event->xcookie)) { if (event->xcookie.evtype == XI_RawMotion) { @@ -9775,13 +10359,13 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { if (re->valuators.mask_len) { const double* values = re->raw_values; if (XIMaskIsSet(re->valuators.mask, 0)) { - _sapp.mouse.dx = (float) *values; + window->mouse.dx = (float) *values; values++; } if (XIMaskIsSet(re->valuators.mask, 1)) { - _sapp.mouse.dy = (float) *values; + window->mouse.dy = (float) *values; } - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + _sapp_x11_mouse_event(window, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); } } XFreeEventData(_sapp.x11.display, &event->xcookie); @@ -9791,8 +10375,8 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case FocusOut: /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ - if (_sapp.mouse.locked) { - _sapp_x11_lock_mouse(false); + if (window->mouse.locked) { + _sapp_x11_lock_mouse(window, false); } break; case KeyPress: @@ -9803,13 +10387,13 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _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); + _sapp_x11_key_event(window, 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); + _sapp_x11_char_event(window, (uint32_t)chr, repeat, mods); } } break; @@ -9820,7 +10404,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _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); + _sapp_x11_key_event(window, SAPP_EVENTTYPE_KEY_UP, key, false, mods); } } break; @@ -9829,16 +10413,16 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { 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); + _sapp_x11_mouse_event(window, SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); _sapp.x11.mouse_buttons |= (1 << btn); } 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; + case 4: _sapp_x11_scroll_event(window, 0.0f, 1.0f, mods); break; + case 5: _sapp_x11_scroll_event(window, 0.0f, -1.0f, mods); break; + case 6: _sapp_x11_scroll_event(window, 1.0f, 0.0f, mods); break; + case 7: _sapp_x11_scroll_event(window, -1.0f, 0.0f, mods); break; } } } @@ -9847,7 +10431,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { { 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)); + _sapp_x11_mouse_event(window, SAPP_EVENTTYPE_MOUSE_UP, btn, _sapp_x11_mod(event->xbutton.state)); _sapp.x11.mouse_buttons &= ~(1 << btn); } } @@ -9855,48 +10439,48 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { case EnterNotify: /* don't send enter/leave events while mouse button held down */ if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); + _sapp_x11_mouse_event(window, SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); } break; case LeaveNotify: if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); + _sapp_x11_mouse_event(window, SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); } break; case MotionNotify: - if (!_sapp.mouse.locked) { + if (!window->mouse.locked) { const float new_x = (float) event->xmotion.x; const float new_y = (float) event->xmotion.y; - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; + if (window->mouse.pos_valid) { + window->mouse.dx = new_x - window->mouse.x; + window->mouse.dy = new_y - window->mouse.y; } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + window->mouse.x = new_x; + window->mouse.y = new_y; + window->mouse.pos_valid = true; + _sapp_x11_mouse_event(window, 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); + if ((event->xconfigure.width != window->window_width) || (event->xconfigure.height != window->window_height)) { + window->window_width = event->xconfigure.width; + window->window_height = event->xconfigure.height; + window->framebuffer_width = window->window_width; + window->framebuffer_height = window->window_height; + _sapp_x11_app_event(window, 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; + const int state = _sapp_x11_get_window_state(window); + if (state != window->x11.window_state) { + window->x11.window_state = state; if (state == IconicState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_x11_app_event(window, SAPP_EVENTTYPE_ICONIFIED); } else if (state == NormalState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_x11_app_event(window, SAPP_EVENTTYPE_RESTORED); } } } @@ -9907,9 +10491,16 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { return; } 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; + /* Only quit app if main window is being closed */ + if(window == _sapp.main_window){ + const Atom protocol = event->xclient.data.l[0]; + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { + _sapp.quit_requested = true; + } + } + else { + _sapp_x11_app_event(window, SAPP_EVENTTYPE_WINDOW_CLOSED); + sapp_destroy_window((sapp_window){ .id = window->id }); } } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { @@ -9952,17 +10543,17 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _sapp.x11.xdnd.XdndSelection, _sapp.x11.xdnd.format, _sapp.x11.xdnd.XdndSelection, - _sapp.x11.window, + window->x11.window, time); } else if (_sapp.x11.xdnd.version >= 2) { XEvent reply; memset(&reply, 0, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = window->x11.window; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; - reply.xclient.data.l[0] = _sapp.x11.window; + reply.xclient.data.l[0] = window->x11.window; reply.xclient.data.l[1] = 0; // drag was rejected reply.xclient.data.l[2] = None; XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); @@ -9983,7 +10574,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; reply.xclient.format = 32; - reply.xclient.data.l[0] = _sapp.x11.window; + reply.xclient.data.l[0] = window->x11.window; if (_sapp.x11.xdnd.format) { /* reply that we are ready to copy the dragged data */ reply.xclient.data.l[1] = 1; // accept with no rectangle @@ -10002,10 +10593,10 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { event->xselection.property, event->xselection.target, (unsigned char**) &data); - if (_sapp.drop.enabled && result) { - if (_sapp_x11_parse_dropped_files_list(data)) { + if (window->drop.enabled && result) { + if (_sapp_x11_parse_dropped_files_list(window, data)) { if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_init_event(window, SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } } @@ -10014,10 +10605,10 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { XEvent reply; memset(&reply, 0, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = window->x11.window; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; - reply.xclient.data.l[0] = _sapp.x11.window; + reply.xclient.data.l[0] = window->x11.window; reply.xclient.data.l[1] = result; reply.xclient.data.l[2] = _sapp.x11.xdnd.XdndActionCopy; XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); @@ -10032,7 +10623,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_init_state(desc); - _sapp.x11.window_state = NormalState; + _sapp_setup_pools(); XInitThreads(); XrmInitialize(); @@ -10048,21 +10639,14 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_x11_init_extensions(); _sapp_x11_create_hidden_cursor(); _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_window main_window = sapp_create_window(&_sapp.desc.window); + _sapp.main_window = _sapp_lookup_window(main_window.id); + SOKOL_ASSERT(_sapp.main_window); + _sapp.valid = true; - _sapp_x11_show_window(); - if (_sapp.fullscreen) { - _sapp_x11_set_fullscreen(true); - } - _sapp_x11_query_window_size(); - _sapp_glx_swapinterval(_sapp.swap_interval); - XFlush(_sapp.x11.display); while (!_sapp.quit_ordered) { - _sapp_glx_make_current(); + _sapp_glx_make_current(_sapp.main_window); int count = XPending(_sapp.x11.display); while (count--) { XEvent event; @@ -10070,12 +10654,21 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_x11_process_event(&event); } _sapp_frame(); - _sapp_glx_swap_buffers(); + + // DISCUSS: Not ideal, since we're checking for valid ids, but probably still better than keeping an additional linked list? + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + _sapp_window_t* window = &_sapp.windows[i]; + + _sapp_glx_swap_buffers(window); + } + } + 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); + _sapp_x11_app_event(_sapp.main_window, SAPP_EVENTTYPE_QUIT_REQUESTED); /* if user code hasn't intervened, quit the app */ if (_sapp.quit_requested) { _sapp.quit_ordered = true; @@ -10083,8 +10676,13 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { } } _sapp_call_cleanup(); - _sapp_glx_destroy_context(); - _sapp_x11_destroy_window(); + for (int i = 1; i < _sapp.window_pool.size; i++) { + if(_sapp.windows[i].id != SAPP_INVALID_ID){ + // TODO: Is there a better way to handle this instead of creating a tmp object? + sapp_destroy_window(_sapp_window(_sapp.windows[i].id)); + } + } + XCloseDisplay(_sapp.x11.display); _sapp_discard_state(); } @@ -10137,10 +10735,112 @@ SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) { } #endif +SOKOL_APP_IMPL sapp_window sapp_main_window(){ + return _sapp_window(_sapp.main_window->id); +} + +SOKOL_API_IMPL sapp_window sapp_create_window(const sapp_window_desc* desc){ + SOKOL_ASSERT(desc); + sapp_window_desc desc_def = _sapp_window_desc_defaults(desc); + sapp_window window_id = _sapp_alloc_window(); + if(window_id.id != SAPP_INVALID_ID){ + _sapp_window_t* window = _sapp_init_window(window_id, &desc_def); + + #if defined(_SAPP_WIN32) + _sapp_win32_uwp_utf8_to_wide(window->window_title, window->window_title_wide, sizeof(window->window_title_wide)); + _sapp_win32_create_window(window, &desc_def); + + #if defined(SOKOL_D3D11) + _sapp_d3d11_create_swapchain(window); + _sapp_d3d11_create_default_render_target(window); + #elif defined(SOKOL_GLCORE33) + _sapp_wgl_create_context(window); + #endif + #elif defined(_SAPP_UWP) + _sapp_win32_uwp_utf8_to_wide(window->window_title, window->window_title_wide, sizeof(window->window_title_wide)); + #elif defined(_SAPP_LINUX) + Visual* visual = 0; + int depth = 0; + _sapp_glx_choose_visual(window, &visual, &depth); + _sapp_x11_create_window(window, visual, depth); + _sapp_glx_create_context(window); + #elif defined(_SAPP_MACOS) + _sapp_macos_create_window(window, &desc_def); + #else + _SOKOL_UNUSED(window); + #endif + } + else { + _sapp_fail("windows pool exhausted!"); + } + return window_id; +} + +SOKOL_API_IMPL void sapp_destroy_window(sapp_window window_id){ + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + // Destroy context + #if defined(_SAPP_WIN32) + #if defined(SOKOL_D3D11) + _sapp_d3d11_destroy_default_render_target(window); + _sapp_d3d11_destroy_swapchain(window); + #elif defined(SOKOL_GLCORE33) + _sapp.wgl.MakeCurrent(window->win32.dc, window->wgl.gl_ctx); + _sapp_wgl_destroy_context(window); + #endif + + _sapp_win32_destroy_window(window); + #elif defined(_SAPP_LINUX) + _sapp_glx_destroy_context(window); + _sapp_x11_destroy_window(window); + #elif defined(_SAPP_MACOS) + _sapp_macos_destroy_window(window); + #endif + + if (window->clipboard.enabled) { + SOKOL_ASSERT(window->clipboard.buffer); + SOKOL_FREE((void*)window->clipboard.buffer); + } + if (window->drop.enabled) { + SOKOL_ASSERT(window->drop.buffer); + SOKOL_FREE((void*)window->drop.buffer); + } + + // Add back into pool and mark as invalid resource + int slot_index = _sapp_slot_index(window_id.id); + _sapp_reset_window(window); + _sapp_pool_free_index(&_sapp.window_pool, slot_index); +} + +SOKOL_API_IMPL void sapp_gl_make_context_current(sapp_window window_id){ + #if defined(SOKOL_GLCORE33) + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + #if defined(_SAPP_WIN32) + _sapp.wgl.MakeCurrent(window->win32.dc, window->wgl.gl_ctx); + #elif defined(_SAPP_LINUX) + _sapp_glx_make_current(window); + #elif defined(_SAPP_MACOS) + [window->macos.gl_ctx makeCurrentContext]; + #endif + #else + _SOKOL_UNUSED(window_id); + #endif +} + SOKOL_API_IMPL bool sapp_isvalid(void) { return _sapp.valid; } +SOKOL_API_IMPL bool sapp_window_isvalid(sapp_window window) { + bool is_valid = !(window.id == SAPP_INVALID_ID) && _sapp_lookup_window(window.id); + return is_valid; +} + SOKOL_API_IMPL void* sapp_userdata(void) { return _sapp.desc.user_data; } @@ -10153,8 +10853,15 @@ 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_width() { + return sapp_window_width(sapp_main_window()); +} + +SOKOL_API_IMPL int sapp_window_width(sapp_window window_id) { + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + return (window->framebuffer_width > 0) ? window->framebuffer_width : 1; } SOKOL_API_IMPL int sapp_color_format(void) { @@ -10180,15 +10887,30 @@ SOKOL_API_IMPL int sapp_depth_format(void) { } SOKOL_API_IMPL int sapp_sample_count(void) { - return _sapp.sample_count; + return sapp_window_sample_count(sapp_main_window()); +} + +SOKOL_API_IMPL int sapp_window_sample_count(sapp_window window_id) { + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + return window->sample_count; } -SOKOL_API_IMPL int sapp_height(void) { - return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; +SOKOL_API_IMPL int sapp_height() { + return sapp_window_height(sapp_main_window()); +} + +SOKOL_API_IMPL int sapp_window_height(sapp_window window_id) { + SOKOL_ASSERT(window_id.id != SAPP_INVALID_ID); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + return (window->framebuffer_height > 0) ? window->framebuffer_height : 1; } SOKOL_API_IMPL bool sapp_high_dpi(void) { - return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); + // TODO: currently uses main window high_dpi flag + return _sapp.desc.window.high_dpi && (_sapp.dpi_scale >= 1.5f); } SOKOL_API_IMPL float sapp_dpi_scale(void) { @@ -10215,58 +10937,104 @@ SOKOL_API_IMPL bool sapp_keyboard_shown(void) { return _sapp.onscreen_keyboard_shown; } -SOKOL_APP_API_DECL bool sapp_is_fullscreen(void) { - return _sapp.fullscreen; +SOKOL_API_IMPL bool sapp_is_fullscreen() { + return sapp_window_is_fullscreen(_sapp_window(_sapp.main_window->id)); +} + +SOKOL_API_IMPL bool sapp_window_is_fullscreen(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + return window->fullscreen; +} + +SOKOL_API_IMPL void sapp_toggle_fullscreen() { + sapp_window_toggle_fullscreen(_sapp_window(_sapp.main_window->id)); } -SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void) { +SOKOL_API_IMPL void sapp_window_toggle_fullscreen(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(_SAPP_MACOS) - _sapp_macos_toggle_fullscreen(); + _sapp_macos_toggle_fullscreen(window); #elif defined(_SAPP_WIN32) - _sapp_win32_toggle_fullscreen(); + _sapp_win32_toggle_fullscreen(window); #elif defined(_SAPP_UWP) _sapp_uwp_toggle_fullscreen(); #elif defined(_SAPP_LINUX) - _sapp_x11_toggle_fullscreen(); + _sapp_x11_toggle_fullscreen(window); + #else + _SOKOL_UNUSED(window); #endif } -/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ -SOKOL_API_IMPL void sapp_show_mouse(bool show) { - if (_sapp.mouse.shown != show) { +SOKOL_API_IMPL void sapp_window_show_mouse(sapp_window window_id, bool show) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + if (window->mouse.shown != show) { #if defined(_SAPP_MACOS) _sapp_macos_show_mouse(show); #elif defined(_SAPP_WIN32) _sapp_win32_show_mouse(show); #elif defined(_SAPP_LINUX) - _sapp_x11_show_mouse(show); + _sapp_x11_show_mouse(window, show); #elif defined(_SAPP_UWP) _sapp_uwp_show_mouse(show); #endif - _sapp.mouse.shown = show; + window->mouse.shown = show; } } +/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ +SOKOL_API_IMPL void sapp_show_mouse(bool show) { + sapp_window_show_mouse(_sapp_window(_sapp.main_window->id), show); +} + +SOKOL_API_IMPL bool sapp_window_mouse_shown(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + return window->mouse.shown; +} + SOKOL_API_IMPL bool sapp_mouse_shown(void) { - return _sapp.mouse.shown; + return sapp_window_mouse_shown(_sapp_window(_sapp.main_window->id)); } SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { + sapp_window_lock_mouse(_sapp_window(_sapp.main_window->id), lock); +} + +SOKOL_API_IMPL void sapp_window_lock_mouse(sapp_window window_id, bool lock) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(_SAPP_MACOS) - _sapp_macos_lock_mouse(lock); + _sapp_macos_lock_mouse(window, lock); #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_lock_mouse(lock); + _SOKOL_UNUSED(window); #elif defined(_SAPP_WIN32) - _sapp_win32_lock_mouse(lock); + // TODO: window specific lock + _sapp_win32_lock_mouse(_sapp.main_window, lock); #elif defined(_SAPP_LINUX) - _sapp_x11_lock_mouse(lock); + _sapp_x11_lock_mouse(window, lock); #else - _sapp.mouse.locked = lock; + window->mouse.locked = lock; #endif } SOKOL_API_IMPL bool sapp_mouse_locked(void) { - return _sapp.mouse.locked; + return sapp_window_mouse_locked(_sapp_window(_sapp.main_window->id)); +} + +SOKOL_API_IMPL bool sapp_window_mouse_locked(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + return window->mouse.locked; } SOKOL_API_IMPL void sapp_request_quit(void) { @@ -10286,9 +11054,11 @@ SOKOL_API_IMPL void sapp_consume_event(void) { } /* 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) { - SOKOL_ASSERT(_sapp.clipboard.enabled); - if (!_sapp.clipboard.enabled) { +SOKOL_API_IMPL void sapp_window_set_clipboard_string(sapp_window window_id, const char* str) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + SOKOL_ASSERT(window->clipboard.enabled); + if (!window->clipboard.enabled) { return; } SOKOL_ASSERT(str); @@ -10297,65 +11067,94 @@ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_set_clipboard_string(str); #elif defined(_SAPP_WIN32) - _sapp_win32_set_clipboard_string(str); + _sapp_win32_set_clipboard_string(window, str); #else /* not implemented */ #endif - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy(str, window->clipboard.buffer, window->clipboard.buf_size); } -SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.enabled); - if (!_sapp.clipboard.enabled) { +SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { + sapp_window_set_clipboard_string(sapp_main_window(), str); +} + +SOKOL_API_IMPL const char* sapp_window_get_clipboard_string(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + SOKOL_ASSERT(window->clipboard.enabled); + if (!window->clipboard.enabled) { return ""; } #if defined(_SAPP_MACOS) - return _sapp_macos_get_clipboard_string(); + return _sapp_macos_get_clipboard_string(window); #elif defined(_SAPP_EMSCRIPTEN) - return _sapp.clipboard.buffer; + return _sapp.main_window->clipboard.buffer; #elif defined(_SAPP_WIN32) - return _sapp_win32_get_clipboard_string(); + return _sapp_win32_get_clipboard_string(window); #else /* not implemented */ - return _sapp.clipboard.buffer; + return _sapp.main_window->clipboard.buffer; #endif } +SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { + return sapp_window_get_clipboard_string(sapp_main_window()); +} + SOKOL_API_IMPL void sapp_set_window_title(const char* title) { + sapp_window_set_window_title(sapp_main_window(), title); +} + +SOKOL_API_IMPL void sapp_window_set_window_title(sapp_window window_id, const char* title) { SOKOL_ASSERT(title); - _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + + _sapp_strcpy(title, window->window_title, sizeof(window->window_title)); #if defined(_SAPP_MACOS) - _sapp_macos_update_window_title(); + _sapp_macos_update_window_title(window); #elif defined(_SAPP_WIN32) - _sapp_win32_update_window_title(); + _sapp_win32_update_window_title(window); #elif defined(_SAPP_LINUX) - _sapp_x11_update_window_title(); + _sapp_x11_update_window_title(window); #endif } -SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { - SOKOL_ASSERT(_sapp.drop.enabled); - return _sapp.drop.num_files; +SOKOL_API_IMPL int sapp_window_get_num_dropped_files(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + SOKOL_ASSERT(window->drop.enabled); + return window->drop.num_files; +} + +SOKOL_API_IMPL int sapp_get_num_dropped_files() { + return sapp_window_get_num_dropped_files(_sapp_window(_sapp.main_window->id)); } SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { - SOKOL_ASSERT(_sapp.drop.enabled); - SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); - SOKOL_ASSERT(_sapp.drop.buffer); - if (!_sapp.drop.enabled) { + return sapp_window_get_dropped_file_path(_sapp_window(_sapp.main_window->id), index); +} + +SOKOL_API_IMPL const char* sapp_window_get_dropped_file_path(sapp_window window_id, int index) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + SOKOL_ASSERT(window->drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < window->drop.num_files)); + SOKOL_ASSERT(window->drop.buffer); + if (!window->drop.enabled) { return ""; } - if ((index < 0) || (index >= _sapp.drop.max_files)) { + if ((index < 0) || (index >= window->drop.max_files)) { return ""; } - return (const char*) _sapp_dropped_file_path_ptr(index); + return (const char*) _sapp_dropped_file_path_ptr(window, index); } SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { - SOKOL_ASSERT(_sapp.drop.enabled); - SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + SOKOL_ASSERT(_sapp.main_window->drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.main_window->drop.num_files)); #if defined(_SAPP_EMSCRIPTEN) - if (!_sapp.drop.enabled) { + if (!_sapp.main_window->drop.enabled) { return 0; } return sapp_js_dropped_file_size(index); @@ -10366,7 +11165,7 @@ SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { } SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { - SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT(_sapp.main_window->drop.enabled); SOKOL_ASSERT(request); SOKOL_ASSERT(request->callback); SOKOL_ASSERT(request->buffer_ptr); @@ -10374,7 +11173,7 @@ SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request #if defined(_SAPP_EMSCRIPTEN) const int index = request->dropped_file_index; sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; - if ((index < 0) || (index >= _sapp.drop.num_files)) { + if ((index < 0) || (index >= _sapp.main_window->drop.num_files)) { error_code = SAPP_HTML5_FETCH_ERROR_OTHER; } if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) { @@ -10418,38 +11217,61 @@ SOKOL_API_IMPL const void* sapp_metal_get_device(void) { } SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { + return sapp_metal_window_get_renderpass_descriptor(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_metal_window_get_renderpass_descriptor(sapp_window window_id) { SOKOL_ASSERT(_sapp.valid); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(SOKOL_METAL) #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) [_sapp.macos.view currentRenderPassDescriptor]; + const void* obj = (__bridge const void*) [window->macos.view currentRenderPassDescriptor]; #else const void* obj = (__bridge const void*) [_sapp.ios.view currentRenderPassDescriptor]; #endif SOKOL_ASSERT(obj); return obj; #else + _SOKOL_UNUSED(window); return 0; #endif } SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { + return sapp_metal_window_get_drawable(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_metal_window_get_drawable(sapp_window window_id) { SOKOL_ASSERT(_sapp.valid); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(SOKOL_METAL) #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; + const void* obj = (__bridge const void*) [window->macos.view currentDrawable]; #else const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; #endif SOKOL_ASSERT(obj); return obj; #else + _SOKOL_UNUSED(window); return 0; #endif } SOKOL_API_IMPL const void* sapp_macos_get_window(void) { + return sapp_macos_window_get_window(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_macos_window_get_window(sapp_window window_id) { + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) _sapp.macos.window; + const void* obj = (__bridge const void*) window->macos.window; SOKOL_ASSERT(obj); return obj; #else @@ -10485,34 +11307,57 @@ SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { #endif } -SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { +SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view() { + return sapp_d3d11_window_get_render_target_view(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_d3d11_window_get_render_target_view(sapp_window window_id) { SOKOL_ASSERT(_sapp.valid); + + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(SOKOL_D3D11) - if (_sapp.d3d11.msaa_rtv) { - return _sapp.d3d11.msaa_rtv; + if (window->d3d11.msaa_rtv) { + return window->d3d11.msaa_rtv; } else { - return _sapp.d3d11.rtv; + return window->d3d11.rtv; } #else return 0; #endif } -SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { +SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view() { + return sapp_d3d11_window_get_depth_stencil_view(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_d3d11_window_get_depth_stencil_view(sapp_window window_id) { SOKOL_ASSERT(_sapp.valid); + + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); + #if defined(SOKOL_D3D11) - return _sapp.d3d11.dsv; + return window->d3d11.dsv; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { + return sapp_win32_window_get_hwnd(sapp_main_window()); +} + +SOKOL_API_IMPL const void* sapp_win32_window_get_hwnd(sapp_window window_id) { SOKOL_ASSERT(_sapp.valid); + _sapp_window_t* window = _sapp_lookup_window(window_id.id); + SOKOL_ASSERT(window); #if defined(_SAPP_WIN32) - return _sapp.win32.hwnd; + return window->win32.hwnd; #else + _SOKOL_UNUSED(window); return 0; #endif } @@ -10529,7 +11374,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - if (_sapp.sample_count > 1) { + if (_sapp.main_window->sample_count > 1) { return (const void*) _sapp.emsc.wgpu.msaa_view; } else { @@ -10543,7 +11388,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - if (_sapp.sample_count > 1) { + if (_sapp.main_window->sample_count > 1) { return (const void*) _sapp.emsc.wgpu.swapchain_view; } else { diff --git a/sokol_gfx.h b/sokol_gfx.h index 3173bb71d..58a9179f3 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -2155,6 +2155,8 @@ typedef struct sg_pass_info { */ typedef struct sg_gl_context_desc { bool force_gles2; + void (*context_make_current_userdata_cb)(void*); + void* user_data; } sg_gl_context_desc; typedef struct sg_mtl_context_desc { @@ -2213,7 +2215,7 @@ typedef struct sg_desc { } sg_desc; /* setup and misc functions */ -SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_GFX_API_DECL sg_context sg_setup(const sg_desc* desc); SOKOL_GFX_API_DECL void sg_shutdown(void); SOKOL_GFX_API_DECL bool sg_isvalid(void); SOKOL_GFX_API_DECL void sg_reset_state_cache(void); @@ -2292,7 +2294,7 @@ SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass_id); /* rendering contexts (optional) */ -SOKOL_GFX_API_DECL sg_context sg_setup_context(void); +SOKOL_GFX_API_DECL sg_context sg_setup_context(const sg_context_desc* desc); SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); @@ -2318,7 +2320,7 @@ SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); } /* extern "C" */ /* reference-based equivalents for c++ */ -inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } +inline sg_context sg_setup(const sg_desc& desc) { return sg_setup(&desc); } inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } @@ -3068,6 +3070,8 @@ typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; + void (*context_make_current_userdata_cb)(void*); + void* user_data; #if !defined(SOKOL_GLES2) GLuint vao; #endif @@ -3211,6 +3215,11 @@ typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; + const void* (*render_target_view_cb)(void); + const void* (*render_target_view_userdata_cb)(void*); + const void* (*depth_stencil_view_cb)(void); + const void* (*depth_stencil_view_userdata_cb)(void*); + void* user_data; } _sg_d3d11_context_t; typedef _sg_d3d11_context_t _sg_context_t; @@ -3344,6 +3353,11 @@ typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; + const void* (*renderpass_descriptor_cb)(void); + const void* (*renderpass_descriptor_userdata_cb)(void*); + const void* (*drawable_cb)(void); + const void* (*drawable_userdata_cb)(void*); + void* user_data; } _sg_mtl_context_t; typedef _sg_mtl_context_t _sg_context_t; @@ -3365,11 +3379,6 @@ typedef struct { typedef struct { bool valid; - const void*(*renderpass_descriptor_cb)(void); - const void*(*renderpass_descriptor_userdata_cb)(void*); - const void*(*drawable_cb)(void); - const void*(*drawable_userdata_cb)(void*); - void* user_data; uint32_t frame_index; uint32_t cur_frame_rotate_index; uint32_t ub_size; @@ -4089,9 +4098,10 @@ _SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { /* empty*/ } -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx, const sg_context_desc* desc) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5602,14 +5612,22 @@ _SOKOL_PRIVATE void _sg_gl_discard_backend(void) { _SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { SOKOL_ASSERT(_sg.gl.valid); /* NOTE: ctx can be 0 to unset the current context */ + if(ctx != NULL && ctx->context_make_current_userdata_cb) { + ctx->context_make_current_userdata_cb(ctx->user_data); + } + _sg.gl.cur_context = ctx; _sg_gl_reset_state_cache(); } /*-- GL backend resource creation and destruction ----------------------------*/ -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx, const sg_context_desc* desc) { + SOKOL_ASSERT(desc); SOKOL_ASSERT(ctx); SOKOL_ASSERT(0 == ctx->default_framebuffer); + ctx->context_make_current_userdata_cb = desc->gl.context_make_current_userdata_cb; + ctx->user_data = desc->gl.user_data; + ctx->context_make_current_userdata_cb(ctx->user_data); _SG_GL_CHECK_ERROR(); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); _SG_GL_CHECK_ERROR(); @@ -6319,6 +6337,8 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac glViewport(0, 0, w, h); glScissor(0, 0, w, h); + _SG_GL_CHECK_ERROR(); + /* clear color and depth-stencil attachments if needed */ bool clear_color = false; for (int i = 0; i < num_color_atts; i++) { @@ -6363,6 +6383,7 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac _sg.gl.cache.cur_pipeline = 0; _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; } + _SG_GL_CHECK_ERROR(); bool use_mrt_clear = (0 != pass); #if defined(SOKOL_GLES2) use_mrt_clear = false; @@ -6396,6 +6417,7 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac } #if !defined SOKOL_GLES2 else { + _SG_GL_CHECK_ERROR(); SOKOL_ASSERT(pass); for (int i = 0; i < num_color_atts; i++) { if (action->colors[i].action == SG_ACTION_CLEAR) { @@ -7649,9 +7671,14 @@ _SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { _sg_d3d11_clear_state(); } -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx, const sg_context_desc* desc) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); + ctx->render_target_view_cb = desc->d3d11.render_target_view_cb; + ctx->render_target_view_userdata_cb = desc->d3d11.render_target_view_userdata_cb; + ctx->depth_stencil_view_cb = desc->d3d11.depth_stencil_view_cb; + ctx->depth_stencil_view_userdata_cb = desc->d3d11.depth_stencil_view_userdata_cb; + ctx->user_data = desc->d3d11.user_data; return SG_RESOURCESTATE_VALID; } @@ -8398,11 +8425,15 @@ _SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { return pass->d3d11.ds_att.image; } +// DISCUSS: Had to to forward declare this method here :/ +_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id); _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.d3d11.in_pass); - SOKOL_ASSERT(_sg.d3d11.rtv_cb || _sg.d3d11.rtv_userdata_cb); - SOKOL_ASSERT(_sg.d3d11.dsv_cb || _sg.d3d11.dsv_userdata_cb); + _sg_context_t* cur_context = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + SOKOL_ASSERT(cur_context); + SOKOL_ASSERT(cur_context->render_target_view_cb || cur_context->render_target_view_userdata_cb); + SOKOL_ASSERT(cur_context->depth_stencil_view_cb || cur_context->depth_stencil_view_userdata_cb); _sg.d3d11.in_pass = true; _sg.d3d11.cur_width = w; _sg.d3d11.cur_height = h; @@ -8424,19 +8455,19 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.num_rtvs = 1; if (_sg.d3d11.rtv_cb) { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) cur_context->render_target_view_cb(); } else { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) cur_context->render_target_view_userdata_cb(cur_context->user_data); } for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.d3d11.cur_rtvs[i] = 0; } if (_sg.d3d11.dsv_cb) { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) cur_context->depth_stencil_view_cb(); } else { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) cur_context->depth_stencil_view_userdata_cb(cur_context->user_data); } SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); } @@ -9404,11 +9435,6 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { _sg_mtl_init_sampler_cache(desc); _sg_mtl_clear_state_cache(); _sg.mtl.valid = true; - _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; - _sg.mtl.renderpass_descriptor_userdata_cb = desc->context.metal.renderpass_descriptor_userdata_cb; - _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; - _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; - _sg.mtl.user_data = desc->context.metal.user_data; _sg.mtl.frame_index = 1; _sg.mtl.ub_size = desc->uniform_buffer_size; _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); @@ -9478,9 +9504,15 @@ _SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { } } -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx, const sg_context_desc* desc) { SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); + + ctx->drawable_cb = desc->metal.drawable_cb; + ctx->drawable_userdata_cb = desc->metal.drawable_userdata_cb; + ctx->renderpass_descriptor_cb = desc->metal.renderpass_descriptor_cb; + ctx->renderpass_descriptor_userdata_cb = desc->metal.renderpass_descriptor_userdata_cb; + ctx->user_data = desc->metal.user_data; + return SG_RESOURCESTATE_VALID; } @@ -9994,12 +10026,18 @@ _SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { return pass->mtl.ds_att.image; } +// DISCUSS: Had to to forward declare this method here :/ +_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id); _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.mtl.in_pass); SOKOL_ASSERT(_sg.mtl.cmd_queue); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); - SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb || _sg.mtl.renderpass_descriptor_userdata_cb); + + _sg_context_t* cur_context = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + SOKOL_ASSERT(cur_context); + SOKOL_ASSERT(cur_context->renderpass_descriptor_cb || cur_context->renderpass_descriptor_userdata_cb); + _sg.mtl.in_pass = true; _sg.mtl.cur_width = w; _sg.mtl.cur_height = h; @@ -10025,11 +10063,11 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a } else { /* default render pass, call user-provided callback to provide render pass descriptor */ - if (_sg.mtl.renderpass_descriptor_cb) { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); + if (cur_context->renderpass_descriptor_cb) { + pass_desc = (__bridge MTLRenderPassDescriptor*) cur_context->renderpass_descriptor_cb(); } else { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); + pass_desc = (__bridge MTLRenderPassDescriptor*) cur_context->renderpass_descriptor_userdata_cb(cur_context->user_data); } } @@ -10140,21 +10178,24 @@ _SOKOL_PRIVATE void _sg_mtl_end_pass(void) { _SOKOL_PRIVATE void _sg_mtl_commit(void) { SOKOL_ASSERT(!_sg.mtl.in_pass); SOKOL_ASSERT(!_sg.mtl.pass_valid); - SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + _sg_context_t* cur_context = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + SOKOL_ASSERT(cur_context); + SOKOL_ASSERT(cur_context->drawable_cb || cur_context->drawable_userdata_cb); + #if defined(_SG_TARGET_MACOS) [_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; #endif /* present, commit and signal semaphore when done */ id cur_drawable = nil; - if (_sg.mtl.drawable_cb) { - cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); + if (cur_context->drawable_cb) { + cur_drawable = (__bridge id) cur_context->drawable_cb(); } else { - cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); + cur_drawable = (__bridge id) cur_context->drawable_userdata_cb(cur_context->user_data); } [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { @@ -12200,17 +12241,17 @@ static inline void _sg_activate_context(_sg_context_t* ctx) { #endif } -static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { +static inline sg_resource_state _sg_create_context(_sg_context_t* ctx, const sg_context_desc* desc) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_context(ctx); + return _sg_gl_create_context(ctx, desc); #elif defined(SOKOL_METAL) - return _sg_mtl_create_context(ctx); + return _sg_mtl_create_context(ctx, desc); #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_context(ctx); + return _sg_d3d11_create_context(ctx, desc); #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_context(ctx); + return _sg_wgpu_create_context(ctx, desc); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_context(ctx); + return _sg_dummy_create_context(ctx, desc); #else #error("INVALID BACKEND"); #endif @@ -14000,7 +14041,7 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { #define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } #endif -SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { +SOKOL_API_IMPL sg_context sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); _SG_CLEAR(_sg_state_t, _sg); @@ -14033,7 +14074,7 @@ SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { _sg.frame_index = 1; _sg_setup_backend(&_sg.desc); _sg.valid = true; - sg_setup_context(); + return sg_setup_context(&_sg.desc.context); } SOKOL_API_IMPL void sg_shutdown(void) { @@ -14084,14 +14125,14 @@ SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { return _sg.formats[fmt_index]; } -SOKOL_API_IMPL sg_context sg_setup_context(void) { +SOKOL_API_IMPL sg_context sg_setup_context(const sg_context_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_context res; int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); - ctx->slot.state = _sg_create_context(ctx); + ctx->slot.state = _sg_create_context(ctx, desc); SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); _sg_activate_context(ctx); } diff --git a/sokol_glue.h b/sokol_glue.h index 74ecb7c32..871503895 100644 --- a/sokol_glue.h +++ b/sokol_glue.h @@ -95,6 +95,7 @@ extern "C" { #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void); +SOKOL_GLUE_API_DECL sg_context_desc sapp_window_sgcontext(sapp_window window_id); #endif #ifdef __cplusplus @@ -112,26 +113,67 @@ SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void); #endif #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) -SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { + +// DISCUSS: Not a huge fan of doing the handle->void* casts here +_SOKOL_PRIVATE void _sapp_gl_make_context_current_cb(void* user_data) { + sapp_window window_id; + window_id.id = (uint32_t)(uintptr_t)user_data; + sapp_gl_make_context_current(window_id); +} + +_SOKOL_PRIVATE const void* _sapp_metal_window_get_renderpass_descriptor_cb(void* user_data) { + sapp_window window_id; + window_id.id = (uint32_t)(uintptr_t)user_data; + return sapp_metal_window_get_renderpass_descriptor(window_id); +} + +_SOKOL_PRIVATE const void* _sapp_metal_window_get_drawable_cb(void* user_data) { + sapp_window window_id; + window_id.id = (uint32_t)(uintptr_t)user_data; + return sapp_metal_window_get_drawable(window_id); +} + +_SOKOL_PRIVATE const void* _sapp_d3d11_window_get_render_target_view_cb(void* user_data) { + sapp_window window_id; + window_id.id = (uint32_t)(uintptr_t)user_data; + return sapp_d3d11_window_get_render_target_view(window_id); +} + +_SOKOL_PRIVATE const void* _sapp_d3d11_window_get_depth_stencil_cb(void* user_data) { + sapp_window window_id; + window_id.id = (uint32_t)(uintptr_t)user_data; + return sapp_d3d11_window_get_depth_stencil_view(window_id); +} + +SOKOL_API_IMPL sg_context_desc sapp_window_sgcontext(sapp_window window_id) { sg_context_desc desc; memset(&desc, 0, sizeof(desc)); desc.color_format = (sg_pixel_format) sapp_color_format(); desc.depth_format = (sg_pixel_format) sapp_depth_format(); - desc.sample_count = sapp_sample_count(); + desc.sample_count = sapp_window_sample_count(window_id); desc.gl.force_gles2 = sapp_gles2(); + desc.gl.context_make_current_userdata_cb = _sapp_gl_make_context_current_cb; + desc.gl.user_data = (void*) (uintptr_t)window_id.id; desc.metal.device = sapp_metal_get_device(); - desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; - desc.metal.drawable_cb = sapp_metal_get_drawable; + desc.metal.renderpass_descriptor_userdata_cb = _sapp_metal_window_get_renderpass_descriptor_cb; + desc.metal.drawable_userdata_cb = _sapp_metal_window_get_drawable_cb; + desc.metal.user_data = (void*) (uintptr_t)window_id.id; desc.d3d11.device = sapp_d3d11_get_device(); desc.d3d11.device_context = sapp_d3d11_get_device_context(); - desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view; - desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view; + desc.d3d11.render_target_view_userdata_cb = _sapp_d3d11_window_get_render_target_view_cb; + desc.d3d11.depth_stencil_view_userdata_cb = _sapp_d3d11_window_get_depth_stencil_cb; + desc.d3d11.user_data = (void*) (uintptr_t)window_id.id; desc.wgpu.device = sapp_wgpu_get_device(); desc.wgpu.render_view_cb = sapp_wgpu_get_render_view; desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view; desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view; return desc; } + +SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { + return sapp_window_sgcontext(sapp_main_window()); +} + #endif #endif /* SOKOL_GLUE_IMPL */