/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: dsdm.c /main/6 1997/06/18 17:27:35 samborn $ */ /* Copyright (c) 1990, 1991, 1992, 1993 UNIX System Laboratories, Inc. */ /* Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */ /* UNIX System Laboratories, Inc. */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ /* * Copyright (C) 1986,1992 Sun Microsystems, Inc * All rights reserved. * Notice of copyright on this source code * product does not indicate publication. * * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by * the U.S. Government is subject to restrictions as set forth * in subparagraph (c)(1)(ii) of the Rights in Technical Data * and Computer Software Clause at DFARS 252.227-7013 (Oct. 1988) * and FAR 52.227-19 (c) (June 1987). * * Sun Microsystems, Inc., 2550 Garcia Avenue, * Mountain View, California 94043. * */ /* * @(#)dsdm.c 1.5 91/02/13 * Drop Site Database Manager for drag'n'drop. * * Master algorithm: * * Start with visible region as whole screen. * For each top-level window, do * (0) flatten its interest rectangles * (1) intersect interest rects with the top-level window * (2) intersect them with the visible region * (3) append them to the master list * (4) subtract this top-level frame from the visible region */ #include #include #if defined(SVR4) #include #else /* SVR4 */ #include #endif /* SVR4 */ #define XFreeDefn char * #include #include #include #include /* * Use DPRINTF to write debugging messages. Place all arguments in an * extra pair of parentheses, like so: * DPRINTF(("%s: some error occurred (code=%d)\n", ProgramName, err)); */ #ifdef DEBUG #define DPRINTF(args) (void) printf args #else /* DEBUG */ #define DPRINTF(a) #endif /* DEBUG */ #define INTEREST_MAX 100000000L /*(1024L*1024L)*/ #define FIND_CONTINUE ((Window) 0L) #define FIND_STOP ((Window) 1L) #define PROXY 1 #ifdef PROXY extern Window GetAtomWindow(); extern Atom ATOM_MOTIF_RECEIVER_INFO; Window proxy_win; #endif char *ProgramName; Atom ATOM_DRAGDROP_DSDM; Atom ATOM_DRAGDROP_INTEREST; Atom ATOM_WM_STATE; Atom ATOM_SITE_RECTS; int (*DefaultErrorHandler)(); typedef struct _site { int screen; unsigned long site_id; Window window_id; unsigned long flags; Region region; struct _site *next; } drop_site_t; drop_site_t *MasterSiteList = NULL; drop_site_t **NextSite; int SitesFound = 0; Bool SearchChildren(); /* * Region stuff. Stolen from region.h. */ typedef struct _box { short x1, x2, y1, y2; } BOX; typedef struct { long size; long numRects; BOX *rects; BOX extents; } REGION; #define REGION_NUMRECTS(r) (((REGION *)(r))->numRects) /* * Get the interest property from this window. If a valid interest property * was found, a pointer to the data is returned. This data must be freed with * XFree(). If no valid property is found, NULL is returned. */ unsigned char * GetInterestProperty(Display *dpy, Window win, unsigned long *nitems) { Status s; Atom acttype; int actfmt; unsigned long remain; unsigned char * data; s = XGetWindowProperty(dpy, win, ATOM_DRAGDROP_INTEREST, 0L, INTEREST_MAX, False, ATOM_DRAGDROP_INTEREST, &acttype, &actfmt, nitems, &remain, &data); if (s != Success) return NULL; if (acttype == None) /* property does not exist */ return NULL; if (acttype != ATOM_DRAGDROP_INTEREST) { fputs("dsdm: interest property has wrong type\n", stderr); return NULL; } if (actfmt != 32) { fputs("dsdm: interest property has wrong format\n", stderr); XFree((XFreeDefn) data); return NULL; } if (remain > 0) { /* XXX didn't read it all, punt */ fputs("dsdm: interest property too long\n", stderr); XFree((XFreeDefn) data); return NULL; } return data; } /* end of GetInterestProperty */ /* * Check to see if window win is a top-level window, that is, if it is a * viewable, InputOutput window that has a drop interest or WM_STATE property * on it. If either property is found, return True. Additionally, if True is * returned, psite will be set to point to the drop interest property data if * that property is found, or NULL if not. If neither property is found, all * children of this window are searched. */ Bool FindRecursively(Display *dpy, Window root, Window win, Window *pwin, void **psite, unsigned long *plen, int *px, int *py) { XWindowAttributes attr; Window junk; Atom acttype; int actfmt; unsigned long nitems; unsigned long remain; unsigned char * data; Status s; if (XGetWindowAttributes(dpy, win, &attr) == 0) { fprintf(stderr, "%s: XGetWindowAttributes failed for window 0x%lx\n", ProgramName, win); return False; } if (attr.depth == 0 || attr.class == InputOnly || attr.map_state != IsViewable) { return False; } data = GetInterestProperty(dpy, win, &nitems); if (data != NULL) { if (!XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk)) { fprintf(stderr, "%s: window 0x%lx isn't on the same root!\n", ProgramName, win); XFree((XFreeDefn) data); return False; } *psite = (void *) data; *plen = nitems; *pwin = win; DPRINTF(("%s: found top-level window 0x%lx with an interest\n", ProgramName, win)); return True; } s = XGetWindowProperty(dpy, win, ATOM_WM_STATE, 0L, 1, False, ATOM_WM_STATE, &acttype, &actfmt, &nitems, &remain, &data); if (s != Success) /* couldn't find the window */ return False; if (acttype == ATOM_WM_STATE) { /* found it! */ DPRINTF(("%s: found top-level window 0x%lx with no interest\n", ProgramName, win)); XFree((XFreeDefn) data); *psite = NULL; *plen = 0; #ifdef PROXY #define DRAGDROP_VERSION 0 #define INTEREST_RECT 0 #define DATA_LEN 11 #define MOTIF_RECEIVER_FLAG 0x80000000 /* only do the following if the window doesnot have SUN_DND_INTEREST advertised */ if (GetAtomWindow(dpy, win, ATOM_MOTIF_RECEIVER_INFO)) { /* got a motif receiver, create a drop site */ CARD32 *data = (CARD32 *) malloc(sizeof(CARD32) * DATA_LEN); data[0] = DRAGDROP_VERSION; data[1] = 1; /* site count */ data[2] = proxy_win; /* event_window, xview needs this */ data[3] = win; /* site_id */ data[4] = MOTIF_RECEIVER_FLAG; /*flag to indicate motif receiver */ data[5] = INTEREST_RECT; /* drop site type */ data[6] = 1; /* drop site count */ data[7] = 0; /* drop window x */ data[8] = 0; /* drop window y */ data[9] = attr.width; /* drop window width */ data[10] = attr.height; /* drop window height */ *pwin = win; *psite = (void *) data; *plen = DATA_LEN; XTranslateCoordinates(dpy, win, root, 0, 0, px, py, &junk); /* XView needs this */ XChangeProperty(dpy, win, ATOM_DRAGDROP_INTEREST, /* property */ ATOM_DRAGDROP_INTEREST, /* type */ 32, /* format */ PropModeReplace, /* mode */ (unsigned char *) data, /* data */ DATA_LEN /* number of elements */ ); /* dsdm needs this */ data[2] = win; } #undef DATA_LEN #endif return True; } return(SearchChildren(dpy, root, win, pwin, psite, plen, px, py, True)); } /* end of FindRecursively */ /* * Look through all the children of window win for a top-level window. */ Bool SearchChildren(Display *dpy, Window root, Window win, Window *pwin, void **psite, unsigned long *plen, int *px, int *py, Bool from_FindRec) { Window junk; Window *children; unsigned int nchildren; int i; if (XQueryTree(dpy, win, &junk, &junk, &children, &nchildren) == 0) return False; for (i=0; i= datalen) { \ fprintf(stderr, \ "%s: drop interest data too short on 0x%lx\n", \ ProgramName, win); \ if (region != NULL) \ XDestroyRegion(region); \ if (toprgn != NULL) \ XDestroyRegion(toprgn); \ return; \ } \ (dest) = array[cur]; \ } while (0) void ProcessInterestProperty(Display * dpy, Window win, int screen, void *data, unsigned long datalen, Region visrgn, int xoff, int yoff) { unsigned long *array = (unsigned long *)data; int cur = 0; int i, j, nsites; Window wid; Window wjunk; Window areawin; unsigned long sid; int areatype; int nrects; unsigned long flags; Region region = NULL; Region toprgn = NULL; XRectangle rect; drop_site_t *site; int x, y; unsigned int width, height, junk, border; int ignore; if (array[cur] != DRAGDROP_VERSION) { fprintf(stderr, "%s: unknown drop interest property version (%ld) on 0x%lx\n", ProgramName, array[cur], win); return; } toprgn = GetWindowRegion(dpy, win, False); NEXTWORD(nsites); for (i=0; iscreen = screen; site->site_id = sid; site->window_id = wid; site->flags = flags; site->region = region; site->next = NULL; (*NextSite) = site; NextSite = &site->next; ++SitesFound; region = NULL; } XDestroyRegion(toprgn); } /* end of ProcessInterestProperty */ /* * For the root window of each screen, get the list of children. For each * child, get its drop forwarding information and find the top-level window * underneath that child, and get the top-level window's drop site * information. Add the top-level window's site information and the site * forwarding information to the site database. */ void FindDropSites(Display *dpy) { int s, i; unsigned int nchildren; Window root, junk, *children, topwin; void *sitedata; Region visrgn, framergn, toprgn; XWindowAttributes attr; unsigned long datalen; int xoff, yoff; void *fwdsitedata; unsigned long fwdlen; Bool foundtoplevel; for (s=0; s=0; --i) { if (XGetWindowAttributes(dpy, children[i], &attr) == 0) { fprintf(stderr, "%s: XGetWindowAttributes failed for window 0x%lx\n", ProgramName, children[i]); continue; } if (attr.depth == 0 || attr.class == InputOnly || attr.map_state != IsViewable) { continue; } fwdsitedata = GetInterestProperty(dpy, children[i], &fwdlen); foundtoplevel = SearchChildren(dpy, root, children[i], &topwin, &sitedata, &datalen, &xoff, &yoff, False); if (foundtoplevel && sitedata != NULL) { /* we found a valid drop interest */ ProcessInterestProperty(dpy, topwin, s, sitedata, datalen, visrgn, xoff, yoff); XFree((XFreeDefn)sitedata); if (fwdsitedata != NULL) { framergn = MakeRegionFromRect(attr.x, attr.y, attr.width, attr.height); XIntersectRegion(framergn, visrgn, framergn); toprgn = GetWindowRegion(dpy, topwin, False); XOffsetRegion(toprgn, xoff, yoff); XSubtractRegion(framergn, toprgn, framergn); ProcessInterestProperty(dpy, children[i], s, fwdsitedata, fwdlen, framergn, attr.x, attr.y); XDestroyRegion(framergn); XDestroyRegion(toprgn); XFree((XFreeDefn)fwdsitedata); } } else { if (fwdsitedata != NULL) { ProcessInterestProperty(dpy, children[i], s, fwdsitedata, fwdlen, visrgn, attr.x, attr.y); XFree((XFreeDefn)fwdsitedata); } } SubtractWindowFromVisibleRegion(dpy, children[i], visrgn); } XDestroyRegion(visrgn); if (nchildren) XFree((XFreeDefn)children); } } /* end of FindDropSites */ void FreeDropSites(void) { drop_site_t *next, *temp; next = MasterSiteList; while (next != NULL) { temp = next->next; if (next->region) XDestroyRegion(next->region); XFree((XFreeDefn)next); next = temp; } MasterSiteList = NULL; SitesFound = 0; NextSite = &MasterSiteList; } /* end of FreeDropSites */ /* * Write a property containing site rectangle information. The format * consists of zero or more blocks of 8 words, as follows: * 8k+0 screen number * 8k+1 site id * 8k+2 window id * 8k+3 x * 8k+4 y * 8k+5 width * 8k+6 height * 8k+7 flags */ void WriteSiteRectList(Display *dpy, Window win, Atom prop) { unsigned long *cur; unsigned long *array; drop_site_t *site; int numrects = 0; REGION *region; BOX *box, *last; site = MasterSiteList; while (site != NULL) { numrects += REGION_NUMRECTS(site->region); site = site->next; } /* XXX beware of malloc(0) */ array = (unsigned long *) malloc(8*numrects*sizeof(unsigned long)); cur = array; site = MasterSiteList; while (site != NULL) { region = (REGION *) site->region; box = region->rects; last = box + region->numRects; for ( ; box < last ; ++box) { *cur++ = site->screen; *cur++ = site->site_id; #ifdef PROXY /* if the receiver is motif then let event window be proxy win */ if (site->flags & MOTIF_RECEIVER_FLAG) *cur++ = proxy_win; else *cur++ = site->window_id; #else *cur++ = site->window_id; #endif *cur++ = box->x1; *cur++ = box->y1; *cur++ = box->x2 - box->x1; *cur++ = box->y2 - box->y1; *cur++ = site->flags; } site = site->next; } XChangeProperty(dpy, win, prop, XA_INTEGER, 32, PropModeReplace, (unsigned char *)array, cur - array); XFree((XFreeDefn)array); } /* end of WriteSiteRectList */ /* * Ignore BadWindow and BadDrawable errors on a variety of requests. These * errors often occur if the requester window goes away after requesting the * site database. REMIND: more robust error handling is called for. */ int ErrorHandler(Display *dpy, XErrorEvent *error) { if ( (error->error_code == BadWindow || error->error_code == BadDrawable) && (error->request_code == X_GetWindowAttributes || error->request_code == X_ChangeWindowAttributes || error->request_code == X_GetGeometry || error->request_code == X_QueryTree || error->request_code == X_ChangeProperty || error->request_code == X_GetProperty || error->request_code == X_SendEvent)) { DPRINTF(("ignored BadWindow error on request %d\n", error->request_code)); return 0; } fputs("dsdm: ", stderr); (*DefaultErrorHandler)(dpy, error); exit(1); /*NOTREACHED*/ } /* end of ErrorHandler */ int main(int argc, char **argv) { enum { XA_SUN_DRAGDROP_DSDM, XA_SUN_DRAGDROP_INTEREST, XA_SUN_DRAGDROP_SITE_RECTS, XAWM_STATE, NUM_ATOMS }; static char *atom_names[] = { "_SUN_DRAGDROP_DSDM", "_SUN_DRAGDROP_INTEREST", "_SUN_DRAGDROP_SITE_RECTS", "WM_STATE" }; Display *dpy; Window selwin, win; XSetWindowAttributes attr; XEvent e; XEvent reply; int xflag = 0; char *slash = NULL; char *cp; char call_proxy_main; Atom atoms[NUM_ATOMS]; cp = argv[0]; while (*cp != '\0') { if (*cp == '/') slash = cp; ++cp; } if (slash != NULL) ProgramName = slash + 1; else ProgramName = argv[0]; dpy = XOpenDisplay(NULL); if (dpy == NULL) { fprintf(stderr, "%s: can't open display %s\n", ProgramName, XDisplayName(NULL)); exit(1); } DefaultErrorHandler = XSetErrorHandler(ErrorHandler); XInternAtoms(dpy, atom_names, NUM_ATOMS, False, atoms); ATOM_DRAGDROP_DSDM = atoms[XA_SUN_DRAGDROP_DSDM]; ATOM_DRAGDROP_INTEREST = atoms[XA_SUN_DRAGDROP_INTEREST]; ATOM_SITE_RECTS = atoms[XA_SUN_DRAGDROP_SITE_RECTS]; ATOM_WM_STATE = atoms[XAWM_STATE]; attr.event_mask = PropertyChangeMask; selwin = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, CWEventMask, &attr); if (argc > 1 && 0 == strcmp(argv[1], "-x")) xflag = 1; XGrabServer(dpy); win = XGetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM); if (xflag) { if (win == None) { fprintf(stderr, "%s: no DSDM is running\n", ProgramName); exit(1); } /* Clear any DSDM selection to force the running DSDM to exit */ XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, None, CurrentTime); XFlush(dpy); exit(0); } if (win != None) { fprintf(stderr, "%s: another DSDM is already running\n", ProgramName); exit(1); } #ifdef PROXY ProxyInit(dpy, selwin); #endif XSetSelectionOwner(dpy, ATOM_DRAGDROP_DSDM, selwin, CurrentTime); /* no need to get owner per ICCCM, because we have the server grabbed */ XUngrabServer(dpy); XFlush(dpy); /* An attempt to minimize contention at startup: in the olinitrc file, DO * NOT start the window manager or the file manager in the background. This * way, the initialization code for the two applications is run sequentially. */ if (argc == 1) if (fork() != 0) exit(0); /* on parent success or failure */ for (;;) { call_proxy_main = True; XNextEvent(dpy, &e); switch (e.type) { case SelectionRequest: if (e.xselectionrequest.selection != ATOM_DRAGDROP_DSDM) { #ifndef PROXY DPRINTF(("%s: got SelectionRequest on wrong selection?\n", ProgramName)); #endif break; } if (e.xselectionrequest.owner != selwin) { fprintf(stderr, "%s: got SelectionRequest on wrong window?\n", ProgramName); break; } reply.xselection.display = e.xselectionrequest.display; reply.xselection.type = SelectionNotify; reply.xselection.requestor = e.xselectionrequest.requestor; reply.xselection.selection = ATOM_DRAGDROP_DSDM; reply.xselection.target = e.xselectionrequest.target; reply.xselection.time = e.xselectionrequest.time; if (e.xselectionrequest.target != ATOM_SITE_RECTS) { fprintf(stderr, "%s: got SelectionRequest for unknown target\n", ProgramName); reply.xselection.property = None; } else { DPRINTF(("%s: got SelectionRequest event OK\n", ProgramName)); FreeDropSites(); FindDropSites(dpy); WriteSiteRectList(dpy, e.xselectionrequest.requestor, e.xselectionrequest.property); reply.xselection.property = e.xselectionrequest.property; } (void) XSendEvent(dpy, reply.xselection.requestor, False, 0, &reply); call_proxy_main = False; break; case SelectionClear: exit(0); } #ifdef PROXY if (call_proxy_main) ProxyMain(dpy, &e); #endif } } /* end of main */