Skip to content

Commit f23fda3

Browse files
authored
Merge pull request #87776 from bruvzg/wl_nfd
[Wayland] Add support for native file dialogs.
2 parents f8a039e + edb21e0 commit f23fda3

File tree

9 files changed

+271
-3
lines changed

9 files changed

+271
-3
lines changed

doc/classes/DisplayServer.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
<description>
121121
Displays OS native dialog for selecting files or directories in the file system.
122122
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int[/code].
123-
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature, i.e. Linux (X11), Windows, and macOS.
123+
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11 and Wayland), Windows, and macOS.
124124
[b]Note:[/b] [param current_directory] might be ignored.
125125
[b]Note:[/b] On Linux, [param show_hidden] is ignored.
126126
[b]Note:[/b] On macOS, native file dialogs have no title.
@@ -145,7 +145,7 @@
145145
- [code]"values"[/code] - [PackedStringArray] of values. If empty, boolean option (check box) is used.
146146
- [code]"default"[/code] - default selected option index ([int]) or default boolean value ([bool]).
147147
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int, selected_option: Dictionary[/code].
148-
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature, i.e. Linux, Windows, and macOS.
148+
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11 and Wayland), Windows, and macOS.
149149
[b]Note:[/b] [param current_directory] might be ignored.
150150
[b]Note:[/b] On Linux (X11), [param show_hidden] is ignored.
151151
[b]Note:[/b] On macOS, native file dialogs have no title.

platform/linuxbsd/wayland/SCsub

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,22 @@ env.WAYLAND_API_CODE(
151151
source="#thirdparty/wayland-protocols/unstable/tablet/tablet-unstable-v2.xml",
152152
)
153153

154+
env.WAYLAND_API_HEADER(
155+
target="protocol/xdg_foreign.gen.h",
156+
source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml",
157+
)
158+
159+
env.WAYLAND_API_CODE(
160+
target="protocol/xdg_foreign.gen.c",
161+
source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml",
162+
)
163+
154164
source_files = [
155165
"protocol/wayland.gen.c",
156166
"protocol/viewporter.gen.c",
157167
"protocol/fractional_scale.gen.c",
158168
"protocol/xdg_shell.gen.c",
169+
"protocol/xdg_foreign.gen.c",
159170
"protocol/xdg_decoration.gen.c",
160171
"protocol/xdg_activation.gen.c",
161172
"protocol/relative_pointer.gen.c",

platform/linuxbsd/wayland/display_server_wayland.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ bool DisplayServerWayland::has_feature(Feature p_feature) const {
196196
case FEATURE_SWAP_BUFFERS:
197197
case FEATURE_KEEP_SCREEN_ON:
198198
case FEATURE_CLIPBOARD_PRIMARY:
199+
#ifdef DBUS_ENABLED
200+
case FEATURE_NATIVE_DIALOG:
201+
#endif
199202
case FEATURE_HIDPI: {
200203
return true;
201204
} break;
@@ -269,6 +272,22 @@ bool DisplayServerWayland::is_dark_mode() const {
269272
}
270273
}
271274

275+
Error DisplayServerWayland::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
276+
WindowID window_id = MAIN_WINDOW_ID;
277+
// TODO: Use window IDs for multiwindow support.
278+
279+
WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));
280+
return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, String(), p_filename, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
281+
}
282+
283+
Error DisplayServerWayland::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) {
284+
WindowID window_id = MAIN_WINDOW_ID;
285+
// TODO: Use window IDs for multiwindow support.
286+
287+
WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));
288+
return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, p_root, p_filename, p_mode, p_filters, p_options, p_callback, true);
289+
}
290+
272291
#endif
273292

274293
void DisplayServerWayland::mouse_set_mode(MouseMode p_mode) {
@@ -1192,10 +1211,11 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win
11921211

11931212
if (context_rd) {
11941213
if (context_rd->initialize() != OK) {
1214+
ERR_PRINT(vformat("Could not initialize %s", context_rd->get_api_name()));
11951215
memdelete(context_rd);
11961216
context_rd = nullptr;
11971217
r_error = ERR_CANT_CREATE;
1198-
ERR_FAIL_MSG(vformat("Could not initialize %s", context_rd->get_api_name()));
1218+
return;
11991219
}
12001220
}
12011221
#endif

platform/linuxbsd/wayland/display_server_wayland.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ class DisplayServerWayland : public DisplayServer {
171171
#ifdef DBUS_ENABLED
172172
virtual bool is_dark_mode_supported() const override;
173173
virtual bool is_dark_mode() const override;
174+
175+
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
176+
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override;
174177
#endif
175178

176179
virtual void mouse_set_mode(MouseMode p_mode) override;

platform/linuxbsd/wayland/wayland_thread.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
370370
return;
371371
}
372372

373+
if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) {
374+
registry->wl_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
375+
registry->wl_exporter_name = name;
376+
return;
377+
}
378+
373379
if (strcmp(interface, wl_compositor_interface.name) == 0) {
374380
registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
375381
registry->wl_compositor_name = name;
@@ -570,6 +576,17 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
570576
return;
571577
}
572578

579+
if (name == registry->wl_exporter_name) {
580+
if (registry->wl_exporter) {
581+
zxdg_exporter_v1_destroy(registry->wl_exporter);
582+
registry->wl_exporter = nullptr;
583+
}
584+
585+
registry->wl_exporter_name = 0;
586+
587+
return;
588+
}
589+
573590
if (name == registry->wl_compositor_name) {
574591
if (registry->wl_compositor) {
575592
wl_compositor_destroy(registry->wl_compositor);
@@ -1107,6 +1124,13 @@ void WaylandThread::_xdg_toplevel_on_wm_capabilities(void *data, struct xdg_topl
11071124
}
11081125
}
11091126

1127+
void WaylandThread::_xdg_exported_on_exported(void *data, zxdg_exported_v1 *exported, const char *handle) {
1128+
WindowState *ws = (WindowState *)data;
1129+
ERR_FAIL_NULL(ws);
1130+
1131+
ws->exported_handle = vformat("wayland:%s", String::utf8(handle));
1132+
}
1133+
11101134
void WaylandThread::_xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode) {
11111135
if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
11121136
#ifdef LIBDECOR_ENABLED
@@ -2975,6 +2999,11 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
29752999
// "loop".
29763000
wl_surface_commit(ws.wl_surface);
29773001

3002+
if (registry.wl_exporter) {
3003+
ws.xdg_exported = zxdg_exporter_v1_export(registry.wl_exporter, ws.wl_surface);
3004+
zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws);
3005+
}
3006+
29783007
// Wait for the surface to be configured before continuing.
29793008
wl_display_roundtrip(wl_display);
29803009
}
@@ -3980,6 +4009,10 @@ void WaylandThread::destroy() {
39804009
xdg_wm_base_destroy(registry.xdg_wm_base);
39814010
}
39824011

4012+
if (registry.wl_exporter) {
4013+
zxdg_exporter_v1_destroy(registry.wl_exporter);
4014+
}
4015+
39834016
if (registry.wl_shm) {
39844017
wl_shm_destroy(registry.wl_shm);
39854018
}

platform/linuxbsd/wayland/wayland_thread.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "wayland/protocol/wayland.gen.h"
6464
#include "wayland/protocol/xdg_activation.gen.h"
6565
#include "wayland/protocol/xdg_decoration.gen.h"
66+
#include "wayland/protocol/xdg_foreign.gen.h"
6667
#include "wayland/protocol/xdg_shell.gen.h"
6768

6869
#ifdef LIBDECOR_ENABLED
@@ -132,6 +133,9 @@ class WaylandThread {
132133
struct xdg_wm_base *xdg_wm_base = nullptr;
133134
uint32_t xdg_wm_base_name = 0;
134135

136+
struct zxdg_exporter_v1 *wl_exporter = nullptr;
137+
uint32_t wl_exporter_name = 0;
138+
135139
// wayland-protocols globals.
136140

137141
struct wp_viewporter *wp_viewporter = nullptr;
@@ -197,6 +201,9 @@ class WaylandThread {
197201

198202
struct wp_viewport *wp_viewport = nullptr;
199203
struct wp_fractional_scale_v1 *wp_fractional_scale = nullptr;
204+
struct zxdg_exported_v1 *xdg_exported = nullptr;
205+
206+
String exported_handle;
200207

201208
// Currently applied buffer scale.
202209
int buffer_scale = 1;
@@ -599,6 +606,8 @@ class WaylandThread {
599606

600607
static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode);
601608

609+
static void _xdg_exported_on_exported(void *data, zxdg_exported_v1 *exported, const char *handle);
610+
602611
static void _xdg_activation_token_on_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token, const char *token);
603612

604613
// Core Wayland event listeners.
@@ -753,6 +762,10 @@ class WaylandThread {
753762
.frame = _wp_tablet_tool_on_frame,
754763
};
755764

765+
static constexpr struct zxdg_exported_v1_listener xdg_exported_listener = {
766+
.handle = _xdg_exported_on_exported
767+
};
768+
756769
static constexpr struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = {
757770
.configure = _xdg_toplevel_decoration_on_configure,
758771
};

thirdparty/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,8 @@ Files extracted from upstream source:
971971
- `unstable/tablet/tablet-unstable-v2.xml`
972972
- `unstable/xdg-decoration/README`
973973
- `unstable/xdg-decoration/xdg-decoration-unstable-v1.xml`
974+
- `unstable/xdg-foreign/README`
975+
- `unstable/xdg-foreign/xdg-foreign-unstable-v1.xml`
974976
- `COPYING`
975977

976978

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
xdg foreign protocol
2+
3+
Maintainers:
4+
Jonas Ådahl <[email protected]>

0 commit comments

Comments
 (0)