2626#define getrandom (buf, sz, flags ) syscall(SYS_getrandom, buf, sz, flags)
2727#endif
2828
29+ #ifdef NFD_WAYLAND
30+ #include < wayland-client.h>
31+ #include " xdg-foreign-unstable-v1.h"
32+ struct wl_display * wayland_display;
33+ struct wl_registry * wayland_registry;
34+ uint32_t wayland_xdg_exporter_v1_name;
35+ struct zxdg_exporter_v1 * wayland_xdg_exporter_v1;
36+ #endif
37+
2938#include " nfd.h"
3039
3140/*
@@ -68,6 +77,17 @@ struct FreeCheck_Guard {
6877 }
6978};
7079
80+ void EmptyFn (void *) {}
81+
82+ struct DestroyFunc {
83+ DestroyFunc () : fn(&EmptyFn), context(nullptr ) {}
84+ ~DestroyFunc () {
85+ (*fn)(context);
86+ }
87+ void (*fn)(void *);
88+ void * context;
89+ };
90+
7191struct DBusMessage_Guard {
7292 DBusMessage* data;
7393 DBusMessage_Guard (DBusMessage* freeable) noexcept : data(freeable) {}
@@ -172,9 +192,43 @@ constexpr const char* DBUS_PATH = "/org/freedesktop/portal/desktop";
172192constexpr const char * DBUS_FILECHOOSER_IFACE = " org.freedesktop.portal.FileChooser" ;
173193constexpr const char * DBUS_REQUEST_IFACE = " org.freedesktop.portal.Request" ;
174194
175- void AppendOpenFileQueryParentWindow (DBusMessageIter& iter, const nfdwindowhandle_t & parentWindow) {
195+ #ifdef NFD_WAYLAND
196+ constexpr const char * XDG_EXPORTER_V1 = " zxdg_exporter_v1" ;
197+ constexpr const char * WAYLAND_PREFIX = " wayland:" ;
198+
199+ void DestroyXdgExported (void * context) {
200+ zxdg_exported_v1_destroy (static_cast <struct zxdg_exported_v1 *>(context));
201+ }
202+
203+ void zxdg_exported_v1_handle (void * context,
204+ struct zxdg_exported_v1 * zxdg_exported_v1,
205+ const char * handle) {
206+ if (!context) return ;
207+ DBusMessageIter& iter = *static_cast <DBusMessageIter*>(context);
208+ const size_t handle_len = strlen (handle);
209+ const size_t prefix_len = strlen (WAYLAND_PREFIX);
210+ char * const buf = NFDi_Malloc<char >(prefix_len + handle_len + 1 );
211+ char * buf_end = copy (WAYLAND_PREFIX, WAYLAND_PREFIX + prefix_len, buf);
212+ buf_end = copy (handle, handle+handle_len, buf_end);
213+ *buf_end = ' \0 ' ;
214+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &buf);
215+ NFDi_Free (buf);
216+ }
217+
218+ constexpr struct zxdg_exported_v1_listener wayland_xdg_exported_v1_listener {
219+ &zxdg_exported_v1_handle
220+ };
221+ #endif
222+
223+ void AppendOpenFileQueryParentWindow (DBusMessageIter& iter, const nfdwindowhandle_t & parentWindow, void (*&destroyFn)(void *), void*& destroyFnContext) {
224+ (void ) iter;
225+ (void ) parentWindow;
226+ (void ) destroyFn;
227+ (void ) destroyFnContext;
176228 switch (parentWindow.type ) {
229+ #ifdef NFD_X11
177230 case NFD_WINDOW_HANDLE_TYPE_X11: {
231+ fprintf (stderr, " X11\n " );
178232 constexpr size_t maxX11WindowStrLen =
179233 4 + sizeof (uintptr_t ) * 2 + 1 ; // "x11:" + "<hex>" + "\0"
180234 char serializedWindowBuf[maxX11WindowStrLen];
@@ -190,6 +244,26 @@ void AppendOpenFileQueryParentWindow(DBusMessageIter& iter, const nfdwindowhandl
190244 dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &serializedWindow);
191245 return ;
192246 }
247+ #endif
248+ #ifdef NFD_WAYLAND
249+ case NFD_WINDOW_HANDLE_TYPE_WAYLAND: {
250+ fprintf (stderr, " Wayland\n " );
251+ if (wayland_xdg_exporter_v1) {
252+ struct zxdg_exported_v1 * exported = zxdg_exporter_v1_export (wayland_xdg_exporter_v1, static_cast <struct wl_surface *>(parentWindow.handle ));
253+ if (!exported) {
254+ // if we fail to export the wl_surface, act as if the window has no parent
255+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &STR_EMPTY);
256+ return ;
257+ }
258+ zxdg_exported_v1_add_listener (exported, &wayland_xdg_exported_v1_listener, static_cast <void *>(&iter));
259+ wl_display_roundtrip (wayland_display);
260+ zxdg_exported_v1_set_user_data (exported, nullptr );
261+ destroyFn = &DestroyXdgExported;
262+ destroyFnContext = static_cast <void *>(exported);
263+ }
264+ return ;
265+ }
266+ #endif
193267 default : {
194268 dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &STR_EMPTY);
195269 return ;
@@ -619,11 +693,11 @@ void AppendOpenFileQueryParams(DBusMessage* query,
619693 const nfdnfilteritem_t * filterList,
620694 nfdfiltersize_t filterCount,
621695 const nfdnchar_t * defaultPath,
622- const nfdwindowhandle_t & parentWindow) {
696+ const nfdwindowhandle_t & parentWindow, void (*&destroyFn)( void *), void*& destroyFnContext ) {
623697 DBusMessageIter iter;
624698 dbus_message_iter_init_append (query, &iter);
625699
626- AppendOpenFileQueryParentWindow (iter, parentWindow);
700+ AppendOpenFileQueryParentWindow (iter, parentWindow, destroyFn, destroyFnContext );
627701 AppendOpenFileQueryTitle<Multiple, Directory>(iter);
628702
629703 DBusMessageIter sub_iter;
@@ -643,11 +717,11 @@ void AppendSaveFileQueryParams(DBusMessage* query,
643717 nfdfiltersize_t filterCount,
644718 const nfdnchar_t * defaultPath,
645719 const nfdnchar_t * defaultName,
646- const nfdwindowhandle_t & parentWindow) {
720+ const nfdwindowhandle_t & parentWindow, void (*&destroyFn)( void *), void*& destroyFnContext ) {
647721 DBusMessageIter iter;
648722 dbus_message_iter_init_append (query, &iter);
649723
650- AppendOpenFileQueryParentWindow (iter, parentWindow);
724+ AppendOpenFileQueryParentWindow (iter, parentWindow, destroyFn, destroyFnContext );
651725 AppendSaveFileQueryTitle (iter);
652726
653727 DBusMessageIter sub_iter;
@@ -1195,8 +1269,10 @@ nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg,
11951269 DBusMessage* query = dbus_message_new_method_call (
11961270 DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, " OpenFile" );
11971271 DBusMessage_Guard query_guard (query);
1272+
1273+ DestroyFunc destroy;
11981274 AppendOpenFileQueryParams<Multiple, Directory>(
1199- query, handle_token_ptr, filterList, filterCount, defaultPath, parentWindow);
1275+ query, handle_token_ptr, filterList, filterCount, defaultPath, parentWindow, destroy. fn , destroy. context );
12001276
12011277 DBusMessage* reply =
12021278 dbus_connection_send_with_reply_and_block (dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err);
@@ -1278,8 +1354,10 @@ nfdresult_t NFD_DBus_SaveFile(DBusMessage*& outMsg,
12781354 DBusMessage* query = dbus_message_new_method_call (
12791355 DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, " SaveFile" );
12801356 DBusMessage_Guard query_guard (query);
1357+
1358+ DestroyFunc destroy;
12811359 AppendSaveFileQueryParams (
1282- query, handle_token_ptr, filterList, filterCount, defaultPath, defaultName, parentWindow);
1360+ query, handle_token_ptr, filterList, filterCount, defaultPath, defaultName, parentWindow, destroy. fn , destroy. context );
12831361
12841362 DBusMessage* reply =
12851363 dbus_connection_send_with_reply_and_block (dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err);
@@ -1383,6 +1461,27 @@ nfdresult_t NFD_DBus_GetVersion(dbus_uint32_t& outVersion) {
13831461 return NFD_OKAY;
13841462}
13851463
1464+ #ifdef NFD_WAYLAND
1465+ void registry_handle_global (void * context, struct wl_registry * registry, uint32_t name, const char * interface, uint32_t version) {
1466+ if (strcmp (interface, XDG_EXPORTER_V1) == 0 ) {
1467+ wayland_xdg_exporter_v1_name = name;
1468+ wayland_xdg_exporter_v1 = static_cast <struct zxdg_exporter_v1 *>(wl_registry_bind (registry, name, &zxdg_exporter_v1_interface, zxdg_exporter_v1_interface.version ));
1469+ }
1470+ }
1471+
1472+ void registry_handle_global_remove (void * context, struct wl_registry * registry, uint32_t name) {
1473+ if (wayland_xdg_exporter_v1 && name == wayland_xdg_exporter_v1_name) {
1474+ zxdg_exporter_v1_destroy (wayland_xdg_exporter_v1);
1475+ wayland_xdg_exporter_v1 = nullptr ;
1476+ }
1477+ }
1478+
1479+ constexpr struct wl_registry_listener wayland_registry_listener = {
1480+ ®istry_handle_global,
1481+ ®istry_handle_global_remove
1482+ };
1483+ #endif
1484+
13861485} // namespace
13871486
13881487/* public */
@@ -1408,11 +1507,30 @@ nfdresult_t NFD_Init(void) {
14081507 dbus_unique_name = dbus_bus_get_unique_name (dbus_conn);
14091508 if (!dbus_unique_name) {
14101509 NFDi_SetError (" Unable to get the unique name of our D-Bus connection." );
1510+ dbus_connection_unref (dbus_conn);
14111511 return NFD_ERROR;
14121512 }
1513+ #ifdef NFD_WAYLAND
1514+ // This might fail, but it is fine because the system might not actually have Wayland installed
1515+ wayland_display = wl_display_connect (nullptr );
1516+ if (wayland_display) {
1517+ wayland_registry = wl_display_get_registry (wayland_display);
1518+ wayland_xdg_exporter_v1 = nullptr ;
1519+ // seems like registry can't be null
1520+ wl_registry_add_listener (wayland_registry, &wayland_registry_listener, nullptr );
1521+ wl_display_roundtrip (wayland_display);
1522+ }
1523+ #endif
14131524 return NFD_OKAY;
14141525}
14151526void NFD_Quit (void ) {
1527+ #ifdef NFD_WAYLAND
1528+ if (wayland_display) {
1529+ if (wayland_xdg_exporter_v1) zxdg_exporter_v1_destroy (wayland_xdg_exporter_v1);
1530+ wl_registry_destroy (wayland_registry);
1531+ wl_display_disconnect (wayland_display);
1532+ }
1533+ #endif
14161534 dbus_connection_unref (dbus_conn);
14171535 // Note: We do not free dbus_error since NFD_Init might set it.
14181536 // To avoid leaking memory, the caller should explicitly call NFD_ClearError after reading the
0 commit comments