--- a/widget/gtk/nsClipboardWayland.cpp
+++ b/widget/gtk/nsClipboardWayland.cpp
@@ -31,91 +31,297 @@
#include <sys/epoll.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <gtk/gtk.h>
#include <gdk/gdkwayland.h>
#include <errno.h>
-void
-nsRetrievalContextWayland::ResetMIMETypeList(void)
-{
- mTargetMIMETypes.Clear();
-}
-
-void
-nsRetrievalContextWayland::AddMIMEType(const char *aMimeType)
-{
- GdkAtom atom = gdk_atom_intern(aMimeType, FALSE);
- mTargetMIMETypes.AppendElement(atom);
-}
+#include "wayland/gtk-primary-selection-client-protocol.h"
void
-nsRetrievalContextWayland::SetDataOffer(wl_data_offer *aDataOffer)
+DataOffer::AddMIMEType(const char *aMimeType)
+{
+ GdkAtom atom = gdk_atom_intern(aMimeType, FALSE);
+ mTargetMIMETypes.AppendElement(atom);
+}
+
+GdkAtom*
+DataOffer::GetTargets(int* aTargetNum)
{
- if(mDataOffer) {
- wl_data_offer_destroy(mDataOffer);
+ int length = mTargetMIMETypes.Length();
+ if (!length) {
+ *aTargetNum = 0;
+ return nullptr;
}
- mDataOffer = aDataOffer;
+
+ GdkAtom* targetList = reinterpret_cast<GdkAtom*>(
+ g_malloc(sizeof(GdkAtom)*length));
+ for (int32_t j = 0; j < length; j++) {
+ targetList[j] = mTargetMIMETypes[j];
+ }
+
+ *aTargetNum = length;
+ return targetList;
}
-static void
-data_device_selection (void *data,
- struct wl_data_device *wl_data_device,
- struct wl_data_offer *offer)
+char*
+DataOffer::GetData(wl_display* aDisplay, const char* aMimeType,
+ uint32_t* aContentLength)
{
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland*>(data);
- context->SetDataOffer(offer);
+ int pipe_fd[2];
+ if (pipe(pipe_fd) == -1)
+ return nullptr;
+
+ if (!RequestDataTransfer(aMimeType, pipe_fd[1])) {
+ NS_WARNING("DataOffer::RequestDataTransfer() failed!");
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ return nullptr;
+ }
+
+ close(pipe_fd[1]);
+ wl_display_flush(aDisplay);
+
+ 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;
+ char* clipboardData;
+
+ g_io_channel_set_encoding(channel, nullptr, &error);
+ if (!error) {
+ gsize length = 0;
+ g_io_channel_read_to_end(channel, &clipboardData, &length, &error);
+ if (length == 0) {
+ // We don't have valid clipboard data although
+ // g_io_channel_read_to_end() allocated clipboardData for us.
+ // Release it now and return nullptr to indicate
+ // we don't have reqested data flavour.
+ g_free((void *)clipboardData);
+ clipboardData = nullptr;
+ }
+ *aContentLength = 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]);
+
+ return clipboardData;
+}
+
+bool
+WaylandDataOffer::RequestDataTransfer(const char* aMimeType, int fd)
+{
+ if (mWaylandDataOffer) {
+ wl_data_offer_receive(mWaylandDataOffer, aMimeType, fd);
+ return true;
+ }
+
+ return false;
}
static void
data_offer_offer (void *data,
struct wl_data_offer *wl_data_offer,
const char *type)
{
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland*>(data);
- context->AddMIMEType(type);
+ auto *offer = static_cast<DataOffer*>(data);
+ offer->AddMIMEType(type);
}
static void
data_offer_source_actions(void *data,
struct wl_data_offer *wl_data_offer,
uint32_t source_actions)
{
}
static void
data_offer_action(void *data,
struct wl_data_offer *wl_data_offer,
uint32_t dnd_action)
{
}
+/* wl_data_offer callback description:
+ *
+ * data_offer_offer - Is called for each MIME type available at wl_data_offer.
+ * data_offer_source_actions - Exposes all available D&D actions.
+ * data_offer_action - Expose one actually selected D&D action.
+ */
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
data_offer_source_actions,
data_offer_action
};
+WaylandDataOffer::WaylandDataOffer(wl_data_offer* aWaylandDataOffer)
+ : mWaylandDataOffer(aWaylandDataOffer)
+{
+ wl_data_offer_add_listener(mWaylandDataOffer, &data_offer_listener, this);
+}
+
+WaylandDataOffer::~WaylandDataOffer(void)
+{
+ if(mWaylandDataOffer) {
+ wl_data_offer_destroy(mWaylandDataOffer);
+ }
+}
+
+bool
+PrimaryDataOffer::RequestDataTransfer(const char* aMimeType, int fd)
+{
+ if (mPrimaryDataOffer) {
+ gtk_primary_selection_offer_receive(mPrimaryDataOffer, aMimeType, fd);
+ return true;
+ }
+ return false;
+}
+
+static void
+primary_data_offer(void *data,
+ gtk_primary_selection_offer *gtk_primary_selection_offer,
+ const char *mime_type)
+{
+ auto *offer = static_cast<DataOffer*>(data);
+ offer->AddMIMEType(mime_type);
+}
+
+/* gtk_primary_selection_offer_listener callback description:
+ *
+ * primary_data_offer - Is called for each MIME type available at
+ * gtk_primary_selection_offer.
+ */
+static const struct gtk_primary_selection_offer_listener
+primary_selection_offer_listener = {
+ primary_data_offer
+};
+
+PrimaryDataOffer::PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer)
+ : mPrimaryDataOffer(aPrimaryDataOffer)
+{
+ gtk_primary_selection_offer_add_listener(aPrimaryDataOffer,
+ &primary_selection_offer_listener, this);
+}
+
+PrimaryDataOffer::~PrimaryDataOffer(void)
+{
+ if(mPrimaryDataOffer) {
+ gtk_primary_selection_offer_destroy(mPrimaryDataOffer);
+ }
+}
+
+void
+nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer)
+{
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aWaylandDataOffer));
+ if (!dataOffer) {
+ dataOffer = new WaylandDataOffer(aWaylandDataOffer);
+ g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
+ }
+}
+
+void
+nsRetrievalContextWayland::RegisterDataOffer(
+ gtk_primary_selection_offer *aPrimaryDataOffer)
+{
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aPrimaryDataOffer));
+ if (!dataOffer) {
+ dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
+ g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
+ }
+}
+
+void
+nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer)
+{
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aWaylandDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing clipboard data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
+ mClipboardOffer = dataOffer;
+ }
+}
+
+void
+nsRetrievalContextWayland::SetPrimaryDataOffer(
+ gtk_primary_selection_offer *aPrimaryDataOffer)
+{
+ if (aPrimaryDataOffer == nullptr) {
+ // Release any primary offer we have.
+ mPrimaryOffer = nullptr;
+ } else {
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aPrimaryDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing primary data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aPrimaryDataOffer);
+ mPrimaryOffer = dataOffer;
+ }
+ }
+}
+
+void
+nsRetrievalContextWayland::ClearDataOffers(void)
+{
+ if (mClipboardOffer)
+ mClipboardOffer = nullptr;
+ if (mPrimaryOffer)
+ mPrimaryOffer = nullptr;
+}
+
+// We have a new fresh data content.
+// We should attach listeners to it and save for further use.
static void
data_device_data_offer (void *data,
struct wl_data_device *data_device,
struct wl_data_offer *offer)
{
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland*>(data);
-
- // We have a new fresh clipboard content
- context->ResetMIMETypeList();
- wl_data_offer_add_listener (offer, &data_offer_listener, data);
+ context->RegisterDataOffer(offer);
}
+// The new fresh data content is clipboard.
+static void
+data_device_selection (void *data,
+ struct wl_data_device *wl_data_device,
+ struct wl_data_offer *offer)
+{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+ context->SetClipboardDataOffer(offer);
+}
+
+// The new fresh wayland data content is drag and drop.
static void
data_device_enter (void *data,
struct wl_data_device *data_device,
uint32_t time,
struct wl_surface *surface,
int32_t x,
int32_t y,
struct wl_data_offer *offer)
@@ -138,26 +344,79 @@ data_device_motion (void
}
static void
data_device_drop (void *data,
struct wl_data_device *data_device)
{
}
+/* wl_data_device callback description:
+ *
+ * data_device_data_offer - It's called when there's a new wl_data_offer
+ * available. We need to attach wl_data_offer_listener
+ * to it to get available MIME types.
+ *
+ * data_device_selection - It's called when the new wl_data_offer
+ * is a clipboard content.
+ *
+ * data_device_enter - It's called when the new wl_data_offer is a drag & drop
+ * content and it's tied to actual wl_surface.
+ * data_device_leave - It's called when the wl_data_offer (drag & dop) is not
+ * valid any more.
+ * data_device_motion - It's called when the drag and drop selection moves across
+ * wl_surface.
+ * data_device_drop - It's called when D&D operation is sucessfully finished and
+ * we can read the data from D&D.
+ * It's generated only if we call wl_data_offer_accept() and
+ * wl_data_offer_set_actions() from data_device_motion callback.
+ */
static const struct wl_data_device_listener data_device_listener = {
data_device_data_offer,
data_device_enter,
data_device_leave,
data_device_motion,
data_device_drop,
data_device_selection
};
static void
+primary_selection_data_offer (void *data,
+ struct gtk_primary_selection_device *gtk_primary_selection_device,
+ struct gtk_primary_selection_offer *gtk_primary_offer)
+{
+ // create and add listener
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+ context->RegisterDataOffer(gtk_primary_offer);
+}
+
+static void
+primary_selection_selection (void *data,
+ struct gtk_primary_selection_device *gtk_primary_selection_device,
+ struct gtk_primary_selection_offer *gtk_primary_offer)
+{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+ context->SetPrimaryDataOffer(gtk_primary_offer);
+}
+
+static const struct
+gtk_primary_selection_device_listener primary_selection_device_listener = {
+ primary_selection_data_offer,
+ primary_selection_selection,
+};
+
+bool
+nsRetrievalContextWayland::HasSelectionSupport(void)
+{
+ return mPrimarySelectionDataDeviceManager != nullptr;
+}
+
+static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
}
static void
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
@@ -168,18 +427,17 @@ keyboard_handle_enter(void *data, struct
static void
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
// We lost focus so our clipboard data are outdated
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland*>(data);
- context->ResetMIMETypeList();
- context->SetDataOffer(nullptr);
+ context->ClearDataOffers();
}
static void
keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state)
{
}
@@ -228,44 +486,56 @@ static const struct wl_seat_listener sea
seat_handle_capabilities,
};
void
nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry,
uint32_t id,
uint32_t version)
{
- int data_device_manager_version = MIN (version, 3);
- mDataDeviceManager = (wl_data_device_manager *)wl_registry_bind(registry, id,
- &wl_data_device_manager_interface, data_device_manager_version);
+ int data_device_manager_version = MIN (version, 3);
+ mDataDeviceManager = (wl_data_device_manager *)wl_registry_bind(registry, id,
+ &wl_data_device_manager_interface, data_device_manager_version);
}
-void nsRetrievalContextWayland::InitSeat(wl_registry *registry,
- uint32_t id, uint32_t version,
- void *data)
+void
+nsRetrievalContextWayland::InitPrimarySelectionDataDeviceManager(
+ wl_registry *registry, uint32_t id)
{
- mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
- wl_seat_add_listener(mSeat, &seat_listener, data);
+ mPrimarySelectionDataDeviceManager =
+ (gtk_primary_selection_device_manager *)wl_registry_bind(registry, id,
+ >k_primary_selection_device_manager_interface, 1);
+}
+
+void
+nsRetrievalContextWayland::InitSeat(wl_registry *registry,
+ uint32_t id, uint32_t version,
+ void *data)
+{
+ mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+ wl_seat_add_listener(mSeat, &seat_listener, data);
}
static void
gdk_registry_handle_global(void *data,
struct wl_registry *registry,
uint32_t id,
const char *interface,
uint32_t version)
{
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland*>(data);
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
- if (strcmp (interface, "wl_data_device_manager") == 0) {
- context->InitDataDeviceManager(registry, id, version);
- } else if (strcmp(interface, "wl_seat") == 0) {
- context->InitSeat(registry, id, version, data);
- }
+ if (strcmp (interface, "wl_data_device_manager") == 0) {
+ context->InitDataDeviceManager(registry, id, version);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ context->InitSeat(registry, id, version, data);
+ } else if (strcmp (interface, "gtk_primary_selection_device_manager") == 0) {
+ context->InitPrimarySelectionDataDeviceManager(registry, id);
+ }
}
static void
gdk_registry_handle_global_remove(void *data,
struct wl_registry *registry,
uint32_t id)
{
}
@@ -274,26 +544,25 @@ static const struct wl_registry_listener
gdk_registry_handle_global,
gdk_registry_handle_global_remove
};
nsRetrievalContextWayland::nsRetrievalContextWayland(void)
: mInitialized(false)
, mSeat(nullptr)
, mDataDeviceManager(nullptr)
- , mDataOffer(nullptr)
+ , mPrimarySelectionDataDeviceManager(nullptr)
, mKeyboard(nullptr)
+ , mActiveOffers(g_hash_table_new(NULL, NULL))
+ , mClipboardOffer(nullptr)
+ , mPrimaryOffer(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 *))
dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
mDisplay = sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
wl_registry_add_listener(wl_display_get_registry(mDisplay),
&clipboard_registry_listener, this);
@@ -312,42 +581,48 @@ nsRetrievalContextWayland::nsRetrievalCo
wl_data_device_manager_get_data_device(mDataDeviceManager, mSeat);
wl_data_device_add_listener(dataDevice, &data_device_listener, this);
// We have to call wl_display_roundtrip() twice otherwise data_offer_listener
// may not be processed because it's called from data_device_data_offer
// callback.
wl_display_roundtrip(mDisplay);
wl_display_roundtrip(mDisplay);
+ if (mPrimarySelectionDataDeviceManager) {
+ gtk_primary_selection_device *primaryDataDevice =
+ gtk_primary_selection_device_manager_get_device(mPrimarySelectionDataDeviceManager,
+ mSeat);
+ gtk_primary_selection_device_add_listener(primaryDataDevice,
+ &primary_selection_device_listener, this);
+ }
+
mInitialized = true;
}
nsRetrievalContextWayland::~nsRetrievalContextWayland(void)
{
- g_free(mTextPlainLocale);
+ g_hash_table_destroy(mActiveOffers);
}
GdkAtom*
nsRetrievalContextWayland::GetTargets(int32_t aWhichClipboard,
int* aTargetNum)
{
- int length = mTargetMIMETypes.Length();
- if (!length) {
- *aTargetNum = 0;
- return nullptr;
+ if (GetSelectionAtom(aWhichClipboard) == GDK_SELECTION_CLIPBOARD) {
+ if (mClipboardOffer) {
+ return mClipboardOffer->GetTargets(aTargetNum);
+ }
+ } else {
+ if (mPrimaryOffer) {
+ return mPrimaryOffer->GetTargets(aTargetNum);
+ }
}
- GdkAtom* targetList = reinterpret_cast<GdkAtom*>(
- g_malloc(sizeof(GdkAtom)*length));
- for (int32_t j = 0; j < length; j++) {
- targetList[j] = mTargetMIMETypes[j];
- }
-
- *aTargetNum = length;
- return targetList;
+ *aTargetNum = 0;
+ return nullptr;
}
struct FastTrackClipboard
{
FastTrackClipboard(int aClipboardRequestNumber,
nsRetrievalContextWayland* aRetrievalContex)
: mClipboardRequestNumber(aClipboardRequestNumber)
, mRetrievalContex(aRetrievalContex)
@@ -401,76 +676,27 @@ nsRetrievalContextWayland::GetClipboardD
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
+ DataOffer* dataOffer = (selection == GDK_SELECTION_PRIMARY) ?
+ mPrimaryOffer : mClipboardOffer;
+ if (!dataOffer) {
// 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;
-
- 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]);
-
- // We don't have valid clipboard data although
- // g_io_channel_read_to_end() allocated mClipboardData for us.
- // Release it now and return nullptr to indicate
- // we don't have reqested data flavour.
- if (mClipboardData && mClipboardDataLength == 0) {
- ReleaseClipboardData(mClipboardData);
+ // but we haven't got any from wayland.
+ NS_WARNING("Requested data without valid DataOffer!");
+ mClipboardData = nullptr;
+ mClipboardDataLength = 0;
+ } else {
+ mClipboardData = dataOffer->GetData(mDisplay,
+ aMimeType, &mClipboardDataLength);
}
}
*aContentLength = mClipboardDataLength;
return reinterpret_cast<const char*>(mClipboardData);
}
void nsRetrievalContextWayland::ReleaseClipboardData(const char* aClipboardData)