1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/ArrayUtils.h"
10 #include "nsArrayUtils.h"
11 #include "nsClipboard.h"
12 #include "nsClipboardWayland.h"
13 #include "nsSupportsPrimitives.h"
15 #include "nsReadableUtils.h"
16 #include "nsPrimitiveHelpers.h"
17 #include "nsImageToPixbuf.h"
18 #include "nsStringStream.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/TimeStamp.h"
21 #include "nsDragService.h"
22 #include "mozwayland/mozwayland.h"
23 #include "nsWaylandDisplay.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/StaticPrefs_widget.h"
27 #include "nsThreadUtils.h"
36 using namespace mozilla
;
37 using namespace mozilla::widget
;
39 const char* nsRetrievalContextWayland::sTextMimeTypes
[TEXT_MIME_TYPES_NUM
] = {
40 "text/plain;charset=utf-8", "UTF8_STRING", "COMPOUND_TEXT"};
42 static inline GdkDragAction
wl_to_gdk_actions(uint32_t dnd_actions
) {
43 GdkDragAction actions
= GdkDragAction(0);
45 if (dnd_actions
& WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY
)
46 actions
= GdkDragAction(actions
| GDK_ACTION_COPY
);
47 if (dnd_actions
& WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE
)
48 actions
= GdkDragAction(actions
| GDK_ACTION_MOVE
);
53 static inline uint32_t gdk_to_wl_actions(GdkDragAction action
) {
54 uint32_t dnd_actions
= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
;
56 if (action
& (GDK_ACTION_COPY
| GDK_ACTION_LINK
| GDK_ACTION_PRIVATE
))
57 dnd_actions
|= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY
;
58 if (action
& GDK_ACTION_MOVE
)
59 dnd_actions
|= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE
;
64 static GtkWidget
* get_gtk_widget_for_wl_surface(struct wl_surface
* surface
) {
65 GdkWindow
* gdkParentWindow
=
66 static_cast<GdkWindow
*>(wl_surface_get_user_data(surface
));
68 gpointer user_data
= nullptr;
69 gdk_window_get_user_data(gdkParentWindow
, &user_data
);
71 return GTK_WIDGET(user_data
);
74 static void data_offer_offer(void* data
, struct wl_data_offer
* wl_data_offer
,
76 auto* offer
= static_cast<DataOffer
*>(data
);
77 LOGCLIP("Data offer %p add MIME %s\n", wl_data_offer
, type
);
78 offer
->AddMIMEType(type
);
81 /* Advertise all available drag and drop actions from source.
82 * We don't use that but follow gdk_wayland_drag_context_commit_status()
83 * from gdkdnd-wayland.c here.
85 static void data_offer_source_actions(void* data
,
86 struct wl_data_offer
* wl_data_offer
,
87 uint32_t source_actions
) {
88 auto* dragContext
= static_cast<DataOffer
*>(data
);
89 dragContext
->SetAvailableDragActions(source_actions
);
92 /* Advertise recently selected drag and drop action by compositor, based
93 * on source actions and user choice (key modifiers, etc.).
95 static void data_offer_action(void* data
, struct wl_data_offer
* wl_data_offer
,
96 uint32_t dnd_action
) {
97 auto* dropContext
= static_cast<DataOffer
*>(data
);
98 dropContext
->SetSelectedDragAction(dnd_action
);
100 if (dropContext
->GetWidget()) {
103 dropContext
->GetLastDropInfo(&time
, &x
, &y
);
104 WindowDragMotionHandler(dropContext
->GetWidget(), nullptr, dropContext
, x
,
109 /* wl_data_offer callback description:
111 * data_offer_offer - Is called for each MIME type available at wl_data_offer.
112 * data_offer_source_actions - This event indicates the actions offered by
114 * data_offer_action - This event indicates the action selected by
115 * the compositor after matching the source/destination
118 static const moz_wl_data_offer_listener data_offer_listener
= {
119 data_offer_offer
, data_offer_source_actions
, data_offer_action
};
121 DataOffer::DataOffer(wl_data_offer
* aDataOffer
)
122 : mWaylandDataOffer(aDataOffer
),
124 mAsyncContentLength(),
127 mSelectedDragAction(),
128 mAvailableDragActions(),
133 if (mWaylandDataOffer
) {
134 wl_data_offer_add_listener(
135 mWaylandDataOffer
, (struct wl_data_offer_listener
*)&data_offer_listener
,
140 DataOffer::~DataOffer() {
141 g_clear_pointer(&mWaylandDataOffer
, wl_data_offer_destroy
);
143 // Async transfer was finished after time limit. In such case release the
145 if (mGetterFinished
&& mAsyncContentLength
&& mAsyncContentData
) {
146 g_free((void*)mAsyncContentData
);
150 bool DataOffer::RequestDataTransfer(const char* aMimeType
, int fd
) {
151 LOGCLIP("DataOffer::RequestDataTransfer MIME %s FD %d\n", aMimeType
, fd
);
152 if (mWaylandDataOffer
) {
153 wl_data_offer_receive(mWaylandDataOffer
, aMimeType
, fd
);
160 void DataOffer::AddMIMEType(const char* aMimeType
) {
161 GdkAtom atom
= gdk_atom_intern(aMimeType
, FALSE
);
162 mTargetMIMETypes
.AppendElement(atom
);
165 GdkAtom
* DataOffer::GetTargets(int* aTargetNum
) {
166 int length
= mTargetMIMETypes
.Length();
172 GdkAtom
* targetList
=
173 reinterpret_cast<GdkAtom
*>(g_malloc(sizeof(GdkAtom
) * length
));
174 for (int32_t j
= 0; j
< length
; j
++) {
175 targetList
[j
] = mTargetMIMETypes
[j
];
178 *aTargetNum
= length
;
182 bool DataOffer::HasTarget(const char* aMimeType
) {
183 int length
= mTargetMIMETypes
.Length();
184 for (int32_t j
= 0; j
< length
; j
++) {
185 if (mTargetMIMETypes
[j
] == gdk_atom_intern(aMimeType
, FALSE
)) {
186 LOGCLIP("DataOffer::HasTarget() we have mime %s\n", aMimeType
);
190 LOGCLIP("DataOffer::HasTarget() missing mime %s\n", aMimeType
);
194 static bool MakeFdNonBlocking(int fd
) {
195 return fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) != -1;
198 char* DataOffer::GetDataInternal(const char* aMimeType
,
199 uint32_t* aContentLength
) {
200 LOGCLIP("GetDataInternal() mime %s\n", aMimeType
);
203 if (pipe(pipe_fd
) == -1) {
207 GIOChannel
* channel
= nullptr;
209 auto free
= mozilla::MakeScopeExit([&] {
211 g_io_channel_unref(channel
);
213 if (pipe_fd
[0] >= 0) {
216 if (pipe_fd
[1] >= 0) {
221 if (!MakeFdNonBlocking(pipe_fd
[0]) || !MakeFdNonBlocking(pipe_fd
[1])) {
225 if (!RequestDataTransfer(aMimeType
, pipe_fd
[1])) {
226 NS_WARNING("DataOffer::RequestDataTransfer() failed!");
233 // Flush wl_display connection to get clipboard data uploaded from display to
235 wl_display_flush(WaylandDisplayGet()->GetDisplay());
237 channel
= g_io_channel_unix_new(pipe_fd
[0]);
238 GError
* error
= nullptr;
239 char* clipboardData
= nullptr;
242 ret
= g_io_channel_set_encoding(channel
, nullptr, &error
);
243 g_clear_pointer(&error
, g_error_free
);
244 if (ret
!= G_IO_STATUS_NORMAL
) {
245 NS_WARNING("g_io_channel_set_encoding failed!");
249 const PRTime entryTime
= PR_Now();
252 LOGCLIP("reading data...\n");
253 ret
= g_io_channel_read_to_end(channel
, &clipboardData
, &len
, &error
);
254 if (ret
== G_IO_STATUS_NORMAL
) {
257 if (ret
== G_IO_STATUS_AGAIN
) {
258 wl_display_flush(WaylandDisplayGet()->GetDisplay());
259 // check the number of iterations
260 PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
261 if (PR_Now() - entryTime
> kClipboardTimeout
) {
264 } else { // G_IO_STATUS_ERROR
267 nsPrintfCString("Unexpected error when reading clipboard data: %s",
275 *aContentLength
= len
;
277 if (*aContentLength
== 0) {
278 // We don't have valid clipboard data although
279 // g_io_channel_read_to_end() allocated clipboardData for us.
280 // Release it now and return nullptr to indicate
281 // we don't have reqested data flavour.
282 g_free((void*)clipboardData
);
283 clipboardData
= nullptr;
286 LOGCLIP(" Got clipboard data length %d\n", *aContentLength
);
287 return clipboardData
;
290 void DataOffer::GetDataAsyncInternal(const char* aMimeType
) {
291 mAsyncContentData
= GetDataInternal(aMimeType
, &mAsyncContentLength
);
292 mGetterFinished
= true;
295 char* DataOffer::GetData(const char* aMimeType
, uint32_t* aContentLength
) {
296 LOGCLIP("DataOffer::GetData() mime %s\n", aMimeType
);
298 if (!HasTarget(aMimeType
)) {
299 LOGCLIP(" Failed: DataOffer does not contain %s MIME!\n", aMimeType
);
303 return GetDataInternal(aMimeType
, aContentLength
);
306 char* DataOffer::GetDataAsync(const char* aMimeType
, uint32_t* aContentLength
) {
307 LOGCLIP("DataOffer::GetDataAsync() mime %s\n", aMimeType
);
309 if (!HasTarget(aMimeType
)) {
310 LOGCLIP(" Failed: DataOffer does not contain %s MIME!\n", aMimeType
);
314 if (!mMutex
.TryLock()) {
315 LOGCLIP(" Failed: DataOffer is already used!\n");
318 auto unlock
= mozilla::MakeScopeExit([&] {
319 // If async getter was sucessful transfer ownership of the clipboard data
321 if (mGetterFinished
) {
322 mAsyncContentLength
= 0;
323 mAsyncContentData
= nullptr;
325 // Remove offers for failed transfers
326 LOGCLIP(" data offer was not finished in time, clearing\n");
327 g_clear_pointer(&mWaylandDataOffer
, wl_data_offer_destroy
);
332 mAsyncContentLength
= 0;
333 mAsyncContentData
= nullptr;
334 mGetterFinished
= false;
336 RefPtr
<DataOffer
> offer(this);
337 NS_DispatchBackgroundTask(
338 NS_NewRunnableFunction("DataOffer::GetDataInternal",
339 [offer
, aMimeType
]() -> void {
340 offer
->GetDataAsyncInternal(aMimeType
);
342 nsIEventTarget::NS_DISPATCH_NORMAL
);
345 PRTime entryTime
= PR_Now();
346 while (!mGetterFinished
) {
347 if (iteration
++ > kClipboardFastIterationNum
) {
348 PR_Sleep(PR_MillisecondsToInterval(10)); /* sleep for 10 ms/iteration */
349 if (PR_Now() - entryTime
> kClipboardTimeout
) {
350 LOGCLIP(" hit time limit\n");
354 LOGCLIP("doing iteration %d msec %ld ...\n", (iteration
- 1),
355 (long)((PR_Now() - entryTime
) / 1000));
356 gtk_main_iteration();
359 if (!mGetterFinished
) {
360 LOGCLIP(" failed to get async clipboard data in time limit\n");
365 LOGCLIP(" ineration over, got data %p len %d\n", mAsyncContentData
,
366 mAsyncContentLength
);
367 *aContentLength
= mAsyncContentLength
;
368 return mAsyncContentData
;
371 void DataOffer::DragOfferAccept(const char* aMimeType
) {
372 LOGDRAG("DataOffer::DragOfferAccept MIME %s mTime %d\n", aMimeType
, mTime
);
373 if (!HasTarget(aMimeType
)) {
374 LOGCLIP(" DataOffer: DataOffer does not contain %s MIME!\n", aMimeType
);
377 wl_data_offer_accept(mWaylandDataOffer
, mTime
, aMimeType
);
380 /* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c
383 void DataOffer::SetDragStatus(GdkDragAction aPreferredAction
) {
384 uint32_t preferredAction
= gdk_to_wl_actions(aPreferredAction
);
385 uint32_t allActions
= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
;
387 LOGDRAG("DataOffer::SetDragStatus aPreferredAction %d\n", aPreferredAction
);
389 /* We only don't choose a preferred action if we don't accept any.
390 * If we do accept any, it is currently alway copy and move
392 if (preferredAction
!= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
) {
393 allActions
= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY
|
394 WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE
;
397 wl_data_offer_set_actions(mWaylandDataOffer
, allActions
, preferredAction
);
399 /* Workaround Wayland D&D architecture here. To get the data_device_drop()
400 signal (which routes to nsDragService::GetData() call) we need to
401 accept at least one mime type before data_device_leave().
403 Real wl_data_offer_accept() for actualy requested data mime type is
404 called from nsDragService::GetData().
406 if (mTargetMIMETypes
[0]) {
407 wl_data_offer_accept(mWaylandDataOffer
, mTime
,
408 gdk_atom_name(mTargetMIMETypes
[0]));
412 void DataOffer::SetSelectedDragAction(uint32_t aWaylandAction
) {
413 mSelectedDragAction
= aWaylandAction
;
416 GdkDragAction
DataOffer::GetSelectedDragAction() {
417 return wl_to_gdk_actions(mSelectedDragAction
);
420 void DataOffer::SetAvailableDragActions(uint32_t aWaylandActions
) {
421 mAvailableDragActions
= aWaylandActions
;
424 bool PrimaryDataOffer::RequestDataTransfer(const char* aMimeType
, int fd
) {
425 if (mPrimaryDataOfferGtk
) {
426 gtk_primary_selection_offer_receive(mPrimaryDataOfferGtk
, aMimeType
, fd
);
429 if (mPrimaryDataOfferZwpV1
) {
430 zwp_primary_selection_offer_v1_receive(mPrimaryDataOfferZwpV1
, aMimeType
,
437 static void primary_data_offer(
438 void* data
, gtk_primary_selection_offer
* primary_selection_offer
,
439 const char* mime_type
) {
440 LOGCLIP("Primary data offer %p add MIME %s\n", primary_selection_offer
,
442 auto* offer
= static_cast<DataOffer
*>(data
);
443 offer
->AddMIMEType(mime_type
);
446 static void primary_data_offer(
447 void* data
, zwp_primary_selection_offer_v1
* primary_selection_offer
,
448 const char* mime_type
) {
449 LOGCLIP("Primary data offer %p add MIME %s\n", primary_selection_offer
,
451 auto* offer
= static_cast<DataOffer
*>(data
);
452 offer
->AddMIMEType(mime_type
);
455 /* gtk_primary_selection_offer_listener callback description:
457 * primary_data_offer - Is called for each MIME type available at
458 * gtk_primary_selection_offer.
460 static const struct gtk_primary_selection_offer_listener
461 primary_selection_offer_listener_gtk
= {primary_data_offer
};
463 static const struct zwp_primary_selection_offer_v1_listener
464 primary_selection_offer_listener_zwp_v1
= {primary_data_offer
};
466 PrimaryDataOffer::PrimaryDataOffer(
467 gtk_primary_selection_offer
* aPrimaryDataOffer
)
468 : DataOffer(nullptr),
469 mPrimaryDataOfferGtk(aPrimaryDataOffer
),
470 mPrimaryDataOfferZwpV1(nullptr) {
471 gtk_primary_selection_offer_add_listener(
472 aPrimaryDataOffer
, &primary_selection_offer_listener_gtk
, this);
475 PrimaryDataOffer::PrimaryDataOffer(
476 zwp_primary_selection_offer_v1
* aPrimaryDataOffer
)
477 : DataOffer(nullptr),
478 mPrimaryDataOfferGtk(nullptr),
479 mPrimaryDataOfferZwpV1(aPrimaryDataOffer
) {
480 zwp_primary_selection_offer_v1_add_listener(
481 aPrimaryDataOffer
, &primary_selection_offer_listener_zwp_v1
, this);
484 PrimaryDataOffer::~PrimaryDataOffer(void) {
485 if (mPrimaryDataOfferGtk
) {
486 gtk_primary_selection_offer_destroy(mPrimaryDataOfferGtk
);
488 if (mPrimaryDataOfferZwpV1
) {
489 zwp_primary_selection_offer_v1_destroy(mPrimaryDataOfferZwpV1
);
493 void DataOffer::DropDataEnter(GtkWidget
* aGtkWidget
, uint32_t aTime
, nscoord aX
,
496 mGtkWidget
= aGtkWidget
;
501 void DataOffer::DropMotion(uint32_t aTime
, nscoord aX
, nscoord aY
) {
507 void DataOffer::GetLastDropInfo(uint32_t* aTime
, nscoord
* aX
, nscoord
* aY
) {
513 GdkDragAction
DataOffer::GetAvailableDragActions() {
514 GdkDragAction gdkAction
= GetSelectedDragAction();
516 // We emulate gdk_drag_context_get_actions() here.
518 gdkAction
= wl_to_gdk_actions(mAvailableDragActions
);
524 GList
* DataOffer::GetDragTargets() {
526 GdkAtom
* atoms
= GetTargets(&targetNums
);
528 GList
* targetList
= nullptr;
529 for (int i
= 0; i
< targetNums
; i
++) {
530 targetList
= g_list_append(targetList
, GDK_ATOM_TO_POINTER(atoms
[i
]));
536 char* DataOffer::GetDragData(const char* aMimeType
, uint32_t* aContentLength
) {
537 LOGDRAG("DataOffer::GetData %s\n", aMimeType
);
538 if (!HasTarget(aMimeType
)) {
541 DragOfferAccept(aMimeType
);
542 return GetDataAsync(aMimeType
, aContentLength
);
545 RefPtr
<DataOffer
> nsRetrievalContextWayland::FindActiveOffer(
546 wl_data_offer
* aDataOffer
, bool aRemove
) {
547 const int len
= mActiveOffers
.Length();
548 for (int i
= 0; i
< len
; i
++) {
549 if (mActiveOffers
[i
] && mActiveOffers
[i
]->MatchesOffer(aDataOffer
)) {
550 RefPtr
<DataOffer
> ret
= mActiveOffers
[i
];
552 mActiveOffers
[i
] = nullptr;
560 void nsRetrievalContextWayland::InsertOffer(RefPtr
<DataOffer
> aDataOffer
) {
561 const int len
= mActiveOffers
.Length();
562 for (int i
= 0; i
< len
; i
++) {
563 if (!mActiveOffers
[i
]) {
564 mActiveOffers
[i
] = aDataOffer
;
568 mActiveOffers
.AppendElement(aDataOffer
);
571 void nsRetrievalContextWayland::RegisterNewDataOffer(
572 wl_data_offer
* aDataOffer
) {
574 "nsRetrievalContextWayland::RegisterNewDataOffer (wl_data_offer) %p\n",
577 if (FindActiveOffer(aDataOffer
)) {
578 LOGCLIP(" offer already exists, protocol error?\n");
582 InsertOffer(new DataOffer(aDataOffer
));
585 void nsRetrievalContextWayland::RegisterNewDataOffer(
586 gtk_primary_selection_offer
* aPrimaryDataOffer
) {
587 LOGCLIP("nsRetrievalContextWayland::RegisterNewDataOffer (primary) %p\n",
590 if (FindActiveOffer((wl_data_offer
*)aPrimaryDataOffer
)) {
591 LOGCLIP(" offer already exists, protocol error?\n");
595 InsertOffer(new PrimaryDataOffer(aPrimaryDataOffer
));
598 void nsRetrievalContextWayland::RegisterNewDataOffer(
599 zwp_primary_selection_offer_v1
* aPrimaryDataOffer
) {
600 LOGCLIP("nsRetrievalContextWayland::RegisterNewDataOffer (primary ZWP) %p\n",
603 if (FindActiveOffer((wl_data_offer
*)aPrimaryDataOffer
)) {
604 LOGCLIP(" offer already exists, protocol error?\n");
608 InsertOffer(new PrimaryDataOffer(aPrimaryDataOffer
));
611 void nsRetrievalContextWayland::SetClipboardDataOffer(
612 wl_data_offer
* aDataOffer
) {
614 "nsRetrievalContextWayland::SetClipboardDataOffer (wl_data_offer) %p\n",
617 // Delete existing clipboard data offer
618 mClipboardOffer
= nullptr;
620 // null aDataOffer indicates that our clipboard content
621 // is no longer valid and should be release.
623 mClipboardOffer
= FindActiveOffer(aDataOffer
, /* remove */ true);
627 void nsRetrievalContextWayland::SetPrimaryDataOffer(
628 gtk_primary_selection_offer
* aPrimaryDataOffer
) {
629 LOGCLIP("nsRetrievalContextWayland::SetPrimaryDataOffer (primary) %p\n",
632 // Release any primary offer we have.
633 mPrimaryOffer
= nullptr;
635 // aPrimaryDataOffer can be null which means we lost
636 // the mouse selection.
637 if (aPrimaryDataOffer
) {
639 FindActiveOffer((wl_data_offer
*)aPrimaryDataOffer
, /* remove */ true);
643 void nsRetrievalContextWayland::SetPrimaryDataOffer(
644 zwp_primary_selection_offer_v1
* aPrimaryDataOffer
) {
645 LOGCLIP("nsRetrievalContextWayland::SetPrimaryDataOffer (primary ZWP)%p\n",
648 // Release any primary offer we have.
649 mPrimaryOffer
= nullptr;
651 // aPrimaryDataOffer can be null which means we lost
652 // the mouse selection.
653 if (aPrimaryDataOffer
) {
655 FindActiveOffer((wl_data_offer
*)aPrimaryDataOffer
, /* remove */ true);
659 void nsRetrievalContextWayland::AddDragAndDropDataOffer(
660 wl_data_offer
* aDropDataOffer
) {
661 LOGDRAG("nsRetrievalContextWayland::AddDragAndDropDataOffer %p\n",
664 // Remove any existing D&D contexts.
665 mDragContext
= nullptr;
666 if (aDropDataOffer
) {
667 mDragContext
= FindActiveOffer(aDropDataOffer
, /* remove */ true);
671 // We have a new fresh data content.
672 // We should attach listeners to it and save for further use.
673 static void data_device_data_offer(void* data
,
674 struct wl_data_device
* data_device
,
675 struct wl_data_offer
* offer
) {
676 LOGCLIP("data_device_data_offer(), wl_data_offer %p\n", offer
);
677 nsRetrievalContextWayland
* context
=
678 static_cast<nsRetrievalContextWayland
*>(data
);
679 context
->RegisterNewDataOffer(offer
);
682 // The new fresh data content is clipboard.
683 static void data_device_selection(void* data
,
684 struct wl_data_device
* wl_data_device
,
685 struct wl_data_offer
* offer
) {
686 LOGCLIP("data_device_selection(), set wl_data_offer %p\n", offer
);
687 nsRetrievalContextWayland
* context
=
688 static_cast<nsRetrievalContextWayland
*>(data
);
689 context
->SetClipboardDataOffer(offer
);
692 // The new fresh wayland data content is drag and drop.
693 static void data_device_enter(void* data
, struct wl_data_device
* data_device
,
694 uint32_t time
, struct wl_surface
* surface
,
695 int32_t x_fixed
, int32_t y_fixed
,
696 struct wl_data_offer
* offer
) {
697 LOGDRAG("nsWindow data_device_enter");
698 nsRetrievalContextWayland
* context
=
699 static_cast<nsRetrievalContextWayland
*>(data
);
700 context
->AddDragAndDropDataOffer(offer
);
702 RefPtr
<DataOffer
> dragContext
= context
->GetDragContext();
704 GtkWidget
* gtkWidget
= get_gtk_widget_for_wl_surface(surface
);
706 NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
710 LOGDRAG("nsWindow data_device_enter for GtkWidget %p\n", (void*)gtkWidget
);
711 dragContext
->DropDataEnter(gtkWidget
, time
, wl_fixed_to_int(x_fixed
),
712 wl_fixed_to_int(y_fixed
));
716 static void data_device_leave(void* data
, struct wl_data_device
* data_device
) {
717 LOGDRAG("nsWindow data_device_leave");
718 nsRetrievalContextWayland
* context
=
719 static_cast<nsRetrievalContextWayland
*>(data
);
721 RefPtr
<DataOffer
> dropContext
= context
->GetDragContext();
723 WindowDragLeaveHandler(dropContext
->GetWidget());
725 LOGDRAG("nsWindow data_device_leave for GtkWidget %p\n",
726 (void*)dropContext
->GetWidget());
727 context
->ClearDragAndDropDataOffer();
731 static void data_device_motion(void* data
, struct wl_data_device
* data_device
,
732 uint32_t time
, int32_t x_fixed
,
734 LOGDRAG("nsWindow data_device_motion");
735 nsRetrievalContextWayland
* context
=
736 static_cast<nsRetrievalContextWayland
*>(data
);
738 RefPtr
<DataOffer
> dropContext
= context
->GetDragContext();
740 nscoord x
= wl_fixed_to_int(x_fixed
);
741 nscoord y
= wl_fixed_to_int(y_fixed
);
742 dropContext
->DropMotion(time
, x
, y
);
744 LOGDRAG("nsWindow data_device_motion for GtkWidget %p\n",
745 (void*)dropContext
->GetWidget());
746 WindowDragMotionHandler(dropContext
->GetWidget(), nullptr, dropContext
, x
,
751 static void data_device_drop(void* data
, struct wl_data_device
* data_device
) {
752 LOGDRAG("nsWindow data_device_drop");
753 nsRetrievalContextWayland
* context
=
754 static_cast<nsRetrievalContextWayland
*>(data
);
756 RefPtr
<DataOffer
> dropContext
= context
->GetDragContext();
760 dropContext
->GetLastDropInfo(&time
, &x
, &y
);
762 LOGDRAG("nsWindow data_device_drop GtkWidget %p\n",
763 (void*)dropContext
->GetWidget());
764 WindowDragDropHandler(dropContext
->GetWidget(), nullptr, dropContext
, x
, y
,
769 /* wl_data_device callback description:
771 * data_device_data_offer - It's called when there's a new wl_data_offer
772 * available. We need to attach wl_data_offer_listener
773 * to it to get available MIME types.
775 * data_device_selection - It's called when the new wl_data_offer
776 * is a clipboard content.
777 * data_device_enter - It's called when the new wl_data_offer is a drag & drop
778 * content and it's tied to actual wl_surface.
780 * data_device_leave - It's called when the wl_data_offer (drag & dop) is not
782 * data_device_motion - It's called when the drag and drop selection moves
784 * data_device_drop - It's called when D&D operation is sucessfully finished
785 * and we can read the data from D&D.
786 * It's generated only if we call wl_data_offer_accept() and
787 * wl_data_offer_set_actions() from data_device_motion
790 static const struct wl_data_device_listener data_device_listener
= {
791 data_device_data_offer
, data_device_enter
, data_device_leave
,
792 data_device_motion
, data_device_drop
, data_device_selection
};
794 static void primary_selection_data_offer(
795 void* data
, struct gtk_primary_selection_device
* primary_selection_device
,
796 struct gtk_primary_selection_offer
* primary_offer
) {
797 LOGCLIP("primary_selection_data_offer()\n");
798 // create and add listener
799 nsRetrievalContextWayland
* context
=
800 static_cast<nsRetrievalContextWayland
*>(data
);
801 context
->RegisterNewDataOffer(primary_offer
);
804 static void primary_selection_data_offer(
806 struct zwp_primary_selection_device_v1
* primary_selection_device
,
807 struct zwp_primary_selection_offer_v1
* primary_offer
) {
808 LOGCLIP("primary_selection_data_offer()\n");
809 // create and add listener
810 nsRetrievalContextWayland
* context
=
811 static_cast<nsRetrievalContextWayland
*>(data
);
812 context
->RegisterNewDataOffer(primary_offer
);
815 static void primary_selection_selection(
816 void* data
, struct gtk_primary_selection_device
* primary_selection_device
,
817 struct gtk_primary_selection_offer
* primary_offer
) {
818 LOGCLIP("primary_selection_selection()\n");
819 nsRetrievalContextWayland
* context
=
820 static_cast<nsRetrievalContextWayland
*>(data
);
821 context
->SetPrimaryDataOffer(primary_offer
);
824 static void primary_selection_selection(
826 struct zwp_primary_selection_device_v1
* primary_selection_device
,
827 struct zwp_primary_selection_offer_v1
* primary_offer
) {
828 LOGCLIP("primary_selection_selection()\n");
829 nsRetrievalContextWayland
* context
=
830 static_cast<nsRetrievalContextWayland
*>(data
);
831 context
->SetPrimaryDataOffer(primary_offer
);
834 /* gtk_primary_selection_device callback description:
836 * primary_selection_data_offer - It's called when there's a new
837 * gtk_primary_selection_offer available. We need to
838 * attach gtk_primary_selection_offer_listener to it
839 * to get available MIME types.
841 * primary_selection_selection - It's called when the new
842 * gtk_primary_selection_offer is a primary selection
843 * content. It can be also called with
844 * gtk_primary_selection_offer = null which means
845 * there's no primary selection.
847 static const struct gtk_primary_selection_device_listener
848 primary_selection_device_listener_gtk
= {
849 primary_selection_data_offer
,
850 primary_selection_selection
,
853 static const struct zwp_primary_selection_device_v1_listener
854 primary_selection_device_listener_zwp_v1
= {
855 primary_selection_data_offer
,
856 primary_selection_selection
,
859 bool nsRetrievalContextWayland::HasSelectionSupport(void) {
860 return (mDisplay
->GetPrimarySelectionDeviceManagerZwpV1() != nullptr ||
861 mDisplay
->GetPrimarySelectionDeviceManagerGtk() != nullptr);
864 void nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) {
865 LOGDRAG("nsRetrievalContextWayland::ClearDragAndDropDataOffer()\n");
866 mDragContext
= nullptr;
869 nsRetrievalContextWayland::nsRetrievalContextWayland(void)
870 : mDisplay(WaylandDisplayGet()),
871 mClipboardRequestNumber(0),
872 mClipboardData(nullptr),
873 mClipboardDataLength(0),
875 StaticPrefs::widget_wayland_async_data_transfer_enabled_AtStartup()) {
876 wl_data_device
* dataDevice
= wl_data_device_manager_get_data_device(
877 mDisplay
->GetDataDeviceManager(), mDisplay
->GetSeat());
878 wl_data_device_add_listener(dataDevice
, &data_device_listener
, this);
880 if (mDisplay
->GetPrimarySelectionDeviceManagerZwpV1()) {
881 zwp_primary_selection_device_v1
* primaryDataDevice
=
882 zwp_primary_selection_device_manager_v1_get_device(
883 mDisplay
->GetPrimarySelectionDeviceManagerZwpV1(),
884 mDisplay
->GetSeat());
885 zwp_primary_selection_device_v1_add_listener(
886 primaryDataDevice
, &primary_selection_device_listener_zwp_v1
, this);
887 } else if (mDisplay
->GetPrimarySelectionDeviceManagerGtk()) {
888 gtk_primary_selection_device
* primaryDataDevice
=
889 gtk_primary_selection_device_manager_get_device(
890 mDisplay
->GetPrimarySelectionDeviceManagerGtk(),
891 mDisplay
->GetSeat());
892 gtk_primary_selection_device_add_listener(
893 primaryDataDevice
, &primary_selection_device_listener_gtk
, this);
897 nsRetrievalContextWayland::~nsRetrievalContextWayland(void) {}
899 struct FastTrackClipboard
{
900 FastTrackClipboard(ClipboardDataType aDataType
, int aClipboardRequestNumber
,
901 RefPtr
<nsRetrievalContextWayland
> aRetrievalContex
)
902 : mClipboardRequestNumber(aClipboardRequestNumber
),
903 mRetrievalContex(std::move(aRetrievalContex
)),
904 mDataType(aDataType
) {}
905 int mClipboardRequestNumber
;
906 RefPtr
<nsRetrievalContextWayland
> mRetrievalContex
;
907 ClipboardDataType mDataType
;
910 static void wayland_clipboard_contents_received(
911 GtkClipboard
* clipboard
, GtkSelectionData
* selection_data
, gpointer data
) {
912 LOGCLIP("wayland_clipboard_contents_received() selection_data = %p\n",
914 FastTrackClipboard
* fastTrack
= static_cast<FastTrackClipboard
*>(data
);
915 fastTrack
->mRetrievalContex
->TransferFastTrackClipboard(
916 fastTrack
->mDataType
, fastTrack
->mClipboardRequestNumber
, selection_data
);
920 void nsRetrievalContextWayland::TransferFastTrackClipboard(
921 ClipboardDataType aDataType
, int aClipboardRequestNumber
,
922 GtkSelectionData
* aSelectionData
) {
924 "nsRetrievalContextWayland::TransferFastTrackClipboard(), "
925 "aSelectionData = %p\n",
928 if (mClipboardRequestNumber
!= aClipboardRequestNumber
) {
929 LOGCLIP(" request number does not match!\n");
932 LOGCLIP(" request number matches\n");
934 int dataLength
= gtk_selection_data_get_length(aSelectionData
);
935 if (dataLength
< 0) {
937 " gtk_clipboard_request_contents() failed to get clipboard "
939 ReleaseClipboardData(mClipboardData
);
944 case CLIPBOARD_TARGETS
: {
945 LOGCLIP(" fastracking %d bytes of clipboard targets.\n", dataLength
);
947 GdkAtom
* targets
= nullptr;
949 if (!gtk_selection_data_get_targets(aSelectionData
, &targets
,
952 ReleaseClipboardData(mClipboardData
);
955 mClipboardData
= reinterpret_cast<char*>(targets
);
956 mClipboardDataLength
= n_targets
;
960 case CLIPBOARD_TEXT
: {
961 LOGCLIP(" fastracking %d bytes of data.\n", dataLength
);
962 mClipboardDataLength
= dataLength
;
963 if (dataLength
> 0) {
964 mClipboardData
= reinterpret_cast<char*>(
965 g_malloc(sizeof(char) * (mClipboardDataLength
+ 1)));
966 memcpy(mClipboardData
, gtk_selection_data_get_data(aSelectionData
),
967 sizeof(char) * mClipboardDataLength
);
968 mClipboardData
[mClipboardDataLength
] = '\0';
969 LOGCLIP(" done, mClipboardData = %p\n", mClipboardData
);
971 ReleaseClipboardData(mClipboardData
);
977 GdkAtom
* nsRetrievalContextWayland::GetTargets(int32_t aWhichClipboard
,
979 /* If actual clipboard data is owned by us we don't need to go
980 * through Wayland but we ask Gtk+ to directly call data
981 * getter callback nsClipboard::SelectionGetEvent().
982 * see gtk_selection_convert() at gtk+/gtkselection.c.
984 GdkAtom selection
= GetSelectionAtom(aWhichClipboard
);
985 if (gdk_selection_owner_get(selection
)) {
986 LOGCLIP(" Asking for internal clipboard content.\n");
987 mClipboardRequestNumber
++;
988 gtk_clipboard_request_contents(
989 gtk_clipboard_get(selection
), gdk_atom_intern("TARGETS", FALSE
),
990 wayland_clipboard_contents_received
,
991 new FastTrackClipboard(CLIPBOARD_TARGETS
, mClipboardRequestNumber
,
993 *aTargetNum
= mClipboardDataLength
;
994 GdkAtom
* targets
= static_cast<GdkAtom
*>((void*)mClipboardData
);
995 // We don't hold the target list internally but we transfer the ownership.
996 mClipboardData
= nullptr;
997 mClipboardDataLength
= 0;
1001 if (GetSelectionAtom(aWhichClipboard
) == GDK_SELECTION_CLIPBOARD
) {
1002 if (mClipboardOffer
) {
1003 return mClipboardOffer
->GetTargets(aTargetNum
);
1006 if (mPrimaryOffer
) {
1007 return mPrimaryOffer
->GetTargets(aTargetNum
);
1015 const char* nsRetrievalContextWayland::GetClipboardData(
1016 const char* aMimeType
, int32_t aWhichClipboard
, uint32_t* aContentLength
) {
1017 NS_ASSERTION(mClipboardData
== nullptr && mClipboardDataLength
== 0,
1018 "Looks like we're leaking clipboard data here!");
1020 LOGCLIP("nsRetrievalContextWayland::GetClipboardData [%p] mime %s\n", this,
1023 /* If actual clipboard data is owned by us we don't need to go
1024 * through Wayland but we ask Gtk+ to directly call data
1025 * getter callback nsClipboard::SelectionGetEvent().
1026 * see gtk_selection_convert() at gtk+/gtkselection.c.
1028 GdkAtom selection
= GetSelectionAtom(aWhichClipboard
);
1029 if (gdk_selection_owner_get(selection
)) {
1030 LOGCLIP(" Asking for internal clipboard content.\n");
1031 mClipboardRequestNumber
++;
1032 gtk_clipboard_request_contents(
1033 gtk_clipboard_get(selection
), gdk_atom_intern(aMimeType
, FALSE
),
1034 wayland_clipboard_contents_received
,
1035 new FastTrackClipboard(CLIPBOARD_DATA
, mClipboardRequestNumber
, this));
1037 LOGCLIP(" Asking for remote clipboard content.\n");
1038 RefPtr
<DataOffer
> dataOffer
=
1039 (selection
== GDK_SELECTION_PRIMARY
) ? mPrimaryOffer
: mClipboardOffer
;
1041 // Something went wrong. We're requested to provide clipboard data
1042 // but we haven't got any from wayland.
1043 LOGCLIP(" We're missing dataOffer! mClipboardData = null\n");
1044 mClipboardData
= nullptr;
1045 mClipboardDataLength
= 0;
1047 LOGCLIP(" Getting clipboard data from compositor, MIME %s\n", aMimeType
);
1050 ? dataOffer
->GetDataAsync(aMimeType
, &mClipboardDataLength
)
1051 : dataOffer
->GetData(aMimeType
, &mClipboardDataLength
);
1052 LOGCLIP(" Got %d bytes of data, mClipboardData = %p\n",
1053 mClipboardDataLength
, mClipboardData
);
1057 *aContentLength
= mClipboardDataLength
;
1058 return reinterpret_cast<const char*>(mClipboardData
);
1061 const char* nsRetrievalContextWayland::GetClipboardText(
1062 int32_t aWhichClipboard
) {
1063 GdkAtom selection
= GetSelectionAtom(aWhichClipboard
);
1065 LOGCLIP("nsRetrievalContextWayland::GetClipboardText [%p], clipboard %s\n",
1066 this, (selection
== GDK_SELECTION_PRIMARY
) ? "Primary" : "Selection");
1068 const auto& dataOffer
=
1069 (selection
== GDK_SELECTION_PRIMARY
) ? mPrimaryOffer
: mClipboardOffer
;
1071 LOGCLIP(" We're missing data offer!\n");
1075 for (unsigned int i
= 0; i
< TEXT_MIME_TYPES_NUM
; i
++) {
1076 if (dataOffer
->HasTarget(sTextMimeTypes
[i
])) {
1077 LOGCLIP(" We have %s MIME type in clipboard, ask for it.\n",
1080 return GetClipboardData(sTextMimeTypes
[i
], aWhichClipboard
, &unused
);
1084 LOGCLIP(" There isn't text MIME type in clipboard!\n");
1088 void nsRetrievalContextWayland::ReleaseClipboardData(
1089 const char* aClipboardData
) {
1090 LOGCLIP("nsRetrievalContextWayland::ReleaseClipboardData [%p]\n",
1092 if (aClipboardData
!= mClipboardData
) {
1093 NS_WARNING("Wayland clipboard: Releasing unknown clipboard data!");
1095 g_free((void*)mClipboardData
);
1096 mClipboardDataLength
= 0;
1097 mClipboardData
= nullptr;