Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / gtk / nsClipboardX11.cpp
blob2693622922e6a93bb7081e005169a6788efe48d3
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3 */
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"
16 #include <gtk/gtk.h>
18 // For manipulation of the X event queue
19 #include <X11/Xlib.h>
20 #include <poll.h>
21 #include <gdk/gdkx.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <errno.h>
25 #include <unistd.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);
34 if (window) {
35 GdkEvent event = {};
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)) {
52 GdkEvent event = {};
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 {
64 GtkWidget* cbWidget;
65 Atom selAtom;
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);
76 if (cbWindow) {
77 GtkWidget* cbWidget = nullptr;
78 gdk_window_get_user_data(cbWindow, (gpointer*)&cbWidget);
79 if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
80 context->cbWidget = cbWidget;
81 return X11True;
86 return X11False;
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;
104 context.selAtom =
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.
111 int poll_result;
113 struct pollfd pfd;
114 pfd.fd = ConnectionNumber(xDisplay);
115 pfd.events = POLLIN;
116 TimeStamp start = TimeStamp::Now();
118 do {
119 XEvent xevent;
121 while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
122 (XPointer)&context)) {
123 if (xevent.xany.type == SelectionNotify)
124 DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
125 else
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");
142 return {};
145 ClipboardTargets nsRetrievalContextX11::GetTargetsImpl(
146 int32_t aWhichClipboard) {
147 MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetTargetsImpl(%s)\n",
148 aWhichClipboard == nsClipboard::kSelectionClipboard
149 ? "primary"
150 : "clipboard");
151 return WaitForClipboardData(ClipboardDataType::Targets, aWhichClipboard)
152 .ExtractTargets();
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
159 ? "primary"
160 : "clipboard",
161 aMimeType);
163 return WaitForClipboardData(ClipboardDataType::Data, aWhichClipboard,
164 aMimeType);
167 GUniquePtr<char> nsRetrievalContextX11::GetClipboardText(
168 int32_t aWhichClipboard) {
169 MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetClipboardText(%s)\n",
170 aWhichClipboard == nsClipboard::kSelectionClipboard
171 ? "primary"
172 : "clipboard");
174 return WaitForClipboardData(ClipboardDataType::Text, aWhichClipboard)
175 .ExtractText();