Bug 1434572 - [Wayland] Implement Gtk+ clipboard shortcut for copy->paste between Firefox windows, r?jhorak
When we perform copy -> paste in one Firefox process on Wayland we're locked because Wayland clipboard paste
operation just reads data from filedescriptor and does not run main event loop.
A solution is to use Gtk+ shortcut here, when clipboard selection owner is the same as data receiver.
Gtk+ then does not go through X11/Wayland but calls clipboard data getter callback directly,
which we can use on Wayland because it also does not main event loop and the operation
stays synchronous.
MozReview-Commit-ID: G8myCBUSzxb
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -67,16 +67,17 @@ STUB(gdk_screen_get_number)
STUB(gdk_screen_get_resolution)
STUB(gdk_screen_get_rgba_visual)
STUB(gdk_screen_get_root_window)
STUB(gdk_screen_get_system_visual)
STUB(gdk_screen_get_width)
STUB(gdk_screen_height)
STUB(gdk_screen_is_composited)
STUB(gdk_screen_width)
+STUB(gdk_selection_owner_get)
STUB(gdk_set_program_class)
STUB(gdk_unicode_to_keyval)
STUB(gdk_visual_get_depth)
STUB(gdk_visual_get_system)
STUB(gdk_window_add_filter)
STUB(gdk_window_begin_move_drag)
STUB(gdk_window_begin_resize_drag)
STUB(gdk_window_destroy)
--- a/widget/gtk/nsClipboardWayland.cpp
+++ b/widget/gtk/nsClipboardWayland.cpp
@@ -276,16 +276,19 @@ static const struct wl_registry_listener
};
nsRetrievalContextWayland::nsRetrievalContextWayland(void)
: mInitialized(false)
, mSeat(nullptr)
, mDataDeviceManager(nullptr)
, mDataOffer(nullptr)
, mKeyboard(nullptr)
+ , mClipboardRequestNumber(0)
+ , mClipboardData(nullptr)
+ , mClipboardDataLength(0)
{
const gchar* charset;
g_get_charset(&charset);
mTextPlainLocale = g_strdup_printf("text/plain;charset=%s", charset);
// Available as of GTK 3.8+
static auto sGdkWaylandDisplayGetWlDisplay =
(wl_display *(*)(GdkDisplay *))
@@ -337,69 +340,134 @@ nsRetrievalContextWayland::GetTargets(in
for (int32_t j = 0; j < length; j++) {
targetList[j] = mTargetMIMETypes[j];
}
*aTargetNum = length;
return targetList;
}
+struct FastTrackClipboard
+{
+ FastTrackClipboard(int aClipboardRequestNumber,
+ nsRetrievalContextWayland* aRetrievalContex)
+ : mClipboardRequestNumber(aClipboardRequestNumber)
+ , mRetrievalContex(aRetrievalContex)
+ {}
+
+ int mClipboardRequestNumber;
+ nsRetrievalContextWayland* mRetrievalContex;
+};
+
+static void
+wayland_clipboard_contents_received(GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ FastTrackClipboard* fastTrack = static_cast<FastTrackClipboard*>(data);
+ fastTrack->mRetrievalContex->TransferFastTrackClipboard(
+ fastTrack->mClipboardRequestNumber, selection_data);
+ delete fastTrack;
+}
+
+void
+nsRetrievalContextWayland::TransferFastTrackClipboard(
+ int aClipboardRequestNumber, GtkSelectionData *aSelectionData)
+{
+ if (mClipboardRequestNumber == aClipboardRequestNumber) {
+ mClipboardDataLength = gtk_selection_data_get_length(aSelectionData);
+ if (mClipboardDataLength > 0) {
+ mClipboardData = reinterpret_cast<char*>(
+ g_malloc(sizeof(char)*mClipboardDataLength));
+ memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
+ sizeof(char)*mClipboardDataLength);
+ }
+ } else {
+ NS_WARNING("Received obsoleted clipboard data!");
+ }
+}
+
const char*
nsRetrievalContextWayland::GetClipboardData(const char* aMimeType,
int32_t aWhichClipboard,
uint32_t* aContentLength)
{
- NS_ASSERTION(mDataOffer, "Requested data without valid data offer!");
+ /* If actual clipboard data is owned by us we don't need to go
+ * through Wayland but we ask Gtk+ to directly call data
+ * getter callback nsClipboard::SelectionGetEvent().
+ * see gtk_selection_convert() at gtk+/gtkselection.c.
+ */
+ GdkAtom selection = GetSelectionAtom(aWhichClipboard);
+ if (gdk_selection_owner_get(selection)) {
+ mClipboardRequestNumber++;
+ gtk_clipboard_request_contents(gtk_clipboard_get(selection),
+ gdk_atom_intern(aMimeType, FALSE),
+ wayland_clipboard_contents_received,
+ new FastTrackClipboard(mClipboardRequestNumber, this));
+ } else {
+ /* TODO: We need to implement GDK_SELECTION_PRIMARY (X11 text selection)
+ * for Wayland backend.
+ */
+ if (selection == GDK_SELECTION_PRIMARY)
+ return nullptr;
+
+ NS_ASSERTION(mDataOffer, "Requested data without valid data offer!");
+
+ if (!mDataOffer) {
+ // TODO
+ // Something went wrong. We're requested to provide clipboard data
+ // but we haven't got any from wayland. Looks like rhbz#1455915.
+ return nullptr;
+ }
+
+ int pipe_fd[2];
+ if (pipe(pipe_fd) == -1)
+ return nullptr;
- if (!mDataOffer) {
- // TODO
- // Something went wrong. We're requested to provide clipboard data
- // but we haven't got any from wayland. Looks like rhbz#1455915.
- return nullptr;
+ wl_data_offer_receive(mDataOffer, aMimeType, pipe_fd[1]);
+ close(pipe_fd[1]);
+ wl_display_flush(mDisplay);
+
+ struct pollfd fds;
+ fds.fd = pipe_fd[0];
+ fds.events = POLLIN;
+
+ // Choose some reasonable timeout here
+ int ret = poll(&fds, 1, kClipboardTimeout / 1000);
+ if (!ret || ret == -1) {
+ close(pipe_fd[0]);
+ return nullptr;
+ }
+
+ GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
+ GError* error = nullptr;
+
+ g_io_channel_set_encoding(channel, nullptr, &error);
+ if (!error) {
+ gsize length = 0;
+ g_io_channel_read_to_end(channel, &mClipboardData, &length, &error);
+ mClipboardDataLength = length;
+ }
+
+ if (error) {
+ NS_WARNING(
+ nsPrintfCString("Unexpected error when reading clipboard data: %s",
+ error->message).get());
+ g_error_free(error);
+ }
+
+ g_io_channel_unref(channel);
+ close(pipe_fd[0]);
}
- int pipe_fd[2];
- if (pipe(pipe_fd) == -1)
- return nullptr;
-
- wl_data_offer_receive(mDataOffer, aMimeType, pipe_fd[1]);
- close(pipe_fd[1]);
- wl_display_flush(mDisplay);
-
- struct pollfd fds;
- fds.fd = pipe_fd[0];
- fds.events = POLLIN;
-
- // Choose some reasonable timeout here
- int ret = poll(&fds, 1, kClipboardTimeout / 1000);
- if (!ret || ret == -1) {
- close(pipe_fd[0]);
- return nullptr;
- }
-
- GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
- GError* error = nullptr;
- gchar *clipboardData = nullptr;
- gsize dataLength = 0;
-
- g_io_channel_set_encoding(channel, nullptr, &error);
- if (!error) {
- g_io_channel_read_to_end(channel, &clipboardData, &dataLength, &error);
- }
-
- if (error) {
- NS_WARNING(
- nsPrintfCString("Unexpected error when reading clipboard data: %s",
- error->message).get());
- g_error_free(error);
- }
-
- g_io_channel_unref(channel);
- close(pipe_fd[0]);
-
- *aContentLength = dataLength;
- return reinterpret_cast<const char*>(clipboardData);
+ *aContentLength = mClipboardDataLength;
+ return reinterpret_cast<const char*>(mClipboardData);
}
void nsRetrievalContextWayland::ReleaseClipboardData(const char* aClipboardData)
{
+ NS_ASSERTION(aClipboardData == mClipboardData,
+ "Releasing unknown clipboard data!");
g_free((void*)aClipboardData);
+
+ mClipboardData = nullptr;
+ mClipboardDataLength = 0;
}
--- a/widget/gtk/nsClipboardWayland.h
+++ b/widget/gtk/nsClipboardWayland.h
@@ -8,16 +8,18 @@
#ifndef __nsClipboardWayland_h_
#define __nsClipboardWayland_h_
#include "nsIClipboard.h"
#include <gtk/gtk.h>
#include <gdk/gdkwayland.h>
#include <nsTArray.h>
+struct FastTrackClipboard;
+
class nsRetrievalContextWayland : public nsRetrievalContext
{
public:
nsRetrievalContextWayland();
virtual const char* GetClipboardData(const char* aMimeType,
int32_t aWhichClipboard,
uint32_t* aContentLength) override;
@@ -25,25 +27,31 @@ public:
virtual GdkAtom* GetTargets(int32_t aWhichClipboard,
int* aTargetNum) override;
void SetDataOffer(wl_data_offer *aDataOffer);
void AddMIMEType(const char *aMimeType);
void ResetMIMETypeList(void);
void ConfigureKeyboard(wl_seat_capability caps);
+ void TransferFastTrackClipboard(int aClipboardRequestNumber,
+ GtkSelectionData *aSelectionData);
void InitDataDeviceManager(wl_registry *registry, uint32_t id, uint32_t version);
void InitSeat(wl_registry *registry, uint32_t id, uint32_t version, void *data);
virtual ~nsRetrievalContextWayland() override;
private:
bool mInitialized;
wl_display *mDisplay;
wl_seat *mSeat;
wl_data_device_manager *mDataDeviceManager;
wl_data_offer *mDataOffer;
wl_keyboard *mKeyboard;
nsTArray<GdkAtom> mTargetMIMETypes;
gchar *mTextPlainLocale;
+
+ int mClipboardRequestNumber;
+ char* mClipboardData;
+ uint32_t mClipboardDataLength;
};
#endif /* __nsClipboardWayland_h_ */