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
) {
33 GdkWindow
* window
= gtk_widget_get_window(widget
);
36 event
.selection
.type
= GDK_SELECTION_NOTIFY
;
37 event
.selection
.window
= window
;
38 event
.selection
.selection
=
39 gdk_x11_xatom_to_atom(xevent
->xselection
.selection
);
40 event
.selection
.target
= gdk_x11_xatom_to_atom(xevent
->xselection
.target
);
41 event
.selection
.property
=
42 gdk_x11_xatom_to_atom(xevent
->xselection
.property
);
43 event
.selection
.time
= xevent
->xselection
.time
;
45 gtk_widget_event(widget
, &event
);
49 static void DispatchPropertyNotifyEvent(GtkWidget
* widget
, XEvent
* xevent
) {
50 GdkWindow
* window
= gtk_widget_get_window(widget
);
51 if (window
&& ((gdk_window_get_events(window
)) & GDK_PROPERTY_CHANGE_MASK
)) {
53 event
.property
.type
= GDK_PROPERTY_NOTIFY
;
54 event
.property
.window
= window
;
55 event
.property
.atom
= gdk_x11_xatom_to_atom(xevent
->xproperty
.atom
);
56 event
.property
.time
= xevent
->xproperty
.time
;
57 event
.property
.state
= xevent
->xproperty
.state
;
59 gtk_widget_event(widget
, &event
);
63 struct checkEventContext
{
68 static Bool
checkEventProc(Display
* display
, XEvent
* event
, XPointer arg
) {
69 checkEventContext
* context
= (checkEventContext
*)arg
;
71 if (event
->xany
.type
== SelectionNotify
||
72 (event
->xany
.type
== PropertyNotify
&&
73 event
->xproperty
.atom
== context
->selAtom
)) {
74 GdkWindow
* cbWindow
= gdk_x11_window_lookup_for_display(
75 gdk_x11_lookup_xdisplay(display
), event
->xany
.window
);
77 GtkWidget
* cbWidget
= nullptr;
78 gdk_window_get_user_data(cbWindow
, (gpointer
*)&cbWidget
);
79 if (cbWidget
&& GTK_IS_WIDGET(cbWidget
)) {
80 context
->cbWidget
= cbWidget
;
89 ClipboardData
nsRetrievalContextX11::WaitForClipboardData(
90 ClipboardDataType aDataType
, int32_t aWhichClipboard
,
91 const char* aMimeType
) {
92 AsyncGtkClipboardRequest
request(aDataType
, aWhichClipboard
, aMimeType
);
93 if (request
.HasCompleted()) {
94 // the request completed synchronously
95 return request
.TakeResult();
98 GdkDisplay
* gdkDisplay
= gdk_display_get_default();
99 // gdk_display_get_default() returns null on headless
100 if (widget::GdkIsX11Display(gdkDisplay
)) {
101 Display
* xDisplay
= GDK_DISPLAY_XDISPLAY(gdkDisplay
);
102 checkEventContext context
;
103 context
.cbWidget
= nullptr;
105 gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION", FALSE
));
107 // Send X events which are relevant to the ongoing selection retrieval
108 // to the clipboard widget. Wait until either the operation completes, or
109 // we hit our timeout. All other X events remain queued.
114 pfd
.fd
= ConnectionNumber(xDisplay
);
116 TimeStamp start
= TimeStamp::Now();
121 while (XCheckIfEvent(xDisplay
, &xevent
, checkEventProc
,
122 (XPointer
)&context
)) {
123 if (xevent
.xany
.type
== SelectionNotify
)
124 DispatchSelectionNotifyEvent(context
.cbWidget
, &xevent
);
126 DispatchPropertyNotifyEvent(context
.cbWidget
, &xevent
);
128 if (request
.HasCompleted()) {
129 return request
.TakeResult();
133 TimeStamp now
= TimeStamp::Now();
134 int timeout
= std::max
<int>(
135 0, kClipboardTimeout
/ 1000 - (now
- start
).ToMilliseconds());
136 poll_result
= poll(&pfd
, 1, timeout
);
137 } while ((poll_result
== 1 && (pfd
.revents
& (POLLHUP
| POLLERR
)) == 0) ||
138 (poll_result
== -1 && errno
== EINTR
));
141 MOZ_CLIPBOARD_LOG("exceeded clipboard timeout");
145 ClipboardTargets
nsRetrievalContextX11::GetTargetsImpl(
146 int32_t aWhichClipboard
) {
147 MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetTargetsImpl(%s)\n",
148 aWhichClipboard
== nsClipboard::kSelectionClipboard
151 return WaitForClipboardData(ClipboardDataType::Targets
, aWhichClipboard
)
155 ClipboardData
nsRetrievalContextX11::GetClipboardData(const char* aMimeType
,
156 int32_t aWhichClipboard
) {
157 MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetClipboardData(%s) MIME %s\n",
158 aWhichClipboard
== nsClipboard::kSelectionClipboard
163 return WaitForClipboardData(ClipboardDataType::Data
, aWhichClipboard
,
167 GUniquePtr
<char> nsRetrievalContextX11::GetClipboardText(
168 int32_t aWhichClipboard
) {
169 MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetClipboardText(%s)\n",
170 aWhichClipboard
== nsClipboard::kSelectionClipboard
174 return WaitForClipboardData(ClipboardDataType::Text
, aWhichClipboard
)