]>
Commit | Line | Data |
---|---|---|
d11ebe2c GH |
1 | /* |
2 | * GTK UI -- clipboard support | |
3 | * | |
4 | * Copyright (C) 2021 Gerd Hoffmann <kraxel@redhat.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
22 | #include "qemu-common.h" | |
23 | #include "qemu/main-loop.h" | |
24 | ||
25 | #include "ui/gtk.h" | |
26 | ||
27 | static QemuClipboardSelection gd_find_selection(GtkDisplayState *gd, | |
28 | GtkClipboard *clipboard) | |
29 | { | |
30 | QemuClipboardSelection s; | |
31 | ||
32 | for (s = 0; s < QEMU_CLIPBOARD_SELECTION__COUNT; s++) { | |
33 | if (gd->gtkcb[s] == clipboard) { | |
34 | return s; | |
35 | } | |
36 | } | |
37 | return QEMU_CLIPBOARD_SELECTION_CLIPBOARD; | |
38 | } | |
39 | ||
40 | static void gd_clipboard_get_data(GtkClipboard *clipboard, | |
41 | GtkSelectionData *selection_data, | |
42 | guint selection_info, | |
43 | gpointer data) | |
44 | { | |
45 | GtkDisplayState *gd = data; | |
46 | QemuClipboardSelection s = gd_find_selection(gd, clipboard); | |
47 | QemuClipboardType type = QEMU_CLIPBOARD_TYPE_TEXT; | |
48 | QemuClipboardInfo *info = qemu_clipboard_info_ref(gd->cbinfo[s]); | |
49 | ||
50 | qemu_clipboard_request(info, type); | |
51 | while (info == gd->cbinfo[s] && | |
52 | info->types[type].available && | |
53 | info->types[type].data == NULL) { | |
54 | main_loop_wait(false); | |
55 | } | |
56 | ||
57 | if (info == gd->cbinfo[s] && gd->cbowner[s]) { | |
58 | gtk_selection_data_set_text(selection_data, | |
59 | info->types[type].data, | |
60 | info->types[type].size); | |
61 | } else { | |
62 | /* clipboard owner changed while waiting for the data */ | |
63 | } | |
64 | ||
65 | qemu_clipboard_info_unref(info); | |
66 | } | |
67 | ||
68 | static void gd_clipboard_clear(GtkClipboard *clipboard, | |
69 | gpointer data) | |
70 | { | |
71 | GtkDisplayState *gd = data; | |
72 | QemuClipboardSelection s = gd_find_selection(gd, clipboard); | |
73 | ||
74 | gd->cbowner[s] = false; | |
75 | } | |
76 | ||
77 | static void gd_clipboard_notify(Notifier *notifier, void *data) | |
78 | { | |
79 | GtkDisplayState *gd = container_of(notifier, GtkDisplayState, cbpeer.update); | |
80 | QemuClipboardInfo *info = data; | |
81 | QemuClipboardSelection s = info->selection; | |
82 | bool self_update = info->owner == &gd->cbpeer; | |
83 | ||
84 | if (info != gd->cbinfo[s]) { | |
85 | qemu_clipboard_info_unref(gd->cbinfo[s]); | |
86 | gd->cbinfo[s] = qemu_clipboard_info_ref(info); | |
87 | gd->cbpending[s] = 0; | |
88 | if (!self_update) { | |
89 | GtkTargetList *list; | |
90 | GtkTargetEntry *targets; | |
91 | gint n_targets; | |
92 | ||
93 | list = gtk_target_list_new(NULL, 0); | |
94 | if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) { | |
95 | gtk_target_list_add_text_targets(list, 0); | |
96 | } | |
97 | targets = gtk_target_table_new_from_list(list, &n_targets); | |
98 | ||
99 | gtk_clipboard_clear(gd->gtkcb[s]); | |
100 | gd->cbowner[s] = true; | |
101 | gtk_clipboard_set_with_data(gd->gtkcb[s], | |
102 | targets, n_targets, | |
103 | gd_clipboard_get_data, | |
104 | gd_clipboard_clear, | |
105 | gd); | |
106 | ||
107 | gtk_target_table_free(targets, n_targets); | |
108 | gtk_target_list_unref(list); | |
109 | } | |
110 | return; | |
111 | } | |
112 | ||
113 | if (self_update) { | |
114 | return; | |
115 | } | |
116 | ||
117 | /* | |
118 | * Clipboard got updated, with data probably. No action here, we | |
119 | * are waiting for updates in gd_clipboard_get_data(). | |
120 | */ | |
121 | } | |
122 | ||
123 | static void gd_clipboard_request(QemuClipboardInfo *info, | |
124 | QemuClipboardType type) | |
125 | { | |
126 | GtkDisplayState *gd = container_of(info->owner, GtkDisplayState, cbpeer); | |
127 | char *text; | |
128 | ||
129 | switch (type) { | |
130 | case QEMU_CLIPBOARD_TYPE_TEXT: | |
131 | text = gtk_clipboard_wait_for_text(gd->gtkcb[info->selection]); | |
132 | if (text) { | |
133 | qemu_clipboard_set_data(&gd->cbpeer, info, type, | |
134 | strlen(text), text, true); | |
135 | g_free(text); | |
136 | } | |
137 | break; | |
138 | default: | |
139 | break; | |
140 | } | |
141 | } | |
142 | ||
143 | static void gd_owner_change(GtkClipboard *clipboard, | |
144 | GdkEvent *event, | |
145 | gpointer data) | |
146 | { | |
147 | GtkDisplayState *gd = data; | |
148 | QemuClipboardSelection s = gd_find_selection(gd, clipboard); | |
149 | QemuClipboardInfo *info; | |
150 | ||
151 | if (gd->cbowner[s]) { | |
152 | /* ignore notifications about our own grabs */ | |
153 | return; | |
154 | } | |
155 | ||
156 | ||
157 | switch (event->owner_change.reason) { | |
6b32aef0 | 158 | case GDK_OWNER_CHANGE_NEW_OWNER: |
d11ebe2c GH |
159 | info = qemu_clipboard_info_new(&gd->cbpeer, s); |
160 | if (gtk_clipboard_wait_is_text_available(clipboard)) { | |
161 | info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; | |
162 | } | |
163 | ||
164 | qemu_clipboard_update(info); | |
165 | qemu_clipboard_info_unref(info); | |
166 | break; | |
167 | default: | |
168 | break; | |
169 | } | |
170 | } | |
171 | ||
172 | void gd_clipboard_init(GtkDisplayState *gd) | |
173 | { | |
174 | gd->cbpeer.name = "gtk"; | |
175 | gd->cbpeer.update.notify = gd_clipboard_notify; | |
176 | gd->cbpeer.request = gd_clipboard_request; | |
177 | qemu_clipboard_peer_register(&gd->cbpeer); | |
178 | ||
179 | gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPBOARD] = | |
c311e8d7 | 180 | gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); |
d11ebe2c | 181 | gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMARY] = |
c311e8d7 | 182 | gtk_clipboard_get(GDK_SELECTION_PRIMARY); |
d11ebe2c | 183 | gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECONDARY] = |
c311e8d7 | 184 | gtk_clipboard_get(GDK_SELECTION_SECONDARY); |
d11ebe2c GH |
185 | |
186 | g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPBOARD], | |
187 | "owner-change", G_CALLBACK(gd_owner_change), gd); | |
188 | g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMARY], | |
189 | "owner-change", G_CALLBACK(gd_owner_change), gd); | |
190 | g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECONDARY], | |
191 | "owner-change", G_CALLBACK(gd_owner_change), gd); | |
192 | } |