26
26
#define getrandom (buf, sz, flags ) syscall(SYS_getrandom, buf, sz, flags)
27
27
#endif
28
28
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
+
29
38
#include " nfd.h"
30
39
31
40
/*
@@ -68,6 +77,17 @@ struct FreeCheck_Guard {
68
77
}
69
78
};
70
79
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
+
71
91
struct DBusMessage_Guard {
72
92
DBusMessage* data;
73
93
DBusMessage_Guard (DBusMessage* freeable) noexcept : data(freeable) {}
@@ -172,9 +192,43 @@ constexpr const char* DBUS_PATH = "/org/freedesktop/portal/desktop";
172
192
constexpr const char * DBUS_FILECHOOSER_IFACE = " org.freedesktop.portal.FileChooser" ;
173
193
constexpr const char * DBUS_REQUEST_IFACE = " org.freedesktop.portal.Request" ;
174
194
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;
176
228
switch (parentWindow.type ) {
229
+ #ifdef NFD_X11
177
230
case NFD_WINDOW_HANDLE_TYPE_X11: {
231
+ fprintf (stderr, " X11\n " );
178
232
constexpr size_t maxX11WindowStrLen =
179
233
4 + sizeof (uintptr_t ) * 2 + 1 ; // "x11:" + "<hex>" + "\0"
180
234
char serializedWindowBuf[maxX11WindowStrLen];
@@ -190,6 +244,26 @@ void AppendOpenFileQueryParentWindow(DBusMessageIter& iter, const nfdwindowhandl
190
244
dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &serializedWindow);
191
245
return ;
192
246
}
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
193
267
default : {
194
268
dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &STR_EMPTY);
195
269
return ;
@@ -619,11 +693,11 @@ void AppendOpenFileQueryParams(DBusMessage* query,
619
693
const nfdnfilteritem_t * filterList,
620
694
nfdfiltersize_t filterCount,
621
695
const nfdnchar_t * defaultPath,
622
- const nfdwindowhandle_t & parentWindow) {
696
+ const nfdwindowhandle_t & parentWindow, void (*&destroyFn)( void *), void*& destroyFnContext ) {
623
697
DBusMessageIter iter;
624
698
dbus_message_iter_init_append (query, &iter);
625
699
626
- AppendOpenFileQueryParentWindow (iter, parentWindow);
700
+ AppendOpenFileQueryParentWindow (iter, parentWindow, destroyFn, destroyFnContext );
627
701
AppendOpenFileQueryTitle<Multiple, Directory>(iter);
628
702
629
703
DBusMessageIter sub_iter;
@@ -643,11 +717,11 @@ void AppendSaveFileQueryParams(DBusMessage* query,
643
717
nfdfiltersize_t filterCount,
644
718
const nfdnchar_t * defaultPath,
645
719
const nfdnchar_t * defaultName,
646
- const nfdwindowhandle_t & parentWindow) {
720
+ const nfdwindowhandle_t & parentWindow, void (*&destroyFn)( void *), void*& destroyFnContext ) {
647
721
DBusMessageIter iter;
648
722
dbus_message_iter_init_append (query, &iter);
649
723
650
- AppendOpenFileQueryParentWindow (iter, parentWindow);
724
+ AppendOpenFileQueryParentWindow (iter, parentWindow, destroyFn, destroyFnContext );
651
725
AppendSaveFileQueryTitle (iter);
652
726
653
727
DBusMessageIter sub_iter;
@@ -1195,8 +1269,10 @@ nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg,
1195
1269
DBusMessage* query = dbus_message_new_method_call (
1196
1270
DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, " OpenFile" );
1197
1271
DBusMessage_Guard query_guard (query);
1272
+
1273
+ DestroyFunc destroy;
1198
1274
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 );
1200
1276
1201
1277
DBusMessage* reply =
1202
1278
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,
1278
1354
DBusMessage* query = dbus_message_new_method_call (
1279
1355
DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, " SaveFile" );
1280
1356
DBusMessage_Guard query_guard (query);
1357
+
1358
+ DestroyFunc destroy;
1281
1359
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 );
1283
1361
1284
1362
DBusMessage* reply =
1285
1363
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) {
1383
1461
return NFD_OKAY;
1384
1462
}
1385
1463
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
+
1386
1485
} // namespace
1387
1486
1388
1487
/* public */
@@ -1408,11 +1507,30 @@ nfdresult_t NFD_Init(void) {
1408
1507
dbus_unique_name = dbus_bus_get_unique_name (dbus_conn);
1409
1508
if (!dbus_unique_name) {
1410
1509
NFDi_SetError (" Unable to get the unique name of our D-Bus connection." );
1510
+ dbus_connection_unref (dbus_conn);
1411
1511
return NFD_ERROR;
1412
1512
}
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
1413
1524
return NFD_OKAY;
1414
1525
}
1415
1526
void 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
1416
1534
dbus_connection_unref (dbus_conn);
1417
1535
// Note: We do not free dbus_error since NFD_Init might set it.
1418
1536
// To avoid leaking memory, the caller should explicitly call NFD_ClearError after reading the
0 commit comments