From 1ebbd7c7684f3887e74c369522c65ebbd8cd1e67 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 26 Nov 2018 12:54:23 +0100 Subject: [PATCH] st: Reimplement StClipboard on top of MetaSelection And stop using X11/gdk for this. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/331 --- src/shell-global.c | 1 + src/st/meson.build | 4 +- src/st/st-clipboard.c | 368 ++++++++++++------------------------------ src/st/st-clipboard.h | 5 +- 4 files changed, 109 insertions(+), 269 deletions(-) diff --git a/src/shell-global.c b/src/shell-global.c index df84b6b0d..4295162a8 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -863,6 +863,7 @@ _shell_global_set_plugin (ShellGlobal *global, } st_entry_set_cursor_func (entry_cursor_func, global); + st_clipboard_set_selection (meta_display_get_selection (display)); g_signal_connect (global->stage, "notify::width", G_CALLBACK (global_stage_notify_width), global); diff --git a/src/st/meson.build b/src/st/meson.build index 444ed4901..7263c2686 100644 --- a/src/st/meson.build +++ b/src/st/meson.build @@ -125,7 +125,7 @@ st_cflags = [ libst = shared_library('st-1.0', sources: st_gir_sources + st_non_gir_sources, c_args: st_cflags, - dependencies: [clutter_dep, gtk_dep, croco_dep, x11_dep, m_dep], + dependencies: [clutter_dep, gtk_dep, croco_dep, mutter_dep, m_dep], build_rpath: mutter_typelibdir, install_rpath: mutter_typelibdir, install_dir: pkglibdir, @@ -152,7 +152,7 @@ libst_gir = gnome.generate_gir(libst, sources: st_gir_sources, nsversion: '1.0', namespace: 'St', - includes: ['Clutter-' + mutter_api_version, 'Cally-' + mutter_api_version, 'Gtk-3.0'], + includes: ['Clutter-' + mutter_api_version, 'Cally-' + mutter_api_version, 'Meta-' + mutter_api_version, 'Gtk-3.0'], dependencies: [mutter_dep], include_directories: include_directories('..'), extra_args: ['-DST_COMPILATION', '--quiet'], diff --git a/src/st/st-clipboard.c b/src/st/st-clipboard.c index 2c86f9a22..bdd4104f3 100644 --- a/src/st/st-clipboard.c +++ b/src/st/st-clipboard.c @@ -19,251 +19,49 @@ /** * SECTION:st-clipboard - * @short_description: a simple representation of the X clipboard + * @short_description: a simple representation of the clipboard * * #StCliboard is a very simple object representation of the clipboard * available to applications. Text is always assumed to be UTF-8 and non-text * items are not handled. */ +#include "config.h" #include "st-clipboard.h" -#include -#include -#include -#include -#include -struct _StClipboardPrivate -{ - Window clipboard_window; - gchar *clipboard_text; +#include +#include +#include - Atom *supported_targets; - gint n_targets; -}; +G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT) -G_DEFINE_TYPE_WITH_PRIVATE (StClipboard, st_clipboard, G_TYPE_OBJECT) - -typedef struct _EventFilterData EventFilterData; -struct _EventFilterData +typedef struct _TransferData TransferData; +struct _TransferData { StClipboard *clipboard; StClipboardCallbackFunc callback; gpointer user_data; + GOutputStream *stream; }; -static Atom __atom_primary = None; -static Atom __atom_clip = None; -static Atom __utf8_string = None; -static Atom __atom_targets = None; - -static void -st_clipboard_dispose (GObject *object) -{ - G_OBJECT_CLASS (st_clipboard_parent_class)->dispose (object); -} - -static void -st_clipboard_finalize (GObject *object) -{ - StClipboardPrivate *priv = ((StClipboard *) object)->priv; - - g_free (priv->clipboard_text); - priv->clipboard_text = NULL; - - g_free (priv->supported_targets); - priv->supported_targets = NULL; - priv->n_targets = 0; - - G_OBJECT_CLASS (st_clipboard_parent_class)->finalize (object); -} - -static GdkFilterReturn -st_clipboard_provider (GdkXEvent *xevent_p, - GdkEvent *gev, - void *user_data) -{ - StClipboard *clipboard = user_data; - XEvent *xev = (XEvent *) xevent_p; - XSelectionEvent notify_event; - XSelectionRequestEvent *req_event; - GdkDisplay *display = gdk_display_get_default (); - - if (xev->type != SelectionRequest || - xev->xany.window != clipboard->priv->clipboard_window || - !clipboard->priv->clipboard_text) - return GDK_FILTER_CONTINUE; - - req_event = &xev->xselectionrequest; - - gdk_x11_display_error_trap_push (display); - - if (req_event->target == __atom_targets) - { - XChangeProperty (req_event->display, - req_event->requestor, - req_event->property, - XA_ATOM, - 32, - PropModeReplace, - (guchar*) clipboard->priv->supported_targets, - clipboard->priv->n_targets); - } - else - { - XChangeProperty (req_event->display, - req_event->requestor, - req_event->property, - req_event->target, - 8, - PropModeReplace, - (guchar*) clipboard->priv->clipboard_text, - strlen (clipboard->priv->clipboard_text)); - } - - notify_event.type = SelectionNotify; - notify_event.display = req_event->display; - notify_event.requestor = req_event->requestor; - notify_event.selection = req_event->selection; - notify_event.target = req_event->target; - notify_event.time = req_event->time; - - if (req_event->property == None) - notify_event.property = req_event->target; - else - notify_event.property = req_event->property; - - /* notify the requestor that they have a copy of the selection */ - XSendEvent (req_event->display, req_event->requestor, False, 0, - (XEvent *) ¬ify_event); - /* Make it happen non async */ - XSync (GDK_DISPLAY_XDISPLAY (display), FALSE); - - if (gdk_x11_display_error_trap_pop (display)) - { - /* FIXME: Warn here on fail ? */ - } - - return GDK_FILTER_REMOVE; -} +const char *supported_mimetypes[] = { + "text/plain;charset=utf-8", + "UTF8_STRING", + "text/plain", + "STRING", +}; +static MetaSelection *meta_selection = NULL; static void st_clipboard_class_init (StClipboardClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = st_clipboard_dispose; - object_class->finalize = st_clipboard_finalize; } static void st_clipboard_init (StClipboard *self) { - GdkDisplay *gdk_display; - Display *dpy; - StClipboardPrivate *priv; - - priv = self->priv = st_clipboard_get_instance_private (self); - - gdk_display = gdk_display_get_default (); - dpy = GDK_DISPLAY_XDISPLAY (gdk_display); - - priv->clipboard_window = - XCreateSimpleWindow (dpy, - gdk_x11_get_default_root_xwindow (), - -1, -1, 1, 1, 0, 0, 0); - - /* Only create once */ - if (__atom_primary == None) - __atom_primary = XInternAtom (dpy, "PRIMARY", 0); - - if (__atom_clip == None) - __atom_clip = XInternAtom (dpy, "CLIPBOARD", 0); - - if (__utf8_string == None) - __utf8_string = XInternAtom (dpy, "UTF8_STRING", 0); - - if (__atom_targets == None) - __atom_targets = XInternAtom (dpy, "TARGETS", 0); - - priv->n_targets = 2; - priv->supported_targets = g_new (Atom, priv->n_targets); - - priv->supported_targets[0] = __utf8_string; - priv->supported_targets[1] = __atom_targets; - - gdk_window_add_filter (NULL, /* all windows */ - st_clipboard_provider, - self); -} - -static GdkFilterReturn -st_clipboard_x11_event_filter (GdkXEvent *xevent_p, - GdkEvent *gev, - void *user_data) -{ - XEvent *xev = (XEvent *) xevent_p; - EventFilterData *filter_data = user_data; - StClipboardPrivate *priv = filter_data->clipboard->priv; - Atom actual_type; - int actual_format, result; - unsigned long nitems, bytes_after; - unsigned char *data = NULL; - GdkDisplay *display = gdk_display_get_default (); - - if(xev->type != SelectionNotify || - xev->xany.window != priv->clipboard_window) - return GDK_FILTER_CONTINUE; - - if (xev->xselection.property == None) - { - /* clipboard empty */ - filter_data->callback (filter_data->clipboard, - NULL, - filter_data->user_data); - - gdk_window_remove_filter (NULL, - st_clipboard_x11_event_filter, - filter_data); - g_free (filter_data); - return GDK_FILTER_REMOVE; - } - - gdk_x11_display_error_trap_push (display); - - result = XGetWindowProperty (xev->xselection.display, - xev->xselection.requestor, - xev->xselection.property, - 0L, G_MAXINT, - True, - AnyPropertyType, - &actual_type, - &actual_format, - &nitems, - &bytes_after, - &data); - - if (gdk_x11_display_error_trap_pop (display) || result != Success) - { - /* FIXME: handle failure better */ - g_warning ("Clipboard: prop retrival failed"); - } - - filter_data->callback (filter_data->clipboard, (char*) data, - filter_data->user_data); - - gdk_window_remove_filter (NULL, - st_clipboard_x11_event_filter, - filter_data); - - g_free (filter_data); - - if (data) - XFree (data); - - return GDK_FILTER_REMOVE; } /** @@ -287,10 +85,57 @@ st_clipboard_get_default (void) return default_clipboard; } -static Atom -atom_for_clipboard_type (StClipboardType type) +static gboolean +convert_type (StClipboardType type, + MetaSelectionType *type_out) { - return type == ST_CLIPBOARD_TYPE_CLIPBOARD ? __atom_clip : __atom_primary; + if (type == ST_CLIPBOARD_TYPE_PRIMARY) + *type_out = META_SELECTION_PRIMARY; + else if (type == ST_CLIPBOARD_TYPE_CLIPBOARD) + *type_out = META_SELECTION_CLIPBOARD; + else + return FALSE; + + return TRUE; +} + +static const char * +pick_mimetype (MetaSelection *meta_selection, + MetaSelectionType selection_type) +{ + const char *selected_mimetype = NULL; + GList *mimetypes; + int i; + + mimetypes = meta_selection_get_mimetypes (meta_selection, selection_type); + + for (i = 0; i < G_N_ELEMENTS (supported_mimetypes); i++) + { + if (g_list_find_custom (mimetypes, supported_mimetypes[i], + (GCompareFunc) g_strcmp0)) + { + selected_mimetype = supported_mimetypes[i]; + break; + } + } + + g_list_free_full (mimetypes, g_free); + return selected_mimetype; +} + +static void +transfer_cb (MetaSelection *selection, + GAsyncResult *res, + TransferData *data) +{ + const gchar *text = NULL; + + if (meta_selection_transfer_finish (selection, res, NULL)) + text = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (data->stream)); + + data->callback (data->clipboard, text, data->user_data); + g_object_unref (data->stream); + g_free (data); } /** @@ -310,37 +155,35 @@ st_clipboard_get_text (StClipboard *clipboard, StClipboardCallbackFunc callback, gpointer user_data) { - EventFilterData *data; - GdkDisplay *gdk_display; - Display *dpy; + MetaSelectionType selection_type; + TransferData *data; + const char *mimetype = NULL; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); + g_return_if_fail (meta_selection != NULL); g_return_if_fail (callback != NULL); - data = g_new0 (EventFilterData, 1); + if (convert_type (type, &selection_type)) + mimetype = pick_mimetype (meta_selection, selection_type); + + if (!mimetype) + { + callback (clipboard, NULL, user_data); + return; + } + + data = g_new0 (TransferData, 1); data->clipboard = clipboard; data->callback = callback; data->user_data = user_data; + data->stream = g_memory_output_stream_new_resizable (); - gdk_window_add_filter (NULL, /* all windows */ - st_clipboard_x11_event_filter, - data); - - gdk_display = gdk_display_get_default (); - dpy = GDK_DISPLAY_XDISPLAY (gdk_display); - - gdk_x11_display_error_trap_push (gdk_display); - - XConvertSelection (dpy, - atom_for_clipboard_type (type), - __utf8_string, __utf8_string, - clipboard->priv->clipboard_window, - CurrentTime); - - if (gdk_x11_display_error_trap_pop (gdk_display)) - { - /* FIXME */ - } + meta_selection_transfer_async (meta_selection, + selection_type, + mimetype, -1, + data->stream, NULL, + (GAsyncReadyCallback) transfer_cb, + data); } /** @@ -356,31 +199,26 @@ st_clipboard_set_text (StClipboard *clipboard, StClipboardType type, const gchar *text) { - StClipboardPrivate *priv; - GdkDisplay *gdk_display; - Display *dpy; + MetaSelectionType selection_type; + MetaSelectionSource *source; + GBytes *bytes; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); + g_return_if_fail (meta_selection != NULL); g_return_if_fail (text != NULL); - priv = clipboard->priv; + if (!convert_type (type, &selection_type)) + return; - /* make a copy of the text */ - g_free (priv->clipboard_text); - priv->clipboard_text = g_strdup (text); + bytes = g_bytes_new_take (g_strdup (text), strlen (text)); + source = meta_selection_source_memory_new ("text/plain;charset=utf-8", bytes); + g_bytes_unref (bytes); - /* tell X we own the clipboard selection */ - gdk_display = gdk_display_get_default (); - dpy = GDK_DISPLAY_XDISPLAY (gdk_display); - - gdk_x11_display_error_trap_push (gdk_display); - - XSetSelectionOwner (dpy, atom_for_clipboard_type (type), priv->clipboard_window, CurrentTime); - - XSync (dpy, FALSE); - - if (gdk_x11_display_error_trap_pop (gdk_display)) - { - /* FIXME */ - } + meta_selection_set_owner (meta_selection, selection_type, source); +} + +void +st_clipboard_set_selection (MetaSelection *selection) +{ + meta_selection = selection; } diff --git a/src/st/st-clipboard.h b/src/st/st-clipboard.h index 9ea412e3b..86e67e1ee 100644 --- a/src/st/st-clipboard.h +++ b/src/st/st-clipboard.h @@ -25,6 +25,7 @@ #define _ST_CLIPBOARD_H #include +#include G_BEGIN_DECLS @@ -32,7 +33,6 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (StClipboard, st_clipboard, ST, CLIPBOARD, GObject) typedef struct _StClipboard StClipboard; -typedef struct _StClipboardPrivate StClipboardPrivate; /** * StClipboard: @@ -44,7 +44,6 @@ struct _StClipboard { /*< private >*/ GObject parent; - StClipboardPrivate *priv; }; typedef enum { @@ -74,6 +73,8 @@ void st_clipboard_set_text (StClipboard *clipboard, StClipboardType type, const gchar *text); +void st_clipboard_set_selection (MetaSelection *selection); + G_END_DECLS #endif /* _ST_CLIPBOARD_H */