aboutsummaryrefslogtreecommitdiffstats
path: root/xdnd.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdnd.c')
-rw-r--r--xdnd.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/xdnd.c b/xdnd.c
new file mode 100644
index 0000000..978d7ae
--- /dev/null
+++ b/xdnd.c
@@ -0,0 +1,321 @@
+/*
+ * basic Xdnd support for Motif 2.x
+ *
+ * Receive drops only. Works fine in parallel with Motif DnD.
+ *
+ * Initiate drags is probably hard do do without breaking Motif DnD or
+ * heavily mucking with the Motif internals. Highlighting seems to be
+ * non-trivial too.
+ *
+ * Usage:
+ * (1) register XdndAction as "Xdnd"
+ * (2) register Widgets using XdndDropSink (acts like XmeDropSink
+ * for Motif DnD)
+ * (3) the transfer callback functions have to call
+ * XdndDropFinished() when they are done (i.e. after calling
+ * XmTransferDone)
+ *
+ * Data transfer is done using the usual Motif 2.x way, using UTM.
+ * Read: XmNdestinationCallback will be called, with
+ * XmDestinationCallbackStruct->selection set to XdndSelection.
+ *
+ * It is up to the application to handle the Xdnd MIME targets
+ * correctly, i.e. accept both "TEXT" and "text/plain" targets for
+ * example. Otherwise Xdnd support shouldn't need much work if Motif
+ * DnD support is present already.
+ *
+ * Known problems:
+ * - Not working with KDE 2.x as they don't provide a TARGETS
+ * target (which IMO is illegal, see ICCCM specs).
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <Xm/Xm.h>
+#include <Xm/Transfer.h>
+#include <Xm/TransferP.h>
+
+#include "xdnd.h"
+
+/* ---------------------------------------------------------------------- */
+
+int xdnd_debug = 0;
+
+static Atom XdndAware;
+static Atom XdndTypeList;
+static Atom XdndSelection;
+
+static Atom XdndEnter;
+static Atom XdndPosition;
+static Atom XdndStatus;
+static Atom XdndLeave;
+static Atom XdndDrop;
+static Atom XdndFinished;
+
+static Atom XdndActionCopy;
+static Atom XdndActionMove;
+static Atom XdndActionLink;
+static Atom XdndActionAsk;
+static Atom XdndActionPrivate;
+
+/* ---------------------------------------------------------------------- */
+
+static void XdndInit(Display *dpy)
+{
+ if (XdndAware)
+ return;
+
+ XdndAware = XInternAtom(dpy, "XdndAware", False);
+ XdndTypeList = XInternAtom(dpy, "XdndTypeList", False);
+ XdndSelection = XInternAtom(dpy, "XdndSelection", False);
+
+ /* client messages */
+ XdndEnter = XInternAtom(dpy, "XdndEnter", False);
+ XdndPosition = XInternAtom(dpy, "XdndPosition", False);
+ XdndStatus = XInternAtom(dpy, "XdndStatus", False);
+ XdndLeave = XInternAtom(dpy, "XdndLeave", False);
+ XdndDrop = XInternAtom(dpy, "XdndDrop", False);
+ XdndFinished = XInternAtom(dpy, "XdndFinished", False);
+
+ /* actions */
+ XdndActionCopy = XInternAtom(dpy, "XdndActionCopy", False);
+ XdndActionMove = XInternAtom(dpy, "XdndActionMove", False);
+ XdndActionLink = XInternAtom(dpy, "XdndActionLink", False);
+ XdndActionAsk = XInternAtom(dpy, "XdndActionAsk", False);
+ XdndActionPrivate = XInternAtom(dpy, "XdndActionPrivate", False);
+}
+
+static void
+init_window(Widget widget)
+{
+ int version = 4;
+
+ /* window */
+ XChangeProperty(XtDisplay(widget),XtWindow(widget),
+ XdndAware, XA_ATOM, 32, PropModeReplace,
+ (XtPointer)&version, 1);
+
+ /* shell */
+ while (!XtIsShell(widget))
+ widget = XtParent(widget);
+
+ XChangeProperty(XtDisplay(widget),XtWindow(widget),
+ XdndAware, XA_ATOM, 32, PropModeReplace,
+ (XtPointer)&version, 1);
+ XtOverrideTranslations(widget, XtParseTranslationTable
+ ("<Message>XdndEnter: Xdnd()\n"
+ "<Message>XdndPosition: Xdnd()\n"
+ "<Message>XdndLeave: Xdnd()\n"
+ "<Message>XdndDrop: Xdnd()"));
+}
+
+static Widget
+find_window(Widget widget, int wx, int wy, int rx, int ry)
+{
+ WidgetList children;
+ Cardinal nchildren;
+ Dimension x,y,w,h;
+ Widget found = NULL;
+ int i;
+
+ nchildren = 0;
+ XtVaGetValues(widget,XtNchildren,&children,
+ XtNnumChildren,&nchildren,NULL);
+ fprintf(stderr,"findwindow %s\n",XtName(widget));
+ for (i = nchildren-1; i >= 0; i--) {
+ XtVaGetValues(children[i],XtNx,&x,XtNy,&y,
+ XtNwidth,&w,XtNheight,&h,NULL);
+ if (!XtIsManaged(children[i]))
+ continue;
+ if (XtIsSubclass(children[i],xmGadgetClass))
+ continue;
+ if (rx < wx+x || rx > wx+x+w)
+ continue;
+ if (ry < wy+y || ry > wy+y+h)
+ continue;
+ found = children[i];
+ break;
+ }
+ if (found) {
+ fprintf(stderr," more: %s\n",XtName(found));
+ return find_window(found,wx+x,wy+y,rx,ry);
+ }
+ fprintf(stderr," done: %s\n",XtName(widget));
+ return widget;
+}
+
+static int
+check_window(Widget widget)
+{
+ Atom type;
+ int format,rc;
+ unsigned long nitems,rest;
+ unsigned long *ldata;
+
+ rc = XGetWindowProperty(XtDisplay(widget),XtWindow(widget),
+ XdndAware,0,64,False,AnyPropertyType,
+ &type,&format,&nitems,&rest,
+ (XtPointer)&ldata);
+ XFree(ldata);
+ return rc == Success && nitems > 0;
+}
+
+static XtEnum get_operation(Atom action)
+{
+ if (XdndActionCopy == action)
+ return XmCOPY;
+ if (XdndActionLink == action)
+ return XmLINK;
+ if (XdndActionMove == action)
+ return XmMOVE;
+ return 0;
+}
+
+static Atom get_action(XtEnum operation)
+{
+ if (XmCOPY == operation)
+ return XdndActionCopy;
+ if (XmLINK == operation)
+ return XdndActionLink;
+ if (XmMOVE == operation)
+ return XdndActionMove;
+ return None;
+}
+
+static void
+XdndEvent(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont)
+{
+ switch(event->type) {
+ case MapNotify:
+ init_window(widget);
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * not very nice this way, but as you can hardly have two drags at the
+ * same time with one pointer only it should be fine ...
+ */
+static Widget target;
+static int target_ok,drop_ok;
+static Window source;
+static XtEnum operation;
+
+void
+XdndAction(Widget widget, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ char *name;
+ XEvent reply;
+
+ if (NULL == event)
+ return;
+ if (ClientMessage != event->type)
+ return;
+
+ if (XdndEnter == event->xclient.message_type) {
+ if (xdnd_debug)
+ fprintf(stderr,"Xdnd: Enter: win=0x%lx ver=%ld more=%s\n",
+ event->xclient.data.l[0],
+ event->xclient.data.l[1] >> 24,
+ (event->xclient.data.l[1] & 1) ? "yes" : "no");
+ }
+
+ if (XdndPosition == event->xclient.message_type) {
+ source = event->xclient.data.l[0];
+ target = find_window(widget,0,0,
+ event->xclient.data.l[2] >> 16,
+ event->xclient.data.l[2] & 0xffff);
+ target_ok = check_window(target);
+ if (target_ok) {
+ operation = get_operation(event->xclient.data.l[4]);
+ operation = XmCOPY; /* FIXME */
+ drop_ok = 1;
+ } else {
+ operation = 0;
+ drop_ok = 0;
+ }
+ if (xdnd_debug) {
+ name = NULL;
+ if (event->xclient.data.l[4])
+ name=XGetAtomName(XtDisplay(widget),event->xclient.data.l[4]);
+ fprintf(stderr,"Xdnd: Position: win=0x%lx pos=+%ld+%ld ts=%ld "
+ "ac=%s op=%d widget=%s drop=%s\n",
+ event->xclient.data.l[0],
+ event->xclient.data.l[2] >> 16,
+ event->xclient.data.l[2] & 0xffff,
+ event->xclient.data.l[3],
+ name,operation,
+ XtName(target),target_ok ? "yes" : "no");
+ if (name)
+ XFree(name);
+ }
+ memset(&reply,0,sizeof(reply));
+ reply.xany.type = ClientMessage;
+ reply.xany.display = XtDisplay(widget);
+ reply.xclient.window = event->xclient.data.l[0];
+ reply.xclient.message_type = XdndStatus;
+ reply.xclient.format = 32;
+ reply.xclient.data.l[0] = XtWindow(widget);
+ reply.xclient.data.l[1] = drop_ok ? 1 : 0;
+ reply.xclient.data.l[4] = get_action(operation);
+ XSendEvent(XtDisplay(widget),reply.xclient.window,0,0,&reply);
+ }
+
+ if (XdndDrop == event->xclient.message_type) {
+ source = event->xclient.data.l[0];
+ if (xdnd_debug)
+ fprintf(stderr,"Xdnd: Drop: win=0x%lx ts=%ld\n",
+ event->xclient.data.l[0],
+ event->xclient.data.l[2]);
+ XmeNamedSink(target,XdndSelection,XmCOPY,NULL,
+ XtLastTimestampProcessed(XtDisplay(widget)));
+ }
+
+ if (XdndLeave == event->xclient.message_type) {
+ source = 0;
+ if (xdnd_debug)
+ fprintf(stderr,"Xdnd: Leave: win=0x%lx\n",
+ event->xclient.data.l[0]);
+ }
+}
+
+void XdndDropFinished(Widget widget, XmSelectionCallbackStruct *scs)
+{
+ XEvent reply;
+
+ if (XdndSelection != scs->selection)
+ return;
+ if (0 == source)
+ return;
+
+ if (xdnd_debug)
+ fprintf(stderr,"Xdnd: sending Finished (0x%lx)\n",source);
+ memset(&reply,0,sizeof(reply));
+ reply.xany.type = ClientMessage;
+ reply.xany.display = XtDisplay(widget);
+ reply.xclient.window = source;
+ reply.xclient.message_type = XdndFinished;
+ reply.xclient.format = 32;
+ while (!XtIsShell(widget))
+ widget = XtParent(widget);
+ reply.xclient.data.l[0] = XtWindow(widget);
+ XSendEvent(XtDisplay(widget),reply.xclient.window,0,0,&reply);
+ source = 0;
+}
+
+void XdndDropSink(Widget widget)
+{
+ XdndInit(XtDisplay(widget));
+
+ if (XtWindow(widget))
+ init_window(widget);
+ XtAddEventHandler(widget,StructureNotifyMask,True,XdndEvent,NULL);
+}