2 * Wine Clipboard Server
4 * Copyright 1999 Noel Borthwick
7 * wineclipsrv [selection_mask] [debugClass_mask] [clearAllSelections]
9 * The optional selection-mask argument is a bit mask of the selection
10 * types to be acquired. Currently two selections are supported:
11 * 1. PRIMARY (mask value 1)
12 * 2. CLIPBOARD (mask value 2).
14 * debugClass_mask is a bit mask of all debugging classes for which messages
15 * are to be output. The standard Wine debug class set FIXME(1), ERR(2),
16 * WARN(4) and TRACE(8) are supported.
18 * If clearAllSelections == 1 *all* selections are lost whenever a SelectionClear
21 * If no arguments are supplied the server aquires all selections. (mask value 3)
22 * and defaults to output of only FIXME(1) and ERR(2) messages. The default for
23 * clearAllSelections is 0.
27 * The Wine Clipboard Server is a standalone XLib application whose
28 * purpose is to manage the X selection when Wine exits.
29 * The server itself is started automatically with the appropriate
30 * selection masks, whenever Wine exits after acquiring the PRIMARY and/or
31 * CLIPBOARD selection. (See X11DRV_CLIPBOARD_ResetOwner)
32 * When the server starts, it first proceeds to capture the selection data from
33 * Wine and then takes over the selection ownership. It does this by querying
34 * the current selection owner(of the specified selections) for the TARGETS
35 * selection target. It then proceeds to cache all the formats exposed by
36 * TARGETS. If the selection does not support the TARGETS target, or if no
37 * target formats are exposed, the server simply exits.
38 * Once the cache has been filled, the server then actually acquires ownership
39 * of the respective selection and begins fielding selection requests.
40 * Selection requests are serviced from the cache. If a selection is lost the
41 * server flushes its internal cache, destroying all data previously saved.
42 * Once ALL selections have been lost the server terminates.
50 #include <X11/Xutil.h>
52 #include <X11/Xatom.h>
57 * Lightweight debug definitions for Wine Clipboard Server.
58 * The standard FIXME, ERR, WARN & TRACE classes are supported
59 * without debug channels.
60 * The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
61 * TRACE, WARN and ERR and FIXME message displays.
64 /* Internal definitions (do not use these directly) */
66 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
68 extern char __debug_msg_enabled[__DBCL_COUNT];
70 extern const char * const debug_cl_name[__DBCL_COUNT];
72 #define DEBUG_CLASS_COUNT __DBCL_COUNT
74 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
75 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
78 #define __DPRINTF(dbcl) \
79 (!__GET_DEBUGGING(dbcl) || \
80 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
83 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
85 /* use configure to allow user to compile out debugging messages */
87 #define TRACE __DPRINTF(__DBCL_TRACE)
89 #define TRACE __DUMMY_DPRINTF
90 #endif /* NO_TRACE_MSGS */
93 #define WARN __DPRINTF(__DBCL_WARN)
94 #define FIXME __DPRINTF(__DBCL_FIXME)
96 #define WARN __DUMMY_DPRINTF
97 #define FIXME __DUMMY_DPRINTF
98 #endif /* NO_DEBUG_MSGS */
100 /* define error macro regardless of what is configured */
101 #define ERR __DPRINTF(__DBCL_ERR)
108 /* Internal definitions for debugging messages(do not use these directly) */
109 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
110 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
113 /* Selection masks */
115 #define S_NOSELECTION 0
117 #define S_CLIPBOARD 2
119 /* Debugging class masks */
130 static Display *g_display = NULL;
131 static int screen_num;
132 static char *progname; /* name this program was invoked by */
133 static Window g_win = 0; /* the hidden clipboard server window */
136 static char *g_szOutOfMemory = "Insufficient memory!\n";
138 /* X selection context info */
139 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
140 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
141 static int g_selectionAcquired = 0; /* Contains the current selection masks */
142 static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
144 /* Selection cache */
145 typedef struct tag_CACHEENTRY
152 } CACHEENTRY, *PCACHEENTRY;
154 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
155 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
156 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
157 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
160 static const char * const event_names[] =
162 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
163 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
164 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
165 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
166 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
167 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
168 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
169 "ClientMessage", "MappingNotify"
177 BOOL Init(int argc, char **argv);
178 void TerminateServer( int ret );
179 int AcquireSelection();
180 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
181 void EmptyCache(PCACHEENTRY pCache, int nItems);
182 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
183 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
184 void EVENT_ProcessEvent( XEvent *event );
185 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
186 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
187 void EVENT_SelectionClear( XSelectionClearEvent *event );
188 void EVENT_PropertyNotify( XPropertyEvent *event );
189 Pixmap DuplicatePixmap(Pixmap pixmap);
190 void TextOut(Window win, GC gc, char *pStr);
191 void getGC(Window win, GC *gc);
194 int main(int argc, char **argv)
198 if ( !Init(argc, argv) )
201 /* Acquire the selection after retrieving all clipboard data
202 * owned by the current selection owner. If we were unable to
203 * Acquire any selection, terminate right away.
205 if ( AcquireSelection() == S_NOSELECTION )
208 TRACE("Clipboard server running...\n");
210 /* Start an X event loop */
213 XNextEvent(g_display, &event);
215 EVENT_ProcessEvent( &event );
220 /**************************************************************************
222 * Initialize the clipboard server
224 BOOL Init(int argc, char **argv)
226 unsigned int width, height; /* window size */
227 unsigned int border_width = 4; /* four pixels */
228 unsigned int display_width, display_height;
229 char *window_name = "Wine Clipboard Server";
230 XSizeHints *size_hints = NULL;
231 XWMHints *wm_hints = NULL;
232 XClassHint *class_hints = NULL;
233 XTextProperty windowName;
234 char *display_name = NULL;
238 if (!(size_hints = XAllocSizeHints()))
240 ERR(g_szOutOfMemory);
243 if (!(wm_hints = XAllocWMHints()))
245 ERR(g_szOutOfMemory);
248 if (!(class_hints = XAllocClassHint()))
250 ERR(g_szOutOfMemory);
254 /* connect to X server */
255 if ( (g_display=XOpenDisplay(display_name)) == NULL )
257 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
261 /* get screen size from display structure macro */
262 screen_num = DefaultScreen(g_display);
263 display_width = DisplayWidth(g_display, screen_num);
264 display_height = DisplayHeight(g_display, screen_num);
266 /* size window with enough room for text */
267 width = display_width/3, height = display_height/4;
269 /* create opaque window */
270 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
271 0, 0, width, height, border_width, BlackPixel(g_display,
272 screen_num), WhitePixel(g_display,screen_num));
275 /* Set size hints for window manager. The window manager may
276 * override these settings. */
278 /* x, y, width, and height hints are now taken from
279 * the actual settings of the window when mapped. Note
280 * that PPosition and PSize must be specified anyway. */
282 size_hints->flags = PPosition | PSize | PMinSize;
283 size_hints->min_width = 300;
284 size_hints->min_height = 200;
286 /* These calls store window_name into XTextProperty structures
287 * and sets the other fields properly. */
288 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
290 ERR( "structure allocation for windowName failed.\n");
294 wm_hints->initial_state = NormalState;
295 wm_hints->input = True;
296 wm_hints->flags = StateHint | InputHint;
298 class_hints->res_name = progname;
299 class_hints->res_class = "WineClipSrv";
301 XSetWMProperties(g_display, g_win, &windowName, NULL,
302 argv, argc, size_hints, wm_hints,
305 /* Select event types wanted */
306 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
307 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
309 /* create GC for text and drawing */
313 /* XMapWindow(g_display, g_win); */
315 /* Set the selections to be acquired from the command line argument.
316 * If none specified, default to all selections we understand.
319 g_selectionToAcquire = atoi(argv[1]);
321 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
323 /* Set the debugging class state from the command line argument */
326 int dbgClasses = atoi(argv[2]);
328 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
329 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
330 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
331 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
334 /* Set the "ClearSelections" state from the command line argument */
336 g_clearAllSelections = atoi(argv[3]);
342 /**************************************************************************
345 void TerminateServer( int ret )
347 TRACE("Terminating Wine clipboard server...\n");
349 /* Free Primary and Clipboard selection caches */
350 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
351 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
354 XFreeGC(g_display, g_gc);
357 XCloseDisplay(g_display);
363 /**************************************************************************
366 * Acquire the selection after retrieving all clipboard data owned by
367 * the current selection owner.
369 int AcquireSelection()
371 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
374 * For all selections we need to acquire, get a list of all targets
375 * supplied by the current selection owner.
377 if (g_selectionToAcquire & S_PRIMARY)
379 TRACE("Acquiring PRIMARY selection...\n");
380 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
381 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
383 if (g_selectionToAcquire & S_CLIPBOARD)
385 TRACE("Acquiring CLIPBOARD selection...\n");
386 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
387 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
391 * Now that we have cached the data, we proceed to acquire the selections
393 if (g_cPrimaryTargets)
395 /* Acquire the PRIMARY selection */
396 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
397 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
399 g_selectionAcquired |= S_PRIMARY;
402 TRACE("No PRIMARY targets - ownership not acquired.\n");
404 if (g_cClipboardTargets)
406 /* Acquire the CLIPBOARD selection */
407 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
408 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
410 g_selectionAcquired |= S_CLIPBOARD;
413 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
415 return g_selectionAcquired;
418 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
422 /* Set up a 10 second time out */
423 end_time=time(NULL)+10;
429 if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
431 if( xe->xselection.selection == SelectionSrc )
435 if (time(NULL)>end_time)
438 /* Sleep a bit to make this busy wait less brutal */
441 select(0, NULL, NULL, NULL, &nap);
448 /**************************************************************************
451 * Allocates and caches the list of data formats available from the current selection.
452 * This queries the selection owner for the TARGETS property and saves all
453 * reported property types.
455 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
459 Atom atype=AnyPropertyType;
461 unsigned long remain;
462 unsigned long cSelectionTargets = 0;
463 Atom* targetList=NULL;
464 Window ownerSelection = 0;
470 /* Get the selection owner */
471 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
472 if ( ownerSelection == None )
473 return cSelectionTargets;
476 * Query the selection owner for the TARGETS property
478 aTargets = XInternAtom(g_display, "TARGETS", False);
480 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
481 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
483 XConvertSelection(g_display, SelectionSrc, aTargets,
484 XInternAtom(g_display, "SELECTION_DATA", False),
488 * Wait until SelectionNotify is received
490 if (!GetSelectionEvent(SelectionSrc, &xe))
493 /* Verify that the selection returned a valid TARGETS property */
494 if ( (xe.xselection.target != aTargets)
495 || (xe.xselection.property == None) )
497 TRACE("\tCould not retrieve TARGETS\n");
498 return cSelectionTargets;
501 /* Read the TARGETS property contents */
502 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
503 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
504 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
505 TRACE("\tCouldn't read TARGETS property\n");
508 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
509 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
511 * The TARGETS property should have returned us a list of atoms
512 * corresponding to each selection target format supported.
514 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
518 /* Allocate the selection cache */
519 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
521 /* Cache these formats in the selection cache */
522 for (i = 0; i < cSelectionTargets; i++)
524 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
526 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
528 /* Populate the cache entry */
529 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
530 ERR("Failed to fill cache entry!\n");
536 /* Free the list of targets */
540 return cSelectionTargets;
543 /***********************************************************************
546 * Populates the specified cache entry
548 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
553 Atom atype=AnyPropertyType;
555 unsigned long nitems,remain,itemSize;
557 unsigned char* val=NULL;
560 TRACE("Requesting %s selection from %s...\n",
561 XGetAtomName(g_display, target),
562 XGetAtomName(g_display, SelectionSrc) );
564 /* Ask the selection owner to convert the selection to the target format */
565 XConvertSelection(g_display, SelectionSrc, target,
566 XInternAtom(g_display, "SELECTION_DATA", False),
569 /* wait until SelectionNotify is received */
570 if (!GetSelectionEvent(SelectionSrc,&xe))
573 /* Now proceed to retrieve the actual converted property from
574 * the SELECTION_DATA atom */
576 w = xe.xselection.requestor;
577 prop = xe.xselection.property;
578 reqType = xe.xselection.target;
582 TRACE("\tOwner failed to convert selection!\n");
586 TRACE("\tretrieving property %s from window %ld into %s\n",
587 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
590 * First request a zero length in order to figure out the request size.
592 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
593 &atype, &aformat, &nitems, &itemSize, &val) != Success)
595 WARN("\tcouldn't get property size\n");
599 /* Free zero length return data if any */
606 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
607 lRequestLength = (itemSize * aformat/8)/4 + 1;
610 * Retrieve the actual property in the required X format.
612 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
613 &atype, &aformat, &nitems, &remain, &val) != Success)
615 WARN("\tcouldn't read property\n");
619 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
620 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
624 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
629 * Populate the cache entry
631 pCacheEntry->target = target;
632 pCacheEntry->type = atype;
633 pCacheEntry->nFormat = aformat;
634 pCacheEntry->nElements = nitems;
636 if (atype == XA_PIXMAP)
638 Pixmap *pPixmap = (Pixmap *)val;
639 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
640 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
641 *pPixmap = newPixmap;
642 pCacheEntry->pData = pPixmap;
645 pCacheEntry->pData = val;
648 /* Delete the property on the window now that we are done
649 * This will send a PropertyNotify event to the selection owner. */
650 XDeleteProperty(g_display,w,prop);
656 /***********************************************************************
659 * Lookup a target atom in the cache and get the matching cache entry
661 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
664 int nCachetargets = 0;
665 PCACHEENTRY pCache = NULL;
666 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
668 /* Locate the cache to be used based on the selection type */
669 if ( selection == XA_PRIMARY )
671 pCache = g_pPrimaryCache;
672 nCachetargets = g_cPrimaryTargets;
674 else if ( selection == xaClipboard )
676 pCache = g_pClipboardCache;
677 nCachetargets = g_cClipboardTargets;
680 if (!pCache || !ppCacheEntry)
683 *ppCacheEntry = NULL;
685 /* Look for the target item in the cache */
686 for (i = 0; i < nCachetargets; i++)
688 if (pCache[i].target == target)
690 *ppCacheEntry = &pCache[i];
699 /***********************************************************************
702 * Empties the specified cache
704 void EmptyCache(PCACHEENTRY pCache, int nItems)
711 /* Release all items in the cache */
712 for (i = 0; i < nItems; i++)
714 if (pCache[i].target && pCache[i].pData)
716 /* If we have a Pixmap, free it first */
717 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
719 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
721 TRACE("Freeing %s (handle=%ld)...\n",
722 XGetAtomName(g_display, pCache[i].target), *pPixmap);
724 XFreePixmap(g_display, *pPixmap);
726 /* Free the cached data item (allocated by us) */
727 free(pCache[i].pData);
731 TRACE("Freeing %s (%p)...\n",
732 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
734 /* Free the cached data item (allocated by X) */
735 XFree(pCache[i].pData);
740 /* Destroy the cache */
745 /***********************************************************************
748 * Process an X event.
750 void EVENT_ProcessEvent( XEvent *event )
753 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
759 /* don't draw the window */
760 if (event->xexpose.count != 0)
763 /* Output something */
764 TextOut(g_win, g_gc, "Click here to terminate");
767 case ConfigureNotify:
771 /* fall into KeyPress (no break) */
776 case SelectionRequest:
777 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
781 EVENT_SelectionClear( (XSelectionClearEvent*)event );
785 // EVENT_PropertyNotify( (XPropertyEvent *)event );
788 default: /* ignore all other events */
796 /***********************************************************************
797 * EVENT_SelectionRequest_MULTIPLE
798 * Service a MULTIPLE selection request event
799 * rprop contains a list of (target,property) atom pairs.
800 * The first atom names a target and the second names a property.
801 * The effect is as if we have received a sequence of SelectionRequest events
802 * (one for each atom pair) except that:
803 * 1. We reply with a SelectionNotify only when all the requested conversions
804 * have been performed.
805 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
806 * we replace the atom in the property by None.
808 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
811 Atom atype=AnyPropertyType;
813 unsigned long remain;
814 Atom* targetPropList=NULL;
815 unsigned long cTargetPropList = 0;
816 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
818 /* If the specified property is None the requestor is an obsolete client.
819 * We support these by using the specified target atom as the reply property.
821 rprop = pevent->property;
823 rprop = pevent->target;
827 /* Read the MULTIPLE property contents. This should contain a list of
828 * (target,property) atom pairs.
830 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
831 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
832 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
833 TRACE("\tCouldn't read MULTIPLE property\n");
836 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
837 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
840 * Make sure we got what we expect.
841 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
842 * in a MULTIPLE selection request should be of type ATOM_PAIR.
843 * However some X apps(such as XPaint) are not compliant with this and return
844 * a user defined atom in atype when XGetWindowProperty is called.
845 * The data *is* an atom pair but is not denoted as such.
847 if(aformat == 32 /* atype == xAtomPair */ )
851 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
852 * for each (target,property) pair */
854 for (i = 0; i < cTargetPropList; i+=2)
856 char *targetName = XGetAtomName(g_display, targetPropList[i]);
857 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
858 XSelectionRequestEvent event;
860 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
864 /* We must have a non "None" property to service a MULTIPLE target atom */
865 if ( !targetPropList[i+1] )
867 TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
871 /* Set up an XSelectionRequestEvent for this (target,property) pair */
872 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
873 event.target = targetPropList[i];
874 event.property = targetPropList[i+1];
876 /* Fire a SelectionRequest, informing the handler that we are processing
877 * a MULTIPLE selection request event.
879 EVENT_SelectionRequest( &event, TRUE );
883 /* Free the list of targets/properties */
884 XFree(targetPropList);
892 /***********************************************************************
893 * EVENT_SelectionRequest
894 * Process an event selection request event.
895 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
896 * recursively while servicing a "MULTIPLE" selection target.
899 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
901 XSelectionEvent result;
903 Window request = event->requestor;
904 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
905 PCACHEENTRY pCacheEntry = NULL;
909 /* If the specified property is None the requestor is an obsolete client.
910 * We support these by using the specified target atom as the reply property.
912 rprop = event->property;
914 rprop = event->target;
916 TRACE("Request for %s in selection %s\n",
917 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
919 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
920 if(event->target == xaMultiple)
922 /* MULTIPLE selection request - will call us back recursively */
923 rprop = EVENT_SelectionRequest_MULTIPLE( event );
927 /* Lookup the requested target property in the cache */
928 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
930 TRACE("Item not available in cache!\n");
934 /* Update the X property */
935 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
937 /* If we have a request for a pixmap, return a duplicate */
939 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
941 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
942 pixmap = DuplicatePixmap( *pPixmap );
946 pData = pCacheEntry->pData;
948 XChangeProperty(g_display, request, rprop,
949 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
950 (unsigned char *)pData, pCacheEntry->nElements);
954 TRACE("\tRequest ignored\n");
957 * SelectionNotify should be sent only at the end of a MULTIPLE request
961 result.type = SelectionNotify;
962 result.display = g_display;
963 result.requestor = request;
964 result.selection = event->selection;
965 result.property = rprop;
966 result.target = event->target;
967 result.time = event->time;
968 TRACE("Sending SelectionNotify event...\n");
969 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
974 /***********************************************************************
975 * EVENT_SelectionClear
976 * We receive this event when another client grabs the X selection.
977 * If we lost both PRIMARY and CLIPBOARD we must terminate.
979 void EVENT_SelectionClear( XSelectionClearEvent *event )
981 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
985 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
986 * dictate that *all* selections should be cleared on loss of a selection,
987 * we must give up all the selections we own.
989 if ( g_clearAllSelections || (event->selection == xaClipboard) )
991 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
993 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
994 if ( (event->selection == xaClipboard)
995 && (g_selectionAcquired & S_PRIMARY) )
997 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1000 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
1001 if ( (event->selection == XA_PRIMARY)
1002 && (g_selectionAcquired & S_CLIPBOARD) )
1004 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1007 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
1009 else if (event->selection == XA_PRIMARY)
1011 TRACE("Lost PRIMARY selection...\n");
1012 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
1015 /* Once we lose all our selections we have nothing more to do */
1016 if (g_selectionAcquired == S_NOSELECTION)
1020 /***********************************************************************
1021 * EVENT_PropertyNotify
1022 * We use this to release resources like Pixmaps when a selection
1023 * client no longer needs them.
1025 void EVENT_PropertyNotify( XPropertyEvent *event )
1029 /* Check if we have any resources to free */
1031 switch(event->state)
1033 case PropertyDelete:
1035 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1036 XGetAtomName(event->display, event->atom), (long)event->window);
1038 /* FreeResources( event->atom ); */
1042 case PropertyNewValue:
1044 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1045 XGetAtomName(event->display, event->atom), (long)event->window);
1054 /***********************************************************************
1057 Pixmap DuplicatePixmap(Pixmap pixmap)
1062 int x,y; /* Unused */
1063 unsigned border_width; /* Unused */
1064 unsigned int depth, width, height;
1066 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1068 /* Get the Pixmap dimensions and bit depth */
1069 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1070 &border_width, &depth) )
1073 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1074 width, height, depth);
1076 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1078 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1080 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1084 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1088 /***********************************************************************
1090 * Get a GC to use for drawing
1092 void getGC(Window win, GC *gc)
1094 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1096 unsigned int line_width = 6;
1097 int line_style = LineOnOffDash;
1098 int cap_style = CapRound;
1099 int join_style = JoinRound;
1100 int dash_offset = 0;
1101 static char dash_list[] = {12, 24};
1102 int list_length = 2;
1104 /* Create default Graphics Context */
1105 *gc = XCreateGC(g_display, win, valuemask, &values);
1107 /* specify black foreground since default window background is
1108 * white and default foreground is undefined. */
1109 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1111 /* set line attributes */
1112 XSetLineAttributes(g_display, *gc, line_width, line_style,
1113 cap_style, join_style);
1116 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1120 /***********************************************************************
1123 void TextOut(Window win, GC gc, char *pStr)
1125 int y_offset, x_offset;
1130 /* output text, centered on each line */
1131 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,