diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index fea38aad55c8..79869a3a5c56 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -37,6 +37,13 @@
[b]Note:[/b] This method is only implemented on Linux (X11/Wayland).
+
+
+
+
+ Returns the contents of the user's clipboard as the MIME type [param type].
+
+
@@ -49,6 +56,13 @@
Returns [code]true[/code] if there is an image content on the user's clipboard.
+
+
+
+
+ Returns [code]true[/code] if the user's clipboard data can be retrieved as the MIME type [param type].
+
+
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 90bd3d57d4b7..d17a6897357d 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -719,9 +719,9 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A
return ret;
}
-Atom DisplayServerX11::_clipboard_get_image_target(Atom p_source, Window x11_window) const {
- Atom target = XInternAtom(x11_display, "TARGETS", 0);
- Atom png = XInternAtom(x11_display, "image/png", 0);
+Atom DisplayServerX11::_clipboard_get_type_target(Atom p_source, Window x11_window, const String &p_type) const {
+ Atom targets_atom = XInternAtom(x11_display, "TARGETS", 0);
+ Atom target = XInternAtom(x11_display, p_type.ascii().get_data(), 0);
Atom *valid_targets = nullptr;
unsigned long atom_count = 0;
@@ -731,7 +731,7 @@ Atom DisplayServerX11::_clipboard_get_image_target(Atom p_source, Window x11_win
MutexLock mutex_lock(events_mutex);
Atom selection = XA_PRIMARY;
- XConvertSelection(x11_display, p_source, target, selection, x11_window, CurrentTime);
+ XConvertSelection(x11_display, p_source, targets_atom, selection, x11_window, CurrentTime);
XFlush(x11_display);
@@ -777,9 +777,9 @@ Atom DisplayServerX11::_clipboard_get_image_target(Atom p_source, Window x11_win
}
for (unsigned long i = 0; i < atom_count; i++) {
Atom atom = valid_targets[i];
- if (atom == png) {
+ if (atom == target) {
XFree(valid_targets);
- return png;
+ return target;
}
}
@@ -830,7 +830,7 @@ Ref DisplayServerX11::clipboard_get_image() const {
Atom clipboard = XInternAtom(x11_display, "CLIPBOARD", 0);
Window x11_window = windows[MAIN_WINDOW_ID].x11_window;
Ref ret;
- Atom target = _clipboard_get_image_target(clipboard, x11_window);
+ Atom target = _clipboard_get_type_target(clipboard, x11_window, String("image/png"));
if (target == None) {
return ret;
}
@@ -971,12 +971,166 @@ Ref DisplayServerX11::clipboard_get_image() const {
}
bool DisplayServerX11::clipboard_has_image() const {
- Atom target = _clipboard_get_image_target(
+ Atom target = _clipboard_get_type_target(
XInternAtom(x11_display, "CLIPBOARD", 0),
- windows[MAIN_WINDOW_ID].x11_window);
+ windows[MAIN_WINDOW_ID].x11_window,
+ String("image/png"));
return target != None;
}
+bool DisplayServerX11::clipboard_has_type(const String &p_type) const {
+ Atom target = _clipboard_get_type_target(
+ XInternAtom(x11_display, "CLIPBOARD", 0),
+ windows[MAIN_WINDOW_ID].x11_window,
+ p_type);
+ return target != None;
+}
+
+Vector DisplayServerX11::clipboard_get_type(const String &p_type) const {
+ _THREAD_SAFE_METHOD_
+ Atom clipboard = XInternAtom(x11_display, "CLIPBOARD", 0);
+ Window x11_window = windows[MAIN_WINDOW_ID].x11_window;
+ Vector ret;
+ Atom target = _clipboard_get_type_target(clipboard, x11_window, p_type);
+ if (target == None) {
+ return ret;
+ }
+
+ Window selection_owner = XGetSelectionOwner(x11_display, clipboard);
+
+ if (selection_owner != None && selection_owner != x11_window) {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ // Identifier for the property the other window
+ // will send the converted data to.
+ Atom transfer_prop = XA_PRIMARY;
+ XConvertSelection(x11_display,
+ clipboard, // source selection
+ target, // format to convert to
+ transfer_prop, // output property
+ x11_window, CurrentTime);
+
+ XFlush(x11_display);
+
+ // Blocking wait for predicate to be True and remove the event from the queue.
+ XEvent event;
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+
+ // Do not get any data, see how much data is there.
+ Atom type;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ unsigned char *data;
+ XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, // Property data is transferred through
+ 0, 1, // offset, len (4 so we can get the size if INCR is used)
+ 0, // Delete 0==FALSE
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ if (type == XInternAtom(x11_display, "INCR", 0)) {
+ ERR_FAIL_COND_V_MSG(len != 1, ret, "Incremental transfer initial value was not length.");
+
+ // Data is going to be received incrementally.
+ DEBUG_LOG_X11("INCR selection started.\n");
+
+ LocalVector incr_data;
+ uint32_t data_size = 0;
+ bool success = false;
+
+ // Initial response is the lower bound of the length of the transferred data.
+ incr_data.resize(*(unsigned long *)data);
+ XFree(data);
+ data = nullptr;
+
+ // Delete INCR property to notify the owner.
+ XDeleteProperty(x11_display, x11_window, transfer_prop);
+
+ // Process events from the queue.
+ bool done = false;
+ while (!done) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, (XPointer)&transfer_prop)) {
+ result = XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, // output property
+ 0, LONG_MAX, // offset - len
+ True, // delete property to notify the owner
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format);
+
+ if (result == Success) {
+ if (data && (len > 0)) {
+ uint32_t prev_size = incr_data.size();
+ // New chunk, resize to be safe and append data.
+ incr_data.resize(MAX(data_size + len, prev_size));
+ memcpy(incr_data.ptr() + data_size, data, len);
+ data_size += len;
+ } else if (!(format == 0 && len == 0)) {
+ // For unclear reasons the first GetWindowProperty always returns a length and format of 0.
+ // Otherwise, last chunk, process finished.
+ done = true;
+ success = true;
+ }
+ } else {
+ print_verbose("Failed to get selection data chunk.");
+ done = true;
+ }
+
+ if (data) {
+ XFree(data);
+ data = nullptr;
+ }
+
+ if (done) {
+ break;
+ }
+ }
+ }
+
+ if (success && (data_size > 0)) {
+ ret.resize(incr_data.size());
+ memcpy(ret.ptrw(), incr_data.ptr(), incr_data.size());
+ }
+ } else if (bytes_left > 0) {
+ if (data) {
+ XFree(data);
+ data = nullptr;
+ }
+ // Data is ready and can be processed all at once.
+ result = XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, 0, bytes_left + 4, 0,
+ AnyPropertyType, &type, &format,
+ &len, &dummy, &data);
+ if (result == Success) {
+ ret.resize(bytes_left);
+ memcpy(ret.ptrw(), data, bytes_left);
+ } else {
+ print_verbose("Failed to get selection data.");
+ }
+
+ if (data) {
+ XFree(data);
+ }
+ }
+ }
+
+ return ret;
+}
+
Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
if (event->xany.window == *(Window *)arg) {
return (event->type == SelectionRequest) ||
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 31e4d548b9ab..744c5863e0b2 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -305,7 +305,7 @@ class DisplayServerX11 : public DisplayServer {
String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
String _clipboard_get(Atom p_source, Window x11_window) const;
- Atom _clipboard_get_image_target(Atom p_source, Window x11_window) const;
+ Atom _clipboard_get_type_target(Atom p_source, Window x11_window, const String &p_type) const;
void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
bool do_mouse_warp = false;
@@ -422,6 +422,9 @@ class DisplayServerX11 : public DisplayServer {
virtual void clipboard_set_primary(const String &p_text) override;
virtual String clipboard_get_primary() const override;
+ virtual bool clipboard_has_type(const String &p_type) const override;
+ virtual Vector clipboard_get_type(const String &p_type) const override;
+
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
virtual int get_keyboard_focus_screen() const override;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index ff9a07f3970e..d42dc4de57a0 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -30,6 +30,7 @@
#include "display_server.h"
+#include "core/error/error_list.h"
#include "core/input/input.h"
#include "scene/resources/texture.h"
#include "servers/display_server_headless.h"
@@ -540,6 +541,14 @@ String DisplayServer::clipboard_get_primary() const {
ERR_FAIL_V_MSG(String(), "Primary clipboard is not supported by this display server.");
}
+bool DisplayServer::clipboard_has_type(const String &p_type) const {
+ ERR_FAIL_V_MSG(false, "Dynamic Clipboard is not implemented yet by this display server.");
+}
+
+Vector DisplayServer::clipboard_get_type(const String &p_type) const {
+ ERR_FAIL_V_MSG(Vector(), "Dynamic Clipboard is not implemented yet by this display server.");
+}
+
void DisplayServer::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) {
WARN_PRINT("Orientation not supported by this display server.");
}
@@ -886,6 +895,9 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("clipboard_set_primary", "clipboard_primary"), &DisplayServer::clipboard_set_primary);
ClassDB::bind_method(D_METHOD("clipboard_get_primary"), &DisplayServer::clipboard_get_primary);
+ ClassDB::bind_method(D_METHOD("clipboard_has_type", "type"), &DisplayServer::clipboard_has_type);
+ ClassDB::bind_method(D_METHOD("clipboard_get_type", "type"), &DisplayServer::clipboard_get_type);
+
ClassDB::bind_method(D_METHOD("get_display_cutouts"), &DisplayServer::get_display_cutouts);
ClassDB::bind_method(D_METHOD("get_display_safe_area"), &DisplayServer::get_display_safe_area);
@@ -1305,6 +1317,13 @@ bool DisplayServer::can_create_rendering_device() {
return false;
}
+Ref _get_png_from_buffer(Vector p_buffer) {
+ Ref image;
+ image.instantiate();
+ image->load_png_from_buffer(p_buffer);
+ return image;
+}
+
DisplayServer::DisplayServer() {
singleton = this;
Input::set_mouse_mode_func = _input_set_mouse_mode;
diff --git a/servers/display_server.h b/servers/display_server.h
index 805d2374ab01..cc838156bba8 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -35,7 +35,11 @@
#include "core/io/image.h"
#include "core/io/resource.h"
#include "core/os/os.h"
+#include "core/string/string_name.h"
+#include "core/templates/hash_map.h"
#include "core/variant/callable.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/variant.h"
#include "display/native_menu.h"
@@ -267,6 +271,7 @@ class DisplayServer : public Object {
protected:
static bool _get_window_early_clear_override(Color &r_color);
+
public:
static void set_early_window_clear_color_override(bool p_enabled, Color p_color = Color(0, 0, 0, 0));
@@ -293,6 +298,9 @@ class DisplayServer : public Object {
virtual void clipboard_set_primary(const String &p_text);
virtual String clipboard_get_primary() const;
+ virtual bool clipboard_has_type(const String &p_type) const;
+ virtual PackedByteArray clipboard_get_type(const String &p_type) const;
+
virtual TypedArray get_display_cutouts() const { return TypedArray(); }
virtual Rect2i get_display_safe_area() const { return screen_get_usable_rect(); }