From d71ff7f5a05f4f08ab61bf6347e4a913339d2711 Mon Sep 17 00:00:00 2001 From: Ignacio Sanchez Gines <863613+drhelius@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:35:38 +0100 Subject: [PATCH] Associate native window to NFD parent window --- platforms/desktop-shared/application.cpp | 31 ++++---- platforms/desktop-shared/application.h | 1 + platforms/desktop-shared/gui.cpp | 98 +++++++++++++++++++++--- platforms/desktop-shared/nfd/nfd_sdl2.h | 76 ++++++++++++++++++ 4 files changed, 179 insertions(+), 27 deletions(-) create mode 100644 platforms/desktop-shared/nfd/nfd_sdl2.h diff --git a/platforms/desktop-shared/application.cpp b/platforms/desktop-shared/application.cpp index 20b1d998..fc6c74eb 100644 --- a/platforms/desktop-shared/application.cpp +++ b/platforms/desktop-shared/application.cpp @@ -29,7 +29,6 @@ #define APPLICATION_IMPORT #include "application.h" -static SDL_Window* sdl_window; static SDL_GLContext gl_context; static bool running = true; static bool paused_when_focus_lost = false; @@ -65,7 +64,7 @@ int application_init(const char* rom_file, const char* symbol_file) gui_init(); - ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context); + ImGui_ImplSDL2_InitForOpenGL(application_sdl_window, gl_context); renderer_init(); @@ -121,17 +120,17 @@ void application_trigger_quit(void) void application_trigger_fullscreen(bool fullscreen) { - SDL_SetWindowFullscreen(sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + SDL_SetWindowFullscreen(application_sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); } void application_trigger_fit_to_content(int width, int height) { - SDL_SetWindowSize(sdl_window, width, height); + SDL_SetWindowSize(application_sdl_window, width, height); } void application_update_title(char* title) { - SDL_SetWindowTitle(sdl_window, title); + SDL_SetWindowTitle(application_sdl_window, title); } static int sdl_init(void) @@ -158,12 +157,12 @@ static int sdl_init(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - sdl_window = SDL_CreateWindow(GEARBOY_TITLE " " GEARBOY_VERSION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config_emulator.window_width, config_emulator.window_height, window_flags); - gl_context = SDL_GL_CreateContext(sdl_window); - SDL_GL_MakeCurrent(sdl_window, gl_context); + application_sdl_window = SDL_CreateWindow(GEARBOY_TITLE " " GEARBOY_VERSION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config_emulator.window_width, config_emulator.window_height, window_flags); + gl_context = SDL_GL_CreateContext(application_sdl_window); + SDL_GL_MakeCurrent(application_sdl_window, gl_context); SDL_GL_SetSwapInterval(0); - SDL_SetWindowMinimumSize(sdl_window, 500, 300); + SDL_SetWindowMinimumSize(application_sdl_window, 500, 300); application_gamepad_mappings = SDL_GameControllerAddMappingsFromRW(SDL_RWFromFile("gamecontrollerdb.txt", "rb"), 1); @@ -196,8 +195,8 @@ static int sdl_init(void) int w, h; int display_w, display_h; - SDL_GetWindowSize(sdl_window, &w, &h); - SDL_GL_GetDrawableSize(sdl_window, &display_w, &display_h); + SDL_GetWindowSize(application_sdl_window, &w, &h); + SDL_GL_GetDrawableSize(application_sdl_window, &display_w, &display_h); if (w > 0 && h > 0) { @@ -216,7 +215,7 @@ static void sdl_destroy(void) { SDL_GameControllerClose(application_gamepad); SDL_GL_DeleteContext(gl_context); - SDL_DestroyWindow(sdl_window); + SDL_DestroyWindow(application_sdl_window); SDL_Quit(); } @@ -232,7 +231,7 @@ static void sdl_events(void) break; } - if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)) + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(application_sdl_window)) { running = false; break; @@ -257,7 +256,7 @@ static void sdl_events_emu(const SDL_Event* event) char* dropped_filedir = event->drop.file; gui_load_rom(dropped_filedir); SDL_free(dropped_filedir); - SDL_SetWindowInputFocus(sdl_window); + SDL_SetWindowInputFocus(application_sdl_window); break; } case SDL_WINDOWEVENT: @@ -533,7 +532,7 @@ static void render(void) renderer_render(); renderer_end_render(); - SDL_GL_SwapWindow(sdl_window); + SDL_GL_SwapWindow(application_sdl_window); } static void frame_throttle(void) @@ -575,7 +574,7 @@ static void save_window_size(void) if (!config_emulator.fullscreen) { int width, height; - SDL_GetWindowSize(sdl_window, &width, &height); + SDL_GetWindowSize(application_sdl_window, &width, &height); config_emulator.window_width = width; config_emulator.window_height = height; } diff --git a/platforms/desktop-shared/application.h b/platforms/desktop-shared/application.h index 238dad07..8e88b841 100644 --- a/platforms/desktop-shared/application.h +++ b/platforms/desktop-shared/application.h @@ -28,6 +28,7 @@ #define EXTERN extern #endif +EXTERN SDL_Window* application_sdl_window; EXTERN SDL_GameController* application_gamepad; EXTERN int application_gamepad_mappings; EXTERN float application_display_scale; diff --git a/platforms/desktop-shared/gui.cpp b/platforms/desktop-shared/gui.cpp index cbd64472..d3d96464 100644 --- a/platforms/desktop-shared/gui.cpp +++ b/platforms/desktop-shared/gui.cpp @@ -16,10 +16,12 @@ * along with this program. If not, see http://www.gnu.org/licenses/ * */ + #include #include "imgui/imgui.h" #include "imgui/fonts/RobotoMedium.h" #include "nfd/nfd.h" +#include "nfd/nfd_sdl2.h" #include "config.h" #include "emu.h" #include "../../src/gearboy.h" @@ -66,6 +68,7 @@ static void file_dialog_load_dmg_bootrom(void); static void file_dialog_load_gbc_bootrom(void); static void file_dialog_load_symbols(void); static void file_dialog_save_screenshot(void); +static void file_dialog_set_native_window(SDL_Window* window, nfdwindowhandle_t* native_window); static void keyboard_configuration_item(const char* text, SDL_Scancode* key); static void gamepad_configuration_item(const char* text, int* button); static void popup_modal_keyboard(void); @@ -1129,7 +1132,13 @@ static void file_dialog_open_rom(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[4] = { { "ROM Files", "gb,gbc,cgb,sgb,dmg,rom,zip" }, { "Game Boy", "gb,dmg" }, { "Game Boy Color", "gbc,cgb" }, { "Super Game Boy", "sgb" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 4, config_emulator.last_open_path.c_str()); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 4; + args.defaultPath = config_emulator.last_open_path.c_str(); + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { std::string path = outPath; @@ -1148,7 +1157,13 @@ static void file_dialog_load_ram(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "RAM Files", "sav" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, NULL); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { emu_load_ram(outPath, config_emulator.force_dmg, get_mbc(config_emulator.mbc), config_emulator.force_gba); @@ -1164,7 +1179,14 @@ static void file_dialog_save_ram(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "RAM Files", "sav" } }; - nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, NULL, NULL); + nfdsavedialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + args.defaultName = NULL; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_SaveDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { emu_save_ram(outPath); @@ -1180,7 +1202,13 @@ static void file_dialog_load_state(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "Save State Files", "state" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, NULL); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { std::string message("Loading state from "); @@ -1199,7 +1227,14 @@ static void file_dialog_save_state(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "Save State Files", "state" } }; - nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, NULL, NULL); + nfdsavedialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + args.defaultName = NULL; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_SaveDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { std::string message("Saving state to "); @@ -1217,7 +1252,11 @@ static void file_dialog_save_state(void) static void file_dialog_choose_save_file_path(void) { nfdchar_t *outPath; - nfdresult_t result = NFD_PickFolder(&outPath, savefiles_path); + nfdpickfolderu8args_t args = { }; + args.defaultPath = savefiles_path; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_PickFolderU8_With(&outPath, &args); if (result == NFD_OKAY) { strcpy(savefiles_path, outPath); @@ -1233,7 +1272,11 @@ static void file_dialog_choose_save_file_path(void) static void file_dialog_choose_savestate_path(void) { nfdchar_t *outPath; - nfdresult_t result = NFD_PickFolder(&outPath, savestates_path); + nfdpickfolderu8args_t args = { }; + args.defaultPath = savestates_path; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_PickFolderU8_With(&outPath, &args); if (result == NFD_OKAY) { strcpy(savestates_path, outPath); @@ -1250,7 +1293,13 @@ static void file_dialog_load_dmg_bootrom(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "BIOS Files", "bin,rom,bios,gb" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, NULL); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { strcpy(dmg_bootrom_path, outPath); @@ -1268,7 +1317,13 @@ static void file_dialog_load_gbc_bootrom(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "BIOS Files", "bin,rom,bios,gbc" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, NULL); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = config_emulator.last_open_path.c_str(); + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { strcpy(gbc_bootrom_path, outPath); @@ -1286,7 +1341,13 @@ static void file_dialog_load_symbols(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "Symbol Files", "sym" } }; - nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, NULL); + nfdopendialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = NULL; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { gui_debug_reset_symbols(); @@ -1303,7 +1364,14 @@ static void file_dialog_save_screenshot(void) { nfdchar_t *outPath; nfdfilteritem_t filterItem[1] = { { "PNG Files", "png" } }; - nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, NULL, NULL); + nfdsavedialogu8args_t args = { }; + args.filterList = filterItem; + args.filterCount = 1; + args.defaultPath = NULL; + args.defaultName = NULL; + file_dialog_set_native_window(application_sdl_window, &args.parentWindow); + + nfdresult_t result = NFD_SaveDialogU8_With(&outPath, &args); if (result == NFD_OKAY) { call_save_screenshot(outPath); @@ -1315,6 +1383,14 @@ static void file_dialog_save_screenshot(void) } } +static void file_dialog_set_native_window(SDL_Window* window, nfdwindowhandle_t* native_window) +{ + if (!NFD_GetNativeWindowFromSDLWindow(window, native_window)) + { + Log("NFD_GetNativeWindowFromSDLWindow failed: %s\n", SDL_GetError()); + } +} + static void keyboard_configuration_item(const char* text, SDL_Scancode* key) { ImGui::Text("%s", text); diff --git a/platforms/desktop-shared/nfd/nfd_sdl2.h b/platforms/desktop-shared/nfd/nfd_sdl2.h new file mode 100644 index 00000000..0928e9dc --- /dev/null +++ b/platforms/desktop-shared/nfd/nfd_sdl2.h @@ -0,0 +1,76 @@ +/* + Native File Dialog Extended + Repository: https://github.com/btzy/nativefiledialog-extended + License: Zlib + Authors: Bernard Teo + + This header contains a function to convert an SDL window handle to a native window handle for + passing to NFDe. + + This is meant to be used with SDL2, but if there are incompatibilities with future SDL versions, + we can conditionally compile based on SDL_MAJOR_VERSION. + */ + +#ifndef _NFD_SDL2_H +#define _NFD_SDL2_H + +#include +#include +#include +#include "nfd.h" + +#ifdef __cplusplus +extern "C" { +#define NFD_INLINE inline +#else +#define NFD_INLINE static inline +#endif // __cplusplus + +/** + * Converts an SDL window handle to a native window handle that can be passed to NFDe. + * @param sdlWindow The SDL window handle. + * @param[out] nativeWindow The output native window handle, populated if and only if this function + * returns true. + * @return Either true to indicate success, or false to indicate failure. If false is returned, + * you can call SDL_GetError() for more information. However, it is intended that users ignore the + * error and simply pass a value-initialized nfdwindowhandle_t to NFDe if this function fails. */ +NFD_INLINE bool NFD_GetNativeWindowFromSDLWindow(SDL_Window* sdlWindow, + nfdwindowhandle_t* nativeWindow) { + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (!SDL_GetWindowWMInfo(sdlWindow, &info)) { + return false; + } + switch (info.subsystem) { +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + case SDL_SYSWM_WINDOWS: + nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WINDOWS; + nativeWindow->handle = (void*)info.info.win.window; + return true; +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + case SDL_SYSWM_COCOA: + nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_COCOA; + nativeWindow->handle = (void*)info.info.cocoa.window; + return true; +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + case SDL_SYSWM_X11: + nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11; + nativeWindow->handle = (void*)info.info.x11.window; + return true; +#endif + default: + // Silence the warning in case we are not using a supported backend. + (void)nativeWindow; + SDL_SetError("Unsupported native window type."); + return false; + } +} + +#undef NFD_INLINE +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // _NFD_SDL2_H \ No newline at end of file