clipboard.c - ledit - Text editor (WIP) | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/ledit.git (encrypted, but very slow) | |
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/… | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
clipboard.c (7329B) | |
--- | |
1 #include <time.h> | |
2 #include <stdio.h> | |
3 #include <stdlib.h> | |
4 #include <string.h> | |
5 | |
6 #include <X11/Xlib.h> | |
7 #include <X11/Xatom.h> | |
8 | |
9 #include "util.h" | |
10 #include "memory.h" | |
11 #include "common.h" | |
12 #include "clipboard.h" | |
13 #include "macros.h" | |
14 #include "config.h" | |
15 #include "ctrlsel.h" | |
16 | |
17 /* Some *inspiration* taken from SDL (https://libsdl.org), mainly | |
18 the idea to create a separate window just for clipboard handling. */ | |
19 | |
20 static Window get_clipboard_window(ledit_clipboard *clip); | |
21 static Bool check_window(Display *dpy, XEvent *event, XPointer arg); | |
22 static txtbuf *get_text(ledit_clipboard *clip, int primary); | |
23 | |
24 struct ledit_clipboard { | |
25 txtbuf *primary; | |
26 txtbuf *clipboard; | |
27 txtbuf *rbuf; | |
28 ledit_common *common; | |
29 Window window; | |
30 struct CtrlSelTarget starget; | |
31 struct CtrlSelTarget rtarget; | |
32 CtrlSelContext *scontext; | |
33 Atom xtarget; | |
34 }; | |
35 | |
36 ledit_clipboard * | |
37 clipboard_create(ledit_common *common) { | |
38 ledit_clipboard *clip = ledit_malloc(sizeof(ledit_clipboard)); | |
39 clip->primary = txtbuf_new(); | |
40 clip->clipboard = txtbuf_new(); | |
41 clip->rbuf = txtbuf_new(); | |
42 clip->common = common; | |
43 clip->window = None; | |
44 clip->xtarget = None; | |
45 #ifdef X_HAVE_UTF8_STRING | |
46 clip->xtarget = XInternAtom(common->dpy, "UTF8_STRING", False); | |
47 #else | |
48 clip->xtarget = XA_STRING; | |
49 #endif | |
50 clip->scontext = NULL; | |
51 return clip; | |
52 } | |
53 | |
54 void | |
55 clipboard_destroy(ledit_clipboard *clip) { | |
56 txtbuf_destroy(clip->primary); | |
57 txtbuf_destroy(clip->clipboard); | |
58 txtbuf_destroy(clip->rbuf); | |
59 if (clip->scontext) | |
60 ctrlsel_disown(clip->scontext); | |
61 if (clip->window != None) | |
62 XDestroyWindow(clip->common->dpy, clip->window); | |
63 free(clip); | |
64 } | |
65 | |
66 static Window | |
67 get_clipboard_window(ledit_clipboard *clip) { | |
68 if (clip->window == None) { | |
69 clip->window = XCreateWindow( | |
70 clip->common->dpy, DefaultRootWindow(clip->common->d… | |
71 -10, -10, 1, 1, 0, CopyFromParent, InputOnly, CopyFr… | |
72 ); | |
73 XFlush(clip->common->dpy); | |
74 } | |
75 return clip->window; | |
76 } | |
77 | |
78 void | |
79 clipboard_set_primary_text(ledit_clipboard *clip, char *text) { | |
80 txtbuf_set_text(clip->primary, text); | |
81 clipboard_set_primary_selection_owner(clip); | |
82 } | |
83 | |
84 txtbuf * | |
85 clipboard_get_primary_buffer(ledit_clipboard *clip) { | |
86 return clip->primary; | |
87 } | |
88 | |
89 void | |
90 clipboard_set_primary_selection_owner(ledit_clipboard *clip) { | |
91 Window window = get_clipboard_window(clip); | |
92 if (clip->scontext) | |
93 ctrlsel_disown(clip->scontext); | |
94 clip->scontext = NULL; | |
95 /* FIXME: is it fine to cast to unsigned char everywhere? */ | |
96 ctrlsel_filltarget(clip->xtarget, clip->xtarget, 8, (unsigned ch… | |
97 /* FIXME: use proper time */ | |
98 clip->scontext = ctrlsel_setowner(clip->common->dpy, window, XA_… | |
99 if (!clip->scontext) | |
100 fprintf(stderr, "WARNING: Could not own primary selectio… | |
101 } | |
102 | |
103 void | |
104 clipboard_set_clipboard_text(ledit_clipboard *clip, char *text) { | |
105 txtbuf_set_text(clip->clipboard, text); | |
106 clipboard_set_clipboard_selection_owner(clip); | |
107 } | |
108 | |
109 txtbuf * | |
110 clipboard_get_clipboard_buffer(ledit_clipboard *clip) { | |
111 return clip->clipboard; | |
112 } | |
113 | |
114 void | |
115 clipboard_set_clipboard_selection_owner(ledit_clipboard *clip) { | |
116 Atom clip_atom; | |
117 Window window = get_clipboard_window(clip); | |
118 clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False); | |
119 if (clip->scontext) | |
120 ctrlsel_disown(clip->scontext); | |
121 clip->scontext = NULL; | |
122 /* FIXME: see clipboard_set_primary_selection_owner */ | |
123 ctrlsel_filltarget(clip->xtarget, clip->xtarget, 8, (unsigned ch… | |
124 /* FIXME: use proper time */ | |
125 clip->scontext = ctrlsel_setowner(clip->common->dpy, window, cli… | |
126 if (!clip->scontext) | |
127 fprintf(stderr, "WARNING: Could not own clipboard select… | |
128 } | |
129 | |
130 void | |
131 clipboard_primary_to_clipboard(ledit_clipboard *clip) { | |
132 if (clip->primary->len > 0) { | |
133 txtbuf_copy(clip->clipboard, clip->primary); | |
134 clipboard_set_clipboard_selection_owner(clip); | |
135 } | |
136 } | |
137 | |
138 int | |
139 clipboard_filter_event(ledit_clipboard *clip, XEvent *e) { | |
140 if (clip->window != None && e->xany.window == clip->window) { | |
141 if (clip->scontext) | |
142 ctrlsel_send(clip->scontext, e); | |
143 /* other events are discarded since there | |
144 was no request to get the clipboard text */ | |
145 return 1; | |
146 } | |
147 return 0; | |
148 } | |
149 | |
150 static Bool | |
151 check_window(Display *dpy, XEvent *event, XPointer arg) { | |
152 (void)dpy; | |
153 return *(Window *)arg == event->xany.window; | |
154 } | |
155 | |
156 /* WARNING: The returned txtbuf needs to be copied before further proces… | |
157 static txtbuf * | |
158 get_text(ledit_clipboard *clip, int primary) { | |
159 CtrlSelContext *context; | |
160 Window window = get_clipboard_window(clip); | |
161 ctrlsel_filltarget(clip->xtarget, clip->xtarget, 0, NULL, 0, &cl… | |
162 Atom clip_atom = primary ? XA_PRIMARY : XInternAtom(clip->common… | |
163 /* FIXME: use proper time here */ | |
164 context = ctrlsel_request(clip->common->dpy, window, clip_atom, … | |
165 /* FIXME: show error in window? */ | |
166 if (!context) { | |
167 fprintf(stderr, "WARNING: Unable to request selection.\n… | |
168 return NULL; | |
169 } | |
170 | |
171 struct timespec now, elapsed, last, start, sleep_time; | |
172 sleep_time.tv_sec = 0; | |
173 clock_gettime(CLOCK_MONOTONIC, &start); | |
174 last = start; | |
175 XEvent event; | |
176 while (1) { | |
177 /* FIXME: I have no idea how inefficient this is */ | |
178 if (XCheckIfEvent(clip->common->dpy, &event, &check_wind… | |
179 switch (ctrlsel_receive(context, &event)) { | |
180 case CTRLSEL_RECEIVED: | |
181 goto done; | |
182 case CTRLSEL_ERROR: | |
183 fprintf(stderr, "WARNING: Could not get … | |
184 ctrlsel_cancel(context); | |
185 return NULL; | |
186 default: | |
187 continue; | |
188 } | |
189 } | |
190 clock_gettime(CLOCK_MONOTONIC, &now); | |
191 ledit_timespecsub(&now, &start, &elapsed); | |
192 /* Timeout if it takes too long. When that happens, beco… | |
193 avoid further timeouts in the future (I think I copie… | |
194 /* FIXME: configure timeout */ | |
195 if (elapsed.tv_sec > 0) { | |
196 if (primary) | |
197 clipboard_set_primary_text(clip, ""); | |
198 else | |
199 clipboard_set_clipboard_text(clip, ""); | |
200 return NULL; | |
201 } | |
202 ledit_timespecsub(&now, &last, &elapsed); | |
203 if (elapsed.tv_sec == 0 && elapsed.tv_nsec < TICK) { | |
204 sleep_time.tv_nsec = TICK - elapsed.tv_nsec; | |
205 nanosleep(&sleep_time, NULL); | |
206 } | |
207 last = now; | |
208 } | |
209 return NULL; | |
210 done: | |
211 /* FIXME: this is a bit ugly because it fiddles around with txtb… | |
212 free(clip->rbuf->text); | |
213 clip->rbuf->cap = clip->rbuf->len = clip->rtarget.bufsize; | |
214 /* FIXME: again weird conversion between char and unsigned char … | |
215 clip->rbuf->text = (char *)clip->rtarget.buffer; | |
216 clip->rtarget.buffer = NULL; /* important so ctrlsel_cancel does… | |
217 ctrlsel_cancel(context); | |
218 return clip->rbuf; | |
219 } | |
220 | |
221 txtbuf * | |
222 clipboard_get_clipboard_text(ledit_clipboard *clip) { | |
223 Atom clip_atom; | |
224 clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False); | |
225 Window window = get_clipboard_window(clip); | |
226 Window owner = XGetSelectionOwner(clip->common->dpy, clip_atom); | |
227 if (owner == None) { | |
228 return NULL; | |
229 } else if (owner == window) { | |
230 return clip->clipboard; | |
231 } else { | |
232 return get_text(clip, 0); | |
233 } | |
234 } | |
235 | |
236 txtbuf * | |
237 clipboard_get_primary_text(ledit_clipboard *clip) { | |
238 Window window = get_clipboard_window(clip); | |
239 Window owner = XGetSelectionOwner(clip->common->dpy, XA_PRIMARY); | |
240 if (owner == None) { | |
241 return NULL; | |
242 } else if (owner == window) { | |
243 return clip->primary; | |
244 } else { | |
245 return get_text(clip, 1); | |
246 } | |
247 } |