diff --git a/src/core/display-private.h b/src/core/display-private.h index 6a378ee98..421dc8e6c 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -248,6 +248,9 @@ struct _MetaDisplay MetaSoundPlayer *sound_player; + MetaSelectionSource *selection_source; + GBytes *saved_clipboard; + gchar *saved_clipboard_mimetype; MetaSelection *selection; }; diff --git a/src/core/display.c b/src/core/display.c index 903024269..ce882826f 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -57,6 +57,7 @@ #include "core/frame.h" #include "core/keybindings-private.h" #include "core/main-private.h" +#include "core/meta-clipboard-manager.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "core/window-private.h" @@ -725,6 +726,7 @@ meta_display_open (void) display->bell = meta_bell_new (display); display->selection = meta_selection_new (display); + meta_clipboard_manager_init (display); if (meta_should_autostart_x11_display ()) { @@ -965,6 +967,8 @@ meta_display_close (MetaDisplay *display, g_clear_object (&display->workspace_manager); g_clear_object (&display->sound_player); + meta_clipboard_manager_shutdown (display); + g_object_unref (display); the_display = NULL; diff --git a/src/core/meta-clipboard-manager.c b/src/core/meta-clipboard-manager.c new file mode 100644 index 000000000..432d845f4 --- /dev/null +++ b/src/core/meta-clipboard-manager.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2018 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Carlos Garnacho + */ + +#include "config.h" + +#include "core/meta-clipboard-manager.h" +#include "core/meta-selection-source-memory.h" + +#define MAX_TEXT_SIZE (4 * 1024 * 1024) /* 4MB */ +#define MAX_IMAGE_SIZE (200 * 1024 * 1024) /* 200MB */ + +/* Supported mimetype globs, from least to most preferred */ +static struct { + const char *mimetype_glob; + ssize_t max_transfer_size; +} supported_mimetypes[] = { + { "text/plain", MAX_TEXT_SIZE }, + { "text/plain;charset=utf-8", MAX_TEXT_SIZE }, + { "image/*", MAX_IMAGE_SIZE }, +}; + +static gboolean +mimetype_match (const char *mimetype, + int *idx, + gssize *max_transfer_size) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (supported_mimetypes); i++) + { + if (g_pattern_match_simple (supported_mimetypes[i].mimetype_glob, mimetype)) + { + *max_transfer_size = supported_mimetypes[i].max_transfer_size; + *idx = i; + return TRUE; + } + } + + return FALSE; +} + +static void +transfer_cb (MetaSelection *selection, + GAsyncResult *result, + GOutputStream *output) +{ + MetaDisplay *display = meta_get_display (); + GError *error = NULL; + + if (!meta_selection_transfer_finish (selection, result, &error)) + { + g_warning ("Failed to store clipboard: %s", error->message); + g_error_free (error); + g_object_unref (output); + return; + } + + g_output_stream_close (output, NULL, NULL); + display->saved_clipboard = + g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output)); + g_object_unref (output); +} + +static void +owner_changed_cb (MetaSelection *selection, + MetaSelectionType selection_type, + MetaSelectionSource *new_owner, + MetaDisplay *display) +{ + if (selection_type != META_SELECTION_CLIPBOARD) + return; + + if (new_owner && new_owner != display->selection_source) + { + GOutputStream *output; + GList *mimetypes, *l; + int best_idx = -1; + const char *best = NULL; + ssize_t transfer_size = -1; + + /* New selection source, find the best mimetype in order to + * keep a copy of it. + */ + g_clear_object (&display->selection_source); + g_clear_pointer (&display->saved_clipboard_mimetype, g_free); + g_clear_pointer (&display->saved_clipboard, g_bytes_unref); + + mimetypes = meta_selection_get_mimetypes (selection, selection_type); + + for (l = mimetypes; l; l = l->next) + { + gssize max_transfer_size; + int idx; + + if (!mimetype_match (l->data, &idx, &max_transfer_size)) + continue; + + if (best_idx < idx) + { + best_idx = idx; + best = l->data; + transfer_size = max_transfer_size; + } + } + + if (best_idx < 0) + return; + + display->saved_clipboard_mimetype = g_strdup (best); + output = g_memory_output_stream_new_resizable (); + meta_selection_transfer_async (selection, + META_SELECTION_CLIPBOARD, + best, + transfer_size, + output, + NULL, + (GAsyncReadyCallback) transfer_cb, + output); + } + else if (!new_owner && display->saved_clipboard) + { + /* Old owner is gone, time to take over */ + new_owner = meta_selection_source_memory_new (display->saved_clipboard_mimetype, + display->saved_clipboard); + g_set_object (&display->selection_source, new_owner); + meta_selection_set_owner (selection, selection_type, new_owner); + } +} + +void +meta_clipboard_manager_init (MetaDisplay *display) +{ + MetaSelection *selection; + + selection = meta_display_get_selection (display); + g_signal_connect_after (selection, "owner-changed", + G_CALLBACK (owner_changed_cb), display); +} + +void +meta_clipboard_manager_shutdown (MetaDisplay *display) +{ + MetaSelection *selection; + + g_clear_pointer (&display->saved_clipboard, g_bytes_unref); + g_clear_pointer (&display->saved_clipboard_mimetype, g_free); + selection = meta_display_get_selection (display); + g_signal_handlers_disconnect_by_func (selection, owner_changed_cb, display); +} diff --git a/src/core/meta-clipboard-manager.h b/src/core/meta-clipboard-manager.h new file mode 100644 index 000000000..2ba130f9b --- /dev/null +++ b/src/core/meta-clipboard-manager.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Carlos Garnacho + */ + +#ifndef META_CLIPBOARD_MANAGER_H +#define META_CLIPBOARD_MANAGER_H + +#include "core/display-private.h" + +void meta_clipboard_manager_init (MetaDisplay *display); +void meta_clipboard_manager_shutdown (MetaDisplay *display); + +#endif /* META_CLIPBOARD_MANAGER_H */ diff --git a/src/meson.build b/src/meson.build index 61214f70f..0e5cd0aa0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -329,6 +329,8 @@ mutter_sources = [ 'core/meta-accel-parse.h', 'core/meta-border.c', 'core/meta-border.h', + 'core/meta-clipboard-manager.c', + 'core/meta-clipboard-manager.h', 'core/meta-close-dialog.c', 'core/meta-close-dialog-default.c', 'core/meta-close-dialog-default-private.h',