Skip to content

Commit

Permalink
Bug 1667674 - [Wayland] Support public primary-selection unstable pro…
Browse files Browse the repository at this point in the history
…tocol. r=stransky

This is required to support pasting the primary selection into Firefox on compositors only
supporting the public protocol, such as KWin. Getting the selection *from* Firefox is done
via GTK and will be supported from GTK 3.24.23 on.

The public protocol, while practically identical, will replace the gtk-private one eventually.
However, support for the private one will still be needed for a while.

Note: this also updates the auto-generated gtk-primary-selection files.

Differential Revision: https://phabricator.services.mozilla.com/D91594
  • Loading branch information
rmader committed Oct 2, 2020
1 parent 5084f69 commit 324a54b
Show file tree
Hide file tree
Showing 10 changed files with 880 additions and 57 deletions.
2 changes: 2 additions & 0 deletions .clang-format-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ media/mp4parse-rust/mp4parse.h
security/manager/ssl/StaticHPKPins.h
widget/gtk/wayland/gtk-primary-selection-client-protocol.h
widget/gtk/wayland/gtk-primary-selection-protocol.c
widget/gtk/wayland/primary-selection-unstable-v1-client-protocol.h
widget/gtk/wayland/primary-selection-unstable-v1-protocol.c

# Ignored because these files are used to generate a windows.h STL wrapper,
# and reformatting them can break generating that wrapper.
Expand Down
133 changes: 111 additions & 22 deletions widget/gtk/nsClipboardWayland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,27 @@ WaylandDataOffer::~WaylandDataOffer(void) {
}

bool PrimaryDataOffer::RequestDataTransfer(const char* aMimeType, int fd) {
if (mPrimaryDataOffer) {
gtk_primary_selection_offer_receive(mPrimaryDataOffer, aMimeType, fd);
if (mPrimaryDataOfferGtk) {
gtk_primary_selection_offer_receive(mPrimaryDataOfferGtk, aMimeType, fd);
return true;
}
if (mPrimaryDataOfferZwpV1) {
zwp_primary_selection_offer_v1_receive(mPrimaryDataOfferZwpV1, aMimeType,
fd);
return true;
}
return false;
}

static void primary_data_offer(
void* data, gtk_primary_selection_offer* gtk_primary_selection_offer,
void* data, gtk_primary_selection_offer* primary_selection_offer,
const char* mime_type) {
auto* offer = static_cast<DataOffer*>(data);
offer->AddMIMEType(mime_type);
}

static void primary_data_offer(
void* data, zwp_primary_selection_offer_v1* primary_selection_offer,
const char* mime_type) {
auto* offer = static_cast<DataOffer*>(data);
offer->AddMIMEType(mime_type);
Expand All @@ -334,18 +346,31 @@ static void primary_data_offer(
* gtk_primary_selection_offer.
*/
static const struct gtk_primary_selection_offer_listener
primary_selection_offer_listener = {primary_data_offer};
primary_selection_offer_listener_gtk = {primary_data_offer};

static const struct zwp_primary_selection_offer_v1_listener
primary_selection_offer_listener_zwp_v1 = {primary_data_offer};

PrimaryDataOffer::PrimaryDataOffer(
gtk_primary_selection_offer* aPrimaryDataOffer)
: mPrimaryDataOffer(aPrimaryDataOffer) {
: mPrimaryDataOfferGtk(aPrimaryDataOffer), mPrimaryDataOfferZwpV1(nullptr) {
gtk_primary_selection_offer_add_listener(
aPrimaryDataOffer, &primary_selection_offer_listener, this);
aPrimaryDataOffer, &primary_selection_offer_listener_gtk, this);
}

PrimaryDataOffer::PrimaryDataOffer(
zwp_primary_selection_offer_v1* aPrimaryDataOffer)
: mPrimaryDataOfferGtk(nullptr), mPrimaryDataOfferZwpV1(aPrimaryDataOffer) {
zwp_primary_selection_offer_v1_add_listener(
aPrimaryDataOffer, &primary_selection_offer_listener_zwp_v1, this);
}

PrimaryDataOffer::~PrimaryDataOffer(void) {
if (mPrimaryDataOffer) {
gtk_primary_selection_offer_destroy(mPrimaryDataOffer);
if (mPrimaryDataOfferGtk) {
gtk_primary_selection_offer_destroy(mPrimaryDataOfferGtk);
}
if (mPrimaryDataOfferZwpV1) {
zwp_primary_selection_offer_v1_destroy(mPrimaryDataOfferZwpV1);
}
}

Expand Down Expand Up @@ -444,6 +469,20 @@ void nsRetrievalContextWayland::RegisterNewDataOffer(
}
}

void nsRetrievalContextWayland::RegisterNewDataOffer(
zwp_primary_selection_offer_v1* aPrimaryDataOffer) {
DataOffer* dataOffer = static_cast<DataOffer*>(
g_hash_table_lookup(mActiveOffers, aPrimaryDataOffer));
MOZ_ASSERT(
dataOffer == nullptr,
"Registered PrimaryDataOffer already exists. Wayland protocol error?");

if (!dataOffer) {
dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
}
}

void nsRetrievalContextWayland::SetClipboardDataOffer(
wl_data_offer* aWaylandDataOffer) {
// Delete existing clipboard data offer
Expand Down Expand Up @@ -480,6 +519,24 @@ void nsRetrievalContextWayland::SetPrimaryDataOffer(
}
}

void nsRetrievalContextWayland::SetPrimaryDataOffer(
zwp_primary_selection_offer_v1* aPrimaryDataOffer) {
// Release any primary offer we have.
mPrimaryOffer = nullptr;

// aPrimaryDataOffer can be null which means we lost
// the mouse selection.
if (aPrimaryDataOffer) {
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 = WrapUnique(dataOffer);
}
}
}

void nsRetrievalContextWayland::AddDragAndDropDataOffer(
wl_data_offer* aDropDataOffer) {
// Remove any existing D&D contexts.
Expand Down Expand Up @@ -615,25 +672,44 @@ 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* primary_selection_device,
struct gtk_primary_selection_offer* primary_offer) {
LOGCLIP(("primary_selection_data_offer() callback\n"));
// create and add listener
nsRetrievalContextWayland* context =
static_cast<nsRetrievalContextWayland*>(data);
context->RegisterNewDataOffer(primary_offer);
}

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) {
struct zwp_primary_selection_device_v1* primary_selection_device,
struct zwp_primary_selection_offer_v1* primary_offer) {
LOGCLIP(("primary_selection_data_offer() callback\n"));
// create and add listener
nsRetrievalContextWayland* context =
static_cast<nsRetrievalContextWayland*>(data);
context->RegisterNewDataOffer(gtk_primary_offer);
context->RegisterNewDataOffer(primary_offer);
}

static void primary_selection_selection(
void* data, struct gtk_primary_selection_device* primary_selection_device,
struct gtk_primary_selection_offer* primary_offer) {
LOGCLIP(("primary_selection_selection() callback\n"));
nsRetrievalContextWayland* context =
static_cast<nsRetrievalContextWayland*>(data);
context->SetPrimaryDataOffer(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) {
struct zwp_primary_selection_device_v1* primary_selection_device,
struct zwp_primary_selection_offer_v1* primary_offer) {
LOGCLIP(("primary_selection_selection() callback\n"));
nsRetrievalContextWayland* context =
static_cast<nsRetrievalContextWayland*>(data);
context->SetPrimaryDataOffer(gtk_primary_offer);
context->SetPrimaryDataOffer(primary_offer);
}

/* gtk_primary_selection_device callback description:
Expand All @@ -650,13 +726,20 @@ static void primary_selection_selection(
* there's no primary selection.
*/
static const struct gtk_primary_selection_device_listener
primary_selection_device_listener = {
primary_selection_device_listener_gtk = {
primary_selection_data_offer,
primary_selection_selection,
};

static const struct zwp_primary_selection_device_v1_listener
primary_selection_device_listener_zwp_v1 = {
primary_selection_data_offer,
primary_selection_selection,
};

bool nsRetrievalContextWayland::HasSelectionSupport(void) {
return mDisplay->GetPrimarySelectionDeviceManager() != nullptr;
return (mDisplay->GetPrimarySelectionDeviceManagerZwpV1() != nullptr ||
mDisplay->GetPrimarySelectionDeviceManagerGtk() != nullptr);
}

nsRetrievalContextWayland::nsRetrievalContextWayland(void)
Expand All @@ -673,14 +756,20 @@ nsRetrievalContextWayland::nsRetrievalContextWayland(void)
mDisplay->GetDataDeviceManager(), mDisplay->GetSeat());
wl_data_device_add_listener(dataDevice, &data_device_listener, this);

gtk_primary_selection_device_manager* manager =
mDisplay->GetPrimarySelectionDeviceManager();
if (manager) {
if (mDisplay->GetPrimarySelectionDeviceManagerZwpV1()) {
zwp_primary_selection_device_v1* primaryDataDevice =
zwp_primary_selection_device_manager_v1_get_device(
mDisplay->GetPrimarySelectionDeviceManagerZwpV1(),
mDisplay->GetSeat());
zwp_primary_selection_device_v1_add_listener(
primaryDataDevice, &primary_selection_device_listener_zwp_v1, this);
} else if (mDisplay->GetPrimarySelectionDeviceManagerGtk()) {
gtk_primary_selection_device* primaryDataDevice =
gtk_primary_selection_device_manager_get_device(manager,
mDisplay->GetSeat());
gtk_primary_selection_device_manager_get_device(
mDisplay->GetPrimarySelectionDeviceManagerGtk(),
mDisplay->GetSeat());
gtk_primary_selection_device_add_listener(
primaryDataDevice, &primary_selection_device_listener, this);
primaryDataDevice, &primary_selection_device_listener_gtk, this);
}

mInitialized = true;
Expand Down
8 changes: 5 additions & 3 deletions widget/gtk/nsClipboardWayland.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
#include "mozilla/UniquePtr.h"
#include "nsClipboard.h"
#include "nsWaylandDisplay.h"
#include "mozwayland/mozwayland.h"
#include "wayland/gtk-primary-selection-client-protocol.h"

struct FastTrackClipboard;

Expand Down Expand Up @@ -69,14 +67,16 @@ class WaylandDataOffer : public DataOffer {
class PrimaryDataOffer : public DataOffer {
public:
explicit PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
explicit PrimaryDataOffer(zwp_primary_selection_offer_v1* aPrimaryDataOffer);
void SetAvailableDragActions(uint32_t aWaylandActions){};

virtual ~PrimaryDataOffer();

private:
bool RequestDataTransfer(const char* aMimeType, int fd) override;

gtk_primary_selection_offer* mPrimaryDataOffer;
gtk_primary_selection_offer* mPrimaryDataOfferGtk;
zwp_primary_selection_offer_v1* mPrimaryDataOfferZwpV1;
};

class nsWaylandDragContext : public nsISupports {
Expand Down Expand Up @@ -124,9 +124,11 @@ class nsRetrievalContextWayland : public nsRetrievalContext {

void RegisterNewDataOffer(wl_data_offer* aWaylandDataOffer);
void RegisterNewDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
void RegisterNewDataOffer(zwp_primary_selection_offer_v1* aPrimaryDataOffer);

void SetClipboardDataOffer(wl_data_offer* aWaylandDataOffer);
void SetPrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
void SetPrimaryDataOffer(zwp_primary_selection_offer_v1* aPrimaryDataOffer);
void AddDragAndDropDataOffer(wl_data_offer* aWaylandDataOffer);
nsWaylandDragContext* GetDragContext();

Expand Down
19 changes: 17 additions & 2 deletions widget/gtk/nsWaylandDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ void nsWaylandDisplay::SetSeat(wl_seat* aSeat) { mSeat = aSeat; }

void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager) {
mPrimarySelectionDeviceManager = aPrimarySelectionDeviceManager;
mPrimarySelectionDeviceManagerGtk = aPrimarySelectionDeviceManager;
}

void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
zwp_primary_selection_device_manager_v1* aPrimarySelectionDeviceManager) {
mPrimarySelectionDeviceManagerZwpV1 = aPrimarySelectionDeviceManager;
}

void nsWaylandDisplay::SetIdleInhibitManager(
Expand Down Expand Up @@ -167,6 +172,15 @@ static void global_registry_handler(void* data, wl_registry* registry,
wl_proxy_set_queue((struct wl_proxy*)primary_selection_device_manager,
display->GetEventQueue());
display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
} else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") ==
0) {
auto* primary_selection_device_manager =
WaylandRegistryBind<gtk_primary_selection_device_manager>(
registry, id, &zwp_primary_selection_device_manager_v1_interface,
1);
wl_proxy_set_queue((struct wl_proxy*)primary_selection_device_manager,
display->GetEventQueue());
display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
} else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) {
auto* idle_inhibit_manager =
WaylandRegistryBind<zwp_idle_inhibit_manager_v1>(
Expand Down Expand Up @@ -284,7 +298,8 @@ nsWaylandDisplay::nsWaylandDisplay(wl_display* aDisplay, bool aLighWrapper)
mSeat(nullptr),
mShm(nullptr),
mSyncCallback(nullptr),
mPrimarySelectionDeviceManager(nullptr),
mPrimarySelectionDeviceManagerGtk(nullptr),
mPrimarySelectionDeviceManagerZwpV1(nullptr),
mIdleInhibitManager(nullptr),
mRegistry(nullptr),
mExplicitSync(false) {
Expand Down
22 changes: 15 additions & 7 deletions widget/gtk/nsWaylandDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
#ifndef __MOZ_WAYLAND_DISPLAY_H__
#define __MOZ_WAYLAND_DISPLAY_H__

#include "mozilla/widget/mozwayland.h"
#include "mozilla/widget/gtk-primary-selection-client-protocol.h"
#include "mozilla/widget/idle-inhibit-unstable-v1-client-protocol.h"

#include "base/message_loop.h" // for MessageLoop
#include "base/task.h" // for NewRunnableMethod, etc
#include "mozilla/StaticMutex.h"

#include "mozilla/widget/mozwayland.h"
#include "mozilla/widget/gbm.h"
#include "mozilla/widget/gtk-primary-selection-client-protocol.h"
#include "mozilla/widget/idle-inhibit-unstable-v1-client-protocol.h"
#include "mozilla/widget/linux-dmabuf-unstable-v1-client-protocol.h"
#include "mozilla/widget/primary-selection-unstable-v1-client-protocol.h"

namespace mozilla {
namespace widget {
Expand Down Expand Up @@ -51,8 +51,13 @@ class nsWaylandDisplay {
};
wl_seat* GetSeat(void) { return mSeat; };
wl_shm* GetShm(void) { return mShm; };
gtk_primary_selection_device_manager* GetPrimarySelectionDeviceManager(void) {
return mPrimarySelectionDeviceManager;
gtk_primary_selection_device_manager* GetPrimarySelectionDeviceManagerGtk(
void) {
return mPrimarySelectionDeviceManagerGtk;
};
zwp_primary_selection_device_manager_v1*
GetPrimarySelectionDeviceManagerZwpV1(void) {
return mPrimarySelectionDeviceManagerZwpV1;
};
zwp_idle_inhibit_manager_v1* GetIdleInhibitManager(void) {
return mIdleInhibitManager;
Expand All @@ -67,6 +72,8 @@ class nsWaylandDisplay {
void SetSeat(wl_seat* aSeat);
void SetPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager);
void SetPrimarySelectionDeviceManager(
zwp_primary_selection_device_manager_v1* aPrimarySelectionDeviceManager);
void SetIdleInhibitManager(zwp_idle_inhibit_manager_v1* aIdleInhibitManager);

MessageLoop* GetThreadLoop() { return mThreadLoop; }
Expand All @@ -87,7 +94,8 @@ class nsWaylandDisplay {
wl_seat* mSeat;
wl_shm* mShm;
wl_callback* mSyncCallback;
gtk_primary_selection_device_manager* mPrimarySelectionDeviceManager;
gtk_primary_selection_device_manager* mPrimarySelectionDeviceManagerGtk;
zwp_primary_selection_device_manager_v1* mPrimarySelectionDeviceManagerZwpV1;
zwp_idle_inhibit_manager_v1* mIdleInhibitManager;
wl_registry* mRegistry;
bool mExplicitSync;
Expand Down
2 changes: 1 addition & 1 deletion widget/gtk/wayland/gtk-primary-selection-client-protocol.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Generated by wayland-scanner 1.14.0 */
/* Generated by wayland-scanner 1.18.0 */

#ifndef GTK_PRIMARY_SELECTION_CLIENT_PROTOCOL_H
#define GTK_PRIMARY_SELECTION_CLIENT_PROTOCOL_H
Expand Down
Loading

0 comments on commit 324a54b

Please sign in to comment.