This patch is adding Drag-and-Drop support to wmiv based on Xdnd protocol
handling library.

BTW as you may have seen with my last patch, these days i am doing
some DnD tests.
This is the implementation I did and used to test wmaker DnD.
It's based on some common xdnd protocol implementation I updated
for wmiv and to pass the checkpatch stage.

As far as i saw there is still one issue in the wmaker DnD protocol
implementation,
I am still trying to pinpoint it. Seems it does not handle drop from
some tools at the first drop
(yes i know it's pretty vague). For example, it will work with Thunar
but not with Rox.


regards,
david


---
 util/Makefile.am |   4 +-
 util/README      |   2 +
 util/wmiv.c      |  60 ++++-
 util/xdnd.c      | 680 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 util/xdnd.h      | 171 ++++++++++++++
 5 files changed, 912 insertions(+), 5 deletions(-)
 create mode 100755 util/xdnd.c
 create mode 100755 util/xdnd.h

diff --git a/util/Makefile.am b/util/Makefile.am
index b5c9d04..16a1295 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -71,11 +71,13 @@ wmmenugen_SOURCES = wmmenugen.c wmmenugen.h
wmmenugen_misc.c \
  wmmenugen_parse_xdg.c

 wmiv_LDADD = \
+ $(top_builddir)/WINGs/libWINGs.la \
+ $(top_builddir)/WINGs/libWUtil.la \
  $(top_builddir)/wrlib/libwraster.la \
  @XLFLAGS@ @XLIBS@ \
  @GFXLIBS@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) $(LIBEXIF)

-wmiv_SOURCES = wmiv.c wmiv.h
+wmiv_SOURCES = wmiv.c wmiv.h xdnd.c xdnd.h

 CLEANFILES = wmaker.inst

diff --git a/util/README b/util/README
index 76b779f..73f2968 100644
--- a/util/README
+++ b/util/README
@@ -22,6 +22,8 @@ wxcopy- copy input file or stdin into X cutbuffer

 wxpaste- copy content of X cutbuffer into stdout

+wmiv- an image viewer
+
 wmsetbg- set the workspace background into a image and make it persist between
  sessions.

diff --git a/util/wmiv.c b/util/wmiv.c
index 45cc4e3..22f0cca 100755
--- a/util/wmiv.c
+++ b/util/wmiv.c
@@ -35,6 +35,7 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include "config.h"
+#include "xdnd.h"

 #ifdef HAVE_EXIF
 #include <libexif/exif-data.h>
@@ -63,7 +64,7 @@ Pixmap pix;

 const char *APPNAME = "wmiv";
 int APPVERSION_MAJOR = 0;
-int APPVERSION_MINOR = 7;
+int APPVERSION_MINOR = 8;
 int NEXT = 0;
 int PREV = 1;
 float zoom_factor = 0;
@@ -629,6 +630,7 @@ void linked_list_free(linked_list_t *list)
  free((char *)link->data);
  free(link);
  }
+ current_link = NULL;
 }

 /*
@@ -686,6 +688,10 @@ int main(int argc, char **argv)
  XClassHint *class_hints;
  XSizeHints *size_hints;
  XWMHints *win_hints;
+ Atom delWindow;
+ Atom xdnd_aware;
+ Atom xdnd_version = XDND_VERSION;
+
 #ifdef USE_XPM
  Pixmap icon_pixmap, icon_shape;
 #endif
@@ -792,11 +798,13 @@ int main(int argc, char **argv)
  size_hints->width = img->width;
  size_hints->height = img->height;

- Atom delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
+ delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dpy, win, &delWindow, 1);
  change_title(&title_property, reading_filename);
-
+ xdnd_aware = XInternAtom (dpy, "XdndAware", False);
+ XChangeProperty(dpy, win, xdnd_aware, XA_ATOM, 32, PropModeReplace,
(unsigned char *) &xdnd_version, 1);
  win_hints = XAllocWMHints();
+
  if (win_hints) {
  win_hints->flags = StateHint|InputHint|WindowGroupHint;

@@ -828,9 +836,46 @@ int main(int argc, char **argv)
  while (!quit) {
  XNextEvent(dpy, &e);
  if (e.type == ClientMessage) {
+ unsigned char *dropBuffer;
+
+ if (xdnd_get_drop(dpy, &e, &dropBuffer)) {
+ char *handled_uri_header = "file://";
+ char *filename_separator = "\n";
+ char *ptr = strtok((char *)dropBuffer, filename_separator);
+
+ linked_list_free(&list);
+ linked_list_init(&list);
+
+ Bool find_one_file = False;
+ while (ptr != NULL) {
+ if (strstr(ptr, handled_uri_header) == ptr) {
+ char *tmp_file;
+ if (ptr[strlen(ptr) - 1] == '\r')
+ ptr[strlen(ptr) - 1] = '\0';
+ tmp_file = ptr + strlen(handled_uri_header);
+
+ /* drag-and-drop file name formatas per the spec is encoded as an URI */
+ decode_uri(tmp_file);
+ if (connect_dir(tmp_file, &list))
+ find_one_file = True;
+ }
+ ptr = strtok(NULL, filename_separator);
+ }
+ free(dropBuffer);
+
+ if (find_one_file) {
+ max_index = list.count;
+ current_link = list.last;
+ change_image(NEXT);
+ } else {
+ if (img)
+ RReleaseImage(img);
+ img = draw_failed_image();
+ }
+ }
  if (e.xclient.data.l[0] == delWindow)
  quit = 1;
- break;
+ continue;
  }
  if (e.type == FocusIn) {
  focus = True;
@@ -850,6 +895,13 @@ int main(int argc, char **argv)
  XConfigureEvent xce = e.xconfigure;
  if (xce.width != img->width || xce.height != img->height) {
  RImage *old_img = img;
+
+ /* there is no file loaded and the window is resized */
+ if (!current_link) {
+ XResizeWindow(dpy, win, img->width, img->height);
+ continue;
+ }
+
  img = load_oriented_image(ctx, current_link->data, 0);
  if (!img) {
  /* keep the old img and window size */
diff --git a/util/xdnd.c b/util/xdnd.c
new file mode 100755
index 0000000..be47a99
--- /dev/null
+++ b/util/xdnd.c
@@ -0,0 +1,680 @@
+/* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
+   Copyright (C) 1996-2000 Paul Sheer
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+ */
+
+
+/*
+   Modified 2014-08-13 - for wmiv usage
+   Released 1998-08-07
+*/
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include "xdnd.h"
+
+static void xdnd_send_status(DndClass *dnd, Window window, Window
from, int will_accept, int want_position,
+ int x, int y, int w, int h, Atom action);
+static void xdnd_send_finished(DndClass *nd, Window window, Window
from, int error);
+static int xdnd_convert_selection(DndClass *dnd, Window window,
Window requester, Atom type);
+static int xdnd_get_selection(DndClass *dnd, Window from, Atom
property, Window insert);
+
+/* #define DND_DEBUG */
+
+#define xdnd_xfree(x) { if (x) { free(x); x = 0; } }
+
+#ifdef DND_DEBUG
+
+#include <sys/time.h>
+#include <unistd.h>
+
+char *xdnd_debug_milliseconds(void)
+{
+ struct timeval tv;
+ static char r[22];
+ gettimeofday(&tv, 0);
+ sprintf(r, "%.2ld.%.3ld", tv.tv_sec % 100L, tv.tv_usec / 1000L);
+ return r;
+}
+
+#define dnd_debug1(a)       printf("%s: %d: %s: " a "\n", __FILE__,
__LINE__, xdnd_debug_milliseconds())
+#define dnd_debug2(a, b)     printf("%s: %d: %s: " a "\n", __FILE__,
__LINE__, xdnd_debug_milliseconds(), b)
+#define dnd_debug3(a, b, c)   printf("%s: %d: %s: " a "\n", __FILE__,
__LINE__, xdnd_debug_milliseconds(), b, c)
+#define dnd_debug4(a, b, c, d) printf("%s: %d: %s: " a "\n",
__FILE__, __LINE__, xdnd_debug_milliseconds(), b, c, d)
+#else
+#define dnd_debug1(a)
+#define dnd_debug2(a, b)
+#define dnd_debug3(a, b, c)
+#define dnd_debug4(a, b, c, d)
+#endif
+
+#define dnd_warning(a) fprintf(stderr, a)
+
+#define dnd_version_at_least(a, b) ((a) >= (b))
+
+void xdnd_reset(DndClass *dnd)
+{
+ dnd->stage = XDND_DROP_STAGE_IDLE;
+ dnd->dragging_version = 0;
+ dnd->internal_drag = 0;
+ dnd->want_position = 0;
+ dnd->ready_to_drop = 0;
+ dnd->will_accept = 0;
+ dnd->rectangle.x = dnd->rectangle.y = 0;
+ dnd->rectangle.width = dnd->rectangle.height = 0;
+ dnd->dropper_window = 0;
+ dnd->dropper_toplevel = 0;
+ dnd->dragger_window = 0;
+ dnd->dragger_typelist = 0;
+ dnd->desired_type = 0;
+ dnd->time = 0;
+}
+
+/*
+ decode_uri: transform the given URI to UTF-8 string
+*/
+void decode_uri(char *uri)
+{
+ char *last = uri + strlen(uri);
+ while (uri < last-2) {
+ if (*uri == '%') {
+ int h;
+ if (sscanf(uri+1, "%2X", &h) != 1)
+ break;
+ *uri = h;
+ memmove(uri+1, uri+3, last - (uri+2));
+ last -= 2;
+ }
+ uri++;
+ }
+}
+
+void xdnd_init(DndClass *dnd, Display *display)
+{
+ memset(dnd, 0, sizeof(*dnd));
+
+ dnd->display = display;
+ dnd->root_window = DefaultRootWindow(display);
+ dnd->version = XDND_VERSION;
+
+ dnd->XdndAware = XInternAtom(dnd->display, "XdndAware", False);
+ dnd->XdndSelection = XInternAtom(dnd->display, "XdndSelection", False);
+ dnd->XdndEnter = XInternAtom(dnd->display, "XdndEnter", False);
+ dnd->XdndLeave = XInternAtom(dnd->display, "XdndLeave", False);
+ dnd->XdndPosition = XInternAtom(dnd->display, "XdndPosition", False);
+ dnd->XdndDrop = XInternAtom(dnd->display, "XdndDrop", False);
+ dnd->XdndFinished = XInternAtom(dnd->display, "XdndFinished", False);
+ dnd->XdndStatus = XInternAtom(dnd->display, "XdndStatus", False);
+ dnd->XdndActionCopy = XInternAtom(dnd->display, "XdndActionCopy", False);
+ dnd->XdndActionMove = XInternAtom(dnd->display, "XdndActionMove", False);
+ dnd->XdndActionLink = XInternAtom(dnd->display, "XdndActionLink", False);
+ dnd->XdndActionAsk = XInternAtom(dnd->display, "XdndActionAsk", False);
+ dnd->XdndActionPrivate = XInternAtom(dnd->display,
"XdndActionPrivate", False);
+ dnd->XdndTypeList = XInternAtom(dnd->display, "XdndTypeList", False);
+ dnd->XdndActionList = XInternAtom(dnd->display, "XdndActionList", False);
+ dnd->XdndActionDescription = XInternAtom(dnd->display,
"XdndActionDescription", False);
+
+ dnd->Xdnd_NON_PROTOCOL_ATOM = XInternAtom(dnd->display,
"JXSelectionWindowProperty", False);
+
+ xdnd_reset(dnd);
+}
+
+/* typelist is a null terminated array */
+static int array_length(Atom *a)
+{
+ int n;
+ for (n = 0; a[n]; n++);
+ return n;
+}
+
+void xdnd_set_type_list(DndClass *dnd, Window window, Atom *typelist)
+{
+ int n;
+ n = array_length(typelist);
+ XChangeProperty(dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) typelist, n);
+}
+
+/* result must be free'd */
+void xdnd_get_type_list(DndClass *dnd, Window window, Atom **typelist)
+{
+ Atom type, *a;
+ int format, i;
+ unsigned long count, remaining;
+ unsigned char *data = NULL;
+
+ *typelist = 0;
+
+ XGetWindowProperty(dnd->display, window, dnd->XdndTypeList,
+ 0, 0x8000000L, False, XA_ATOM,
+ &type, &format, &count, &remaining, &data);
+
+ if (type != XA_ATOM || format != 32 || count == 0 || !data) {
+ if (data)
+ XFree(data);
+ dnd_debug2("XGetWindowPropertyfailed in xdnd_get_type_list-
dnd->XdndTypeList = %ld", dnd->XdndTypeList);
+ return;
+ }
+ *typelist = malloc((count + 1) * sizeof(Atom));
+ a = (Atom *) data;
+ for (i = 0; i < count; i++)
+ (*typelist)[i] = a[i];
+ (*typelist)[count] = 0;
+
+ XFree(data);
+}
+
+void xdnd_get_three_types(DndClass *dnd, XEvent *xevent, Atom **typelist)
+{
+ int i;
+ (void) dnd;
+
+ *typelist = malloc((XDND_THREE + 1) * sizeof(Atom));
+ for (i = 0; i < XDND_THREE; i++)
+ (*typelist)[i] = XDND_ENTER_TYPE(xevent, i);
+ /* although (*typelist)[1] or (*typelist)[2] may also be set to nill */
+ (*typelist)[XDND_THREE] = 0;
+}
+
+static void xdnd_send_event(DndClass *dnd, Window window, XEvent *xevent)
+{
+ dnd_debug4("xdnd_send_event(), window = %ld, l[0] = %ld, l[4] = %ld",
+ window, xevent->xclient.data.l[0], xevent->xclient.data.l[4]);
+ dnd_debug2("xdnd_send_event(), from widget widget %s", (char *) "<WIDGET>");
+ XSendEvent(dnd->display, window, 0, 0, xevent);
+}
+
+static void xdnd_send_status(DndClass *dnd, Window window, Window
from, int will_accept,
+  int want_position, int x, int y, int w, int h, Atom action)
+{
+ XEvent xevent;
+
+ memset(&xevent, 0, sizeof(xevent));
+
+ xevent.xany.type = ClientMessage;
+ xevent.xany.display = dnd->display;
+ xevent.xclient.window = window;
+ xevent.xclient.message_type = dnd->XdndStatus;
+ xevent.xclient.format = 32;
+
+ XDND_STATUS_TARGET_WIN(&xevent) = from;
+ XDND_STATUS_WILL_ACCEPT_SET(&xevent, will_accept);
+ if (will_accept)
+ XDND_STATUS_WANT_POSITION_SET(&xevent, want_position);
+ if (want_position)
+ XDND_STATUS_RECT_SET(&xevent, x, y, w, h);
+ if (dnd_version_at_least(dnd->dragging_version, 2))
+ if (will_accept)
+ XDND_STATUS_ACTION(&xevent) = action;
+
+ xdnd_send_event(dnd, window, &xevent);
+}
+
+
+/* error is not actually used, i think future versions of the
protocol should return an error status
+   to the calling window with the XdndFinished client message */
+static void xdnd_send_finished(DndClass *dnd, Window window, Window
from, int error)
+{
+ XEvent xevent;
+ (void) error;
+
+ memset(&xevent, 0, sizeof(xevent));
+
+ xevent.xany.type = ClientMessage;
+ xevent.xany.display = dnd->display;
+ xevent.xclient.window = window;
+ xevent.xclient.message_type = dnd->XdndFinished;
+ xevent.xclient.format = 32;
+
+ XDND_FINISHED_TARGET_WIN(&xevent) = from;
+
+ xdnd_send_event(dnd, window, &xevent);
+}
+
+/* returns non-zero on error - i.e. no selection owner set. Type is
of course the mime type */
+static int xdnd_convert_selection(DndClass *dnd, Window window,
Window requester, Atom type)
+{
+ window = XGetSelectionOwner(dnd->display, dnd->XdndSelection);
+ if (!window) {
+ dnd_debug1("xdnd_convert_selection(): XGetSelectionOwner failed");
+ return 1;
+ }
+ XConvertSelection(dnd->display, dnd->XdndSelection, type,
+ dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime);
+ return 0;
+}
+
+static int paste_prop_internal(DndClass *dnd, Window from, Window
insert, unsigned long prop, int delete_prop)
+{
+ long nread = 0;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ int error = 0;
+ do {
+ Atom actual_type;
+ int actual_fmt;
+ unsigned char *s = 0;
+ if (XGetWindowProperty(dnd->display, insert, prop,
+ nread / 4, 65536, delete_prop,
+ AnyPropertyType, &actual_type, &actual_fmt,
+ &nitems, &bytes_after, &s) != Success) {
+ XFree(s);
+ return 1;
+ }
+ nread += nitems;
+ if (dnd->widget_insert_drop && !error)
+ error = (*dnd->widget_insert_drop) (dnd, s, nitems, bytes_after,
insert, from, actual_fmt);
+ XFree(s);
+ } while (bytes_after);
+ if (!nread)
+ return 1;
+ return 0;
+}
+
+/*
+ * Respond to a notification that a primary selection has been sent
(supports INCR)
+ */
+static int xdnd_get_selection(DndClass *dnd, Window from, Atom prop,
Window insert)
+{
+ struct timeval tv, tv_start;
+ unsigned long bytes_after;
+ Atom actual_type;
+ int actual_fmt;
+ unsigned long nitems;
+ unsigned char *s = 0;
+ if (prop == None)
+ return 1;
+ if (XGetWindowProperty
+ (dnd->display, insert, prop, 0, 8, False, AnyPropertyType,
&actual_type, &actual_fmt,
+ &nitems, &bytes_after, &s) != Success) {
+ XFree(s);
+ return 1;
+ }
+ XFree(s);
+ if (actual_type != XInternAtom(dnd->display, "INCR", False))
+ return paste_prop_internal(dnd, from, insert, prop, True);
+ XDeleteProperty(dnd->display, insert, prop);
+ gettimeofday(&tv_start, 0);
+ for (;;) {
+ long t;
+ fd_set r;
+ XEvent xe;
+ if (XCheckMaskEvent(dnd->display, PropertyChangeMask, &xe)) {
+ if (xe.type == PropertyNotify && xe.xproperty.state == PropertyNewValue) {
+ /* time between arrivals of data */
+ gettimeofday(&tv_start, 0);
+ if (paste_prop_internal(dnd, from, insert, prop, True))
+ break;
+ }
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ FD_ZERO(&r);
+ FD_SET(ConnectionNumber(dnd->display), &r);
+ select(ConnectionNumber(dnd->display) + 1, &r, 0, 0, &tv);
+ if (FD_ISSET(ConnectionNumber(dnd->display), &r))
+ continue;
+ }
+ gettimeofday(&tv, 0);
+ t = (tv.tv_sec - tv_start.tv_sec) * 1000000L + (tv.tv_usec -
tv_start.tv_usec);
+ /* no data for five seconds, so quit */
+ if (t > 5000000L)
+ return 1;
+ }
+ return 0;
+}
+
+/* returns non-zero if event is handled */
+int xdnd_handle_drop_events(DndClass *dnd, XEvent *xevent)
+{
+ int result = 0;
+ if (xevent->type == SelectionNotify) {
+ dnd_debug1("got SelectionNotify");
+ if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM &&
dnd->stage == XDND_DROP_STAGE_CONVERTING) {
+ int error;
+ dnd_debug1("  property is Xdnd_NON_PROTOCOL_ATOM - getting selection");
+ error = xdnd_get_selection(dnd, dnd->dragger_window,
xevent->xselection.property, xevent->xany.window);
+ /* error is not actually used, i think future versions of the
protocol maybe should return
+   an error status to the calling window with the XdndFinished client
message */
+ if (dnd_version_at_least(dnd->dragging_version, 2)) {
+#if XDND_VERSION >= 3
+ xdnd_send_finished(dnd, dnd->dragger_window, dnd->dropper_toplevel, error);
+#else
+ xdnd_send_finished(dnd, dnd->dragger_window, dnd->dropper_window, error);
+#endif
+ dnd_debug1(" sending finished");
+ }
+ xdnd_xfree(dnd->dragger_typelist);
+ xdnd_reset(dnd);
+ dnd->stage = XDND_DROP_STAGE_IDLE;
+ result = 1;
+ } else {
+ dnd_debug1("  property is not Xdnd_NON_PROTOCOL_ATOM - ignoring");
+ }
+ } else if (xevent->type == ClientMessage) {
+ dnd_debug2("got ClientMessage to xevent->xany.window = %ld",
xevent->xany.window);
+ if (xevent->xclient.message_type == dnd->XdndEnter) {
+ dnd_debug2("  message_type is XdndEnter, version = %ld",
XDND_ENTER_VERSION(xevent));
+#if XDND_VERSION >= 3
+ if (XDND_ENTER_VERSION(xevent) < 3)
+ return 0;
+#endif
+ xdnd_reset(dnd);
+ dnd->dragger_window = XDND_ENTER_SOURCE_WIN(xevent);
+#if XDND_VERSION >= 3
+ dnd->dropper_toplevel = xevent->xany.window;
+ dnd->dropper_window = 0; /* enter goes to the top level window only,
+ so we don't really know what the
+ sub window is yet */
+#else
+ dnd->dropper_window = xevent->xany.window;
+#endif
+ xdnd_xfree(dnd->dragger_typelist);
+ if (XDND_ENTER_THREE_TYPES(xevent)) {
+ dnd_debug1(" three types only");
+ xdnd_get_three_types(dnd, xevent, &dnd->dragger_typelist);
+ } else {
+ dnd_debug1(" more than three types - getting list");
+ xdnd_get_type_list(dnd, dnd->dragger_window, &dnd->dragger_typelist);
+ }
+ if (dnd->dragger_typelist)
+ dnd->stage = XDND_DROP_STAGE_ENTERED;
+ else {
+ dnd_debug1("typelist returned as zero!");
+ }
+
+ dnd->dragging_version = XDND_ENTER_VERSION(xevent);
+ result = 1;
+ } else if (xevent->xclient.message_type == dnd->XdndLeave) {
+#if XDND_VERSION >= 3
+ if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
+ xevent->xany.window = dnd->dropper_window;
+#endif
+ dnd_debug1("  message_type is XdndLeave");
+ if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN(xevent) &&
dnd->stage == XDND_DROP_STAGE_ENTERED) {
+ dnd_debug1(" leaving");
+ if (dnd->widget_apply_leave)
+ (*dnd->widget_apply_leave) (dnd, xevent->xany.window);
+ dnd->stage = XDND_DROP_STAGE_IDLE;
+ xdnd_xfree(dnd->dragger_typelist);
+ result = 1;
+ dnd->dropper_toplevel = dnd->dropper_window = 0;
+ } else {
+ dnd_debug1(" wrong stage or from wrong window");
+ }
+ } else if (xevent->xclient.message_type == dnd->XdndPosition) {
+ dnd_debug2("  message_type is XdndPosition to %ld", xevent->xany.window);
+ if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN(xevent)
+ && dnd->stage == XDND_DROP_STAGE_ENTERED) {
+ int want_position;
+ Atom action;
+ XRectangle rectangle;
+ Window last_window;
+ last_window = dnd->dropper_window;
+#if XDND_VERSION >= 3
+ /* version 3 gives us the top-level window only.
+   WE have to find the child that the pointer is over: */
+ if (1 || xevent->xany.window != dnd->dropper_toplevel ||
!dnd->dropper_window) {
+ Window parent, child, new_child = 0;
+ dnd->dropper_toplevel = xevent->xany.window;
+ parent = dnd->root_window;
+ child = dnd->dropper_toplevel;
+ for (;;) {
+ int xd, yd;
+ new_child = 0;
+ if (!XTranslateCoordinates(dnd->display, parent, child,
+ XDND_POSITION_ROOT_X(xevent), XDND_POSITION_ROOT_Y(xevent),
+ &xd, &yd, &new_child))
+ break;
+ if (!new_child)
+ break;
+ child = new_child;
+ }
+ dnd->dropper_window = xevent->xany.window = child;
+ dnd_debug2("   child window translates to %ld", dnd->dropper_window);
+ } else if (xevent->xany.window == dnd->dropper_toplevel &&
dnd->dropper_window) {
+ xevent->xany.window = dnd->dropper_window;
+ dnd_debug2("   child window previously found: %ld", dnd->dropper_window);
+ }
+#endif
+ action = dnd->XdndActionCopy;
+ dnd->supported_action = dnd->XdndActionCopy;
+ dnd->x = XDND_POSITION_ROOT_X(xevent);
+ dnd->y = XDND_POSITION_ROOT_Y(xevent);
+ dnd->time = CurrentTime;
+ if (dnd_version_at_least(dnd->dragging_version, 1))
+ dnd->time = XDND_POSITION_TIME(xevent);
+ if (dnd_version_at_least(dnd->dragging_version, 1))
+ action = XDND_POSITION_ACTION(xevent);
+#if XDND_VERSION >= 3
+ if (last_window && last_window != xevent->xany.window)
+ if (dnd->widget_apply_leave)
+ (*dnd->widget_apply_leave) (dnd, last_window);
+#endif
+ dnd->will_accept = (*dnd->widget_apply_position) (dnd,
xevent->xany.window, dnd->dragger_window,
+ action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
+ &want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
+ dnd_debug2(" will accept = %d", dnd->will_accept);
+#if XDND_VERSION >= 3
+ dnd_debug2(" sending status of %ld", dnd->dropper_toplevel);
+ xdnd_send_status(dnd, dnd->dragger_window, dnd->dropper_toplevel,
dnd->will_accept,
+ want_position, rectangle.x, rectangle.y,
+ rectangle.width, rectangle.height, dnd->supported_action);
+#else
+ dnd_debug2(" sending status of %ld", xevent->xany.window);
+ xdnd_send_status(dnd, dnd->dragger_window, xevent->xany.window,
dnd->will_accept,
+ want_position, rectangle.x, rectangle.y, rectangle.width,
+ rectangle.height, dnd->supported_action);
+#endif
+ result = 1;
+ } else {
+ dnd_debug1(" wrong stage or from wrong window");
+ }
+ } else if (xevent->xclient.message_type == dnd->XdndDrop) {
+#if XDND_VERSION >= 3
+ if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
+ xevent->xany.window = dnd->dropper_window;
+#endif
+ dnd_debug1("  message_type is XdndDrop");
+ if (dnd->dragger_window == XDND_DROP_SOURCE_WIN(xevent)
+ && dnd->stage == XDND_DROP_STAGE_ENTERED) {
+ dnd->time = CurrentTime;
+ if (dnd_version_at_least(dnd->dragging_version, 1))
+ dnd->time = XDND_DROP_TIME(xevent);
+ if (dnd->will_accept) {
+ dnd_debug1(" will_accept is true - converting selectiong");
+ dnd_debug2("  my window is %ld", dnd->dropper_window);
+ dnd_debug2(" source window is %ld", dnd->dragger_window);
+ xdnd_convert_selection(dnd, dnd->dragger_window,
dnd->dropper_window, dnd->desired_type);
+ dnd->stage = XDND_DROP_STAGE_CONVERTING;
+ } else {
+ dnd_debug1(" will_accept is false - sending finished");
+ if (dnd_version_at_least(dnd->dragging_version, 2)) {
+#if XDND_VERSION >= 3
+ xdnd_send_finished(dnd, dnd->dragger_window, dnd->dropper_toplevel, 1);
+#else
+ xdnd_send_finished(dnd, dnd->dragger_window, xevent->xany.window, 1);
+#endif
+ }
+ xdnd_xfree(dnd->dragger_typelist);
+ xdnd_reset(dnd);
+ dnd->stage = XDND_DROP_STAGE_IDLE;
+ }
+ result = 1;
+ } else {
+ dnd_debug1(" wrong stage or from wrong window");
+ }
+ }
+ }
+ return result;
+}
+
+struct xdnd_get_drop_info {
+ unsigned char *drop_data;
+ int drop_data_length;
+ int x, y;
+ Atom return_type;
+ Atom return_action;
+ Atom *typelist;
+ Atom *actionlist;
+};
+
+static int widget_insert_drop(DndClass *dnd, unsigned char *data, int length,
+ int remaining, Window into, Window from, Atom type)
+{
+ struct xdnd_get_drop_info *i;
+ (void) remaining;
+ (void) into;
+ (void) from;
+ (void) type;
+
+ i = (struct xdnd_get_drop_info *) dnd->user_hook1;
+ if (!i->drop_data) {
+ i->drop_data = malloc(length);
+ if (!i->drop_data)
+ return 1;
+ memcpy(i->drop_data, data, length);
+ i->drop_data_length = length;
+ } else {
+ unsigned char *t;
+ t = malloc(i->drop_data_length + length);
+ if (!t) {
+ free(i->drop_data);
+ i->drop_data = 0;
+ return 1;
+ }
+ memcpy(t, i->drop_data, i->drop_data_length);
+ memcpy(t + i->drop_data_length, data, length);
+ free(i->drop_data);
+ i->drop_data = t;
+ i->drop_data_length += length;
+ }
+ return 0;
+}
+
+static int widget_apply_position(DndClass *dnd, Window
widgets_window, Window from,
+ Atom action, int x, int y, Time t, Atom *typelist,
+ int *want_position, Atom *supported_action_return,
+ Atom *desired_type, XRectangle *rectangle)
+{
+ int i, j;
+ struct xdnd_get_drop_info *info;
+ Atom *dropper_typelist, supported_type = 0;
+ Atom *supported_actions, supported_action = 0;
+ (void) widgets_window;
+ (void) from;
+ (void) t;
+
+ info = (struct xdnd_get_drop_info *) dnd->user_hook1;
+ dropper_typelist = info->typelist;
+ supported_actions = info->actionlist;
+
+ if (dropper_typelist) {
+ /* find a correlation: */
+ for (j = 0; dropper_typelist[j]; j++) {
+ for (i = 0; typelist[i]; i++) {
+ if (typelist[i] == dropper_typelist[j]) {
+ supported_type = typelist[i];
+ break;
+ }
+ }
+ if (supported_type)
+ break;
+ }
+ } else {
+ /* user did not specify, so return first type */
+ supported_type = typelist[0];
+ }
+ /* not supported, so return false */
+ if (!supported_type)
+ return 0;
+
+ if (supported_actions) {
+ for (j = 0; supported_actions[j]; j++) {
+ if (action == supported_actions[j]) {
+ supported_action = action;
+ break;
+ }
+ }
+ } else {
+ /* user did not specify */
+ if (action == dnd->XdndActionCopy)
+ supported_action = action;
+ }
+ if (!supported_action)
+ return 0;
+
+ *want_position = 1;
+ rectangle->x = rectangle->y = 0;
+ rectangle->width = rectangle->height = 0;
+
+ info->return_action = *supported_action_return = supported_action;
+ info->return_type = *desired_type = supported_type;
+ info->x = x;
+ info->y = y;
+
+ return 1;
+}
+
+Atom xdnd_get_drop(Display *display, XEvent *xevent, unsigned char **data)
+{
+ Atom action = 0;
+ static int initialised = 0;
+ static DndClass dnd;
+ static Atom typelist[2];
+
+ if (!initialised) {
+ const char *m_dndMimeTypes[] = { "text/uri-list" };
+ xdnd_init(&dnd, display);
+ XInternAtoms(display, (char **)m_dndMimeTypes, 1, 0, typelist);
+ typelist[1] = 0;
+ initialised = 1;
+ }
+
+ if (xevent->type != ClientMessage || xevent->xclient.message_type !=
dnd.XdndEnter) {
+ return 0;
+ } else {
+ struct xdnd_get_drop_info i;
+
+ /* setup user structure */
+ memset(&i, 0, sizeof(i));
+ i.typelist = typelist;
+ dnd.user_hook1 = &i;
+
+ /* setup methods */
+ dnd.widget_insert_drop = widget_insert_drop;
+ dnd.widget_apply_position = widget_apply_position;
+
+ /* main loop */
+ for (;;) {
+ xdnd_handle_drop_events(&dnd, xevent);
+ if (dnd.stage == XDND_DROP_STAGE_IDLE)
+ break;
+ XNextEvent(dnd.display, xevent);
+ }
+ /* return results */
+ if (i.drop_data) {
+ *data = i.drop_data;
+ action = i.return_action;
+ }
+ }
+ return action;
+}
diff --git a/util/xdnd.h b/util/xdnd.h
new file mode 100755
index 0000000..91ef33b
--- /dev/null
+++ b/util/xdnd.h
@@ -0,0 +1,171 @@
+/* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
+   Copyright (C) 1996-2000 Paul Sheer
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+ */
+
+#ifndef _X_DND_H
+#define _X_DND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* you can set this to either 2 (which support 0 and 1 as well) or 3 */
+#define XDND_VERSION 3
+
+
+/* XdndEnter */
+#define XDND_THREE 3
+#define XDND_ENTER_SOURCE_WIN(e) ((e)->xclient.data.l[0])
+#define XDND_ENTER_THREE_TYPES(e) (((e)->xclient.data.l[1] & 0x1UL) == 0)
+#define XDND_ENTER_THREE_TYPES_SET(e, b) (e)->xclient.data.l[1] =
((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
+#define XDND_ENTER_VERSION(e) ((e)->xclient.data.l[1] >> 24)
+#define XDND_ENTER_VERSION_SET(e, v) (e)->xclient.data.l[1] =
((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
+#define XDND_ENTER_TYPE(e, i) ((e)->xclient.data.l[2 + i]) /* i => (0, 1, 2) */
+
+/* XdndPosition */
+#define XDND_POSITION_SOURCE_WIN(e) ((e)->xclient.data.l[0])
+#define XDND_POSITION_ROOT_X(e) ((e)->xclient.data.l[2] >> 16)
+#define XDND_POSITION_ROOT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFUL)
+#define XDND_POSITION_ROOT_SET(e, x, y) (e)->xclient.data.l[2]  =
((x) << 16) | ((y) & 0xFFFFUL)
+#define XDND_POSITION_TIME(e) ((e)->xclient.data.l[3])
+#define XDND_POSITION_ACTION(e) ((e)->xclient.data.l[4])
+
+/* XdndStatus */
+#define XDND_STATUS_TARGET_WIN(e) ((e)->xclient.data.l[0])
+#define XDND_STATUS_WILL_ACCEPT(e) ((e)->xclient.data.l[1] & 0x1L)
+#define XDND_STATUS_WILL_ACCEPT_SET(e, b) (e)->xclient.data.l[1] =
((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
+#define XDND_STATUS_WANT_POSITION(e) ((e)->xclient.data.l[1] & 0x2UL)
+#define XDND_STATUS_WANT_POSITION_SET(e, b) (e)->xclient.data.l[1] =
((e)->xclient.data.l[1] & ~0x2UL) | (((b) == 0) ? 0 : 0x2UL)
+#define XDND_STATUS_RECT_X(e) ((e)->xclient.data.l[2] >> 16)
+#define XDND_STATUS_RECT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFL)
+#define XDND_STATUS_RECT_WIDTH(e) ((e)->xclient.data.l[3] >> 16)
+#define XDND_STATUS_RECT_HEIGHT(e) ((e)->xclient.data.l[3] & 0xFFFFL)
+#define XDND_STATUS_RECT_SET(e, x, y, w, h) {(e)->xclient.data.l[2] =
((x) << 16) | ((y) & 0xFFFFUL); (e)->xclient.data.l[3] = ((w) << 16) |
((h) & 0xFFFFUL); }
+#define XDND_STATUS_ACTION(e) ((e)->xclient.data.l[4])
+
+/* XdndLeave */
+#define XDND_LEAVE_SOURCE_WIN(e) ((e)->xclient.data.l[0])
+
+/* XdndDrop */
+#define XDND_DROP_SOURCE_WIN(e) ((e)->xclient.data.l[0])
+#define XDND_DROP_TIME(e) ((e)->xclient.data.l[2])
+
+/* XdndFinished */
+#define XDND_FINISHED_TARGET_WIN(e) ((e)->xclient.data.l[0])
+
+typedef struct _DndClass DndClass;
+
+struct _DndClass {
+/* insert chars sequentionally into the target widget, type will be
the same as `desired_type'
+   returned from widget_apply_position. This may be called several
times in succession
+   with sequention blocks of data. Must return non-zero on failure */
+ int (*widget_insert_drop)(DndClass *dnd, unsigned char *data, int length,
+ int remaining, Window into, Window from, Atom type);
+
+/* must update the widgets border to its default appearance */
+ void (*widget_apply_leave)(DndClass *dnd, Window widgets_window);
+
+/* must update the widgets border to give the appearance of being
able to recieve a drop,
+   plus return all data to pointers. As per the protocol, if the widget cannot
+   perform the action specified by `action' then it should return
either XdndActionPrivate
+   or XdndActionCopy into supported_action (leaving 0
supported_action unchanged is equivalent
+   to XdndActionCopy). Returns 1 if ready to ok drop */
+ int (*widget_apply_position)(DndClass *dnd, Window widgets_window,
Window from,
+ Atom action, int x, int y, Time t, Atom *typelist, int *want_position,
+ Atom *supported_action, Atom *desired_type, XRectangle *rectangle);
+
+ void *pad1[8];
+
+ Display *display;
+
+ Atom XdndAware;
+ Atom XdndSelection;
+ Atom XdndEnter;
+ Atom XdndLeave;
+ Atom XdndPosition;
+ Atom XdndDrop;
+ Atom XdndFinished;
+ Atom XdndStatus;
+ Atom XdndActionCopy;
+ Atom XdndActionMove;
+ Atom XdndActionLink;
+ Atom XdndActionAsk;
+ Atom XdndActionPrivate;
+ Atom XdndTypeList;
+ Atom XdndActionList;
+ Atom XdndActionDescription;
+
+ Atom Xdnd_NON_PROTOCOL_ATOM;
+ Atom version;
+
+ Atom pad2[16];
+
+ Window root_window;
+
+#define XDND_DROP_STAGE_IDLE 0
+#define XDND_DRAG_STAGE_DRAGGING 1
+#define XDND_DRAG_STAGE_ENTERED 2
+#define XDND_DROP_STAGE_CONVERTING 3
+#define XDND_DROP_STAGE_ENTERED 4
+ int stage;
+ int dragging_version;
+ int internal_drag;
+ int want_position;
+ int ready_to_drop;
+ int will_accept;
+ XRectangle rectangle;
+ Window dropper_window, dragger_window;
+ Atom *dragger_typelist;
+ Atom desired_type;
+ Atom supported_action;
+ Time time;
+/* drop position from last XdndPosition */
+ int x, y;
+ int pad3[16];
+
+/* move euclidian pixels before considering this to be an actual drag */
+ float drag_threshold;
+
+/* block for only this many seconds on not receiving a XdndFinished
from target, default : 10 */
+ int time_out;
+
+#define XDND_OPTION_NO_HYSTERESIS (1<<0)
+ int options;
+
+/* user hooks */
+ void *user_hook1;
+ void *user_hook2;
+ void *user_hook3;
+ Window dropper_toplevel;
+ void *pad4[15];
+};
+
+void decode_uri(char *uri);
+void xdnd_init(DndClass *dnd, Display *display);
+/* Returns 1 if event is handled, This must be placed in the widget
+libraries main event loop and be called if the event type is
+ClientMessage or SelectionNotify */
+int xdnd_handle_drop_events(DndClass *dnd, XEvent *xevent);
+Atom xdnd_get_drop(Display *display, XEvent *xevent, unsigned char **data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_X_DND_H */

Attachment: 0001-util-wmiv-Add-drag-and-drop-support.patch
Description: Binary data

Reply via email to