1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <unistd.h>
5 #include <errno.h>
6
7 #include <X11/Intrinsic.h>
8 #include <X11/StringDefs.h>
9 #include <Xm/Xm.h>
10 #include <Xm/Transfer.h>
11 #include <Xm/TransferP.h>
12 #include <Xm/DragIcon.h>
13
14 #include "ida.h"
15 #include "readers.h"
16 #include "writers.h"
17 #include "viewer.h"
18 #include "xwd.h"
19 #include "xdnd.h"
20 #include "selections.h"
21 #include "list.h"
22
23 Atom XA_TARGETS, XA_DONE;
24 Atom XA_FILE_NAME, XA_FILE;
25 Atom XA_BACKGROUND, XA_FOREGROUND, XA_PIXEL;
26 Atom _MOTIF_EXPORT_TARGETS;
27 Atom _MOTIF_CLIPBOARD_TARGETS;
28 Atom _MOTIF_DEFERRED_CLIPBOARD_TARGETS;
29 Atom _MOTIF_SNAPSHOT;
30 Atom _MOTIF_DROP;
31 Atom _MOTIF_LOSE_SELECTION;
32 Atom _NETSCAPE_URL;
33 Atom MIME_TEXT_URI_LIST;
34 Atom MIME_IMAGE_PPM;
35 Atom MIME_IMAGE_PGM;
36 Atom MIME_IMAGE_XPM;
37 Atom MIME_IMAGE_BMP;
38 Atom MIME_IMAGE_JPEG;
39 Atom MIME_IMAGE_GIF;
40 Atom MIME_IMAGE_PNG;
41 Atom MIME_IMAGE_TIFF;
42
43 /* ---------------------------------------------------------------------- */
44 /* send data (drags, copy) */
45
46 struct sel_data {
47 struct list_head list;
48 Atom atom;
49 struct ida_image img;
50 Pixmap pixmap;
51 char *filename;
52 Pixmap icon_pixmap;
53 Widget icon_widget;
54 };
55 static struct list_head selections;
56
57 static void
iconify(Widget widget,struct sel_data * data)58 iconify(Widget widget, struct sel_data *data)
59 {
60 struct ida_image small;
61 unsigned int scale,x,y,depth;
62 char *src,*dst;
63 Arg args[4];
64 Cardinal n=0;
65
66 /* calc size */
67 memset(&small,0,sizeof(small));
68 for (scale = 1;; scale++) {
69 small.i.width = data->img.i.width / scale;
70 small.i.height = data->img.i.height / scale;
71 if (small.i.width < 128 && small.i.height < 128)
72 break;
73 }
74
75 /* scale down & create pixmap */
76 ida_image_alloc(&small);
77 for (y = 0; y < small.i.height; y++) {
78 src = ida_image_scanline(&data->img, y * scale);
79 dst = ida_image_scanline(&small, y);
80 for (x = 0; x < small.i.width; x++) {
81 dst[0] = src[0];
82 dst[1] = src[1];
83 dst[2] = src[2];
84 dst += 3;
85 src += 3*scale;
86 }
87 }
88 data->icon_pixmap = image_to_pixmap(&small);
89
90 /* build DnD icon */
91 n = 0;
92 depth = DefaultDepthOfScreen(XtScreen(widget));
93 XtSetArg(args[n], XmNpixmap, data->icon_pixmap); n++;
94 XtSetArg(args[n], XmNwidth, small.i.width); n++;
95 XtSetArg(args[n], XmNheight, small.i.height); n++;
96 XtSetArg(args[n], XmNdepth, depth); n++;
97 data->icon_widget = XmCreateDragIcon(widget,"dragicon",args,n);
98
99 ida_image_free(&small);
100 }
101
102 static struct sel_data*
sel_find(Atom selection)103 sel_find(Atom selection)
104 {
105 struct list_head *item;
106 struct sel_data *sel;
107
108 list_for_each(item,&selections) {
109 sel = list_entry(item, struct sel_data, list);
110 if (sel->atom == selection)
111 return sel;
112 }
113 return NULL;
114 }
115
116 static void
sel_free(Atom selection)117 sel_free(Atom selection)
118 {
119 struct sel_data *sel;
120
121 sel = sel_find(selection);
122 if (NULL == sel)
123 return;
124 if (sel->filename) {
125 unlink(sel->filename);
126 free(sel->filename);
127 }
128 if (sel->icon_widget)
129 XtDestroyWidget(sel->icon_widget);
130 if (sel->icon_pixmap)
131 XFreePixmap(dpy,sel->icon_pixmap);
132 if (sel->pixmap)
133 XFreePixmap(dpy,sel->pixmap);
134 if (sel->img.p)
135 ida_image_free(&sel->img);
136
137 list_del(&sel->list);
138 free(sel);
139 }
140
141 static struct sel_data*
sel_init(Atom selection)142 sel_init(Atom selection)
143 {
144 struct sel_data *sel;
145
146 sel_free(selection);
147 sel = malloc(sizeof(*sel));
148 memset(sel,0,sizeof(*sel));
149
150 sel->atom = selection;
151 sel->img.i = ida->img.i;
152 sel->img.p = pixman_image_ref(ida->img.p);
153
154 list_add_tail(&sel->list,&selections);
155 return sel;
156 }
157
158 static void
sel_tmpfile(struct sel_data * sel,struct ida_writer * wr)159 sel_tmpfile(struct sel_data *sel, struct ida_writer *wr)
160 {
161 static char *base = "ida";
162 char *tmpdir;
163 FILE *fp;
164 int fd;
165
166 tmpdir = getenv("TMPDIR");
167 if (NULL == tmpdir)
168 tmpdir="/tmp";
169 sel->filename = malloc(strlen(tmpdir)+strlen(base)+16);
170 sprintf(sel->filename,"%s/%s-XXXXXX",tmpdir,base);
171 fd = mkstemp(sel->filename);
172 fp = fdopen(fd,"w");
173 wr->write(fp,&sel->img);
174 fclose(fp);
175 }
176
sel_unique_atom(Widget widget)177 Atom sel_unique_atom(Widget widget)
178 {
179 char id_name[32];
180 Atom id;
181 int i;
182
183 for (i = 0;; i++) {
184 sprintf(id_name,"_IDA_DATA_%lX_%d",XtWindow(widget),i);
185 id = XInternAtom(XtDisplay(widget),id_name,False);
186 if (NULL == sel_find(id))
187 break;
188 }
189 return id;
190 }
191
192 void
selection_convert(Widget widget,XtPointer ignore,XtPointer call_data)193 selection_convert(Widget widget, XtPointer ignore, XtPointer call_data)
194 {
195 XmConvertCallbackStruct *ccs = call_data;
196 unsigned long *ldata;
197 unsigned char *cdata;
198 struct sel_data *sel;
199 char *filename;
200 int n;
201
202 if (debug) {
203 char *t = !ccs->target ? NULL : XGetAtomName(dpy,ccs->target);
204 char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection);
205 fprintf(stderr,"conv: target=%s selection=%s\n",t,s);
206 if (t) XFree(t);
207 if (s) XFree(s);
208 }
209
210 /* tell which formats we can handle */
211 if ((ccs->target == XA_TARGETS) ||
212 (ccs->target == _MOTIF_CLIPBOARD_TARGETS) ||
213 (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) ||
214 (ccs->target == _MOTIF_EXPORT_TARGETS)) {
215 n = 0;
216 ldata = (Atom*)XtMalloc(sizeof(Atom)*12);
217 if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) {
218 ldata[n++] = XA_TARGETS;
219 ldata[n++] = MIME_IMAGE_PPM;
220 ldata[n++] = XA_PIXMAP;
221 ldata[n++] = XA_FOREGROUND;
222 ldata[n++] = XA_BACKGROUND;
223 ldata[n++] = XA_COLORMAP;
224 ldata[n++] = XA_FILE_NAME;
225 ldata[n++] = XA_FILE;
226 ldata[n++] = MIME_TEXT_URI_LIST;
227 ldata[n++] = _NETSCAPE_URL;
228 }
229 ccs->value = ldata;
230 ccs->length = n;
231 ccs->type = XA_ATOM;
232 ccs->format = 32;
233 ccs->status = XmCONVERT_DONE;
234 return;
235
236 } else if (ccs->target == _MOTIF_SNAPSHOT) {
237 /* save away clipboard data */
238 n = 0;
239 ldata = (Atom*)XtMalloc(sizeof(Atom));
240 ldata[n++] = sel_unique_atom(widget);
241 sel_init(ldata[0]);
242 ccs->value = ldata;
243 ccs->length = n;
244 ccs->type = XA_ATOM;
245 ccs->format = 32;
246 ccs->status = XmCONVERT_DONE;
247 return;
248 }
249
250 sel = sel_find(ccs->selection);
251 if (NULL == sel) {
252 /* should not happen */
253 fprintf(stderr,"Oops: selection not found\n");
254 ccs->status = XmCONVERT_REFUSE;
255 return;
256 }
257
258 if ((ccs->target == _MOTIF_LOSE_SELECTION) ||
259 (ccs->target == XA_DONE)) {
260 /* free stuff */
261 sel_free(ccs->selection);
262 ccs->value = NULL;
263 ccs->length = 0;
264 ccs->type = XA_INTEGER;
265 ccs->format = 32;
266 ccs->status = XmCONVERT_DONE;
267 return;
268 }
269
270 /* convert data */
271 if (ccs->target == XA_BACKGROUND ||
272 ccs->target == XA_FOREGROUND ||
273 ccs->target == XA_COLORMAP) {
274 n = 0;
275 ldata = (Atom*)XtMalloc(sizeof(Atom)*8);
276 if (ccs->target == XA_BACKGROUND) {
277 ldata[n++] = WhitePixelOfScreen(XtScreen(widget));
278 ccs->type = XA_PIXEL;
279 }
280 if (ccs->target == XA_FOREGROUND) {
281 ldata[n++] = BlackPixelOfScreen(XtScreen(widget));
282 ccs->type = XA_PIXEL;
283 }
284 if (ccs->target == XA_COLORMAP) {
285 ldata[n++] = DefaultColormapOfScreen(XtScreen(widget));
286 ccs->type = XA_COLORMAP;
287 }
288 ccs->value = ldata;
289 ccs->length = n;
290 ccs->format = 32;
291 ccs->status = XmCONVERT_DONE;
292
293 } else if (ccs->target == XA_PIXMAP) {
294 /* xfer pixmap id */
295 if (!sel->pixmap)
296 sel->pixmap = image_to_pixmap(&sel->img);
297 ldata = (Pixmap*)XtMalloc(sizeof(Pixmap));
298 ldata[0] = sel->pixmap;
299 if (debug)
300 fprintf(stderr,"conv: pixmap id is 0x%lx\n",ldata[0]);
301 ccs->value = ldata;
302 ccs->length = 1;
303 ccs->type = XA_DRAWABLE;
304 ccs->format = 32;
305 ccs->status = XmCONVERT_DONE;
306
307 } else if (ccs->target == MIME_IMAGE_PPM) {
308 /* xfer image data directly */
309 cdata = XtMalloc(sel->img.i.width * sel->img.i.height * 3 + 32);
310 n = sprintf(cdata,"P6\n%u %u\n255\n",
311 sel->img.i.width, sel->img.i.height);
312 memcpy(cdata+n, pixman_image_get_data(sel->img.p),
313 sel->img.i.width*sel->img.i.height*3);
314 ccs->value = cdata;
315 ccs->length = n + sel->img.i.width * sel->img.i.height * 3;
316 ccs->type = MIME_IMAGE_PPM;
317 ccs->format = 8;
318 ccs->status = XmCONVERT_DONE;
319
320 } else if (ccs->target == XA_FILE_NAME ||
321 ccs->target == XA_FILE ||
322 ccs->target == XA_STRING ||
323 ccs->target == MIME_TEXT_URI_LIST ||
324 ccs->target == _NETSCAPE_URL) {
325 /* xfer image via tmp file */
326 if (NULL == sel->filename)
327 sel_tmpfile(sel,&jpeg_writer);
328 if (ccs->target == MIME_TEXT_URI_LIST ||
329 ccs->target == _NETSCAPE_URL) {
330 /* filename => url */
331 filename = XtMalloc(strlen(sel->filename)+8);
332 sprintf(filename,"file:%s\r\n",sel->filename);
333 ccs->type = ccs->target;
334 if (debug)
335 fprintf(stderr,"conv: tmp url is %s\n",filename);
336 } else {
337 filename = XtMalloc(strlen(sel->filename));
338 strcpy(filename,sel->filename);
339 ccs->type = XA_STRING;
340 if (debug)
341 fprintf(stderr,"conv: tmp file is %s\n",filename);
342 }
343 ccs->value = filename;
344 ccs->length = strlen(filename);
345 ccs->format = 8;
346 ccs->status = XmCONVERT_DONE;
347
348 } else {
349 /* shouldn't happen */
350 fprintf(stderr,"Huh? unknown target\n");
351 ccs->status = XmCONVERT_REFUSE;
352 }
353 }
354
355 static void
dnd_done(Widget widget,XtPointer ignore,XtPointer call_data)356 dnd_done(Widget widget, XtPointer ignore, XtPointer call_data)
357 {
358 if (debug)
359 fprintf(stderr,"conv: transfer finished\n");
360 sel_free(_MOTIF_DROP);
361 }
362
363 /* ---------------------------------------------------------------------- */
364 /* receive data (drops, paste) */
365
366 static Atom targets[16];
367 static Cardinal ntargets;
368
369 static void
selection_xfer(Widget widget,XtPointer ignore,XtPointer call_data)370 selection_xfer(Widget widget, XtPointer ignore, XtPointer call_data)
371 {
372 XmSelectionCallbackStruct *scs = call_data;
373 unsigned char *cdata = scs->value;
374 unsigned long *ldata = scs->value;
375 Atom target = 0;
376 unsigned int i,j,pending;
377 char *file,*tmp;
378
379 if (debug) {
380 char *y = !scs->type ? NULL : XGetAtomName(dpy,scs->type);
381 char *t = !scs->target ? NULL : XGetAtomName(dpy,scs->target);
382 char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection);
383 fprintf(stderr,"xfer: id=%p target=%s type=%s selection=%s\n",
384 scs->transfer_id,t,y,s);
385 if (y) XFree(y);
386 if (t) XFree(t);
387 if (s) XFree(s);
388 }
389
390 pending = scs->remaining;
391 if (scs->target == XA_TARGETS) {
392 /* look if we find a target we can deal with ... */
393 for (i = 0; !target && i < scs->length; i++) {
394 for (j = 0; j < ntargets; j++) {
395 if (ldata[i] == targets[j]) {
396 target = ldata[i];
397 break;
398 }
399 }
400 }
401 if (target) {
402 XmTransferValue(scs->transfer_id, target, selection_xfer,
403 NULL, XtLastTimestampProcessed(dpy));
404 pending++;
405 }
406 if (debug) {
407 fprintf(stderr,"xfer: available targets: ");
408 for (i = 0; i < scs->length; i++) {
409 char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]);
410 fprintf(stderr,"%s%s", i != 0 ? ", " : "", name);
411 XFree(name);
412 }
413 fprintf(stderr,"\n");
414 if (0 == scs->length)
415 fprintf(stderr,"xfer: Huh? no TARGETS available?\n");
416 }
417 }
418
419 if (scs->target == XA_FILE_NAME ||
420 scs->target == XA_FILE) {
421 /* load file */
422 if (debug)
423 fprintf(stderr,"xfer: => \"%s\"\n",cdata);
424 new_file(cdata,1);
425 }
426
427 if (scs->target == _NETSCAPE_URL) {
428 /* load file */
429 if (NULL != (tmp = strchr(cdata,'\n')))
430 *tmp = 0;
431 if (NULL != (tmp = strchr(cdata,'\r')))
432 *tmp = 0;
433 if (debug)
434 fprintf(stderr,"xfer: => \"%s\"\n",cdata);
435 new_file(cdata,1);
436 }
437
438 if (scs->target == MIME_TEXT_URI_LIST) {
439 /* load file(s) */
440 for (file = strtok(cdata,"\r\n");
441 NULL != file;
442 file = strtok(NULL,"\r\n")) {
443 if (debug)
444 fprintf(stderr,"xfer: => \"%s\"\n",file);
445 new_file(file,1);
446 }
447 }
448
449 if (scs->target == XA_STRING) {
450 /* might be a file name too, but don't complain if not */
451 if (debug)
452 fprintf(stderr,"xfer: => \"%s\"\n",cdata);
453 new_file(cdata,0);
454 }
455
456 if (scs->target == MIME_IMAGE_PPM ||
457 scs->target == MIME_IMAGE_PGM ||
458 scs->target == MIME_IMAGE_JPEG ||
459 scs->target == MIME_IMAGE_GIF ||
460 scs->target == MIME_IMAGE_PNG ||
461 scs->target == MIME_IMAGE_TIFF ||
462 scs->target == MIME_IMAGE_XPM ||
463 scs->target == MIME_IMAGE_BMP) {
464 /* xfer image data directly */
465 char *filename = load_tmpfile("ida");
466 int fd;
467 fd = mkstemp(filename);
468 write(fd,scs->value,scs->length);
469 close(fd);
470 if (0 == viewer_loadimage(ida,filename,0)) {
471 ida->file = "selection";
472 resize_shell();
473 }
474 unlink(filename);
475 free(filename);
476 }
477
478 if (scs->target == XA_PIXMAP) {
479 /* beaming pixmaps between apps */
480 Window root;
481 Pixmap pix;
482 int x,y,w,h,bw,depth;
483 XImage *ximage;
484 struct ida_image img;
485
486 pix = ldata[0];
487 if (debug)
488 fprintf(stderr,"xfer: => id=0x%lx\n",pix);
489 XGetGeometry(dpy,pix,&root,&x,&y,&w,&h,&bw,&depth);
490 ximage = XGetImage(dpy,pix,0,0,w,h,-1,ZPixmap);
491 parse_ximage(&img, ximage);
492 XDestroyImage(ximage);
493 viewer_setimage(ida,&img,"selection");
494 resize_shell();
495 }
496
497 XFree(scs->value);
498 if (1 == pending) {
499 /* all done -- clean up */
500 if (debug)
501 fprintf(stderr,"xfer: all done\n");
502 XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED);
503 XdndDropFinished(widget,scs);
504 }
505 }
506
507 void
selection_dest(Widget w,XtPointer ignore,XtPointer call_data)508 selection_dest(Widget w, XtPointer ignore, XtPointer call_data)
509 {
510 XmDestinationCallbackStruct *dcs = call_data;
511
512 if (NULL != sel_find(_MOTIF_DROP)) {
513 if (debug)
514 fprintf(stderr,"dest: ignore self drop\n");
515 XmTransferDone(dcs->transfer_id, XmTRANSFER_DONE_FAIL);
516 return;
517 }
518 if (debug)
519 fprintf(stderr,"dest: xfer id=%p\n",dcs->transfer_id);
520 XmTransferValue(dcs->transfer_id, XA_TARGETS, selection_xfer,
521 NULL, XtLastTimestampProcessed(dpy));
522 }
523
524 /* ---------------------------------------------------------------------- */
525
ipc_ac(Widget widget,XEvent * event,String * argv,Cardinal * argc)526 void ipc_ac(Widget widget, XEvent *event, String *argv, Cardinal *argc)
527 {
528 struct sel_data *sel;
529 Widget drag;
530 Arg args[4];
531 Cardinal n=0;
532
533 if (0 == *argc)
534 return;
535
536 if (debug)
537 fprintf(stderr,"ipc: %s\n",argv[0]);
538
539 if (0 == strcmp(argv[0],"paste")) {
540 XmeClipboardSink(ida->widget,XmCOPY,NULL);
541
542 } else if (0 == strcmp(argv[0],"copy")) {
543 XmeClipboardSource(ida->widget,XmCOPY,XtLastTimestampProcessed(dpy));
544
545 } else if (0 == strcmp(argv[0],"drag")) {
546 sel = sel_init(_MOTIF_DROP);
547 iconify(widget,sel);
548 XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
549 XtSetArg(args[n], XmNsourcePixmapIcon, sel->icon_widget); n++;
550 drag = XmeDragSource(ida->widget, NULL, event, args, n);
551 XtAddCallback(drag, XmNdragDropFinishCallback, dnd_done, NULL);
552 }
553 }
554
dnd_add(Widget widget)555 void dnd_add(Widget widget)
556 {
557 Arg args[4];
558 Cardinal n=0;
559
560 XtSetArg(args[n], XmNimportTargets, targets); n++;
561 XtSetArg(args[n], XmNnumImportTargets, ntargets); n++;
562 XmeDropSink(widget,args,n);
563 XdndDropSink(widget);
564 }
565
ipc_init()566 void ipc_init()
567 {
568 _MOTIF_EXPORT_TARGETS =
569 XInternAtom(dpy, "_MOTIF_EXPORT_TARGETS", False);
570 _MOTIF_CLIPBOARD_TARGETS =
571 XInternAtom(dpy, "_MOTIF_CLIPBOARD_TARGETS", False);
572 _MOTIF_DEFERRED_CLIPBOARD_TARGETS =
573 XInternAtom(dpy, "_MOTIF_DEFERRED_CLIPBOARD_TARGETS", False);
574 _MOTIF_SNAPSHOT =
575 XInternAtom(dpy, "_MOTIF_SNAPSHOT", False);
576 _MOTIF_DROP =
577 XInternAtom(dpy, "_MOTIF_DROP", False);
578 _MOTIF_LOSE_SELECTION =
579 XInternAtom(dpy, "_MOTIF_LOSE_SELECTION", False);
580
581 XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
582 XA_DONE = XInternAtom(dpy, "DONE", False);
583 XA_FILE_NAME = XInternAtom(dpy, "FILE_NAME", False);
584 XA_FILE = XInternAtom(dpy, "FILE", False);
585 XA_FOREGROUND = XInternAtom(dpy, "FOREGROUND", False);
586 XA_BACKGROUND = XInternAtom(dpy, "BACKGROUND", False);
587 XA_PIXEL = XInternAtom(dpy, "PIXEL", False);
588 _NETSCAPE_URL = XInternAtom(dpy, "_NETSCAPE_URL", False);
589
590 MIME_TEXT_URI_LIST = XInternAtom(dpy, "text/uri-list", False);
591 MIME_IMAGE_PPM = XInternAtom(dpy, "image/ppm", False);
592 MIME_IMAGE_PGM = XInternAtom(dpy, "image/pgm", False);
593 MIME_IMAGE_XPM = XInternAtom(dpy, "image/xpm", False);
594 MIME_IMAGE_BMP = XInternAtom(dpy, "image/bmp", False);
595 MIME_IMAGE_JPEG = XInternAtom(dpy, "image/jpeg", False);
596 MIME_IMAGE_GIF = XInternAtom(dpy, "image/gif", False);
597 MIME_IMAGE_PNG = XInternAtom(dpy, "image/png", False);
598 MIME_IMAGE_TIFF = XInternAtom(dpy, "image/tiff", False);
599
600 targets[ntargets++] = XA_FILE_NAME;
601 targets[ntargets++] = XA_FILE;
602 targets[ntargets++] = _NETSCAPE_URL;
603 targets[ntargets++] = MIME_TEXT_URI_LIST;
604 targets[ntargets++] = MIME_IMAGE_PPM;
605 targets[ntargets++] = MIME_IMAGE_PGM;
606 targets[ntargets++] = MIME_IMAGE_XPM;
607 targets[ntargets++] = MIME_IMAGE_BMP;
608 targets[ntargets++] = MIME_IMAGE_JPEG;
609 #ifdef HAVE_LIBGIF
610 targets[ntargets++] = MIME_IMAGE_GIF;
611 #endif
612 targets[ntargets++] = MIME_IMAGE_PNG;
613 targets[ntargets++] = MIME_IMAGE_TIFF;
614 targets[ntargets++] = XA_PIXMAP;
615 targets[ntargets++] = XA_STRING;
616
617 INIT_LIST_HEAD(&selections);
618 }
619