Back to index

plt-scheme  4.2.1
xdnd.c
Go to the documentation of this file.
00001 /* 
00002    xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
00003 
00004    Copyright (C) 1998  Paul Sheer
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public
00017    License along with this library; if not, write to the Free Software
00018    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00019    02110-1301 USA.
00020 
00021    http://www.cco.caltech.edu/~jafl/xdnd/
00022 
00023    Further info can also be obtained by emailing the author at,
00024        psheer@obsidian.co.za
00025 
00026    Released 1998-08-07
00027 */
00028 
00029 #include <X11/Xlib.h>
00030 #include <X11/X.h>
00031 #include <X11/Xatom.h>
00032 #include <string.h>
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include "xdnd.h"
00036 
00037 
00038 // #define DND_DEBUG 
00039 #define dnd_version_at_least(a,b) ((a) <= (b))
00040 
00041 #ifdef DND_DEBUG
00042 #define dnd_debug(a,b...) printf("%s: %d: " a "\n", __FILE__, __LINE__ , ## b)
00043 #else
00044 
00045 #ifdef NeXT_RUNTIME
00046 #define dnd_debug //
00047 #else  /* !NeXT_RUNTIME */
00048 #define dnd_debug(a,b...)
00049 #endif /* NeXT_RUNTIME */
00050 
00051 #endif
00052 
00053 #ifdef MZ_PRECISE_GC
00054 START_XFORM_SKIP;
00055 #endif
00056 
00057 void
00058 xdnd_reset(DndClass * dnd)
00059 {
00060   dnd->stage = XDND_DROP_STAGE_IDLE;
00061   dnd->dragging_version = 0;
00062   dnd->internal_drag = 0;
00063   dnd->want_position = 0;
00064   dnd->ready_to_drop = 0;
00065   dnd->will_accept = 0;
00066   dnd->rectangle.x = dnd->rectangle.y = 0;
00067   dnd->rectangle.width = dnd->rectangle.height = 0;
00068   dnd->dropper_window = 0;
00069   dnd->dragger_window = 0;
00070   dnd->dragger_typelist = 0;
00071   dnd->desired_type = 0;
00072   dnd->time = 0;
00073 }
00074 
00075 void
00076 xdnd_init(DndClass * dnd, Display * display)
00077 {
00078   memset (dnd, 0, sizeof (*dnd));
00079 
00080   dnd->display = display;
00081   dnd->root_window = DefaultRootWindow (display);
00082   dnd->version = XDND_VERSION;
00083   dnd->XdndAware = XInternAtom (dnd->display, "XdndAware", False);
00084   dnd->XdndSelection = XInternAtom (dnd->display, "XdndSelection", False);
00085   dnd->XdndEnter = XInternAtom (dnd->display, "XdndEnter", False);
00086   dnd->XdndLeave = XInternAtom (dnd->display, "XdndLeave", False);
00087   dnd->XdndPosition = XInternAtom (dnd->display, "XdndPosition", False);
00088   dnd->XdndDrop = XInternAtom (dnd->display, "XdndDrop", False);
00089   dnd->XdndFinished = XInternAtom (dnd->display, "XdndFinished", False);
00090   dnd->XdndStatus = XInternAtom (dnd->display, "XdndStatus", False);
00091   dnd->XdndActionCopy = XInternAtom (dnd->display, "XdndActionCopy", False);
00092   dnd->XdndActionMove = XInternAtom (dnd->display, "XdndActionMove", False);
00093   dnd->XdndActionLink = XInternAtom (dnd->display, "XdndActionLink", False);
00094   dnd->XdndActionAsk = XInternAtom (dnd->display, "XdndActionAsk", False);
00095   dnd->XdndActionPrivate=XInternAtom(dnd->display,"XdndActionPrivate",False);
00096   dnd->XdndTypeList = XInternAtom (dnd->display, "XdndTypeList", False);
00097   dnd->XdndActionList = XInternAtom (dnd->display, "XdndActionList", False);
00098   dnd->XdndActionDescription = XInternAtom(dnd->display, "XdndActionDescription", False);
00099   dnd->text_uri_list = XInternAtom(dnd->display, "text/uri-list", False);
00100   xdnd_reset(dnd);
00101 }
00102 
00103 static int
00104 array_length(Atom * a)
00105 {                           // typelist is a null terminated array
00106   int n = 0;
00107 
00108   while (a[n])
00109     n++;
00110   return n;
00111 }
00112 
00113 void
00114 xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist)
00115 {
00116   XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, 
00117     PropModeReplace, (unsigned char *) &dnd->version, 1);
00118   if (typelist) 
00119     {
00120       int n = array_length (typelist);
00121       if (n)
00122        XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, 
00123          PropModeAppend, (unsigned char *) typelist, n);
00124     }
00125 }
00126 
00127 void
00128 xdnd_set_dnd_unaware (DndClass * dnd, Window window)
00129 {
00130   XDeleteProperty (dnd->display, window, dnd->XdndAware);
00131 }
00132 
00133 int
00134 xdnd_is_dnd_aware(DndClass *dnd, Window window, int *version, Atom *typelist)
00135 {
00136   Atom actual;
00137   int format;
00138   unsigned long count, remaining;
00139   unsigned char *data = 0;
00140   Atom *types, *t;
00141   int result = 1;
00142 
00143   *version = 0;
00144   XGetWindowProperty (dnd->display, window, dnd->XdndAware,
00145     0, 0x8000000L, False, XA_ATOM, &actual, &format,
00146     &count, &remaining, &data);
00147 
00148   if (actual != XA_ATOM || format != 32 || count == 0 || !data) 
00149     {
00150       dnd_debug("XGetWindowProperty failed in xdnd_is_dnd_aware - XdndAware = %ld", dnd->XdndAware);
00151       if (data)
00152        XFree(data);
00153       return 0;
00154     }
00155 
00156   types = (Atom *) data;
00157   *version = dnd->version < types[0] ? dnd->version : types[0];       // minimum
00158   dnd_debug ("Using XDND version %d", *version);
00159   if (count > 1) 
00160     {
00161       result = 0;
00162       for (t = typelist; *t; t++) 
00163        {
00164          unsigned long j;
00165          for (j = 1; j < count; j++) 
00166            {
00167              if (types[j] == *t) 
00168               {
00169                 result = 1;
00170                 break;
00171               }
00172            }
00173          if (result)
00174            break;
00175        }
00176     }
00177   XFree(data);
00178   return result;
00179 }
00180 
00181 void
00182 xdnd_send_enter(DndClass *dnd, Window window, Window from, Atom *typelist)
00183 {
00184   XEvent xevent;
00185   int n, i;
00186 
00187   n = array_length (typelist);
00188 
00189   memset(&xevent, 0, sizeof (xevent));
00190 
00191   xevent.xany.type = ClientMessage;
00192   xevent.xany.display = dnd->display;
00193   xevent.xclient.window = window;
00194   xevent.xclient.message_type = dnd->XdndEnter;
00195   xevent.xclient.format = 32;
00196 
00197   XDND_ENTER_SOURCE_WIN (&xevent) = from;
00198   XDND_ENTER_THREE_TYPES_SET (&xevent, n > XDND_THREE);
00199   XDND_ENTER_VERSION_SET (&xevent, dnd->version);
00200   for (i = 0; i < n && i < XDND_THREE; i++)
00201     {
00202       XDND_ENTER_TYPE (&xevent, i) = typelist[i];
00203     }
00204 
00205   XSendEvent (dnd->display, window, 0, 0, &xevent);
00206 }
00207 
00208 void
00209 xdnd_send_position(DndClass *dnd, Window window, Window from, Atom action, 
00210    int x, int y, unsigned long time)
00211 {
00212   XEvent xevent;
00213 
00214   memset (&xevent, 0, sizeof (xevent));
00215 
00216   xevent.xany.type = ClientMessage;
00217   xevent.xany.display = dnd->display;
00218   xevent.xclient.window = window;
00219   xevent.xclient.message_type = dnd->XdndPosition;
00220   xevent.xclient.format = 32;
00221 
00222   XDND_POSITION_SOURCE_WIN (&xevent) = from;
00223   XDND_POSITION_ROOT_SET (&xevent, x, y);
00224   if (dnd_version_at_least (dnd->dragging_version, 1))
00225     XDND_POSITION_TIME (&xevent) = time;
00226   if (dnd_version_at_least (dnd->dragging_version, 2))
00227     XDND_POSITION_ACTION (&xevent) = action;
00228 
00229   XSendEvent (dnd->display, window, 0, 0, &xevent);
00230 }
00231 
00232 void
00233 xdnd_send_status(DndClass *dnd, Window window, Window from, int will_accept, 
00234   int want_position, int x, int y, int w, int h, Atom action)
00235 {
00236   XEvent xevent;
00237 
00238   memset (&xevent, 0, sizeof (xevent));
00239 
00240   xevent.xany.type = ClientMessage;
00241   xevent.xany.display = dnd->display;
00242   xevent.xclient.window = window;
00243   xevent.xclient.message_type = dnd->XdndStatus;
00244   xevent.xclient.format = 32;
00245 
00246   XDND_STATUS_TARGET_WIN (&xevent) = from;
00247   XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept);
00248   if (will_accept)
00249     XDND_STATUS_WANT_POSITION_SET (&xevent, want_position);
00250   if (want_position)
00251     XDND_STATUS_RECT_SET (&xevent, x, y, w, h);
00252   if (dnd_version_at_least (dnd->dragging_version, 2))
00253     if (will_accept)
00254       XDND_STATUS_ACTION (&xevent) = action;
00255 
00256   XSendEvent (dnd->display, window, 0, 0, &xevent);
00257 }
00258 
00259 void
00260 xdnd_send_leave(DndClass *dnd, Window window, Window from)
00261 {
00262   XEvent xevent;
00263 
00264   memset(&xevent, 0, sizeof (xevent));
00265 
00266   xevent.xany.type = ClientMessage;
00267   xevent.xany.display = dnd->display;
00268   xevent.xclient.window = window;
00269   xevent.xclient.message_type = dnd->XdndLeave;
00270   xevent.xclient.format = 32;
00271 
00272   XDND_LEAVE_SOURCE_WIN (&xevent) = from;
00273 
00274   XSendEvent (dnd->display, window, 0, 0, &xevent);
00275 }
00276 
00277 void
00278 xdnd_send_drop(DndClass *dnd, Window window, Window from, unsigned long time)
00279 {
00280   XEvent xevent;
00281 
00282   memset (&xevent, 0, sizeof (xevent));
00283 
00284   xevent.xany.type = ClientMessage;
00285   xevent.xany.display = dnd->display;
00286   xevent.xclient.window = window;
00287   xevent.xclient.message_type = dnd->XdndDrop;
00288   xevent.xclient.format = 32;
00289 
00290   XDND_DROP_SOURCE_WIN (&xevent) = from;
00291   if (dnd_version_at_least (dnd->dragging_version, 1))
00292     XDND_DROP_TIME (&xevent) = time;
00293 
00294   XSendEvent (dnd->display, window, 0, 0, &xevent);
00295 }
00296 
00297 void
00298 xdnd_send_finished(DndClass * dnd, Window window, Window from, int error)
00299 {
00300   XEvent xevent;
00301   memset (&xevent, 0, sizeof (xevent));
00302   xevent.xany.type = ClientMessage;
00303   xevent.xany.display = dnd->display;
00304   xevent.xclient.window = window;                       
00305   xevent.xclient.message_type = dnd->XdndFinished;
00306   xevent.xclient.format = 32;
00307 
00308   XDND_FINISHED_TARGET_WIN (&xevent) = from;
00309 
00310   XSendEvent (dnd->display, window, 0, 0, &xevent);
00311 }
00312 
00313 int
00314 xdnd_convert_selection(DndClass *dnd, Window window, Window requester, Atom type)
00315 {
00316   if (window != XGetSelectionOwner (dnd->display, dnd->XdndSelection)) 
00317     {
00318       dnd_debug ("xdnd_convert_selection(): XGetSelectionOwner failed");
00319       return 1;
00320     }
00321 
00322   XConvertSelection (dnd->display, dnd->XdndSelection, type,
00323     dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime);
00324   return 0;
00325 }
00326 
00327 int
00328 xdnd_set_selection_owner(DndClass * dnd, Window window, Atom type)
00329 {
00330   if (!XSetSelectionOwner(dnd->display,dnd->XdndSelection,window,CurrentTime))      
00331     {
00332       dnd_debug ("xdnd_set_selection_owner(): XSetSelectionOwner failed");
00333       return 1;      
00334     }  
00335 
00336   return 0;
00337 }
00338 
00339 void
00340 xdnd_selection_send(DndClass * dnd, XSelectionRequestEvent * request, 
00341   unsigned char *data, int length)
00342 {
00343   XEvent xevent;
00344 
00345   dnd_debug (" requestor = %ld", request->requestor);
00346   dnd_debug (" property = %ld", request->property);
00347   dnd_debug (" length = %d", length);
00348 
00349   XChangeProperty (dnd->display, request->requestor, request->property,
00350     request->target, 8, PropModeReplace, data, length);
00351 
00352   xevent.xselection.type = SelectionNotify;
00353   xevent.xselection.property = request->property;
00354   xevent.xselection.display = request->display;
00355   xevent.xselection.requestor = request->requestor;
00356   xevent.xselection.selection = request->selection;
00357   xevent.xselection.target = request->target;
00358   xevent.xselection.time = request->time;
00359 
00360   XSendEvent (dnd->display, request->requestor, 0, 0, &xevent);
00361 }
00362 
00363 //
00364 // Unused
00365 //
00366 
00367 void
00368 xdnd_set_type_list(DndClass * dnd, Window window, Atom * typelist)
00369 {
00370   int n = array_length (typelist);
00371 
00372   XChangeProperty (dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32,
00373     PropModeReplace, (unsigned char *) typelist, n);
00374 }
00375 
00376 #ifdef MZ_PRECISE_GC
00377 END_XFORM_SKIP;
00378 #endif
00379 
00380 void
00381 xdnd_get_type_list(DndClass * dnd, Window window, Atom ** typelist)
00382 {
00383   Atom type, *a, *tl;
00384   int format;
00385   unsigned long i, count, remaining;
00386   unsigned char *data = NULL;
00387 
00388   *typelist = 0;
00389 
00390   XGetWindowProperty (dnd->display, window, dnd->XdndTypeList,
00391     0, 0x8000000L, False, XA_ATOM, &type, &format, &count, &remaining, &data);
00392 
00393   if (type != XA_ATOM || format != 32 || count == 0 || !data)
00394     {
00395       if (data)
00396        XFree (data);
00397       dnd_debug ("XGetWindowProperty failed in xdnd_get_type_list - dnd->XdndTypeList = %ld", dnd->XdndTypeList);
00398       return;
00399     }
00400   tl = (Atom *)(new WXGC_ATOMIC char[(count + 1) * sizeof (Atom)]);
00401   *typelist = tl;
00402   a = (Atom *) data;
00403   for (i = 0; i < count; i++) {
00404     (*typelist)[i] = a[i];
00405   }
00406   (*typelist)[count] = 0;
00407 
00408   XFree (data);
00409 }