diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 3ee2273..2c13335 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -37,7 +37,7 @@ jobs: build-ubuntu: - name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.casesensitive.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }} + name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.wayland.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.casesensitive.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }} runs-on: ${{ matrix.os.label }} strategy: @@ -45,6 +45,7 @@ jobs: os: [ {label: ubuntu-latest, name: latest}, {label: ubuntu-22.04, name: 22.04} ] portal: [ {flag: OFF, dep: libgtk-3-dev, name: GTK}, {flag: ON, dep: libdbus-1-dev, name: Portal} ] # The NFD_PORTAL setting defaults to OFF (i.e. uses GTK) autoappend: [ {flag: OFF, name: NoAppendExtn} ] # By default the NFD_PORTAL mode does not append extensions, because it breaks some features of the portal + wayland: [ {flag: OFF, dep: , name: NoWayland} ] casesensitive: [ {flag: OFF, name: CaseInsensitive} ] # Case insensitive or case sensitive file filtering compiler: [ {c: gcc, cpp: g++, name: GCC}, {c: clang, cpp: clang++, name: Clang} ] # The default compiler is gcc/g++ cppstd: [20, 11] @@ -53,6 +54,7 @@ jobs: - os: {label: ubuntu-latest, name: latest} portal: {flag: ON, dep: libdbus-1-dev, name: Portal} autoappend: {flag: ON, name: AutoAppendExtn} + wayland: {flag: OFF, dep: , name: NoWayland} casesensitive: {flag: OFF, name: CaseInsensitive} compiler: {c: gcc, cpp: g++, name: GCC} cppstd: 11 @@ -60,6 +62,7 @@ jobs: - os: {label: ubuntu-latest, name: latest} portal: {flag: ON, dep: libdbus-1-dev, name: Portal} autoappend: {flag: ON, name: AutoAppendExtn} + wayland: {flag: OFF, dep: , name: NoWayland} casesensitive: {flag: OFF, name: CaseInsensitive} compiler: {c: clang, cpp: clang++, name: Clang} cppstd: 11 @@ -67,13 +70,23 @@ jobs: - os: {label: ubuntu-latest, name: latest} portal: {flag: ON, dep: libdbus-1-dev, name: Portal} autoappend: {flag: OFF, name: NoAppendExtn} + wayland: {flag: OFF, dep: , name: NoWayland} casesensitive: {flag: OFF, name: CaseInsensitive} compiler: {c: gcc, cpp: g++, name: GCC} cppstd: 11 shared_lib: {flag: ON, name: Shared} + - os: {label: ubuntu-latest, name: latest} + portal: {flag: ON, dep: libdbus-1-dev, name: Portal} + autoappend: {flag: OFF, name: NoAppendExtn} + wayland: {flag: ON, dep: libwayland-dev libwayland-bin, name: Wayland} + casesensitive: {flag: OFF, name: CaseInsensitive} + compiler: {c: gcc, cpp: g++, name: GCC} + cppstd: 11 + shared_lib: {flag: ON, name: Static} - os: {label: ubuntu-latest, name: latest} portal: {flag: OFF, dep: libgtk-3-dev, name: GTK} autoappend: {flag: OFF, name: NoAppendExtn} + wayland: {flag: OFF, dep: , name: NoWayland} casesensitive: {flag: ON, name: CaseSensitive} compiler: {c: gcc, cpp: g++, name: GCC} cppstd: 11 @@ -81,6 +94,7 @@ jobs: - os: {label: ubuntu-latest, name: latest} portal: {flag: ON, dep: libdbus-1-dev, name: Portal} autoappend: {flag: OFF, name: NoAppendExtn} + wayland: {flag: OFF, dep: , name: NoWayland} casesensitive: {flag: ON, name: CaseSensitive} compiler: {c: gcc, cpp: g++, name: GCC} cppstd: 11 @@ -89,16 +103,18 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: true - name: Install Dependencies - run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }} + run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }} ${{ matrix.wayland.dep }} - name: Configure - run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} -DCMAKE_C_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_APPEND_EXTENSION=${{ matrix.autoappend.flag }} -DNFD_CASE_SENSITIVE_FILTER=${{ matrix.casesensitive.flag }} -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON .. + run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} -DCMAKE_C_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_WAYLAND=${{ matrix.wayland.flag }} -DNFD_APPEND_EXTENSION=${{ matrix.autoappend.flag }} -DNFD_CASE_SENSITIVE_FILTER=${{ matrix.casesensitive.flag }} -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON .. - name: Build run: cmake --build build --target install - name: Upload test binaries uses: actions/upload-artifact@v4 with: - name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.casesensitive.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }} + name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.wayland.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.casesensitive.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }} path: | build/src/* build/test/* @@ -208,26 +224,29 @@ jobs: build-ubuntu-sdl2: - name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, Static, SDL2 + name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, ${{ matrix.wayland.name }}, Static, SDL2 runs-on: ubuntu-latest strategy: matrix: portal: [ {flag: OFF, dep: libgtk-3-dev, name: GTK}, {flag: ON, dep: libdbus-1-dev, name: Portal} ] # The NFD_PORTAL setting defaults to OFF (i.e. uses GTK) + wayland: [ {flag: OFF, dep: , name: NoWayland}, {flag: ON, dep: libwayland-dev libwayland-bin, name: Wayland} ] steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: true - name: Install Dependencies - run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }} libsdl2-dev libsdl2-ttf-dev + run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }} ${{ matrix.wayland.dep }} libsdl2-dev libsdl2-ttf-dev - name: Configure - run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_APPEND_EXTENSION=OFF -DNFD_BUILD_TESTS=OFF -DNFD_BUILD_SDL2_TESTS=ON .. + run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Wshadow -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_WAYLAND=${{ matrix.wayland.flag }} -DNFD_APPEND_EXTENSION=OFF -DNFD_BUILD_TESTS=OFF -DNFD_BUILD_SDL2_TESTS=ON .. - name: Build run: cmake --build build --target install - name: Upload test binaries uses: actions/upload-artifact@v4 with: - name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, Static, SDL2 + name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, ${{ matrix.wayland.name }}, Static, SDL2 path: | build/src/* build/test/* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..efc44be --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3ps/wayland-protocols"] + path = 3ps/wayland-protocols + url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git diff --git a/3ps/wayland-protocols b/3ps/wayland-protocols new file mode 160000 index 0000000..122a47a --- /dev/null +++ b/3ps/wayland-protocols @@ -0,0 +1 @@ +Subproject commit 122a47a1ff17a22c4b964a0bbe2b07f921eab7a5 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1eb7c38..e60c750 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,13 +18,33 @@ if(nfd_PLATFORM STREQUAL PLATFORM_LINUX) option(NFD_PORTAL "Use xdg-desktop-portal instead of GTK" OFF) if(NOT NFD_PORTAL) pkg_check_modules(GTK3 REQUIRED gtk+-3.0) - message("Using GTK version: ${GTK3_VERSION}") + message(STATUS "Using GTK version: ${GTK3_VERSION}") list(APPEND SOURCE_FILES nfd_gtk.cpp) else() pkg_check_modules(DBUS REQUIRED dbus-1) - message("Using DBUS version: ${DBUS_VERSION}") + message(STATUS "Using D-Bus version: ${DBUS_VERSION}") list(APPEND SOURCE_FILES nfd_portal.cpp) endif() + + # for Linux, we support X11, Wayland, or both + option(NFD_X11 "Support X11 on Linux" ON) + option(NFD_WAYLAND "Support Wayland on Linux" ON) + if(NFD_WAYLAND) + pkg_check_modules(WAYLAND REQUIRED wayland-client) + message(STATUS "Using Wayland version: ${WAYLAND_VERSION}") + set(NFD_WAYLAND_PROTOCOL_XDG_FOREIGN ${CMAKE_CURRENT_SOURCE_DIR}/../3ps/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h + COMMAND wayland-scanner client-header < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h + MAIN_DEPENDENCY ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} + ) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c + COMMAND wayland-scanner private-code < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c + MAIN_DEPENDENCY ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} + ) + list(APPEND SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c) + endif() endif() if(nfd_PLATFORM STREQUAL PLATFORM_MACOS) @@ -94,10 +114,21 @@ if(nfd_PLATFORM STREQUAL PLATFORM_LINUX) if(NFD_APPEND_EXTENSION) target_compile_definitions(${TARGET_NAME} PRIVATE NFD_APPEND_EXTENSION) endif() + option(NFD_CASE_SENSITIVE_FILTER "Make filters case sensitive" OFF) if(NFD_CASE_SENSITIVE_FILTER) target_compile_definitions(${TARGET_NAME} PRIVATE NFD_CASE_SENSITIVE_FILTER) endif() + + if(NFD_X11) + target_compile_definitions(${TARGET_NAME} PRIVATE NFD_X11) + endif() + if(NFD_WAYLAND) + target_include_directories(${TARGET_NAME} PRIVATE ${WAYLAND_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} PRIVATE ${WAYLAND_LINK_LIBRARIES}) + target_compile_definitions(${TARGET_NAME} PRIVATE NFD_WAYLAND) + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + endif() endif() if(nfd_PLATFORM STREQUAL PLATFORM_MACOS) @@ -122,7 +153,7 @@ if(nfd_COMPILER STREQUAL COMPILER_CLANGCL) endif() if(nfd_COMPILER STREQUAL COMPILER_GNU) - target_compile_options(${TARGET_NAME} PRIVATE -nostdlib -fno-exceptions -fno-rtti) + target_compile_options(${TARGET_NAME} PRIVATE -nostdlib -fno-exceptions $<$:-fno-rtti>) endif() set_target_properties(${TARGET_NAME} PROPERTIES diff --git a/src/include/nfd.h b/src/include/nfd.h index adb1c81..711e8d8 100644 --- a/src/include/nfd.h +++ b/src/include/nfd.h @@ -105,7 +105,8 @@ enum { NFD_WINDOW_HANDLE_TYPE_COCOA = 2, // X11: handle is Window NFD_WINDOW_HANDLE_TYPE_X11 = 3, - // Wayland support will be implemented separately in the future + // Wayland: handle is wl_surface* + NFD_WINDOW_HANDLE_TYPE_WAYLAND = 4, }; // The native window handle. If using a platform abstraction framework (e.g. SDL2), this should be // obtained using the corresponding NFD glue header (e.g. nfd_sdl2.h). @@ -190,6 +191,11 @@ NFD_API nfdresult_t NFD_Init(void); /** Call this to de-initialize NFD, if NFD_Init returned NFD_OKAY. */ NFD_API void NFD_Quit(void); +struct wl_display; +/** Sets or updates the Wayland display used by your application. Use NULL to remove an existing + * display. Only defined on Linux. */ +NFD_API nfdresult_t NFD_SetWaylandDisplay(struct wl_display*); + /** Single file open dialog * * It's the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns diff --git a/src/include/nfd_sdl2.h b/src/include/nfd_sdl2.h index 5703762..38745f1 100644 --- a/src/include/nfd_sdl2.h +++ b/src/include/nfd_sdl2.h @@ -26,6 +26,20 @@ extern "C" { #define NFD_INLINE static inline #endif // __cplusplus +NFD_INLINE bool NFD_SetDisplayPropertiesFromSDLWindow(SDL_Window* sdlWindow) { +#if defined(SDL_VIDEO_DRIVER_WAYLAND) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (!SDL_GetWindowWMInfo(sdlWindow, &info) || info.subsystem != SDL_SYSWM_WAYLAND) { + return false; + } + return NFD_SetWaylandDisplay(info.info.wl.display) == NFD_OKAY; +#else + (void)sdlWindow; + return true; +#endif +} + /** * Converts an SDL window handle to a native window handle that can be passed to NFDe. * @param sdlWindow The SDL window handle. @@ -59,6 +73,12 @@ NFD_INLINE bool NFD_GetNativeWindowFromSDLWindow(SDL_Window* sdlWindow, nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11; nativeWindow->handle = (void*)info.info.x11.window; return true; +#endif +#if defined(SDL_VIDEO_DRIVER_WAYLAND) + case SDL_SYSWM_WAYLAND: + nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WAYLAND; + nativeWindow->handle = (void*)info.info.wl.surface; + return true; #endif default: // Silence the warning in case we are not using a supported backend. diff --git a/src/nfd_gtk.cpp b/src/nfd_gtk.cpp index bb7d3cc..a005497 100644 --- a/src/nfd_gtk.cpp +++ b/src/nfd_gtk.cpp @@ -549,6 +549,12 @@ nfdresult_t NFD_Init(void) { } return NFD_OKAY; } + +nfdresult_t NFD_SetWaylandDisplay(wl_display*) { + // TODO do something with the display + return NFD_OKAY; +} + void NFD_Quit(void) { // do nothing, GTK cannot be de-initialized } diff --git a/src/nfd_portal.cpp b/src/nfd_portal.cpp index 0ee222b..24fa6de 100644 --- a/src/nfd_portal.cpp +++ b/src/nfd_portal.cpp @@ -26,6 +26,15 @@ #define getrandom(buf, sz, flags) syscall(SYS_getrandom, buf, sz, flags) #endif +#ifdef NFD_WAYLAND +#include +#include "xdg-foreign-unstable-v1.h" +struct wl_display* wayland_display; +struct wl_registry* wayland_registry; +uint32_t wayland_xdg_exporter_v1_name; +struct zxdg_exporter_v1* wayland_xdg_exporter_v1; +#endif + #include "nfd.h" /* @@ -75,6 +84,15 @@ struct FreeCheck_Guard { } }; +void EmptyFn(void*) {} + +struct DestroyFunc { + DestroyFunc() : fn(&EmptyFn), context(nullptr) {} + ~DestroyFunc() { (*fn)(context); } + void (*fn)(void*); + void* context; +}; + struct DBusMessage_Guard { DBusMessage* data; DBusMessage_Guard(DBusMessage* freeable) noexcept : data(freeable) {} @@ -200,8 +218,41 @@ constexpr const char* DBUS_PATH = "/org/freedesktop/portal/desktop"; constexpr const char* DBUS_FILECHOOSER_IFACE = "org.freedesktop.portal.FileChooser"; constexpr const char* DBUS_REQUEST_IFACE = "org.freedesktop.portal.Request"; -void AppendOpenFileQueryParentWindow(DBusMessageIter& iter, const nfdwindowhandle_t& parentWindow) { +#ifdef NFD_WAYLAND +constexpr const char* XDG_EXPORTER_V1 = "zxdg_exporter_v1"; +constexpr const char* WAYLAND_PREFIX = "wayland:"; + +void DestroyXdgExported(void* context) { + zxdg_exported_v1_destroy(static_cast(context)); +} + +void zxdg_exported_v1_handle(void* context, struct zxdg_exported_v1*, const char* handle) { + DBusMessageIter& iter = *static_cast(context); + const size_t handle_len = strlen(handle); + const size_t prefix_len = strlen(WAYLAND_PREFIX); + char* const buf = NFDi_Malloc(prefix_len + handle_len + 1); + char* buf_end = copy(WAYLAND_PREFIX, WAYLAND_PREFIX + prefix_len, buf); + buf_end = copy(handle, handle + handle_len, buf_end); + *buf_end = '\0'; + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &buf); + NFDi_Free(buf); +} + +constexpr struct zxdg_exported_v1_listener wayland_xdg_exported_v1_listener { + &zxdg_exported_v1_handle +}; +#endif + +void AppendOpenFileQueryParentWindow(DBusMessageIter& iter, + const nfdwindowhandle_t& parentWindow, + void (*&destroyFn)(void*), + void*& destroyFnContext) { + (void)iter; + (void)parentWindow; + (void)destroyFn; + (void)destroyFnContext; switch (parentWindow.type) { +#ifdef NFD_X11 case NFD_WINDOW_HANDLE_TYPE_X11: { constexpr size_t maxX11WindowStrLen = 4 + sizeof(uintptr_t) * 2 + 1; // "x11:" + "" + "\0" @@ -218,6 +269,30 @@ void AppendOpenFileQueryParentWindow(DBusMessageIter& iter, const nfdwindowhandl dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &serializedWindow); return; } +#endif +#ifdef NFD_WAYLAND + case NFD_WINDOW_HANDLE_TYPE_WAYLAND: { + if (wayland_display && wayland_xdg_exporter_v1) { + struct zxdg_exported_v1* exported = zxdg_exporter_v1_export( + wayland_xdg_exporter_v1, static_cast(parentWindow.handle)); + if (!exported) { + // if we fail to export the wl_surface, act as if the window has no parent + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_EMPTY); + return; + } + zxdg_exported_v1_add_listener( + exported, &wayland_xdg_exported_v1_listener, static_cast(&iter)); + wl_display_roundtrip(wayland_display); + zxdg_exported_v1_set_user_data(exported, nullptr); + destroyFn = &DestroyXdgExported; + destroyFnContext = static_cast(exported); + return; + } else { + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_EMPTY); + return; + } + } +#endif default: { dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_EMPTY); return; @@ -670,11 +745,13 @@ void AppendOpenFileQueryParams(DBusMessage* query, const nfdnfilteritem_t* filterList, nfdfiltersize_t filterCount, const nfdnchar_t* defaultPath, - const nfdwindowhandle_t& parentWindow) { + const nfdwindowhandle_t& parentWindow, + void (*&destroyFn)(void*), + void*& destroyFnContext) { DBusMessageIter iter; dbus_message_iter_init_append(query, &iter); - AppendOpenFileQueryParentWindow(iter, parentWindow); + AppendOpenFileQueryParentWindow(iter, parentWindow, destroyFn, destroyFnContext); AppendOpenFileQueryTitle(iter); DBusMessageIter sub_iter; @@ -694,11 +771,13 @@ void AppendSaveFileQueryParams(DBusMessage* query, nfdfiltersize_t filterCount, const nfdnchar_t* defaultPath, const nfdnchar_t* defaultName, - const nfdwindowhandle_t& parentWindow) { + const nfdwindowhandle_t& parentWindow, + void (*&destroyFn)(void*), + void*& destroyFnContext) { DBusMessageIter iter; dbus_message_iter_init_append(query, &iter); - AppendOpenFileQueryParentWindow(iter, parentWindow); + AppendOpenFileQueryParentWindow(iter, parentWindow, destroyFn, destroyFnContext); AppendSaveFileQueryTitle(iter); DBusMessageIter sub_iter; @@ -1245,8 +1324,16 @@ nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg, DBusMessage* query = dbus_message_new_method_call( DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, "OpenFile"); DBusMessage_Guard query_guard(query); - AppendOpenFileQueryParams( - query, handle_token_ptr, filterList, filterCount, defaultPath, parentWindow); + + DestroyFunc destroy; + AppendOpenFileQueryParams(query, + handle_token_ptr, + filterList, + filterCount, + defaultPath, + parentWindow, + destroy.fn, + destroy.context); DBusMessage* reply = dbus_connection_send_with_reply_and_block(dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err); @@ -1328,8 +1415,17 @@ nfdresult_t NFD_DBus_SaveFile(DBusMessage*& outMsg, DBusMessage* query = dbus_message_new_method_call( DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, "SaveFile"); DBusMessage_Guard query_guard(query); - AppendSaveFileQueryParams( - query, handle_token_ptr, filterList, filterCount, defaultPath, defaultName, parentWindow); + + DestroyFunc destroy; + AppendSaveFileQueryParams(query, + handle_token_ptr, + filterList, + filterCount, + defaultPath, + defaultName, + parentWindow, + destroy.fn, + destroy.context); DBusMessage* reply = dbus_connection_send_with_reply_and_block(dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err); @@ -1433,6 +1529,34 @@ nfdresult_t NFD_DBus_GetVersion(dbus_uint32_t& outVersion) { return NFD_OKAY; } +#ifdef NFD_WAYLAND +void registry_handle_global(void* context, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) { + (void)context; + (void)version; + if (strcmp(interface, XDG_EXPORTER_V1) == 0) { + wayland_xdg_exporter_v1_name = name; + wayland_xdg_exporter_v1 = static_cast(wl_registry_bind( + registry, name, &zxdg_exporter_v1_interface, zxdg_exporter_v1_interface.version)); + } +} + +void registry_handle_global_remove(void* context, struct wl_registry* registry, uint32_t name) { + (void)context; + (void)registry; + if (wayland_xdg_exporter_v1 && name == wayland_xdg_exporter_v1_name) { + zxdg_exporter_v1_destroy(wayland_xdg_exporter_v1); + wayland_xdg_exporter_v1 = nullptr; + } +} + +constexpr struct wl_registry_listener wayland_registry_listener = {®istry_handle_global, + ®istry_handle_global_remove}; +#endif + } // namespace /* public */ @@ -1458,11 +1582,42 @@ nfdresult_t NFD_Init(void) { dbus_unique_name = dbus_bus_get_unique_name(dbus_conn); if (!dbus_unique_name) { NFDi_SetError("Unable to get the unique name of our D-Bus connection."); + dbus_connection_unref(dbus_conn); return NFD_ERROR; } +#ifdef NFD_WAYLAND + wayland_display = nullptr; +#endif + return NFD_OKAY; +} + +nfdresult_t NFD_SetWaylandDisplay(wl_display* display) { +#ifdef NFD_WAYLAND + if (wayland_display) { + if (wayland_xdg_exporter_v1) zxdg_exporter_v1_destroy(wayland_xdg_exporter_v1); + wl_registry_destroy(wayland_registry); + } + wayland_display = display; + if (wayland_display) { + wayland_registry = wl_display_get_registry(wayland_display); + wayland_xdg_exporter_v1 = nullptr; + // seems like registry can't be null + wl_registry_add_listener(wayland_registry, &wayland_registry_listener, nullptr); + wl_display_roundtrip(wayland_display); + } +#else + (void)display; +#endif return NFD_OKAY; } + void NFD_Quit(void) { +#ifdef NFD_WAYLAND + if (wayland_display) { + if (wayland_xdg_exporter_v1) zxdg_exporter_v1_destroy(wayland_xdg_exporter_v1); + wl_registry_destroy(wayland_registry); + } +#endif dbus_connection_unref(dbus_conn); // Note: We do not free dbus_error since NFD_Init might set it. // To avoid leaking memory, the caller should explicitly call NFD_ClearError after reading the diff --git a/test/test_sdl.c b/test/test_sdl.c index bc4ef8d..6d1ed5f 100644 --- a/test/test_sdl.c +++ b/test/test_sdl.c @@ -172,12 +172,16 @@ void pickfoldermultiple_handler(SDL_Window* window) { } #if defined(_WIN32) -const char font_file[] = "C:\\Windows\\Fonts\\calibri.ttf"; +const char* font_file[] = {"C:\\Windows\\Fonts\\calibri.ttf"}; #elif defined(__APPLE__) -const char font_file[] = "/System/Library/Fonts/SFNS.ttf"; +const char* font_file[] = {"/System/Library/Fonts/SFNS.ttf"}; #else -const char font_file[] = "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf"; +const char* font_file[] = { + "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf", // Ubuntu + "/usr/share/fonts/google-noto/NotoSans-Regular.ttf", // Fedora +}; #endif +const size_t num_font_files = sizeof(font_file) / sizeof(const char*); #define NUM_STATES 3 #define NUM_BUTTONS 5 @@ -237,6 +241,9 @@ int main(void) return 0; } + // this gives NFD the wl_display* on Wayland; this is needed to set the parent window + NFD_SetDisplayPropertiesFromSDLWindow(window); + // create renderer SDL_Renderer* const renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); @@ -248,7 +255,11 @@ int main(void) // prepare the buttons and handlers SDL_Texture* textures_normal[NUM_BUTTONS][NUM_STATES]; - TTF_Font* const font = TTF_OpenFont(font_file, 20); + TTF_Font* font = NULL; + for (size_t i = 0; i != num_font_files; ++i) { + font = TTF_OpenFont(font_file[i], 20); + if (font) break; + } if (!font) { printf("TTF_OpenFont failed: %s\n", TTF_GetError()); return 0;