2 * Wine Clipboard Server
4 * Copyright 1999 Noel Borthwick
7 * This file contains the implementation for the Clipboard server
13 #include <X11/Xutil.h>
15 #include <X11/Xatom.h>
18 /* Lightweight debug definitions */
20 #define __DPRINTF(dbname) (printf("%s:%s:%s ", dbname, progname, __FUNCTION__),0) ? 0 : printf
21 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
24 #define TRACE __DPRINTF("TRACE")
26 #define TRACE __DUMMY_DPRINTF
27 #endif /* NO_TRACE_MSGS */
30 #define WARN __DPRINTF("WARN")
31 #define FIXME __DPRINTF("FIXME")
33 #define WARN __DUMMY_DPRINTF
34 #define FIXME __DUMMY_DPRINTF
35 #endif /* NO_DEBUG_MSGS */
37 #define ERR __DPRINTF("ERROR")
46 #define S_NOSELECTION 0
55 static Display *g_display = NULL;
56 static int screen_num;
57 static char *progname; /* name this program was invoked by */
58 static Window g_win = 0; /* the hidden clipboard server window */
61 static char *g_szOutOfMemory = "Insufficient memory!\n";
63 /* X selection context info */
64 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
65 static char FMT_PREFIX[] = "<WCF>"; /* Prefix for windows specific formats */
66 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
67 static int g_selectionAcquired = 0; /* Contains the current selection masks */
70 typedef struct tag_CACHEENTRY
77 } CACHEENTRY, *PCACHEENTRY;
79 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
80 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
81 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
82 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
85 static const char * const event_names[] =
87 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
88 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
89 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
90 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
91 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
92 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
93 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
94 "ClientMessage", "MappingNotify"
102 BOOL Init(int argc, char **argv);
103 void TerminateServer( int ret );
104 int AcquireSelection();
105 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
106 void EmptyCache(PCACHEENTRY pCache, int nItems);
107 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
108 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
109 void EVENT_ProcessEvent( XEvent *event );
110 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
111 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
112 void EVENT_SelectionClear( XSelectionClearEvent *event );
113 void EVENT_PropertyNotify( XPropertyEvent *event );
114 Pixmap DuplicatePixmap(Pixmap pixmap);
115 void TextOut(Window win, GC gc, char *pStr);
116 void getGC(Window win, GC *gc);
119 void main(int argc, char **argv)
122 unsigned int width, height; /* window size */
124 if ( !Init(argc, argv) )
127 /* Acquire the selection after retrieving all clipboard data
128 * owned by the current selection owner. If we were unable to
129 * Acquire any selection, terminate right away.
131 if ( AcquireSelection() == S_NOSELECTION )
134 /* Start an X event loop */
137 XNextEvent(g_display, &event);
139 EVENT_ProcessEvent( &event );
144 /**************************************************************************
146 * Initialize the clipboard server
148 BOOL Init(int argc, char **argv)
150 unsigned int width, height; /* window size */
151 unsigned int border_width = 4; /* four pixels */
152 unsigned int display_width, display_height;
153 char *window_name = "Wine Clipboard Server";
154 XSizeHints *size_hints = NULL;
155 XWMHints *wm_hints = NULL;
156 XClassHint *class_hints = NULL;
157 XTextProperty windowName;
158 char *display_name = NULL;
162 if (!(size_hints = XAllocSizeHints()))
164 ERR(g_szOutOfMemory);
167 if (!(wm_hints = XAllocWMHints()))
169 ERR(g_szOutOfMemory);
172 if (!(class_hints = XAllocClassHint()))
174 ERR(g_szOutOfMemory);
178 /* connect to X server */
179 if ( (g_display=XOpenDisplay(display_name)) == NULL )
181 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
185 /* get screen size from display structure macro */
186 screen_num = DefaultScreen(g_display);
187 display_width = DisplayWidth(g_display, screen_num);
188 display_height = DisplayHeight(g_display, screen_num);
190 /* size window with enough room for text */
191 width = display_width/3, height = display_height/4;
193 /* create opaque window */
194 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
195 0, 0, width, height, border_width, BlackPixel(g_display,
196 screen_num), WhitePixel(g_display,screen_num));
199 /* Set size hints for window manager. The window manager may
200 * override these settings. */
202 /* x, y, width, and height hints are now taken from
203 * the actual settings of the window when mapped. Note
204 * that PPosition and PSize must be specified anyway. */
206 size_hints->flags = PPosition | PSize | PMinSize;
207 size_hints->min_width = 300;
208 size_hints->min_height = 200;
210 /* These calls store window_name into XTextProperty structures
211 * and sets the other fields properly. */
212 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
214 ERR( "structure allocation for windowName failed.\n");
218 wm_hints->initial_state = NormalState;
219 wm_hints->input = True;
220 wm_hints->flags = StateHint | InputHint;
222 class_hints->res_name = progname;
223 class_hints->res_class = "WineClipSrv";
225 XSetWMProperties(g_display, g_win, &windowName, NULL,
226 argv, argc, size_hints, wm_hints,
229 /* Select event types wanted */
230 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
231 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
233 /* create GC for text and drawing */
237 /* XMapWindow(g_display, g_win); */
239 /* Set the selections to be acquired from the command line argument.
240 * If none specified, default to all selections we understand.
243 g_selectionToAcquire = atoi(argv[1]);
245 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
247 TRACE("Clipboard server running...\n");
251 /**************************************************************************
254 void TerminateServer( int ret )
256 TRACE("Terminating Wine clipboard server...\n");
258 /* Free Primary and Clipboard selection caches */
259 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
260 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
263 XFreeGC(g_display, g_gc);
266 XCloseDisplay(g_display);
272 /**************************************************************************
275 * Acquire the selection after retrieving all clipboard data owned by
276 * the current selection owner.
278 int AcquireSelection()
280 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
283 * For all selections we need to acquire, get a list of all targets
284 * supplied by the current selection owner.
286 if (g_selectionToAcquire & S_PRIMARY)
288 TRACE("Acquiring PRIMARY selection...\n");
289 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
290 if (g_cPrimaryTargets)
291 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
293 TRACE("No PRIMARY targets - ownership not acquired.\n");
295 if (g_selectionToAcquire & S_CLIPBOARD)
297 TRACE("Acquiring CLIPBOARD selection...\n");
298 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
300 if (g_cClipboardTargets)
301 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
303 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
306 /* Remember the acquired selections */
307 if( XGetSelectionOwner(g_display,XA_PRIMARY) == g_win )
308 g_selectionAcquired |= S_PRIMARY;
309 if( XGetSelectionOwner(g_display,xaClipboard) == g_win )
310 g_selectionAcquired |= S_CLIPBOARD;
312 return g_selectionAcquired;
315 /**************************************************************************
318 * Allocates and caches the list of data formats available from the current selection.
319 * This queries the selection owner for the TARGETS property and saves all
320 * reported property types.
322 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
326 Atom atype=AnyPropertyType;
328 unsigned long remain;
329 unsigned long cSelectionTargets = 0;
330 Atom* targetList=NULL;
331 Window ownerSelection = 0;
337 /* Get the selection owner */
338 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
339 if ( ownerSelection == None )
340 return cSelectionTargets;
343 * Query the selection owner for the TARGETS property
345 aTargets = XInternAtom(g_display, "TARGETS", False);
347 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
348 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
350 XConvertSelection(g_display, SelectionSrc, aTargets,
351 XInternAtom(g_display, "SELECTION_DATA", False),
355 * Wait until SelectionNotify is received
359 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
360 if( xe.xselection.selection == SelectionSrc )
364 /* Verify that the selection returned a valid TARGETS property */
365 if ( (xe.xselection.target != aTargets)
366 || (xe.xselection.property == None) )
368 TRACE("\tCould not retrieve TARGETS\n");
369 return cSelectionTargets;
372 /* Read the TARGETS property contents */
373 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
374 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
375 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
376 TRACE("\tCouldn't read TARGETS property\n");
379 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
380 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
382 * The TARGETS property should have returned us a list of atoms
383 * corresponding to each selection target format supported.
385 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
389 /* Allocate the selection cache */
390 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
392 /* Cache these formats in the selection cache */
393 for (i = 0; i < cSelectionTargets; i++)
395 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
397 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
399 /* Populate the cache entry */
400 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
401 ERR("Failed to fill cache entry!");
407 /* Free the list of targets */
411 return cSelectionTargets;
414 /***********************************************************************
417 * Populates the specified cache entry
419 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
424 Atom atype=AnyPropertyType;
426 unsigned long nitems,remain,itemSize;
428 unsigned char* val=NULL;
431 TRACE("Requesting %s selection from %s...\n",
432 XGetAtomName(g_display, target),
433 XGetAtomName(g_display, SelectionSrc) );
435 /* Ask the selection owner to convert the selection to the target format */
436 XConvertSelection(g_display, SelectionSrc, target,
437 XInternAtom(g_display, "SELECTION_DATA", False),
440 /* wait until SelectionNotify is received */
443 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
444 if( xe.xselection.selection == SelectionSrc )
448 /* Now proceed to retrieve the actual converted property from
449 * the SELECTION_DATA atom */
451 w = xe.xselection.requestor;
452 prop = xe.xselection.property;
453 reqType = xe.xselection.target;
458 TRACE("\tretrieving property %s from window %ld into %s\n",
459 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
462 * First request a zero length in order to figure out the request size.
464 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
465 &atype, &aformat, &nitems, &itemSize, &val) != Success)
467 WARN("\tcouldn't get property size\n");
471 /* Free zero length return data if any */
478 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
479 lRequestLength = (itemSize * aformat/8)/4 + 1;
482 * Retrieve the actual property in the required X format.
484 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
485 &atype, &aformat, &nitems, &remain, &val) != Success)
487 WARN("\tcouldn't read property\n");
491 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
492 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
496 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
501 * Populate the cache entry
503 pCacheEntry->target = target;
504 pCacheEntry->type = atype;
505 pCacheEntry->nFormat = aformat;
506 pCacheEntry->nElements = nitems;
508 if (atype == XA_PIXMAP)
510 Pixmap *pPixmap = (Pixmap *)val;
511 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
512 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
513 *pPixmap = newPixmap;
514 pCacheEntry->pData = pPixmap;
517 pCacheEntry->pData = val;
520 /* Delete the property on the window now that we are done
521 * This will send a PropertyNotify event to the selection owner. */
522 XDeleteProperty(g_display,w,prop);
528 /***********************************************************************
531 * Lookup a target atom in the cache and get the matching cache entry
533 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
536 int nCachetargets = 0;
537 PCACHEENTRY pCache = NULL;
538 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
540 /* Locate the cache to be used based on the selection type */
541 if ( selection == XA_PRIMARY )
543 pCache = g_pPrimaryCache;
544 nCachetargets = g_cPrimaryTargets;
546 else if ( selection == xaClipboard )
548 pCache = g_pClipboardCache;
549 nCachetargets = g_cClipboardTargets;
552 if (!pCache || !ppCacheEntry)
555 *ppCacheEntry = NULL;
557 /* Look for the target item in the cache */
558 for (i = 0; i < nCachetargets; i++)
560 if (pCache[i].target == target)
562 *ppCacheEntry = &pCache[i];
571 /***********************************************************************
574 * Empties the specified cache
576 void EmptyCache(PCACHEENTRY pCache, int nItems)
583 /* Release all items in the cache */
584 for (i = 0; i < nItems; i++)
586 if (pCache[i].target && pCache[i].pData)
588 /* If we have a Pixmap, free it first */
589 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
591 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
593 TRACE("Freeing %s (handle=%ld)...\n",
594 XGetAtomName(g_display, pCache[i].target), *pPixmap);
596 XFreePixmap(g_display, *pPixmap);
598 /* Free the cached data item (allocated by us) */
599 free(pCache[i].pData);
603 TRACE("Freeing %s (0x%x)...\n",
604 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
606 /* Free the cached data item (allocated by X) */
607 XFree(pCache[i].pData);
612 /* Destroy the cache */
617 /***********************************************************************
620 * Process an X event.
622 void EVENT_ProcessEvent( XEvent *event )
624 // TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
629 /* don't draw the window */
630 if (event->xexpose.count != 0)
633 /* Output something */
634 TextOut(g_win, g_gc, "Click here to terminate");
637 case ConfigureNotify:
641 /* fall into KeyPress (no break) */
646 case SelectionRequest:
647 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
651 EVENT_SelectionClear( (XSelectionClearEvent*)event );
655 EVENT_PropertyNotify( (XPropertyEvent *)event );
658 default: /* ignore all other events */
666 /***********************************************************************
667 * EVENT_SelectionRequest_MULTIPLE
668 * Service a MULTIPLE selection request event
669 * rprop contains a list of (target,property) atom pairs.
670 * The first atom names a target and the second names a property.
671 * The effect is as if we have received a sequence of SelectionRequest events
672 * (one for each atom pair) except that:
673 * 1. We reply with a SelectionNotify only when all the requested conversions
674 * have been performed.
675 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
676 * we replace the atom in the property by None.
678 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
681 Atom atype=AnyPropertyType;
683 unsigned long remain;
684 Atom* targetPropList=NULL;
685 unsigned long cTargetPropList = 0;
686 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
688 /* If the specified property is None the requestor is an obsolete client.
689 * We support these by using the specified target atom as the reply property.
691 rprop = pevent->property;
693 rprop = pevent->target;
697 /* Read the MULTIPLE property contents. This should contain a list of
698 * (target,property) atom pairs.
700 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
701 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
702 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
703 TRACE("\tCouldn't read MULTIPLE property\n");
706 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
707 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
710 * Make sure we got what we expect.
711 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
712 * in a MULTIPLE selection request should be of type ATOM_PAIR.
713 * However some X apps(such as XPaint) are not compliant with this and return
714 * a user defined atom in atype when XGetWindowProperty is called.
715 * The data *is* an atom pair but is not denoted as such.
717 if(aformat == 32 /* atype == xAtomPair */ )
721 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
722 * for each (target,property) pair */
724 for (i = 0; i < cTargetPropList; i+=2)
726 char *targetName = XGetAtomName(g_display, targetPropList[i]);
727 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
728 XSelectionRequestEvent event;
730 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
734 /* We must have a non "None" property to service a MULTIPLE target atom */
735 if ( !targetPropList[i+1] )
737 TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
741 /* Set up an XSelectionRequestEvent for this (target,property) pair */
742 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
743 event.target = targetPropList[i];
744 event.property = targetPropList[i+1];
746 /* Fire a SelectionRequest, informing the handler that we are processing
747 * a MULTIPLE selection request event.
749 EVENT_SelectionRequest( &event, TRUE );
753 /* Free the list of targets/properties */
754 XFree(targetPropList);
762 /***********************************************************************
763 * EVENT_SelectionRequest
764 * Process an event selection request event.
765 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
766 * recursively while servicing a "MULTIPLE" selection target.
769 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
771 XSelectionEvent result;
773 Window request = event->requestor;
774 BOOL couldOpen = FALSE;
775 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
776 PCACHEENTRY pCacheEntry = NULL;
780 /* If the specified property is None the requestor is an obsolete client.
781 * We support these by using the specified target atom as the reply property.
783 rprop = event->property;
785 rprop = event->target;
787 TRACE("Request for %s in selection %s\n",
788 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
790 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
791 if(event->target == xaMultiple)
793 /* MULTIPLE selection request - will call us back recursively */
794 rprop = EVENT_SelectionRequest_MULTIPLE( event );
798 /* Lookup the requested target property in the cache */
799 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
801 TRACE("Item not available in cache!\n");
805 /* Update the X property */
806 TRACE("\tUpdating property %s...", XGetAtomName(g_display, rprop));
808 /* If we have a request for a pixmap, return a duplicate */
810 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
812 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
813 pixmap = DuplicatePixmap( *pPixmap );
817 pData = pCacheEntry->pData;
819 XChangeProperty(g_display, request, rprop,
820 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
821 (unsigned char *)pData, pCacheEntry->nElements);
825 TRACE("\tRequest ignored\n");
828 * SelectionNotify should be sent only at the end of a MULTIPLE request
832 result.type = SelectionNotify;
833 result.display = g_display;
834 result.requestor = request;
835 result.selection = event->selection;
836 result.property = rprop;
837 result.target = event->target;
838 result.time = event->time;
839 TRACE("Sending SelectionNotify event...\n");
840 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
845 /***********************************************************************
846 * EVENT_SelectionClear
847 * We receive this event when another client grabs the X selection.
848 * If we lost both PRIMARY and CLIPBOARD we must terminate.
850 void EVENT_SelectionClear( XSelectionClearEvent *event )
852 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
856 if (event->selection == XA_PRIMARY)
858 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
859 TRACE("Lost PRIMARY selection...\n");
861 else if (event->selection == xaClipboard)
863 g_selectionAcquired &= ~S_CLIPBOARD; /* Clear the CLIPBOARD flag */
864 TRACE("Lost CLIPBOARD selection...\n");
867 /* Once we lose all our selections we have nothing more to do */
868 if (g_selectionAcquired == S_NOSELECTION)
872 /***********************************************************************
873 * EVENT_PropertyNotify
874 * We use this to release resources like Pixmaps when a selection
875 * client no longer needs them.
877 void EVENT_PropertyNotify( XPropertyEvent *event )
881 /* Check if we have any resources to free */
887 TRACE("\tPropertyDelete for atom %s on window %ld\n",
888 XGetAtomName(event->display, event->atom), (long)event->window);
890 /* FreeResources( event->atom ); */
894 case PropertyNewValue:
896 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
897 XGetAtomName(event->display, event->atom), (long)event->window);
906 /***********************************************************************
909 Pixmap DuplicatePixmap(Pixmap pixmap)
914 int x,y; /* Unused */
915 unsigned border_width; /* Unused */
916 unsigned int depth, width, height;
918 TRACE("\t() Pixmap=%ul\n", pixmap);
920 /* Get the Pixmap dimensions and bit depth */
921 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
922 &border_width, &depth) )
925 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
926 width, height, depth);
928 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
930 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
932 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
936 TRACE("\t() New Pixmap=%ul\n", newPixmap);
940 /***********************************************************************
942 * Get a GC to use for drawing
944 void getGC(Window win, GC *gc)
946 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
948 unsigned int line_width = 6;
949 int line_style = LineOnOffDash;
950 int cap_style = CapRound;
951 int join_style = JoinRound;
953 static char dash_list[] = {12, 24};
956 /* Create default Graphics Context */
957 *gc = XCreateGC(g_display, win, valuemask, &values);
959 /* specify black foreground since default window background is
960 * white and default foreground is undefined. */
961 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
963 /* set line attributes */
964 XSetLineAttributes(g_display, *gc, line_width, line_style,
965 cap_style, join_style);
968 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
972 /***********************************************************************
975 void TextOut(Window win, GC gc, char *pStr)
977 int y_offset, x_offset;
982 /* output text, centered on each line */
983 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,