2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: dsdm.c /main/6 1997/06/18 17:27:35 samborn $ */
24 /* Copyright (c) 1990, 1991, 1992, 1993 UNIX System Laboratories, Inc. */
25 /* Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 AT&T */
26 /* All Rights Reserved */
28 /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
29 /* UNIX System Laboratories, Inc. */
30 /* The copyright notice above does not evidence any */
31 /* actual or intended publication of such source code. */
34 * Copyright (C) 1986,1992 Sun Microsystems, Inc
35 * All rights reserved.
36 * Notice of copyright on this source code
37 * product does not indicate publication.
39 * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by
40 * the U.S. Government is subject to restrictions as set forth
41 * in subparagraph (c)(1)(ii) of the Rights in Technical Data
42 * and Computer Software Clause at DFARS 252.227-7013 (Oct. 1988)
43 * and FAR 52.227-19 (c) (June 1987).
45 * Sun Microsystems, Inc., 2550 Garcia Avenue,
46 * Mountain View, California 94043.
51 * @(#)dsdm.c 1.5 91/02/13
52 * Drop Site Database Manager for drag'n'drop.
56 * Start with visible region as whole screen.
57 * For each top-level window, do
58 * (0) flatten its interest rectangles
59 * (1) intersect interest rects with the top-level window
60 * (2) intersect them with the visible region
61 * (3) append them to the master list
62 * (4) subtract this top-level frame from the visible region
64 * USL changes are #ifdef'd with "oldcode" or commented with *USL* - Sam Chang
68 #if defined(SVR4) || defined(SYSV)
69 #include <string.h> /*USL*/
70 #else /* SVR4 or SYSV */
72 #endif /* SVR4 or SYSV */
74 #if !defined(__STDC__) && !defined(__cplusplus) && !defined(c_plusplus) /*USL*/
78 #define XFreeDefn char *
80 #include <X11/Xproto.h>
82 #include <X11/Xutil.h>
83 #include <X11/Xatom.h>
86 * Use DPRINTF to write debugging messages. Place all arguments in an
87 * extra pair of parentheses, like so:
88 * DPRINTF(("%s: some error occurred (code=%d)\n", ProgramName, err));
92 #define DPRINTF(args) (void) printf args
97 #define INTEREST_MAX 100000000L /*(1024L*1024L)*/
99 #define FIND_CONTINUE ((Window) 0L)
100 #define FIND_STOP ((Window) 1L)
104 extern Window GetAtomWindow();
105 extern Atom ATOM_MOTIF_RECEIVER_INFO;
110 Atom ATOM_DRAGDROP_DSDM;
111 Atom ATOM_DRAGDROP_INTEREST;
113 Atom ATOM_SITE_RECTS;
116 int (*DefaultErrorHandler)();
118 typedef struct _site {
120 unsigned long site_id;
127 drop_site_t *MasterSiteList = NULL;
128 drop_site_t **NextSite;
132 Bool SearchChildren();
135 * Region stuff. Stolen from region.h.
138 typedef struct _box {
139 short x1, x2, y1, y2;
150 #define REGION_NUMRECTS(r) (((REGION *)(r))->numRects)
156 * Flash the visible region. Useful for debugging.
159 FlashRegion(dpy, s, gc)
166 Window root = RootWindow(dpy, s);
168 if (GrabSuccess != XGrabPointer(dpy, root, False,
169 ButtonPressMask, GrabModeAsync, GrabModeAsync,
170 None, None, CurrentTime)) {
171 fputs("can't grab the pointer\n", stderr);
176 XFillRectangle(dpy, root, gc, 0, 0,
177 DisplayWidth(dpy, s),
178 DisplayHeight(dpy, s));
181 XMaskEvent(dpy, ButtonPressMask, &e);
182 if (e.xbutton.button == Button3)
185 XFillRectangle(dpy, root, gc, 0, 0,
186 DisplayWidth(dpy, s),
187 DisplayHeight(dpy, s));
190 XUngrabPointer(dpy, CurrentTime);
191 } /* end of FlashRegion */
198 drop_site_t *site = MasterSiteList;
204 gcv.function = GXinvert;
205 gcv.subwindow_mode = IncludeInferiors;
207 gcs = (GC *) malloc(sizeof(GC)*ScreenCount(dpy));
208 for (s=0; s<ScreenCount(dpy); ++s)
209 gcs[s] = XCreateGC(dpy, RootWindow(dpy, s),
210 GCFunction|GCSubwindowMode, &gcv);
212 while (site != NULL) {
213 printf("sid %ld wid 0x%lx flags 0x%lx\n",
214 site->site_id, site->window_id, site->flags);
215 gc = gcs[site->screen];
216 XSetRegion(dpy, gc, site->region);
217 FlashRegion(dpy, site->screen, gc);
221 for (s=0; s<ScreenCount(dpy); ++s)
222 XFreeGC(dpy, gcs[s]);
224 } /* end of FlashDropSites */
230 * Get the interest property from this window. If a valid interest property
231 * was found, a pointer to the data is returned. This data must be freed with
232 * XFree(). If no valid property is found, NULL is returned.
239 GetInterestProperty(dpy, win, nitems)
245 unsigned long *nitems;
255 unsigned long remain;
256 unsigned char * data;
259 s = XGetWindowProperty(dpy, win, ATOM_DRAGDROP_INTEREST, 0L, INTEREST_MAX,
260 False, ATOM_DRAGDROP_INTEREST, &acttype, &actfmt,
261 nitems, &remain, &data);
267 /* property does not exist */
270 if (acttype != ATOM_DRAGDROP_INTEREST) {
271 fputs("dsdm: interest property has wrong type\n", stderr);
276 fputs("dsdm: interest property has wrong format\n", stderr);
280 XFree((XFreeDefn) data);
286 /* XXX didn't read it all, punt */
287 fputs("dsdm: interest property too long\n", stderr);
291 XFree((XFreeDefn) data);
296 } /* end of GetInterestProperty */
300 * Check to see if window win is a top-level window, that is, if it is a
301 * viewable, InputOutput window that has a drop interest or WM_STATE property
302 * on it. If either property is found, return True. Additionally, if True is
303 * returned, psite will be set to point to the drop interest property data if
304 * that property is found, or NULL if not. If neither property is found, all
305 * children of this window are searched.
308 FindRecursively(dpy, root, win, pwin, psite, plen, px, py)
310 Window root, win, *pwin;
315 XWindowAttributes attr;
319 unsigned long nitems;
320 unsigned long remain;
324 unsigned char * data;
328 if (XGetWindowAttributes(dpy, win, &attr) == 0) {
329 fprintf(stderr, "%s: XGetWindowAttributes failed for window 0x%lx\n",
334 if (attr.depth == 0 || attr.class == InputOnly ||
335 attr.map_state != IsViewable) {
339 data = GetInterestProperty(dpy, win, &nitems);
341 if (!XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk)) {
342 fprintf(stderr, "%s: window 0x%lx isn't on the same root!\n",
347 XFree((XFreeDefn) data);
351 *psite = (void *) data;
354 DPRINTF(("%s: found top-level window 0x%lx with an interest\n",
359 s = XGetWindowProperty(dpy, win, ATOM_WM_STATE, 0L, 1,
360 False, ATOM_WM_STATE, &acttype, &actfmt,
361 &nitems, &remain, &data);
363 if (s != Success) /* couldn't find the window */
366 if (acttype == ATOM_WM_STATE) {
368 DPRINTF(("%s: found top-level window 0x%lx with no interest\n",
373 XFree((XFreeDefn) data);
379 #define DRAGDROP_VERSION 0
380 #define INTEREST_RECT 0
382 #define MOTIF_RECEIVER_FLAG 0x80000000
383 /* only do the following if the window doesnot
384 have SUN_DND_INTEREST advertised */
385 if (GetAtomWindow(dpy, win, ATOM_MOTIF_RECEIVER_INFO)) {
386 /* got a motif receiver, create a drop site */
387 CARD32 *data = (CARD32 *) malloc(sizeof(CARD32) * DATA_LEN);
389 data[0] = DRAGDROP_VERSION;
390 data[1] = 1; /* site count */
391 data[2] = proxy_win; /* event_window, xview needs this */
392 data[3] = win; /* site_id */
393 data[4] = MOTIF_RECEIVER_FLAG; /*flag to indicate motif receiver */
394 data[5] = INTEREST_RECT; /* drop site type */
395 data[6] = 1; /* drop site count */
396 data[7] = 0; /* drop window x */
397 data[8] = 0; /* drop window y */
398 data[9] = attr.width; /* drop window width */
399 data[10] = attr.height; /* drop window height */
402 *psite = (void *) data;
404 XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk);
406 /* XView needs this */
409 ATOM_DRAGDROP_INTEREST, /* property */
410 ATOM_DRAGDROP_INTEREST, /* type */
412 PropModeReplace, /* mode */
413 (unsigned char *) data, /* data */
414 DATA_LEN /* number of elements */
417 /* dsdm needs this */
426 return(SearchChildren(dpy, root, win, pwin, psite, plen, px, py));
428 return(SearchChildren(dpy, root, win, pwin, psite, plen, px, py, True));
430 } /* end of FindRecursively */
435 * Look through all the children of window win for a top-level window.
439 SearchChildren(dpy, root, win, pwin, psite, plen, px, py)
441 Window root, win, *pwin;
446 SearchChildren(dpy, root, win, pwin, psite, plen, px, py, from_FindRec)
448 Window root, win, *pwin;
460 unsigned int nchildren;
464 if (XQueryTree(dpy, win, &junk, &junk, &children, &nchildren) == 0)
467 for (i=0; i<nchildren; ++i) {
468 if (FindRecursively(dpy, root, children[i], pwin, psite, plen, px, py))
473 XFree((XFreeDefn)children);
479 if (from_FindRec == False && nchildren)
480 XFree((XFreeDefn)children);
483 } /* end of SearchChildren */
487 * Create and return a region that contains a given rectangle.
490 MakeRegionFromRect(x, y, w, h)
501 reg = XCreateRegion();
502 XUnionRectWithRegion(&r, reg, reg);
504 } /* end of MakeRegionFromRect */
508 * Create and return a region that contains the geometry of the window.
509 * The region returned must be destroyed with XDestroyRegion(). The offset
510 * parameter indicates whether the window's geometry should be offset by its
511 * (x,y) location w.r.t. its parent. If it is false, the region's upper left
512 * corner is at (0,0).
515 GetWindowRegion(dpy, win, offset)
522 unsigned int width, height, junk;
526 winrgn = XCreateRegion();
528 if (0 == XGetGeometry(dpy, win, &wjunk, &x, &y, &width, &height,
530 fprintf(stderr, "%s: XGetGeometry failed on window 0x%lx\n",
533 winrgn = XCreateRegion();
537 return MakeRegionFromRect(offset ? x : 0, offset ? y: 0, width, height);
538 } /* end of GetWindowRegion */
542 * Subtract the area of a window from the current visible region.
545 SubtractWindowFromVisibleRegion(dpy, win, visrgn)
550 Region winrgn = GetWindowRegion(dpy, win, True);
551 XSubtractRegion(visrgn, winrgn, visrgn);
552 XDestroyRegion(winrgn);
553 } /* end of SubtractWindowFromVisibleRegion */
556 #define DRAGDROP_VERSION 0
557 #define INTEREST_RECT 0
558 #define INTEREST_WINDOW 1
560 #define NEXTWORD(dest) do { \
561 if (++cur >= datalen) { \
563 "%s: drop interest data too short on 0x%lx\n", \
565 if (region != NULL) \
566 XDestroyRegion(region); \
567 if (toprgn != NULL) \
568 XDestroyRegion(toprgn); \
571 (dest) = array[cur]; \
576 ProcessInterestProperty(dpy, win, screen, data, datalen, visrgn, xoff, yoff)
583 unsigned long datalen;
587 unsigned long *array = (unsigned long *)data;
597 Region region = NULL;
598 Region toprgn = NULL;
602 unsigned int width, height, junk, border;
607 if (array[cur] != DRAGDROP_VERSION) {
609 "%s: unknown drop interest property version (%ld) on 0x%lx\n",
610 ProgramName, array[cur], win);
614 toprgn = GetWindowRegion(dpy, win, False);
617 for (i=0; i<nsites; ++i) {
622 if (flags & MOTIF_RECEIVER_FLAG)
623 wid = sid; /* replace proxy window id with receiver window id */
628 region = XCreateRegion();
630 for (j=0; j<nrects; ++j) {
633 NEXTWORD(rect.width);
634 NEXTWORD(rect.height);
635 XUnionRectWithRegion(&rect, region, region);
638 case INTEREST_WINDOW:
639 region = XCreateRegion();
641 for (j=0; j<nrects; ++j) {
643 /* REMIND need to make sure areawin isn't bogus */
645 if (0 == XGetGeometry(dpy, areawin, &wjunk, &junk, &junk,
647 if (0 == XGetGeometry(dpy, areawin, &wjunk, &ignore, &ignore,
649 &width, &height, &border, &junk)) {
651 "%s: XGetGeometry failed on window 0x%lx\n",
655 (void) XTranslateCoordinates(dpy, areawin, win, 0, 0,
659 rect.width = width + border;
660 rect.height = height + border;
661 XUnionRectWithRegion(&rect, region, region);
666 "%s: unknown site area type on window 0x%lx\n",
670 XIntersectRegion(region, toprgn, region);
671 XOffsetRegion(region, xoff, yoff);
672 XIntersectRegion(region, visrgn, region);
673 site = (drop_site_t *) malloc(sizeof(drop_site_t));
674 /* XXX check for site == NULL */
675 site->screen = screen;
677 site->window_id = wid;
679 site->region = region;
682 NextSite = &site->next;
687 XDestroyRegion(toprgn);
689 } /* end of ProcessInterestProperty */
693 * For the root window of each screen, get the list of children. For each
694 * child, get its drop forwarding information and find the top-level window
695 * underneath that child, and get the top-level window's drop site
696 * information. Add the top-level window's site information and the site
697 * forwarding information to the site database.
707 unsigned int nchildren;
709 Window root, junk, *children, topwin;
711 Region visrgn, framergn, toprgn;
712 XWindowAttributes attr;
713 unsigned long datalen;
716 unsigned long fwdlen;
719 for (s=0; s<ScreenCount(dpy); ++s) {
721 /* Find the virtual root here, if necessary. */
722 root = RootWindow(dpy, s);
723 visrgn = GetWindowRegion(dpy, root, False);
725 if (XQueryTree(dpy, root, &junk, &junk, &children, &nchildren) == 0) {
726 fprintf(stderr, "%s: XQueryTree failed on root window 0x%lx\n",
732 * For each mapped, InputOutput, child-of-root window, look for a drop
733 * interest property. This will be a forwarding interest property
734 * placed by the window manager. Then, find the top-level window
735 * underneath this window and process its interest property.
738 for (i=nchildren-1; i>=0; --i) {
739 if (XGetWindowAttributes(dpy, children[i], &attr) == 0) {
741 "%s: XGetWindowAttributes failed for window 0x%lx\n",
742 ProgramName, children[i]);
746 if (attr.depth == 0 || attr.class == InputOnly ||
747 attr.map_state != IsViewable) {
751 fwdsitedata = GetInterestProperty(dpy, children[i], &fwdlen);
754 foundtoplevel = SearchChildren(dpy, root, children[i], &topwin,
755 &sitedata, &datalen, &xoff, &yoff);
757 foundtoplevel = SearchChildren(dpy, root, children[i], &topwin,
758 &sitedata, &datalen, &xoff, &yoff, False);
760 if (foundtoplevel && sitedata != NULL) {
761 /* we found a valid drop interest */
762 ProcessInterestProperty(dpy, topwin, s, sitedata,
763 datalen, visrgn, xoff, yoff);
767 XFree((XFreeDefn)sitedata);
769 if (fwdsitedata != NULL) {
770 framergn = MakeRegionFromRect(attr.x, attr.y,
771 attr.width, attr.height);
772 XIntersectRegion(framergn, visrgn, framergn);
773 toprgn = GetWindowRegion(dpy, topwin, False);
774 XOffsetRegion(toprgn, xoff, yoff);
775 XSubtractRegion(framergn, toprgn, framergn);
776 ProcessInterestProperty(dpy, children[i], s, fwdsitedata,
777 fwdlen, framergn, attr.x, attr.y);
778 XDestroyRegion(framergn);
779 XDestroyRegion(toprgn);
783 XFree((XFreeDefn)fwdsitedata);
787 if (fwdsitedata != NULL) {
788 ProcessInterestProperty(dpy, children[i], s, fwdsitedata,
789 fwdlen, visrgn, attr.x, attr.y);
793 XFree((XFreeDefn)fwdsitedata);
798 SubtractWindowFromVisibleRegion(dpy, children[i], visrgn);
800 XDestroyRegion(visrgn);
803 XFree((XFreeDefn)children);
806 } /* end of FindDropSites */
812 drop_site_t *next, *temp;
814 next = MasterSiteList;
815 while (next != NULL) {
821 XDestroyRegion(next->region);
822 XFree((XFreeDefn)next);
826 MasterSiteList = NULL;
828 NextSite = &MasterSiteList;
829 } /* end of FreeDropSites */
833 * Write a property containing site rectangle information. The format
834 * consists of zero or more blocks of 8 words, as follows:
845 WriteSiteRectList(dpy, win, prop)
851 unsigned long *array;
857 site = MasterSiteList;
858 while (site != NULL) {
859 numrects += REGION_NUMRECTS(site->region);
863 /* XXX beware of malloc(0) */
865 array = (unsigned long *) malloc(8*numrects*sizeof(int));
867 array = (unsigned long *) malloc(8*numrects*sizeof(unsigned long));
870 site = MasterSiteList;
871 while (site != NULL) {
872 region = (REGION *) site->region;
874 last = box + region->numRects;
875 for ( ; box < last ; ++box) {
876 *cur++ = site->screen;
877 *cur++ = site->site_id;
879 /* if the receiver is motif then let event window be proxy win */
880 if (site->flags & MOTIF_RECEIVER_FLAG)
883 *cur++ = site->window_id;
885 *cur++ = site->window_id;
889 *cur++ = box->x2 - box->x1;
890 *cur++ = box->y2 - box->y1;
891 *cur++ = site->flags;
896 XChangeProperty(dpy, win, prop, XA_INTEGER, 32, PropModeReplace,
898 (char *)array, cur - array);
901 (unsigned char *)array, cur - array);
902 XFree((XFreeDefn)array);
904 } /* end of WriteSiteRectList */
908 * Ignore BadWindow and BadDrawable errors on a variety of requests. These
909 * errors often occur if the requester window goes away after requesting the
910 * site database. REMIND: more robust error handling is called for.
913 ErrorHandler(dpy, error)
917 if ( (error->error_code == BadWindow ||
918 error->error_code == BadDrawable) &&
919 (error->request_code == X_GetWindowAttributes ||
920 error->request_code == X_ChangeWindowAttributes ||
921 error->request_code == X_GetGeometry ||
922 error->request_code == X_QueryTree ||
923 error->request_code == X_ChangeProperty ||
924 error->request_code == X_GetProperty ||
925 error->request_code == X_SendEvent)) {
926 DPRINTF(("ignored BadWindow error on request %d\n",
927 error->request_code));
931 fputs("dsdm: ", stderr);
932 (*DefaultErrorHandler)(dpy, error);
935 } /* end of ErrorHandler */
942 enum { XA_SUN_DRAGDROP_DSDM, XA_SUN_DRAGDROP_INTEREST,
943 XA_SUN_DRAGDROP_SITE_RECTS, XAWM_STATE, NUM_ATOMS };
944 static char *atom_names[] = {
945 "_SUN_DRAGDROP_DSDM", "_SUN_DRAGDROP_INTEREST",
946 "_SUN_DRAGDROP_SITE_RECTS", "WM_STATE" };
950 XSetWindowAttributes attr;
956 char call_proxy_main;
957 Atom atoms[NUM_ATOMS];
960 while (*cp != '\0') {
966 ProgramName = slash + 1;
968 ProgramName = argv[0];
970 dpy = XOpenDisplay(NULL);
972 fprintf(stderr, "%s: can't open display %s\n",
973 ProgramName, XDisplayName(NULL));
977 DefaultErrorHandler = XSetErrorHandler(ErrorHandler);
979 XInternAtoms(dpy, atom_names, NUM_ATOMS, False, atoms);
980 ATOM_DRAGDROP_DSDM = atoms[XA_SUN_DRAGDROP_DSDM];
981 ATOM_DRAGDROP_INTEREST = atoms[XA_SUN_DRAGDROP_INTEREST];
982 ATOM_SITE_RECTS = atoms[XA_SUN_DRAGDROP_SITE_RECTS];
983 ATOM_WM_STATE = atoms[XAWM_STATE];
985 attr.event_mask = PropertyChangeMask;
986 selwin = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0,
987 InputOnly, CopyFromParent,
990 if (argc > 1 && 0 == strcmp(argv[1], "-x"))
994 win = XGetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM);
998 fprintf(stderr, "%s: no DSDM is running\n", ProgramName);
1001 /* Clear any DSDM selection to force the running DSDM to exit */
1002 XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, None, CurrentTime);
1009 "%s: another DSDM is already running\n",
1015 ProxyInit(dpy, selwin);
1018 XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, selwin, CurrentTime);
1019 /* no need to get owner per ICCCM, because we have the server grabbed */
1023 /* An attempt to minimize contention at startup: in the olinitrc file, DO
1024 * NOT start the window manager or the file manager in the background. This
1025 * way, the initialization code for the two applications is run sequentially.
1029 exit(0); /* on parent success or failure */
1032 call_proxy_main = True;
1033 XNextEvent(dpy, &e);
1035 case SelectionRequest:
1036 if (e.xselectionrequest.selection != ATOM_DRAGDROP_DSDM) {
1038 DPRINTF(("%s: got SelectionRequest on wrong selection?\n",
1043 if (e.xselectionrequest.owner != selwin) {
1044 fprintf(stderr, "%s: got SelectionRequest on wrong window?\n",
1049 reply.xselection.display = e.xselectionrequest.display;
1050 reply.xselection.type = SelectionNotify;
1051 reply.xselection.requestor = e.xselectionrequest.requestor;
1052 reply.xselection.selection = ATOM_DRAGDROP_DSDM;
1053 reply.xselection.target = e.xselectionrequest.target;
1054 reply.xselection.time = e.xselectionrequest.time;
1056 if (e.xselectionrequest.target != ATOM_SITE_RECTS) {
1058 "%s: got SelectionRequest for unknown target\n",
1060 reply.xselection.property = None;
1062 DPRINTF(("%s: got SelectionRequest event OK\n", ProgramName));
1065 WriteSiteRectList(dpy, e.xselectionrequest.requestor,
1066 e.xselectionrequest.property);
1067 reply.xselection.property =
1068 e.xselectionrequest.property;
1071 (void) XSendEvent(dpy, reply.xselection.requestor, False, 0,
1073 call_proxy_main = False;
1075 case SelectionClear:
1079 if (call_proxy_main)