summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsClipboardX11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/nsClipboardX11.cpp')
-rw-r--r--widget/gtk/nsClipboardX11.cpp173
1 files changed, 173 insertions, 0 deletions
diff --git a/widget/gtk/nsClipboardX11.cpp b/widget/gtk/nsClipboardX11.cpp
new file mode 100644
index 0000000000..db12d1e69c
--- /dev/null
+++ b/widget/gtk/nsClipboardX11.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "AsyncGtkClipboardRequest.h"
+#include "nsClipboardX11.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/WidgetUtilsGtk.h"
+
+#include <gtk/gtk.h>
+
+// For manipulation of the X event queue
+#include <X11/Xlib.h>
+#include <poll.h>
+#include <gdk/gdkx.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include "X11UndefineNone.h"
+
+using namespace mozilla;
+
+nsRetrievalContextX11::nsRetrievalContextX11() = default;
+
+static void DispatchSelectionNotifyEvent(GtkWidget* widget, XEvent* xevent) {
+ GdkWindow* window = gtk_widget_get_window(widget);
+ if (window) {
+ GdkEvent event = {};
+ event.selection.type = GDK_SELECTION_NOTIFY;
+ event.selection.window = window;
+ event.selection.selection =
+ gdk_x11_xatom_to_atom(xevent->xselection.selection);
+ event.selection.target = gdk_x11_xatom_to_atom(xevent->xselection.target);
+ event.selection.property =
+ gdk_x11_xatom_to_atom(xevent->xselection.property);
+ event.selection.time = xevent->xselection.time;
+
+ gtk_widget_event(widget, &event);
+ }
+}
+
+static void DispatchPropertyNotifyEvent(GtkWidget* widget, XEvent* xevent) {
+ GdkWindow* window = gtk_widget_get_window(widget);
+ if (window && ((gdk_window_get_events(window)) & GDK_PROPERTY_CHANGE_MASK)) {
+ GdkEvent event = {};
+ event.property.type = GDK_PROPERTY_NOTIFY;
+ event.property.window = window;
+ event.property.atom = gdk_x11_xatom_to_atom(xevent->xproperty.atom);
+ event.property.time = xevent->xproperty.time;
+ event.property.state = xevent->xproperty.state;
+
+ gtk_widget_event(widget, &event);
+ }
+}
+
+struct checkEventContext {
+ GtkWidget* cbWidget;
+ Atom selAtom;
+};
+
+static Bool checkEventProc(Display* display, XEvent* event, XPointer arg) {
+ checkEventContext* context = (checkEventContext*)arg;
+
+ if (event->xany.type == SelectionNotify ||
+ (event->xany.type == PropertyNotify &&
+ event->xproperty.atom == context->selAtom)) {
+ GdkWindow* cbWindow = gdk_x11_window_lookup_for_display(
+ gdk_x11_lookup_xdisplay(display), event->xany.window);
+ if (cbWindow) {
+ GtkWidget* cbWidget = nullptr;
+ gdk_window_get_user_data(cbWindow, (gpointer*)&cbWidget);
+ if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
+ context->cbWidget = cbWidget;
+ return X11True;
+ }
+ }
+ }
+
+ return X11False;
+}
+
+ClipboardData nsRetrievalContextX11::WaitForClipboardData(
+ ClipboardDataType aDataType, int32_t aWhichClipboard,
+ const char* aMimeType) {
+ AsyncGtkClipboardRequest request(aDataType, aWhichClipboard, aMimeType);
+ if (request.HasCompleted()) {
+ // the request completed synchronously
+ return request.TakeResult();
+ }
+
+ GdkDisplay* gdkDisplay = gdk_display_get_default();
+ // gdk_display_get_default() returns null on headless
+ if (widget::GdkIsX11Display(gdkDisplay)) {
+ Display* xDisplay = GDK_DISPLAY_XDISPLAY(gdkDisplay);
+ checkEventContext context;
+ context.cbWidget = nullptr;
+ context.selAtom =
+ gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION", FALSE));
+
+ // Send X events which are relevant to the ongoing selection retrieval
+ // to the clipboard widget. Wait until either the operation completes, or
+ // we hit our timeout. All other X events remain queued.
+
+ int poll_result;
+
+ struct pollfd pfd;
+ pfd.fd = ConnectionNumber(xDisplay);
+ pfd.events = POLLIN;
+ TimeStamp start = TimeStamp::Now();
+
+ do {
+ XEvent xevent;
+
+ while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
+ (XPointer)&context)) {
+ if (xevent.xany.type == SelectionNotify)
+ DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
+ else
+ DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
+
+ if (request.HasCompleted()) {
+ return request.TakeResult();
+ }
+ }
+
+ TimeStamp now = TimeStamp::Now();
+ int timeout = std::max<int>(
+ 0, kClipboardTimeout / 1000 - (now - start).ToMilliseconds());
+ poll_result = poll(&pfd, 1, timeout);
+ } while ((poll_result == 1 && (pfd.revents & (POLLHUP | POLLERR)) == 0) ||
+ (poll_result == -1 && errno == EINTR));
+ }
+
+ LOGCLIP("exceeded clipboard timeout");
+ return {};
+}
+
+ClipboardTargets nsRetrievalContextX11::GetTargetsImpl(
+ int32_t aWhichClipboard) {
+ LOGCLIP("nsRetrievalContextX11::GetTargetsImpl(%s)\n",
+ aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary"
+ : "clipboard");
+ return WaitForClipboardData(ClipboardDataType::Targets, aWhichClipboard)
+ .ExtractTargets();
+}
+
+ClipboardData nsRetrievalContextX11::GetClipboardData(const char* aMimeType,
+ int32_t aWhichClipboard) {
+ LOGCLIP("nsRetrievalContextX11::GetClipboardData(%s) MIME %s\n",
+ aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary"
+ : "clipboard",
+ aMimeType);
+
+ return WaitForClipboardData(ClipboardDataType::Data, aWhichClipboard,
+ aMimeType);
+}
+
+GUniquePtr<char> nsRetrievalContextX11::GetClipboardText(
+ int32_t aWhichClipboard) {
+ LOGCLIP("nsRetrievalContextX11::GetClipboardText(%s)\n",
+ aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary"
+ : "clipboard");
+
+ return WaitForClipboardData(ClipboardDataType::Text, aWhichClipboard)
+ .ExtractText();
+}