1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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 "AsyncGtkClipboardRequest.h"
11 #include "nsClipboardX11.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/TimeStamp.h"
14 #include "mozilla/WidgetUtilsGtk.h"
18 // For manipulation of the X event queue
23 #include <sys/types.h>
26 #include "X11UndefineNone.h"
28 using namespace mozilla
;
30 nsRetrievalContextX11::nsRetrievalContextX11() = default;
32 static void DispatchSelectionNotifyEvent(GtkWidget
* widget
, XEvent
* xevent
) {
34 event
.selection
.type
= GDK_SELECTION_NOTIFY
;
35 event
.selection
.window
= gtk_widget_get_window(widget
);
36 event
.selection
.selection
=
37 gdk_x11_xatom_to_atom(xevent
->xselection
.selection
);
38 event
.selection
.target
= gdk_x11_xatom_to_atom(xevent
->xselection
.target
);
39 event
.selection
.property
= gdk_x11_xatom_to_atom(xevent
->xselection
.property
);
40 event
.selection
.time
= xevent
->xselection
.time
;
42 gtk_widget_event(widget
, &event
);
45 static void DispatchPropertyNotifyEvent(GtkWidget
* widget
, XEvent
* xevent
) {
46 GdkWindow
* window
= gtk_widget_get_window(widget
);
47 if ((gdk_window_get_events(window
)) & GDK_PROPERTY_CHANGE_MASK
) {
49 event
.property
.type
= GDK_PROPERTY_NOTIFY
;
50 event
.property
.window
= window
;
51 event
.property
.atom
= gdk_x11_xatom_to_atom(xevent
->xproperty
.atom
);
52 event
.property
.time
= xevent
->xproperty
.time
;
53 event
.property
.state
= xevent
->xproperty
.state
;
55 gtk_widget_event(widget
, &event
);
59 struct checkEventContext
{
64 static Bool
checkEventProc(Display
* display
, XEvent
* event
, XPointer arg
) {
65 checkEventContext
* context
= (checkEventContext
*)arg
;
67 if (event
->xany
.type
== SelectionNotify
||
68 (event
->xany
.type
== PropertyNotify
&&
69 event
->xproperty
.atom
== context
->selAtom
)) {
70 GdkWindow
* cbWindow
= gdk_x11_window_lookup_for_display(
71 gdk_x11_lookup_xdisplay(display
), event
->xany
.window
);
73 GtkWidget
* cbWidget
= nullptr;
74 gdk_window_get_user_data(cbWindow
, (gpointer
*)&cbWidget
);
75 if (cbWidget
&& GTK_IS_WIDGET(cbWidget
)) {
76 context
->cbWidget
= cbWidget
;
85 ClipboardData
nsRetrievalContextX11::WaitForClipboardData(
86 ClipboardDataType aDataType
, int32_t aWhichClipboard
,
87 const char* aMimeType
) {
88 AsyncGtkClipboardRequest
request(aDataType
, aWhichClipboard
, aMimeType
);
89 if (request
.HasCompleted()) {
90 // the request completed synchronously
91 return request
.TakeResult();
94 GdkDisplay
* gdkDisplay
= gdk_display_get_default();
95 // gdk_display_get_default() returns null on headless
96 if (widget::GdkIsX11Display(gdkDisplay
)) {
97 Display
* xDisplay
= GDK_DISPLAY_XDISPLAY(gdkDisplay
);
98 checkEventContext context
;
99 context
.cbWidget
= nullptr;
101 gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION", FALSE
));
103 // Send X events which are relevant to the ongoing selection retrieval
104 // to the clipboard widget. Wait until either the operation completes, or
105 // we hit our timeout. All other X events remain queued.
110 pfd
.fd
= ConnectionNumber(xDisplay
);
112 TimeStamp start
= TimeStamp::Now();
117 while (XCheckIfEvent(xDisplay
, &xevent
, checkEventProc
,
118 (XPointer
)&context
)) {
119 if (xevent
.xany
.type
== SelectionNotify
)
120 DispatchSelectionNotifyEvent(context
.cbWidget
, &xevent
);
122 DispatchPropertyNotifyEvent(context
.cbWidget
, &xevent
);
124 if (request
.HasCompleted()) {
125 return request
.TakeResult();
129 TimeStamp now
= TimeStamp::Now();
130 int timeout
= std::max
<int>(
131 0, kClipboardTimeout
/ 1000 - (now
- start
).ToMilliseconds());
132 poll_result
= poll(&pfd
, 1, timeout
);
133 } while ((poll_result
== 1 && (pfd
.revents
& (POLLHUP
| POLLERR
)) == 0) ||
134 (poll_result
== -1 && errno
== EINTR
));
137 LOGCLIP("exceeded clipboard timeout");
141 ClipboardTargets
nsRetrievalContextX11::GetTargetsImpl(
142 int32_t aWhichClipboard
) {
143 LOGCLIP("nsRetrievalContextX11::GetTargetsImpl(%s)\n",
144 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
146 return WaitForClipboardData(ClipboardDataType::Targets
, aWhichClipboard
)
150 ClipboardData
nsRetrievalContextX11::GetClipboardData(const char* aMimeType
,
151 int32_t aWhichClipboard
) {
152 LOGCLIP("nsRetrievalContextX11::GetClipboardData(%s) MIME %s\n",
153 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
157 return WaitForClipboardData(ClipboardDataType::Data
, aWhichClipboard
,
161 GUniquePtr
<char> nsRetrievalContextX11::GetClipboardText(
162 int32_t aWhichClipboard
) {
163 LOGCLIP("nsRetrievalContextX11::GetClipboardText(%s)\n",
164 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
167 return WaitForClipboardData(ClipboardDataType::Text
, aWhichClipboard
)