2 * Copyright Neil Brown ©2020-2021 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * x11selection - integrate X11 clipboards with copybuf and selection.
7 * Use gtk_clipboard interfaces to provide the selection and recent copied
8 * content to other applications, and to use what is provided by those
9 * applications to satisfy internal requests.
11 * We overload copy:save to claim both PRIMARY and CLIPBOARD so other apps will
12 * ask us for content. When asked we call copy:get to get the content, but see
14 * We overload copy:get to interpolate PRIMARY and CLIPBOARD into the list
15 * of copies, if they are exist, are not owned by us and only consider CLIPBOARD
16 * if it is different to PRIMARY.
18 * We also claim the edlib selection at startup on behalf of whichever X11
19 * application owns it. If it is claimed from us, we claim ownership of PRIMARY.
20 * If it is committed, we ask for text from the owner of PRIMARY and save that.
21 * If we lose ownership of the PRIMARY, we reclaim the selection.
26 #define PANE_DATA_TYPE struct xs_info
32 /* sparse/smatch don't like gtk.h, so provide our own declarations */
33 typedef void *gpointer;
34 typedef unsigned int guint;
36 typedef struct _GdkDisplay {} GdkDisplay;
37 typedef struct _GtkTargetEntry {} GtkTargetEntry;
38 typedef struct _GtkTargetList {} GtkTargetList;
39 typedef struct _GtkClipboard {} GtkClipboard;
40 typedef struct _GtkSelectionData {} GtkSelectionData;
43 GdkAtom gdk_atom_intern(gchar *, int);
47 GdkDisplay *gdk_display_open(gchar*);
48 void gdk_display_close(GdkDisplay*);
49 GtkClipboard *gtk_clipboard_get_for_display(GdkDisplay*, GdkAtom);
50 void gtk_clipboard_set_with_data(GtkClipboard*, GtkTargetEntry*, guint,
51 void (*get)(GtkClipboard *, GtkSelectionData *,
52 guint, gpointer d safe),
53 void (*clear)(GtkClipboard *, gpointer safe),
55 void gtk_clipboard_clear(GtkClipboard*);
56 void gtk_selection_data_set_text(GtkSelectionData *, gchar *, guint);
58 gchar *gtk_clipboard_wait_for_text(GtkClipboard*);
59 int gtk_clipboard_wait_is_text_available(GtkClipboard*);
60 void g_free(gpointer);
62 GtkTargetList *gtk_target_list_new(gpointer, guint);
63 void gtk_target_list_add_text_targets(GtkTargetList *, guint);
64 void gtk_target_list_unref(GtkTargetList *);
65 GtkTargetEntry *gtk_target_table_new_from_list(GtkTargetList *, int *);
66 void gtk_target_table_free(GtkTargetEntry*, int);
71 struct pane *self safe;
74 /* 'data' is allocated space that stores a pointer to this
75 * xs_info. Data is given the gtk has a handle.
77 struct xs_info **data;
81 GtkTargetEntry *text_targets;
84 #include "core-pane.h"
86 static void do_get(GtkClipboard *cb, GtkSelectionData *sd,
87 guint info, gpointer vdata safe)
89 /* Another X11 application has asked for clipboard data */
90 struct xs_info **data = vdata;
91 struct xs_info *xsi = *data;
96 if (cb == xsi->primary.cb)
97 /* If there is an active selection, now if the time for
98 * the content to be copied.
100 call("selection:commit", xsi->self);
102 s = call_ret(strsave, "copy:get", xsi->self);
105 gtk_selection_data_set_text(sd, s, strlen(s));
108 static void do_clear(GtkClipboard *cb, gpointer vdata safe)
110 struct xs_info **data = vdata;
111 struct xs_info *xsi = *data;
115 /* Some other X11 application wants us to release ownership
118 if (data == xsi->primary.data) {
119 /* This means some other application now has a "selection",
120 * so we claim it on their behalf.
122 xsi->primary.data = NULL;
123 call("selection:claim", xsi->self);
126 if (data == xsi->clipboard.data)
127 xsi->clipboard.data = NULL;
132 static void claim_primary(struct xs_info *xsi safe)
134 struct xs_info **data;
136 data = malloc(sizeof(*data));
139 gtk_clipboard_set_with_data(xsi->primary.cb,
142 do_get, do_clear, data);
143 xsi->primary.data = data;
144 xsi->primary.saved = 0;
147 static void claim_both(struct xs_info *xsi safe)
149 struct xs_info **data;
153 data = malloc(sizeof(*data));
156 gtk_clipboard_set_with_data(xsi->clipboard.cb,
159 do_get, do_clear, data);
160 xsi->clipboard.data = data;
161 xsi->clipboard.saved = 0;
164 DEF_CMD(xs_copy_save)
166 struct xs_info *xsi = ci->home->data;
169 /* Some edlib pane own the selection, so we renounce any ownership
170 * by any X11 application.
172 call("selection:discard", ci->home);
178 struct xs_info *xsi = ci->home->data;
181 if (xsi->clipboard.data == NULL) {
183 /* Return CLIPBOARD if it exists */
186 if (gtk_clipboard_wait_is_text_available(
188 s = gtk_clipboard_wait_for_text(
191 comm_call(ci->comm2, "cb", ci->focus,
197 /* Just check if a string exists */
198 if (gtk_clipboard_wait_is_text_available(
206 return call_comm(ci->key, ci->home->parent, ci->comm2, num);
209 DEF_CMD(xs_sel_claimed)
211 struct xs_info *xsi = ci->home->data;
213 if (ci->focus != ci->home)
216 /* Some other pane holds the selection, so better tell
223 DEF_CMD(xs_sel_commit)
225 struct xs_info *xsi = ci->home->data;
228 /* Someone wants to paste the selection */
229 /* Record PRIMARY if it exists */
231 if (ci->focus != ci->home)
235 if (xsi->primary.data || xsi->primary.saved)
236 /* We own the primary, so nothing to do */
239 if (!xsi->clipboard.data && !xsi->clipboard.saved) {
240 /* get the clipboard first - to make sure it is available
241 * as second saved text.
244 if (gtk_clipboard_wait_is_text_available(
246 s = gtk_clipboard_wait_for_text(xsi->clipboard.cb);
248 call("copy:save", ci->home->parent,
250 xsi->clipboard.saved = 1;
255 if (gtk_clipboard_wait_is_text_available(xsi->primary.cb))
256 s = gtk_clipboard_wait_for_text(xsi->primary.cb);
258 call("copy:save", ci->home->parent,
260 xsi->primary.saved = 1;
267 DEF_CMD_CLOSED(xs_close)
269 struct xs_info *xsi = ci->home->data;
271 if (xsi->primary.data)
272 gtk_clipboard_clear(xsi->primary.cb);
273 if (xsi->clipboard.data)
274 gtk_clipboard_clear(xsi->clipboard.cb);
275 free(xsi->primary.data);
276 free(xsi->clipboard.data);
277 gtk_target_table_free(xsi->text_targets, xsi->n_text_targets);
278 gdk_display_close(xsi->display);
287 p = call_ret(pane, "attach-x11selection", ci->focus);
288 pane_clone_children(ci->home, p);
292 static struct map *xs_map;
293 DEF_LOOKUP_CMD(xs_handle, xs_map);
300 GdkAtom primary, clipboard;
304 d = pane_attr_get(ci->focus, "DISPLAY");
307 dis = gdk_display_open(d);
311 call("attach-glibevents", ci->focus);
312 p = pane_register(ci->focus, 0, &xs_handle.c);
318 primary = gdk_atom_intern("PRIMARY", TRUE);
319 clipboard = gdk_atom_intern("CLIPBOARD", TRUE);
320 xsi->primary.cb = gtk_clipboard_get_for_display(dis, primary);
321 xsi->clipboard.cb = gtk_clipboard_get_for_display(dis, clipboard);
323 list = gtk_target_list_new(NULL, 0);
324 gtk_target_list_add_text_targets(list, 0);
326 gtk_target_table_new_from_list(list, &xsi->n_text_targets);
327 gtk_target_list_unref (list);
332 return comm_call(ci->comm2, "cb:attach", xsi->self);
335 void edlib_init(struct pane *ed safe)
338 xs_map = key_alloc();
339 key_add(xs_map, "copy:save", &xs_copy_save);
340 key_add(xs_map, "copy:get", &xs_copy_get);
341 key_add(xs_map, "Notify:selection:claimed", &xs_sel_claimed);
342 key_add(xs_map, "Notify:selection:commit", &xs_sel_commit);
343 key_add(xs_map, "Clone", &xs_clone);
344 key_add(xs_map, "Close", &xs_close);
347 call_comm("global-set-command", ed, &xs_attach,
348 0, NULL, "attach-x11selection");