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