1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "headers.h" /* always include this one first, kthx */
25
26 #include "clippy.h"
27 #include "event.h"
28
29 #include "util.h"
30
31 #include "sdlmain.h"
32
33 static char *_current_selection = NULL;
34 static char *_current_clipboard = NULL;
35 static struct widget *_widget_owner[16] = {NULL};
36
37 static int has_sys_clip;
38 #if defined(WIN32)
39 static HWND SDL_Window, _hmem;
40 #elif defined(__QNXNTO__)
41 static unsigned short inputgroup;
42 #elif defined(USE_X11)
43 static Display *SDL_Display = NULL;
__construct($p_timestamp, $p_user_id, $p_issue_id, $p_filename, $p_type)44 static Window SDL_Window;
45 static void (*lock_display)(void);
46 static void (*unlock_display)(void);
47 static Atom atom_sel;
48 static Atom atom_clip;
49 static void __noop_v(void){};
50 #endif
51
52 #ifdef MACOSX
53 extern const char *macosx_clippy_get(void);
54 extern void macosx_clippy_put(const char *buf);
55 #endif
56
html()57 static void _clippy_copy_to_sys(int do_sel)
58 {
59 int j;
60 char *tmp;
61 char *dst;
62 char *freeme;
63 #if defined(__QNXNTO__)
64 PhClipboardHdr clheader = {Ph_CLIPBOARD_TYPE_TEXT, 0, NULL};
65 int *cldata;
66 int status;
67 #endif
68
69 freeme = NULL;
70 if (!_current_selection) {
71 dst = NULL;
72 j = 0;
73 } else
74 #if defined(WIN32)
75 {
76 int i;
77 /* need twice the space since newlines are replaced with \r\n */
78 freeme = tmp = malloc(strlen(_current_selection)*2 + 1);
79 if (!tmp) return;
80 for (i = j = 0; _current_selection[i]; i++) {
81 if (_current_selection[i] == '\r' || _current_selection[i] == '\n') {
82 tmp[j++] = '\r';
83 tmp[j++] = '\n';
84 } else {
85 tmp[j++] = _current_selection[i];
86 }
87 }
88 tmp[j] = '\0';
89 }
90 #else
91 if (has_sys_clip) {
92 int i;
93 /* convert to local */
94 freeme = dst = malloc(strlen(_current_selection)+4);
95 if (!dst) return;
96 for (i = j = 0; _current_selection[i]; i++) {
97 dst[j] = _current_selection[i];
98 if (dst[j] == '\r') dst[j] = '\n';
99 j++;
100 }
101 dst[j] = '\0';
102 } else {
103 dst = NULL;
104 j = 0;
105 }
106 #endif
107 #if defined(USE_X11)
108 if (has_sys_clip) {
109 lock_display();
110 if (!dst) dst = (char *) ""; /* blah */
111 if (j < 0) j = 0;
112 if (do_sel) {
113 if (XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window) {
114 XSetSelectionOwner(SDL_Display, XA_PRIMARY, SDL_Window, CurrentTime);
115 }
116 XChangeProperty(SDL_Display,
117 DefaultRootWindow(SDL_Display),
118 XA_CUT_BUFFER0, XA_STRING, 8,
119 PropModeReplace, (unsigned char *)dst, j);
120 } else {
121 if (XGetSelectionOwner(SDL_Display, atom_clip) != SDL_Window) {
122 XSetSelectionOwner(SDL_Display, atom_clip, SDL_Window, CurrentTime);
123 }
124 XChangeProperty(SDL_Display,
125 DefaultRootWindow(SDL_Display),
126 XA_CUT_BUFFER0, XA_STRING, 8,
127 PropModeReplace, (unsigned char *)dst, j);
128 XChangeProperty(SDL_Display,
129 DefaultRootWindow(SDL_Display),
130 XA_CUT_BUFFER1, XA_STRING, 8,
131 PropModeReplace, (unsigned char *)dst, j);
132 }
133 unlock_display();
134 }
135 #elif defined(WIN32)
136 if (!do_sel && OpenClipboard(SDL_Window)) {
137 _hmem = GlobalAlloc((GMEM_MOVEABLE|GMEM_DDESHARE), j+1);
138 if (_hmem) {
139 dst = (char *)GlobalLock(_hmem);
140 if (dst) {
141 /* this seems wrong, but msdn does this */
142 memcpy(dst, tmp, j);
143 dst[j] = '\0';
144 GlobalUnlock(_hmem);
145 EmptyClipboard();
146 SetClipboardData(CF_TEXT, _hmem);
147 }
148 }
149 CloseClipboard();
150 _hmem = NULL;
151 dst = 0;
152 }
153 #elif defined(__QNXNTO__)
154 if (!do_sel) {
155 tmp = (char *)malloc(j+4);
156 if (!tmp) {
157 cldata=(int*)tmp;
158 *cldata = Ph_CL_TEXT;
159 if (dst) memcpy(tmp+4, dst, j);
160 clheader.data = tmp;
161 #if (NTO_VERSION < 620)
162 if (clheader.length > 65535) clheader.length=65535;
163 #endif
164 clheader.length = j + 4;
165 #if (NTO_VERSION < 620)
166 PhClipboardCopy(inputgroup, 1, &clheader);
167 #else
168 PhClipboardWrite(inputgroup, 1, &clheader);
169 #endif
170 free(tmp);
171 }
172 }
173 #elif defined(MACOSX)
174 if (!do_sel) macosx_clippy_put(_current_clipboard);
175 #else
176 // some other system -- linux without x11, maybe
177 // pretend we used the param to silence warnings
178 (void) do_sel;
179 #endif
180 if (freeme)
181 free(freeme);
182 }
183
184 /* TODO: is the first parameter ever going to be used, or can we kill it? */
185 static void _string_paste(UNUSED int cb, const char *cbptr)
186 {
187 SDL_Event event = {};
188
189 event.user.type = SCHISM_EVENT_PASTE;
190 event.user.data1 = str_dup(cbptr); /* current_clipboard... is it safe? */
191 if (!event.user.data1) return; /* eh... */
192 if (SDL_PushEvent(&event) == -1) {
193 free(event.user.data1);
194 }
195 }
196
197
198 #if defined(USE_X11)
199 static int _x11_clip_filter(const SDL_Event *ev)
200 {
201 XSelectionRequestEvent *req;
202 XEvent sevent;
203 Atom seln_type, seln_target;
204 int seln_format;
205 unsigned long nbytes;
206 unsigned long overflow;
207 unsigned char *seln_data;
208 unsigned char *src;
209
210 if (ev->type != SDL_SYSWMEVENT) return 1;
211 if (ev->syswm.msg->event.xevent.type == SelectionNotify) {
212 sevent = ev->syswm.msg->event.xevent;
213 if (sevent.xselection.requestor == SDL_Window) {
214 lock_display();
215 src = NULL;
216 if (XGetWindowProperty(SDL_Display, SDL_Window, atom_sel,
217 0, 9000, False, XA_STRING,
218 (Atom *)&seln_type,
219 (int *)&seln_format,
220 (unsigned long *)&nbytes,
221 (unsigned long *)&overflow,
222 (unsigned char **)&src) == Success) {
223 if (seln_type == XA_STRING) {
224 if (_current_selection != _current_clipboard) {
225 free(_current_clipboard);
226 }
227 _current_clipboard = strn_dup((const char *)src, nbytes);
228 _string_paste(CLIPPY_BUFFER, _current_clipboard);
229 _widget_owner[CLIPPY_BUFFER]
230 = _widget_owner[CLIPPY_SELECT];
231 }
232 XFree(src);
233 }
234 unlock_display();
235 }
236 return 1;
237 } else if (ev->syswm.msg->event.xevent.type == PropertyNotify) {
238 sevent = ev->syswm.msg->event.xevent;
239 return 1;
240
241 } else if (ev->syswm.msg->event.xevent.type != SelectionRequest) {
242 return 1;
243 }
244
245 req = &ev->syswm.msg->event.xevent.xselectionrequest;
246 sevent.xselection.type = SelectionNotify;
247 sevent.xselection.display = req->display;
248 sevent.xselection.selection = req->selection;
249 sevent.xselection.target = req->target;
250 sevent.xselection.property = None;
251 sevent.xselection.requestor = req->requestor;
252 sevent.xselection.time = req->time;
253 if (XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display),
254 XA_CUT_BUFFER0, 0, 9000, False, req->target,
255 &seln_target, &seln_format,
256 &nbytes, &overflow, &seln_data) == Success) {
257 if (seln_target == req->target) {
258 if (seln_target == XA_STRING) {
259 if (nbytes && seln_data[nbytes-1] == '\0')
260 nbytes--;
261 }
262 XChangeProperty(SDL_Display, req->requestor, req->property,
263 seln_target, seln_format, PropModeReplace,
264 seln_data, nbytes);
265 sevent.xselection.property = req->property;
266 }
267 XFree(seln_data);
268 }
269 XSendEvent(SDL_Display, req->requestor, False, 0, &sevent);
270 XSync(SDL_Display, False);
271 return 1;
272 }
273
274 static int (*orig_xlib_err)(Display *d, XErrorEvent *e) = NULL;
275 static int handle_xlib_err(Display *d, XErrorEvent *e)
276 {
277 /* X_SetSelectionOwner == 22 */
278 if (e->error_code == BadWindow && e->request_code == 22) {
279 /* return 0 here to avoid dying as the result of a nonfatal race condition */
280 return 0;
281 }
282 if (orig_xlib_err) return orig_xlib_err(d,e);
283 return 0;
284 }
285
286 #endif
287
288
289 void clippy_init(void)
290 {
291 SDL_SysWMinfo info = {};
292
293 has_sys_clip = 0;
294 SDL_VERSION(&info.version);
295 if (SDL_GetWMInfo(&info)) {
296 #if defined(USE_X11)
297 if (info.subsystem == SDL_SYSWM_X11) {
298 SDL_Display = info.info.x11.display;
299 SDL_Window = info.info.x11.window;
300 lock_display = info.info.x11.lock_func;
301 unlock_display = info.info.x11.unlock_func;
302 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
303 SDL_SetEventFilter(_x11_clip_filter);
304 has_sys_clip = 1;
305
306 atom_sel = XInternAtom(SDL_Display, "SDL_SELECTION", False);
307 atom_clip = XInternAtom(SDL_Display, "CLIPBOARD", False);
308
309 orig_xlib_err = XSetErrorHandler(handle_xlib_err);
310 }
311 if (!lock_display) lock_display = __noop_v;
312 if (!unlock_display) unlock_display = __noop_v;
313 #elif defined(WIN32)
314 has_sys_clip = 1;
315 SDL_Window = info.window;
316 #elif defined(__QNXNTO__)
317 has_sys_clip = 1;
318 inputgroup = PhInputGroup(NULL);
319 #endif
320 }
321 }
322
323 static char *_internal_clippy_paste(int cb)
324 {
325 #if defined(MACOSX)
326 char *src;
327 #endif
328 #if defined(USE_X11)
329 Window owner;
330 int getme;
331 #elif defined(WIN32)
332 char *src;
333 int clen;
334 #elif defined(__QNXNTO__)
335 void *clhandle;
336 PhClipHeader *clheader;
337 int *cldata;
338 #endif
339
340 if (has_sys_clip) {
341 #if defined(USE_X11)
342 if (cb == CLIPPY_SELECT) {
343 getme = XA_PRIMARY;
344 } else {
345 getme = atom_clip;
346 }
347 lock_display();
348 owner = XGetSelectionOwner(SDL_Display, getme);
349 unlock_display();
350 if (owner == None || owner == SDL_Window) {
351 /* fall through to default implementation */
352 } else {
353 lock_display();
354 XConvertSelection(SDL_Display, getme, XA_STRING, atom_sel, SDL_Window,
355 CurrentTime);
356 /* at some point in the near future, we'll get a SelectionNotify
357 see _x11_clip_filter for more details;
358
359 because of this (otherwise) oddity, we take the selection immediately...
360 */
361 unlock_display();
362 return NULL;
363 }
364 #else
365 if (cb == CLIPPY_BUFFER) {
366 #if defined(WIN32)
367 if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(SDL_Window)) {
368 _hmem = GetClipboardData(CF_TEXT);
369 if (_hmem) {
370 if (_current_selection != _current_clipboard) {
371 free(_current_clipboard);
372 }
373 _current_clipboard = NULL;
374 src = (char*)GlobalLock(_hmem);
375 if (src) {
376 clen = GlobalSize(_hmem);
377 if (clen > 0) {
378 _current_clipboard = strn_dup(src, clen);
379 }
380 GlobalUnlock(_hmem);
381 }
382 }
383 CloseClipboard();
384 _hmem = NULL;
385 }
386 #elif defined(__QNXNTO__)
387 if (_current_selection != _current_clipboard) {
388 free(_current_clipboard);
389 }
390 _current_clipboard = NULL;
391 #if (NTO_VERSION < 620)
392 clhandle = PhClipboardPasteStart(inputgroup);
393 if (clhandle) {
394 clheader = PhClipboardPasteType(clhandle,
395 Ph_CLIPBOARD_TYPE_TEXT);
396 if (clheader) {
397 cldata = clheader->data;
398 if (clheader->length > 4 && *cldata == Ph_CL_TEXT) {
399 src = ((char *)clheader->data)+4;
400 clen = clheader->length - 4;
401 _current_clipboard = strn_dup(src, clen);
402
403 }
404 PhClipboardPasteFinish(clhandle);
405 }
406 }
407 #else
408 /* argh! qnx */
409 clheader = PhClipboardRead(inputgroup, Ph_CLIPBOARD_TYPE_TEXT);
410 if (clheader) {
411 cldata = clheader->data;
412 if (clheader->length > 4 && *cldata == Ph_CL_TEXT) {
413 src = ((char *)clheader->data)+4;
414 clen = clheader->length - 4;
415 _current_clipboard = strn_dup(src, clen);
416 }
417 }
418 #endif /* NTO version selector */
419 /* okay, we either own the buffer, or it's a selection for folks without */
420 #endif /* win32/qnx */
421 }
422 #endif /* x11/others */
423 /* fall through; the current window owns it */
424 }
425 if (cb == CLIPPY_SELECT) return _current_selection;
426 #ifdef MACOSX
427 if (cb == CLIPPY_BUFFER) {
428 src = str_dup(macosx_clippy_get());
429 if (_current_clipboard != _current_selection) {
430 free(_current_clipboard);
431 }
432 _current_clipboard = src;
433 if (!src) return (char *) ""; /* FIXME: de-const-ing is bad */
434 return _current_clipboard;
435 }
436 #else
437 if (cb == CLIPPY_BUFFER) return _current_clipboard;
438 #endif
439 return NULL;
440 }
441
442
443 void clippy_paste(int cb)
444 {
445 char *q;
446 q = _internal_clippy_paste(cb);
447 if (!q) return;
448 _string_paste(cb, q);
449 }
450
451 void clippy_select(struct widget *w, char *addr, int len)
452 {
453 int i;
454
455 if (_current_selection != _current_clipboard) {
456 free(_current_selection);
457 }
458 if (!addr) {
459 _current_selection = NULL;
460 _widget_owner[CLIPPY_SELECT] = NULL;
461 } else {
462 for (i = 0; addr[i] && (len < 0 || i < len); i++) {
463 /* nothing */
464 }
465 _current_selection = strn_dup(addr, i);
466 _widget_owner[CLIPPY_SELECT] = w;
467
468 /* update x11 Select (for xterms and stuff) */
469 _clippy_copy_to_sys(1);
470 }
471 }
472 struct widget *clippy_owner(int cb)
473 {
474 if (cb == CLIPPY_SELECT || cb == CLIPPY_BUFFER)
475 return _widget_owner[cb];
476 return NULL;
477 }
478
479 void clippy_yank(void)
480 {
481 if (_current_selection != _current_clipboard) {
482 free(_current_clipboard);
483 }
484 _current_clipboard = _current_selection;
485 _widget_owner[CLIPPY_BUFFER] = _widget_owner[CLIPPY_SELECT];
486
487 if (_current_selection && strlen(_current_selection) > 0) {
488 status_text_flash("Copied to selection buffer");
489 _clippy_copy_to_sys(0);
490 }
491 }
492