aboutsummaryrefslogtreecommitdiffstats
path: root/selections.c
diff options
context:
space:
mode:
Diffstat (limited to 'selections.c')
-rw-r--r--selections.c623
1 files changed, 623 insertions, 0 deletions
diff --git a/selections.c b/selections.c
new file mode 100644
index 0000000..262caa5
--- /dev/null
+++ b/selections.c
@@ -0,0 +1,623 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <Xm/Xm.h>
+#include <Xm/Transfer.h>
+#include <Xm/TransferP.h>
+#include <Xm/DragIcon.h>
+
+#include "ida.h"
+#include "readers.h"
+#include "writers.h"
+#include "viewer.h"
+#include "xwd.h"
+#include "xdnd.h"
+#include "selections.h"
+#include "list.h"
+
+Atom XA_TARGETS, XA_DONE;
+Atom XA_FILE_NAME, XA_FILE;
+Atom XA_BACKGROUND, XA_FOREGROUND, XA_PIXEL;
+Atom _MOTIF_EXPORT_TARGETS;
+Atom _MOTIF_CLIPBOARD_TARGETS;
+Atom _MOTIF_DEFERRED_CLIPBOARD_TARGETS;
+Atom _MOTIF_SNAPSHOT;
+Atom _MOTIF_DROP;
+Atom _MOTIF_LOSE_SELECTION;
+Atom _NETSCAPE_URL;
+Atom MIME_TEXT_URI_LIST;
+Atom MIME_IMAGE_PPM;
+Atom MIME_IMAGE_PGM;
+Atom MIME_IMAGE_XPM;
+Atom MIME_IMAGE_BMP;
+Atom MIME_IMAGE_JPEG;
+Atom MIME_IMAGE_GIF;
+Atom MIME_IMAGE_PNG;
+Atom MIME_IMAGE_TIFF;
+
+/* ---------------------------------------------------------------------- */
+/* send data (drags, copy) */
+
+struct sel_data {
+ struct list_head list;
+ Atom atom;
+ struct ida_image img;
+ Pixmap pixmap;
+ char *filename;
+ Pixmap icon_pixmap;
+ Widget icon_widget;
+};
+static struct list_head selections;
+
+static void
+iconify(Widget widget, struct sel_data *data)
+{
+ struct ida_image small;
+ unsigned int scale,x,y,depth;
+ char *src,*dst;
+ Arg args[4];
+ Cardinal n=0;
+
+ /* calc size */
+ memset(&small,0,sizeof(small));
+ for (scale = 1;; scale++) {
+ small.i.width = data->img.i.width / scale;
+ small.i.height = data->img.i.height / scale;
+ if (small.i.width < 128 && small.i.height < 128)
+ break;
+ }
+
+ /* scale down & create pixmap */
+ dst = small.data = malloc(small.i.width * small.i.height * 3);
+ for (y = 0; y < small.i.height; y++) {
+ src = data->img.data + 3 * y * scale * data->img.i.width;
+ for (x = 0; x < small.i.width; x++) {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst += 3;
+ src += 3*scale;
+ }
+ }
+ data->icon_pixmap = image_to_pixmap(&small);
+
+ /* build DnD icon */
+ n = 0;
+ depth = DefaultDepthOfScreen(XtScreen(widget));
+ XtSetArg(args[n], XmNpixmap, data->icon_pixmap); n++;
+ XtSetArg(args[n], XmNwidth, small.i.width); n++;
+ XtSetArg(args[n], XmNheight, small.i.height); n++;
+ XtSetArg(args[n], XmNdepth, depth); n++;
+ data->icon_widget = XmCreateDragIcon(widget,"dragicon",args,n);
+
+ free(small.data);
+}
+
+static struct sel_data*
+sel_find(Atom selection)
+{
+ struct list_head *item;
+ struct sel_data *sel;
+
+ list_for_each(item,&selections) {
+ sel = list_entry(item, struct sel_data, list);
+ if (sel->atom == selection)
+ return sel;
+ }
+ return NULL;
+}
+
+static void
+sel_free(Atom selection)
+{
+ struct sel_data *sel;
+
+ sel = sel_find(selection);
+ if (NULL == sel)
+ return;
+ if (sel->filename) {
+ unlink(sel->filename);
+ free(sel->filename);
+ }
+ if (sel->icon_widget)
+ XtDestroyWidget(sel->icon_widget);
+ if (sel->icon_pixmap)
+ XFreePixmap(dpy,sel->icon_pixmap);
+ if (sel->pixmap)
+ XFreePixmap(dpy,sel->pixmap);
+ if (sel->img.data)
+ free(sel->img.data);
+
+ list_del(&sel->list);
+ free(sel);
+}
+
+static struct sel_data*
+sel_init(Atom selection)
+{
+ struct sel_data *sel;
+
+ sel_free(selection);
+ sel = malloc(sizeof(*sel));
+ memset(sel,0,sizeof(*sel));
+
+ sel->atom = selection;
+ sel->img = ida->img;
+ sel->img.data = malloc(ida->img.i.width * ida->img.i.height * 3);
+ memcpy(sel->img.data, ida->img.data,
+ ida->img.i.width * ida->img.i.height * 3);
+
+ list_add_tail(&sel->list,&selections);
+ return sel;
+}
+
+static void
+sel_tmpfile(struct sel_data *sel, struct ida_writer *wr)
+{
+ static char *base = "ida";
+ char *tmpdir;
+ FILE *fp;
+ int fd;
+
+ tmpdir = getenv("TMPDIR");
+ if (NULL == tmpdir)
+ tmpdir="/tmp";
+ sel->filename = malloc(strlen(tmpdir)+strlen(base)+16);
+ sprintf(sel->filename,"%s/%s-XXXXXX",tmpdir,base);
+ fd = mkstemp(sel->filename);
+ fp = fdopen(fd,"w");
+ wr->write(fp,&sel->img);
+ fclose(fp);
+}
+
+Atom sel_unique_atom(Widget widget)
+{
+ char id_name[32];
+ Atom id;
+ int i;
+
+ for (i = 0;; i++) {
+ sprintf(id_name,"_IDA_DATA_%lX_%d",XtWindow(widget),i);
+ id = XInternAtom(XtDisplay(widget),id_name,False);
+ if (NULL == sel_find(id))
+ break;
+ }
+ return id;
+}
+
+void
+selection_convert(Widget widget, XtPointer ignore, XtPointer call_data)
+{
+ XmConvertCallbackStruct *ccs = call_data;
+ unsigned long *ldata;
+ unsigned char *cdata;
+ struct sel_data *sel;
+ char *filename;
+ int n;
+
+ if (debug) {
+ char *t = !ccs->target ? NULL : XGetAtomName(dpy,ccs->target);
+ char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection);
+ fprintf(stderr,"conv: target=%s selection=%s\n",t,s);
+ if (t) XFree(t);
+ if (s) XFree(s);
+ }
+
+ /* tell which formats we can handle */
+ if ((ccs->target == XA_TARGETS) ||
+ (ccs->target == _MOTIF_CLIPBOARD_TARGETS) ||
+ (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) ||
+ (ccs->target == _MOTIF_EXPORT_TARGETS)) {
+ n = 0;
+ ldata = (Atom*)XtMalloc(sizeof(Atom)*12);
+ if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) {
+ ldata[n++] = XA_TARGETS;
+ ldata[n++] = MIME_IMAGE_PPM;
+ ldata[n++] = XA_PIXMAP;
+ ldata[n++] = XA_FOREGROUND;
+ ldata[n++] = XA_BACKGROUND;
+ ldata[n++] = XA_COLORMAP;
+ ldata[n++] = XA_FILE_NAME;
+ ldata[n++] = XA_FILE;
+ ldata[n++] = MIME_TEXT_URI_LIST;
+ ldata[n++] = _NETSCAPE_URL;
+ }
+ ccs->value = ldata;
+ ccs->length = n;
+ ccs->type = XA_ATOM;
+ ccs->format = 32;
+ ccs->status = XmCONVERT_DONE;
+ return;
+
+ } else if (ccs->target == _MOTIF_SNAPSHOT) {
+ /* save away clipboard data */
+ n = 0;
+ ldata = (Atom*)XtMalloc(sizeof(Atom));
+ ldata[n++] = sel_unique_atom(widget);
+ sel_init(ldata[0]);
+ ccs->value = ldata;
+ ccs->length = n;
+ ccs->type = XA_ATOM;
+ ccs->format = 32;
+ ccs->status = XmCONVERT_DONE;
+ return;
+ }
+
+ sel = sel_find(ccs->selection);
+ if (NULL == sel) {
+ /* should not happen */
+ fprintf(stderr,"Oops: selection not found\n");
+ ccs->status = XmCONVERT_REFUSE;
+ return;
+ }
+
+ if ((ccs->target == _MOTIF_LOSE_SELECTION) ||
+ (ccs->target == XA_DONE)) {
+ /* free stuff */
+ sel_free(ccs->selection);
+ ccs->value = NULL;
+ ccs->length = 0;
+ ccs->type = XA_INTEGER;
+ ccs->format = 32;
+ ccs->status = XmCONVERT_DONE;
+ return;
+ }
+
+ /* convert data */
+ if (ccs->target == XA_BACKGROUND ||
+ ccs->target == XA_FOREGROUND ||
+ ccs->target == XA_COLORMAP) {
+ n = 0;
+ ldata = (Atom*)XtMalloc(sizeof(Atom)*8);
+ if (ccs->target == XA_BACKGROUND) {
+ ldata[n++] = WhitePixelOfScreen(XtScreen(widget));
+ ccs->type = XA_PIXEL;
+ }
+ if (ccs->target == XA_FOREGROUND) {
+ ldata[n++] = BlackPixelOfScreen(XtScreen(widget));
+ ccs->type = XA_PIXEL;
+ }
+ if (ccs->target == XA_COLORMAP) {
+ ldata[n++] = DefaultColormapOfScreen(XtScreen(widget));
+ ccs->type = XA_COLORMAP;
+ }
+ ccs->value = ldata;
+ ccs->length = n;
+ ccs->format = 32;
+ ccs->status = XmCONVERT_DONE;
+
+ } else if (ccs->target == XA_PIXMAP) {
+ /* xfer pixmap id */
+ if (!sel->pixmap)
+ sel->pixmap = image_to_pixmap(&sel->img);
+ ldata = (Pixmap*)XtMalloc(sizeof(Pixmap));
+ ldata[0] = sel->pixmap;
+ if (debug)
+ fprintf(stderr,"conv: pixmap id is 0x%lx\n",ldata[0]);
+ ccs->value = ldata;
+ ccs->length = 1;
+ ccs->type = XA_DRAWABLE;
+ ccs->format = 32;
+ ccs->status = XmCONVERT_DONE;
+
+ } else if (ccs->target == MIME_IMAGE_PPM) {
+ /* xfer image data directly */
+ cdata = XtMalloc(sel->img.i.width * sel->img.i.height * 3 + 32);
+ n = sprintf(cdata,"P6\n%d %d\n255\n",
+ sel->img.i.width, sel->img.i.height);
+ memcpy(cdata+n, sel->img.data, sel->img.i.width*sel->img.i.height*3);
+ ccs->value = cdata;
+ ccs->length = n + sel->img.i.width * sel->img.i.height * 3;
+ ccs->type = MIME_IMAGE_PPM;
+ ccs->format = 8;
+ ccs->status = XmCONVERT_DONE;
+
+ } else if (ccs->target == XA_FILE_NAME ||
+ ccs->target == XA_FILE ||
+ ccs->target == XA_STRING ||
+ ccs->target == MIME_TEXT_URI_LIST ||
+ ccs->target == _NETSCAPE_URL) {
+ /* xfer image via tmp file */
+ if (NULL == sel->filename)
+ sel_tmpfile(sel,&jpeg_writer);
+ if (ccs->target == MIME_TEXT_URI_LIST ||
+ ccs->target == _NETSCAPE_URL) {
+ /* filename => url */
+ filename = XtMalloc(strlen(sel->filename)+8);
+ sprintf(filename,"file:%s\r\n",sel->filename);
+ ccs->type = ccs->target;
+ if (debug)
+ fprintf(stderr,"conv: tmp url is %s\n",filename);
+ } else {
+ filename = XtMalloc(strlen(sel->filename));
+ strcpy(filename,sel->filename);
+ ccs->type = XA_STRING;
+ if (debug)
+ fprintf(stderr,"conv: tmp file is %s\n",filename);
+ }
+ ccs->value = filename;
+ ccs->length = strlen(filename);
+ ccs->format = 8;
+ ccs->status = XmCONVERT_DONE;
+
+ } else {
+ /* shouldn't happen */
+ fprintf(stderr,"Huh? unknown target\n");
+ ccs->status = XmCONVERT_REFUSE;
+ }
+}
+
+static void
+dnd_done(Widget widget, XtPointer ignore, XtPointer call_data)
+{
+ if (debug)
+ fprintf(stderr,"conv: transfer finished\n");
+ sel_free(_MOTIF_DROP);
+}
+
+/* ---------------------------------------------------------------------- */
+/* receive data (drops, paste) */
+
+static Atom targets[16];
+static Cardinal ntargets;
+
+static void
+selection_xfer(Widget widget, XtPointer ignore, XtPointer call_data)
+{
+ XmSelectionCallbackStruct *scs = call_data;
+ unsigned char *cdata = scs->value;
+ unsigned long *ldata = scs->value;
+ Atom target = 0;
+ unsigned int i,j,pending;
+ char *file,*tmp;
+
+ if (debug) {
+ char *y = !scs->type ? NULL : XGetAtomName(dpy,scs->type);
+ char *t = !scs->target ? NULL : XGetAtomName(dpy,scs->target);
+ char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection);
+ fprintf(stderr,"xfer: id=%p target=%s type=%s selection=%s\n",
+ scs->transfer_id,t,y,s);
+ if (y) XFree(y);
+ if (t) XFree(t);
+ if (s) XFree(s);
+ }
+
+ pending = scs->remaining;
+ if (scs->target == XA_TARGETS) {
+ /* look if we find a target we can deal with ... */
+ for (i = 0; !target && i < scs->length; i++) {
+ for (j = 0; j < ntargets; j++) {
+ if (ldata[i] == targets[j]) {
+ target = ldata[i];
+ break;
+ }
+ }
+ }
+ if (target) {
+ XmTransferValue(scs->transfer_id, target, selection_xfer,
+ NULL, XtLastTimestampProcessed(dpy));
+ pending++;
+ }
+ if (debug) {
+ fprintf(stderr,"xfer: available targets: ");
+ for (i = 0; i < scs->length; i++) {
+ char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]);
+ fprintf(stderr,"%s%s", i != 0 ? ", " : "", name);
+ XFree(name);
+ }
+ fprintf(stderr,"\n");
+ if (0 == scs->length)
+ fprintf(stderr,"xfer: Huh? no TARGETS available?\n");
+ }
+ }
+
+ if (scs->target == XA_FILE_NAME ||
+ scs->target == XA_FILE) {
+ /* load file */
+ if (debug)
+ fprintf(stderr,"xfer: => \"%s\"\n",cdata);
+ new_file(cdata,1);
+ }
+
+ if (scs->target == _NETSCAPE_URL) {
+ /* load file */
+ if (NULL != (tmp = strchr(cdata,'\n')))
+ *tmp = 0;
+ if (NULL != (tmp = strchr(cdata,'\r')))
+ *tmp = 0;
+ if (debug)
+ fprintf(stderr,"xfer: => \"%s\"\n",cdata);
+ new_file(cdata,1);
+ }
+
+ if (scs->target == MIME_TEXT_URI_LIST) {
+ /* load file(s) */
+ for (file = strtok(cdata,"\r\n");
+ NULL != file;
+ file = strtok(NULL,"\r\n")) {
+ if (debug)
+ fprintf(stderr,"xfer: => \"%s\"\n",file);
+ new_file(file,1);
+ }
+ }
+
+ if (scs->target == XA_STRING) {
+ /* might be a file name too, but don't complain if not */
+ if (debug)
+ fprintf(stderr,"xfer: => \"%s\"\n",cdata);
+ new_file(cdata,0);
+ }
+
+ if (scs->target == MIME_IMAGE_PPM ||
+ scs->target == MIME_IMAGE_PGM ||
+ scs->target == MIME_IMAGE_JPEG ||
+ scs->target == MIME_IMAGE_GIF ||
+ scs->target == MIME_IMAGE_PNG ||
+ scs->target == MIME_IMAGE_TIFF ||
+ scs->target == MIME_IMAGE_XPM ||
+ scs->target == MIME_IMAGE_BMP) {
+ /* xfer image data directly */
+ char *filename = load_tmpfile("ida");
+ int fd;
+ fd = mkstemp(filename);
+ write(fd,scs->value,scs->length);
+ close(fd);
+ if (0 == viewer_loadimage(ida,filename,0)) {
+ ida->file = "selection";
+ resize_shell();
+ }
+ unlink(filename);
+ free(filename);
+ }
+
+ if (scs->target == XA_PIXMAP) {
+ /* beaming pixmaps between apps */
+ Screen *scr;
+ Window root;
+ Pixmap pix;
+ int x,y,w,h,bw,depth;
+ XImage *ximage;
+ struct ida_image img;
+
+ pix = ldata[0];
+ if (debug)
+ fprintf(stderr,"xfer: => id=0x%lx\n",pix);
+ scr = XtScreen(widget);
+ XGetGeometry(dpy,pix,&root,&x,&y,&w,&h,&bw,&depth);
+ ximage = XGetImage(dpy,pix,0,0,w,h,-1,ZPixmap);
+ parse_ximage(&img, ximage);
+ XDestroyImage(ximage);
+ viewer_setimage(ida,&img,"selection");
+ resize_shell();
+ }
+
+ XFree(scs->value);
+ if (1 == pending) {
+ /* all done -- clean up */
+ if (debug)
+ fprintf(stderr,"xfer: all done\n");
+ XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED);
+ XdndDropFinished(widget,scs);
+ }
+}
+
+void
+selection_dest(Widget w, XtPointer ignore, XtPointer call_data)
+{
+ XmDestinationCallbackStruct *dcs = call_data;
+
+ if (NULL != sel_find(_MOTIF_DROP)) {
+ if (debug)
+ fprintf(stderr,"dest: ignore self drop\n");
+ XmTransferDone(dcs->transfer_id, XmTRANSFER_DONE_FAIL);
+ return;
+ }
+ if (debug)
+ fprintf(stderr,"dest: xfer id=%p\n",dcs->transfer_id);
+ XmTransferValue(dcs->transfer_id, XA_TARGETS, selection_xfer,
+ NULL, XtLastTimestampProcessed(dpy));
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ipc_ac(Widget widget, XEvent *event, String *argv, Cardinal *argc)
+{
+ struct sel_data *sel;
+ Widget drag;
+ Arg args[4];
+ Cardinal n=0;
+
+ if (0 == *argc)
+ return;
+
+ if (debug)
+ fprintf(stderr,"ipc: %s\n",argv[0]);
+
+ if (0 == strcmp(argv[0],"paste")) {
+ XmeClipboardSink(ida->widget,XmCOPY,NULL);
+
+ } else if (0 == strcmp(argv[0],"copy")) {
+ XmeClipboardSource(ida->widget,XmCOPY,XtLastTimestampProcessed(dpy));
+
+ } else if (0 == strcmp(argv[0],"drag")) {
+ sel = sel_init(_MOTIF_DROP);
+ iconify(widget,sel);
+ XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
+ XtSetArg(args[n], XmNsourcePixmapIcon, sel->icon_widget); n++;
+ drag = XmeDragSource(ida->widget, NULL, event, args, n);
+ XtAddCallback(drag, XmNdragDropFinishCallback, dnd_done, NULL);
+ }
+}
+
+void dnd_add(Widget widget)
+{
+ Arg args[4];
+ Cardinal n=0;
+
+ XtSetArg(args[n], XmNimportTargets, targets); n++;
+ XtSetArg(args[n], XmNnumImportTargets, ntargets); n++;
+ XmeDropSink(widget,args,n);
+ XdndDropSink(widget);
+}
+
+void ipc_init()
+{
+ _MOTIF_EXPORT_TARGETS =
+ XInternAtom(dpy, "_MOTIF_EXPORT_TARGETS", False);
+ _MOTIF_CLIPBOARD_TARGETS =
+ XInternAtom(dpy, "_MOTIF_CLIPBOARD_TARGETS", False);
+ _MOTIF_DEFERRED_CLIPBOARD_TARGETS =
+ XInternAtom(dpy, "_MOTIF_DEFERRED_CLIPBOARD_TARGETS", False);
+ _MOTIF_SNAPSHOT =
+ XInternAtom(dpy, "_MOTIF_SNAPSHOT", False);
+ _MOTIF_DROP =
+ XInternAtom(dpy, "_MOTIF_DROP", False);
+ _MOTIF_LOSE_SELECTION =
+ XInternAtom(dpy, "_MOTIF_LOSE_SELECTION", False);
+
+ XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
+ XA_DONE = XInternAtom(dpy, "DONE", False);
+ XA_FILE_NAME = XInternAtom(dpy, "FILE_NAME", False);
+ XA_FILE = XInternAtom(dpy, "FILE", False);
+ XA_FOREGROUND = XInternAtom(dpy, "FOREGROUND", False);
+ XA_BACKGROUND = XInternAtom(dpy, "BACKGROUND", False);
+ XA_PIXEL = XInternAtom(dpy, "PIXEL", False);
+ _NETSCAPE_URL = XInternAtom(dpy, "_NETSCAPE_URL", False);
+
+ MIME_TEXT_URI_LIST = XInternAtom(dpy, "text/uri-list", False);
+ MIME_IMAGE_PPM = XInternAtom(dpy, "image/ppm", False);
+ MIME_IMAGE_PGM = XInternAtom(dpy, "image/pgm", False);
+ MIME_IMAGE_XPM = XInternAtom(dpy, "image/xpm", False);
+ MIME_IMAGE_BMP = XInternAtom(dpy, "image/bmp", False);
+ MIME_IMAGE_JPEG = XInternAtom(dpy, "image/jpeg", False);
+ MIME_IMAGE_GIF = XInternAtom(dpy, "image/gif", False);
+ MIME_IMAGE_PNG = XInternAtom(dpy, "image/png", False);
+ MIME_IMAGE_TIFF = XInternAtom(dpy, "image/tiff", False);
+
+ targets[ntargets++] = XA_FILE_NAME;
+ targets[ntargets++] = XA_FILE;
+ targets[ntargets++] = _NETSCAPE_URL;
+ targets[ntargets++] = MIME_TEXT_URI_LIST;
+ targets[ntargets++] = MIME_IMAGE_PPM;
+ targets[ntargets++] = MIME_IMAGE_PGM;
+ targets[ntargets++] = MIME_IMAGE_XPM;
+ targets[ntargets++] = MIME_IMAGE_BMP;
+ targets[ntargets++] = MIME_IMAGE_JPEG;
+#ifdef HAVE_LIBUNGIF
+ targets[ntargets++] = MIME_IMAGE_GIF;
+#endif
+#ifdef HAVE_LIBPNG
+ targets[ntargets++] = MIME_IMAGE_PNG;
+#endif
+#ifdef HAVE_LIBTIFF
+ targets[ntargets++] = MIME_IMAGE_TIFF;
+#endif
+ targets[ntargets++] = XA_PIXMAP;
+ targets[ntargets++] = XA_STRING;
+
+ INIT_LIST_HEAD(&selections);
+}