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
69 #if defined(SVR4) || defined(SYSV)
70 #include <string.h> /*USL*/
71 #else /* SVR4 or SYSV */
73 #endif /* SVR4 or SYSV */
75 #if !defined(__STDC__) && !defined(__cplusplus) && !defined(c_plusplus) /*USL*/
79 #define XFreeDefn char *
81 #include <X11/Xproto.h>
83 #include <X11/Xutil.h>
84 #include <X11/Xatom.h>
87 * Use DPRINTF to write debugging messages. Place all arguments in an
88 * extra pair of parentheses, like so:
89 * DPRINTF(("%s: some error occurred (code=%d)\n", ProgramName, err));
93 #define DPRINTF(args) (void) printf args
98 #define INTEREST_MAX 100000000L /*(1024L*1024L)*/
100 #define FIND_CONTINUE ((Window) 0L)
101 #define FIND_STOP ((Window) 1L)
105 extern Window GetAtomWindow();
106 extern Atom ATOM_MOTIF_RECEIVER_INFO;
111 Atom ATOM_DRAGDROP_DSDM;
112 Atom ATOM_DRAGDROP_INTEREST;
114 Atom ATOM_SITE_RECTS;
117 int (*DefaultErrorHandler)();
119 typedef struct _site {
121 unsigned long site_id;
128 drop_site_t *MasterSiteList = NULL;
129 drop_site_t **NextSite;
133 Bool SearchChildren();
136 * Region stuff. Stolen from region.h.
139 typedef struct _box {
140 short x1, x2, y1, y2;
151 #define REGION_NUMRECTS(r) (((REGION *)(r))->numRects)
157 * Flash the visible region. Useful for debugging.
160 FlashRegion(Display *dpy, int s, GC gc)
164 Window root = RootWindow(dpy, s);
166 if (GrabSuccess != XGrabPointer(dpy, root, False,
167 ButtonPressMask, GrabModeAsync, GrabModeAsync,
168 None, None, CurrentTime)) {
169 fputs("can't grab the pointer\n", stderr);
174 XFillRectangle(dpy, root, gc, 0, 0,
175 DisplayWidth(dpy, s),
176 DisplayHeight(dpy, s));
179 XMaskEvent(dpy, ButtonPressMask, &e);
180 if (e.xbutton.button == Button3)
183 XFillRectangle(dpy, root, gc, 0, 0,
184 DisplayWidth(dpy, s),
185 DisplayHeight(dpy, s));
188 XUngrabPointer(dpy, CurrentTime);
189 } /* end of FlashRegion */
193 FlashDropSites(Display *dpy)
195 drop_site_t *site = MasterSiteList;
201 gcv.function = GXinvert;
202 gcv.subwindow_mode = IncludeInferiors;
204 gcs = (GC *) malloc(sizeof(GC)*ScreenCount(dpy));
205 for (s=0; s<ScreenCount(dpy); ++s)
206 gcs[s] = XCreateGC(dpy, RootWindow(dpy, s),
207 GCFunction|GCSubwindowMode, &gcv);
209 while (site != NULL) {
210 printf("sid %ld wid 0x%lx flags 0x%lx\n",
211 site->site_id, site->window_id, site->flags);
212 gc = gcs[site->screen];
213 XSetRegion(dpy, gc, site->region);
214 FlashRegion(dpy, site->screen, gc);
218 for (s=0; s<ScreenCount(dpy); ++s)
219 XFreeGC(dpy, gcs[s]);
221 } /* end of FlashDropSites */
227 * Get the interest property from this window. If a valid interest property
228 * was found, a pointer to the data is returned. This data must be freed with
229 * XFree(). If no valid property is found, NULL is returned.
233 GetInterestProperty(Display *dpy, Window win, int *nitems)
236 GetInterestProperty(Display *dpy, Window win, unsigned long *nitems)
246 unsigned long remain;
247 unsigned char * data;
250 s = XGetWindowProperty(dpy, win, ATOM_DRAGDROP_INTEREST, 0L, INTEREST_MAX,
251 False, ATOM_DRAGDROP_INTEREST, &acttype, &actfmt,
252 nitems, &remain, &data);
258 /* property does not exist */
261 if (acttype != ATOM_DRAGDROP_INTEREST) {
262 fputs("dsdm: interest property has wrong type\n", stderr);
267 fputs("dsdm: interest property has wrong format\n", stderr);
271 XFree((XFreeDefn) data);
277 /* XXX didn't read it all, punt */
278 fputs("dsdm: interest property too long\n", stderr);
282 XFree((XFreeDefn) data);
287 } /* end of GetInterestProperty */
291 * Check to see if window win is a top-level window, that is, if it is a
292 * viewable, InputOutput window that has a drop interest or WM_STATE property
293 * on it. If either property is found, return True. Additionally, if True is
294 * returned, psite will be set to point to the drop interest property data if
295 * that property is found, or NULL if not. If neither property is found, all
296 * children of this window are searched.
299 FindRecursively(Display *dpy, Window root, Window win, Window *pwin, void **psite, unsigned long *plen, int *px, int *py)
301 XWindowAttributes attr;
305 unsigned long nitems;
306 unsigned long remain;
310 unsigned char * data;
314 if (XGetWindowAttributes(dpy, win, &attr) == 0) {
315 fprintf(stderr, "%s: XGetWindowAttributes failed for window 0x%lx\n",
320 if (attr.depth == 0 || attr.class == InputOnly ||
321 attr.map_state != IsViewable) {
325 data = GetInterestProperty(dpy, win, &nitems);
327 if (!XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk)) {
328 fprintf(stderr, "%s: window 0x%lx isn't on the same root!\n",
333 XFree((XFreeDefn) data);
337 *psite = (void *) data;
340 DPRINTF(("%s: found top-level window 0x%lx with an interest\n",
345 s = XGetWindowProperty(dpy, win, ATOM_WM_STATE, 0L, 1,
346 False, ATOM_WM_STATE, &acttype, &actfmt,
347 &nitems, &remain, &data);
349 if (s != Success) /* couldn't find the window */
352 if (acttype == ATOM_WM_STATE) {
354 DPRINTF(("%s: found top-level window 0x%lx with no interest\n",
359 XFree((XFreeDefn) data);
365 #define DRAGDROP_VERSION 0
366 #define INTEREST_RECT 0
368 #define MOTIF_RECEIVER_FLAG 0x80000000
369 /* only do the following if the window doesnot
370 have SUN_DND_INTEREST advertised */
371 if (GetAtomWindow(dpy, win, ATOM_MOTIF_RECEIVER_INFO)) {
372 /* got a motif receiver, create a drop site */
373 CARD32 *data = (CARD32 *) malloc(sizeof(CARD32) * DATA_LEN);
375 data[0] = DRAGDROP_VERSION;
376 data[1] = 1; /* site count */
377 data[2] = proxy_win; /* event_window, xview needs this */
378 data[3] = win; /* site_id */
379 data[4] = MOTIF_RECEIVER_FLAG; /*flag to indicate motif receiver */
380 data[5] = INTEREST_RECT; /* drop site type */
381 data[6] = 1; /* drop site count */
382 data[7] = 0; /* drop window x */
383 data[8] = 0; /* drop window y */
384 data[9] = attr.width; /* drop window width */
385 data[10] = attr.height; /* drop window height */
388 *psite = (void *) data;
390 XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk);
392 /* XView needs this */
395 ATOM_DRAGDROP_INTEREST, /* property */
396 ATOM_DRAGDROP_INTEREST, /* type */
398 PropModeReplace, /* mode */
399 (unsigned char *) data, /* data */
400 DATA_LEN /* number of elements */
403 /* dsdm needs this */
412 return(SearchChildren(dpy, root, win, pwin, psite, plen, px, py));
414 return(SearchChildren(dpy, root, win, pwin, psite, plen, px, py, True));
416 } /* end of FindRecursively */
421 * Look through all the children of window win for a top-level window.
425 SearchChildren(Display *dpy, Window root, Window win, Window *pwin, void **psite, unsigned long *plen, int *px, int *py)
427 SearchChildren(Display *dpy, Window root, Window win, Window *pwin, void **psite, unsigned long *plen, int *px, int *py, Bool from_FindRec)
435 unsigned int nchildren;
439 if (XQueryTree(dpy, win, &junk, &junk, &children, &nchildren) == 0)
442 for (i=0; i<nchildren; ++i) {
443 if (FindRecursively(dpy, root, children[i], pwin, psite, plen, px, py))
448 XFree((XFreeDefn)children);
454 if (from_FindRec == False && nchildren)
455 XFree((XFreeDefn)children);
458 } /* end of SearchChildren */
462 * Create and return a region that contains a given rectangle.
465 MakeRegionFromRect(int x, int y, unsigned int w, unsigned int h)
474 reg = XCreateRegion();
475 XUnionRectWithRegion(&r, reg, reg);
477 } /* end of MakeRegionFromRect */
481 * Create and return a region that contains the geometry of the window.
482 * The region returned must be destroyed with XDestroyRegion(). The offset
483 * parameter indicates whether the window's geometry should be offset by its
484 * (x,y) location w.r.t. its parent. If it is false, the region's upper left
485 * corner is at (0,0).
488 GetWindowRegion(Display *dpy, Window win, Bool offset)
492 unsigned int width, height, junk;
496 winrgn = XCreateRegion();
498 if (0 == XGetGeometry(dpy, win, &wjunk, &x, &y, &width, &height,
500 fprintf(stderr, "%s: XGetGeometry failed on window 0x%lx\n",
503 winrgn = XCreateRegion();
507 return MakeRegionFromRect(offset ? x : 0, offset ? y: 0, width, height);
508 } /* end of GetWindowRegion */
512 * Subtract the area of a window from the current visible region.
515 SubtractWindowFromVisibleRegion(Display *dpy, Window win, Region visrgn)
517 Region winrgn = GetWindowRegion(dpy, win, True);
518 XSubtractRegion(visrgn, winrgn, visrgn);
519 XDestroyRegion(winrgn);
520 } /* end of SubtractWindowFromVisibleRegion */
523 #define DRAGDROP_VERSION 0
524 #define INTEREST_RECT 0
525 #define INTEREST_WINDOW 1
527 #define NEXTWORD(dest) do { \
528 if (++cur >= datalen) { \
530 "%s: drop interest data too short on 0x%lx\n", \
532 if (region != NULL) \
533 XDestroyRegion(region); \
534 if (toprgn != NULL) \
535 XDestroyRegion(toprgn); \
538 (dest) = array[cur]; \
543 ProcessInterestProperty(dpy, win, screen, data, datalen, visrgn, xoff, yoff)
550 unsigned long datalen;
554 unsigned long *array = (unsigned long *)data;
564 Region region = NULL;
565 Region toprgn = NULL;
569 unsigned int width, height, junk, border;
574 if (array[cur] != DRAGDROP_VERSION) {
576 "%s: unknown drop interest property version (%ld) on 0x%lx\n",
577 ProgramName, array[cur], win);
581 toprgn = GetWindowRegion(dpy, win, False);
584 for (i=0; i<nsites; ++i) {
589 if (flags & MOTIF_RECEIVER_FLAG)
590 wid = sid; /* replace proxy window id with receiver window id */
595 region = XCreateRegion();
597 for (j=0; j<nrects; ++j) {
600 NEXTWORD(rect.width);
601 NEXTWORD(rect.height);
602 XUnionRectWithRegion(&rect, region, region);
605 case INTEREST_WINDOW:
606 region = XCreateRegion();
608 for (j=0; j<nrects; ++j) {
610 /* REMIND need to make sure areawin isn't bogus */
612 if (0 == XGetGeometry(dpy, areawin, &wjunk, &junk, &junk,
614 if (0 == XGetGeometry(dpy, areawin, &wjunk, &ignore, &ignore,
616 &width, &height, &border, &junk)) {
618 "%s: XGetGeometry failed on window 0x%lx\n",
622 (void) XTranslateCoordinates(dpy, areawin, win, 0, 0,
626 rect.width = width + border;
627 rect.height = height + border;
628 XUnionRectWithRegion(&rect, region, region);
633 "%s: unknown site area type on window 0x%lx\n",
637 XIntersectRegion(region, toprgn, region);
638 XOffsetRegion(region, xoff, yoff);
639 XIntersectRegion(region, visrgn, region);
640 site = (drop_site_t *) malloc(sizeof(drop_site_t));
641 /* XXX check for site == NULL */
642 site->screen = screen;
644 site->window_id = wid;
646 site->region = region;
649 NextSite = &site->next;
654 XDestroyRegion(toprgn);
656 } /* end of ProcessInterestProperty */
660 * For the root window of each screen, get the list of children. For each
661 * child, get its drop forwarding information and find the top-level window
662 * underneath that child, and get the top-level window's drop site
663 * information. Add the top-level window's site information and the site
664 * forwarding information to the site database.
667 FindDropSites(Display *dpy)
673 unsigned int nchildren;
675 Window root, junk, *children, topwin;
677 Region visrgn, framergn, toprgn;
678 XWindowAttributes attr;
679 unsigned long datalen;
682 unsigned long fwdlen;
685 for (s=0; s<ScreenCount(dpy); ++s) {
687 /* Find the virtual root here, if necessary. */
688 root = RootWindow(dpy, s);
689 visrgn = GetWindowRegion(dpy, root, False);
691 if (XQueryTree(dpy, root, &junk, &junk, &children, &nchildren) == 0) {
692 fprintf(stderr, "%s: XQueryTree failed on root window 0x%lx\n",
698 * For each mapped, InputOutput, child-of-root window, look for a drop
699 * interest property. This will be a forwarding interest property
700 * placed by the window manager. Then, find the top-level window
701 * underneath this window and process its interest property.
704 for (i=nchildren-1; i>=0; --i) {
705 if (XGetWindowAttributes(dpy, children[i], &attr) == 0) {
707 "%s: XGetWindowAttributes failed for window 0x%lx\n",
708 ProgramName, children[i]);
712 if (attr.depth == 0 || attr.class == InputOnly ||
713 attr.map_state != IsViewable) {
717 fwdsitedata = GetInterestProperty(dpy, children[i], &fwdlen);
720 foundtoplevel = SearchChildren(dpy, root, children[i], &topwin,
721 &sitedata, &datalen, &xoff, &yoff);
723 foundtoplevel = SearchChildren(dpy, root, children[i], &topwin,
724 &sitedata, &datalen, &xoff, &yoff, False);
726 if (foundtoplevel && sitedata != NULL) {
727 /* we found a valid drop interest */
728 ProcessInterestProperty(dpy, topwin, s, sitedata,
729 datalen, visrgn, xoff, yoff);
733 XFree((XFreeDefn)sitedata);
735 if (fwdsitedata != NULL) {
736 framergn = MakeRegionFromRect(attr.x, attr.y,
737 attr.width, attr.height);
738 XIntersectRegion(framergn, visrgn, framergn);
739 toprgn = GetWindowRegion(dpy, topwin, False);
740 XOffsetRegion(toprgn, xoff, yoff);
741 XSubtractRegion(framergn, toprgn, framergn);
742 ProcessInterestProperty(dpy, children[i], s, fwdsitedata,
743 fwdlen, framergn, attr.x, attr.y);
744 XDestroyRegion(framergn);
745 XDestroyRegion(toprgn);
749 XFree((XFreeDefn)fwdsitedata);
753 if (fwdsitedata != NULL) {
754 ProcessInterestProperty(dpy, children[i], s, fwdsitedata,
755 fwdlen, visrgn, attr.x, attr.y);
759 XFree((XFreeDefn)fwdsitedata);
764 SubtractWindowFromVisibleRegion(dpy, children[i], visrgn);
766 XDestroyRegion(visrgn);
769 XFree((XFreeDefn)children);
772 } /* end of FindDropSites */
778 drop_site_t *next, *temp;
780 next = MasterSiteList;
781 while (next != NULL) {
787 XDestroyRegion(next->region);
788 XFree((XFreeDefn)next);
792 MasterSiteList = NULL;
794 NextSite = &MasterSiteList;
795 } /* end of FreeDropSites */
799 * Write a property containing site rectangle information. The format
800 * consists of zero or more blocks of 8 words, as follows:
811 WriteSiteRectList(Display *dpy, Window win, Atom prop)
814 unsigned long *array;
820 site = MasterSiteList;
821 while (site != NULL) {
822 numrects += REGION_NUMRECTS(site->region);
826 /* XXX beware of malloc(0) */
828 array = (unsigned long *) malloc(8*numrects*sizeof(int));
830 array = (unsigned long *) malloc(8*numrects*sizeof(unsigned long));
833 site = MasterSiteList;
834 while (site != NULL) {
835 region = (REGION *) site->region;
837 last = box + region->numRects;
838 for ( ; box < last ; ++box) {
839 *cur++ = site->screen;
840 *cur++ = site->site_id;
842 /* if the receiver is motif then let event window be proxy win */
843 if (site->flags & MOTIF_RECEIVER_FLAG)
846 *cur++ = site->window_id;
848 *cur++ = site->window_id;
852 *cur++ = box->x2 - box->x1;
853 *cur++ = box->y2 - box->y1;
854 *cur++ = site->flags;
859 XChangeProperty(dpy, win, prop, XA_INTEGER, 32, PropModeReplace,
861 (char *)array, cur - array);
864 (unsigned char *)array, cur - array);
865 XFree((XFreeDefn)array);
867 } /* end of WriteSiteRectList */
871 * Ignore BadWindow and BadDrawable errors on a variety of requests. These
872 * errors often occur if the requester window goes away after requesting the
873 * site database. REMIND: more robust error handling is called for.
876 ErrorHandler(Display *dpy, XErrorEvent *error)
878 if ( (error->error_code == BadWindow ||
879 error->error_code == BadDrawable) &&
880 (error->request_code == X_GetWindowAttributes ||
881 error->request_code == X_ChangeWindowAttributes ||
882 error->request_code == X_GetGeometry ||
883 error->request_code == X_QueryTree ||
884 error->request_code == X_ChangeProperty ||
885 error->request_code == X_GetProperty ||
886 error->request_code == X_SendEvent)) {
887 DPRINTF(("ignored BadWindow error on request %d\n",
888 error->request_code));
892 fputs("dsdm: ", stderr);
893 (*DefaultErrorHandler)(dpy, error);
896 } /* end of ErrorHandler */
900 main(int argc, char **argv)
902 enum { XA_SUN_DRAGDROP_DSDM, XA_SUN_DRAGDROP_INTEREST,
903 XA_SUN_DRAGDROP_SITE_RECTS, XAWM_STATE, NUM_ATOMS };
904 static char *atom_names[] = {
905 "_SUN_DRAGDROP_DSDM", "_SUN_DRAGDROP_INTEREST",
906 "_SUN_DRAGDROP_SITE_RECTS", "WM_STATE" };
910 XSetWindowAttributes attr;
916 char call_proxy_main;
917 Atom atoms[NUM_ATOMS];
920 while (*cp != '\0') {
926 ProgramName = slash + 1;
928 ProgramName = argv[0];
930 dpy = XOpenDisplay(NULL);
932 fprintf(stderr, "%s: can't open display %s\n",
933 ProgramName, XDisplayName(NULL));
937 DefaultErrorHandler = XSetErrorHandler(ErrorHandler);
939 XInternAtoms(dpy, atom_names, NUM_ATOMS, False, atoms);
940 ATOM_DRAGDROP_DSDM = atoms[XA_SUN_DRAGDROP_DSDM];
941 ATOM_DRAGDROP_INTEREST = atoms[XA_SUN_DRAGDROP_INTEREST];
942 ATOM_SITE_RECTS = atoms[XA_SUN_DRAGDROP_SITE_RECTS];
943 ATOM_WM_STATE = atoms[XAWM_STATE];
945 attr.event_mask = PropertyChangeMask;
946 selwin = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0,
947 InputOnly, CopyFromParent,
950 if (argc > 1 && 0 == strcmp(argv[1], "-x"))
954 win = XGetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM);
958 fprintf(stderr, "%s: no DSDM is running\n", ProgramName);
961 /* Clear any DSDM selection to force the running DSDM to exit */
962 XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, None, CurrentTime);
969 "%s: another DSDM is already running\n",
975 ProxyInit(dpy, selwin);
978 XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, selwin, CurrentTime);
979 /* no need to get owner per ICCCM, because we have the server grabbed */
983 /* An attempt to minimize contention at startup: in the olinitrc file, DO
984 * NOT start the window manager or the file manager in the background. This
985 * way, the initialization code for the two applications is run sequentially.
989 exit(0); /* on parent success or failure */
992 call_proxy_main = True;
995 case SelectionRequest:
996 if (e.xselectionrequest.selection != ATOM_DRAGDROP_DSDM) {
998 DPRINTF(("%s: got SelectionRequest on wrong selection?\n",
1003 if (e.xselectionrequest.owner != selwin) {
1004 fprintf(stderr, "%s: got SelectionRequest on wrong window?\n",
1009 reply.xselection.display = e.xselectionrequest.display;
1010 reply.xselection.type = SelectionNotify;
1011 reply.xselection.requestor = e.xselectionrequest.requestor;
1012 reply.xselection.selection = ATOM_DRAGDROP_DSDM;
1013 reply.xselection.target = e.xselectionrequest.target;
1014 reply.xselection.time = e.xselectionrequest.time;
1016 if (e.xselectionrequest.target != ATOM_SITE_RECTS) {
1018 "%s: got SelectionRequest for unknown target\n",
1020 reply.xselection.property = None;
1022 DPRINTF(("%s: got SelectionRequest event OK\n", ProgramName));
1025 WriteSiteRectList(dpy, e.xselectionrequest.requestor,
1026 e.xselectionrequest.property);
1027 reply.xselection.property =
1028 e.xselectionrequest.property;
1031 (void) XSendEvent(dpy, reply.xselection.requestor, False, 0,
1033 call_proxy_main = False;
1035 case SelectionClear:
1039 if (call_proxy_main)