2 * Wine Clipboard Server
4 * Copyright 1999 Noel Borthwick
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * wineclipsrv [selection_mask] [debugClass_mask] [clearAllSelections]
23 * The optional selection-mask argument is a bit mask of the selection
24 * types to be acquired. Currently two selections are supported:
25 * 1. PRIMARY (mask value 1)
26 * 2. CLIPBOARD (mask value 2).
28 * debugClass_mask is a bit mask of all debugging classes for which messages
29 * are to be output. The standard Wine debug class set FIXME(1), ERR(2),
30 * WARN(4) and TRACE(8) are supported.
32 * If clearAllSelections == 1 *all* selections are lost whenever a SelectionClear
35 * If no arguments are supplied the server aquires all selections. (mask value 3)
36 * and defaults to output of only FIXME(1) and ERR(2) messages. The default for
37 * clearAllSelections is 0.
41 * The Wine Clipboard Server is a standalone XLib application whose
42 * purpose is to manage the X selection when Wine exits.
43 * The server itself is started automatically with the appropriate
44 * selection masks, whenever Wine exits after acquiring the PRIMARY and/or
45 * CLIPBOARD selection. (See X11DRV_CLIPBOARD_ResetOwner)
46 * When the server starts, it first proceeds to capture the selection data from
47 * Wine and then takes over the selection ownership. It does this by querying
48 * the current selection owner(of the specified selections) for the TARGETS
49 * selection target. It then proceeds to cache all the formats exposed by
50 * TARGETS. If the selection does not support the TARGETS target, or if no
51 * target formats are exposed, the server simply exits.
52 * Once the cache has been filled, the server then actually acquires ownership
53 * of the respective selection and begins fielding selection requests.
54 * Selection requests are serviced from the cache. If a selection is lost the
55 * server flushes its internal cache, destroying all data previously saved.
56 * Once ALL selections have been lost the server terminates.
66 #include <X11/Xutil.h>
68 #include <X11/Xatom.h>
71 * Lightweight debug definitions for Wine Clipboard Server.
72 * The standard FIXME, ERR, WARN & TRACE classes are supported
73 * without debug channels.
74 * The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
75 * TRACE, WARN and ERR and FIXME message displays.
78 /* Internal definitions (do not use these directly) */
81 #define __FUNCTION__ __func__
84 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
86 extern char __debug_msg_enabled[__DBCL_COUNT];
88 extern const char * const debug_cl_name[__DBCL_COUNT];
90 #define DEBUG_CLASS_COUNT __DBCL_COUNT
92 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
93 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
96 #define __DPRINTF(dbcl) \
97 (!__GET_DEBUGGING(dbcl) || \
98 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
101 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
103 /* use configure to allow user to compile out debugging messages */
104 #ifndef NO_TRACE_MSGS
105 #define TRACE __DPRINTF(__DBCL_TRACE)
107 #define TRACE __DUMMY_DPRINTF
108 #endif /* NO_TRACE_MSGS */
110 #ifndef NO_DEBUG_MSGS
111 #define WARN __DPRINTF(__DBCL_WARN)
112 #define FIXME __DPRINTF(__DBCL_FIXME)
114 #define WARN __DUMMY_DPRINTF
115 #define FIXME __DUMMY_DPRINTF
116 #endif /* NO_DEBUG_MSGS */
118 /* define error macro regardless of what is configured */
119 #define ERR __DPRINTF(__DBCL_ERR)
126 /* Internal definitions for debugging messages(do not use these directly) */
127 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
128 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
131 /* Selection masks */
133 #define S_NOSELECTION 0
135 #define S_CLIPBOARD 2
137 /* Debugging class masks */
148 static Display *g_display = NULL;
149 static int screen_num;
150 static char *progname; /* name this program was invoked by */
151 static Window g_win = 0; /* the hidden clipboard server window */
154 static char *g_szOutOfMemory = "Insufficient memory!\n";
156 /* X selection context info */
157 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
158 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
159 static int g_selectionAcquired = 0; /* Contains the current selection masks */
160 static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
162 /* Selection cache */
163 typedef struct tag_CACHEENTRY
170 } CACHEENTRY, *PCACHEENTRY;
172 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
173 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
174 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
175 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
178 static const char * const event_names[] =
180 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
181 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
182 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
183 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
184 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
185 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
186 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
187 "ClientMessage", "MappingNotify"
195 int RunAsDaemon( void );
196 BOOL Init(int argc, char **argv);
197 void TerminateServer( int ret );
198 int AcquireSelection();
199 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
200 void EmptyCache(PCACHEENTRY pCache, int nItems);
201 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
202 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
203 void EVENT_ProcessEvent( XEvent *event );
204 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
205 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
206 void EVENT_SelectionClear( XSelectionClearEvent *event );
207 void EVENT_PropertyNotify( XPropertyEvent *event );
208 Pixmap DuplicatePixmap(Pixmap pixmap);
209 void TextOut(Window win, GC gc, char *pStr);
210 void getGC(Window win, GC *gc);
213 int main(int argc, char **argv)
217 if ( RunAsDaemon() == -1 )
219 ERR("could not run as daemon\n");
223 if ( !Init(argc, argv) )
226 /* Acquire the selection after retrieving all clipboard data
227 * owned by the current selection owner. If we were unable to
228 * Acquire any selection, terminate right away.
230 if ( AcquireSelection() == S_NOSELECTION )
233 TRACE("Clipboard server running...\n");
235 /* Start an X event loop */
238 XNextEvent(g_display, &event);
240 EVENT_ProcessEvent( &event );
245 /**************************************************************************
248 int RunAsDaemon( void )
252 /* fork child process and let parent exit ; gets rid of original PID */
256 ERR("fork failed\n");
263 /* below is child process w/ new PID, set as session leader */
266 /* close stdin,stdout,stderr and file descriptors (overkill method) */
267 for ( i = 0; i < 256 ; i++ )
270 TRACE("now running as daemon...\n");
275 /**************************************************************************
277 * Initialize the clipboard server
279 BOOL Init(int argc, char **argv)
281 unsigned int width, height; /* window size */
282 unsigned int border_width = 4; /* four pixels */
283 unsigned int display_width, display_height;
284 char *window_name = "Wine Clipboard Server";
285 XSizeHints *size_hints = NULL;
286 XWMHints *wm_hints = NULL;
287 XClassHint *class_hints = NULL;
288 XTextProperty windowName;
289 char *display_name = NULL;
293 if (!(size_hints = XAllocSizeHints()))
295 ERR(g_szOutOfMemory);
298 if (!(wm_hints = XAllocWMHints()))
300 ERR(g_szOutOfMemory);
303 if (!(class_hints = XAllocClassHint()))
305 ERR(g_szOutOfMemory);
309 /* connect to X server */
310 if ( (g_display=XOpenDisplay(display_name)) == NULL )
312 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
316 /* get screen size from display structure macro */
317 screen_num = DefaultScreen(g_display);
318 display_width = DisplayWidth(g_display, screen_num);
319 display_height = DisplayHeight(g_display, screen_num);
321 /* size window with enough room for text */
322 width = display_width/3, height = display_height/4;
324 /* create opaque window */
325 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
326 0, 0, width, height, border_width, BlackPixel(g_display,
327 screen_num), WhitePixel(g_display,screen_num));
330 /* Set size hints for window manager. The window manager may
331 * override these settings. */
333 /* x, y, width, and height hints are now taken from
334 * the actual settings of the window when mapped. Note
335 * that PPosition and PSize must be specified anyway. */
337 size_hints->flags = PPosition | PSize | PMinSize;
338 size_hints->min_width = 300;
339 size_hints->min_height = 200;
341 /* These calls store window_name into XTextProperty structures
342 * and sets the other fields properly. */
343 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
345 ERR( "structure allocation for windowName failed.\n");
349 wm_hints->initial_state = NormalState;
350 wm_hints->input = True;
351 wm_hints->flags = StateHint | InputHint;
353 class_hints->res_name = progname;
354 class_hints->res_class = "WineClipSrv";
356 XSetWMProperties(g_display, g_win, &windowName, NULL,
357 argv, argc, size_hints, wm_hints,
360 /* Select event types wanted */
361 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
362 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
364 /* create GC for text and drawing */
368 /* XMapWindow(g_display, g_win); */
370 /* Set the selections to be acquired from the command line argument.
371 * If none specified, default to all selections we understand.
374 g_selectionToAcquire = atoi(argv[1]);
376 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
378 /* Set the debugging class state from the command line argument */
381 int dbgClasses = atoi(argv[2]);
383 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
384 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
385 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
386 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
389 /* Set the "ClearSelections" state from the command line argument */
391 g_clearAllSelections = atoi(argv[3]);
397 /**************************************************************************
400 void TerminateServer( int ret )
402 TRACE("Terminating Wine clipboard server...\n");
404 /* Free Primary and Clipboard selection caches */
405 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
406 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
409 XFreeGC(g_display, g_gc);
412 XCloseDisplay(g_display);
418 /**************************************************************************
421 * Acquire the selection after retrieving all clipboard data owned by
422 * the current selection owner.
424 int AcquireSelection()
426 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
429 * For all selections we need to acquire, get a list of all targets
430 * supplied by the current selection owner.
432 if (g_selectionToAcquire & S_PRIMARY)
434 TRACE("Acquiring PRIMARY selection...\n");
435 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
436 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
438 if (g_selectionToAcquire & S_CLIPBOARD)
440 TRACE("Acquiring CLIPBOARD selection...\n");
441 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
442 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
446 * Now that we have cached the data, we proceed to acquire the selections
448 if (g_cPrimaryTargets)
450 /* Acquire the PRIMARY selection */
451 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
452 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
454 g_selectionAcquired |= S_PRIMARY;
457 TRACE("No PRIMARY targets - ownership not acquired.\n");
459 if (g_cClipboardTargets)
461 /* Acquire the CLIPBOARD selection */
462 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
463 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
465 g_selectionAcquired |= S_CLIPBOARD;
468 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
470 return g_selectionAcquired;
473 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
477 /* Set up a 10 second time out */
478 end_time=time(NULL)+10;
484 if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
486 if( xe->xselection.selection == SelectionSrc )
490 if (time(NULL)>end_time)
493 /* Sleep a bit to make this busy wait less brutal */
496 select(0, NULL, NULL, NULL, &nap);
503 /**************************************************************************
506 * Allocates and caches the list of data formats available from the current selection.
507 * This queries the selection owner for the TARGETS property and saves all
508 * reported property types.
510 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
514 Atom atype=AnyPropertyType;
516 unsigned long remain;
517 unsigned long cSelectionTargets = 0;
518 Atom* targetList=NULL;
519 Window ownerSelection = 0;
525 /* Get the selection owner */
526 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
527 if ( ownerSelection == None )
528 return cSelectionTargets;
531 * Query the selection owner for the TARGETS property
533 aTargets = XInternAtom(g_display, "TARGETS", False);
535 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
536 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
538 XConvertSelection(g_display, SelectionSrc, aTargets,
539 XInternAtom(g_display, "SELECTION_DATA", False),
543 * Wait until SelectionNotify is received
545 if (!GetSelectionEvent(SelectionSrc, &xe))
548 /* Verify that the selection returned a valid TARGETS property */
549 if ( (xe.xselection.target != aTargets)
550 || (xe.xselection.property == None) )
552 TRACE("\tCould not retrieve TARGETS\n");
553 return cSelectionTargets;
556 /* Read the TARGETS property contents */
557 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
558 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
559 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
560 TRACE("\tCouldn't read TARGETS property\n");
563 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
564 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
566 * The TARGETS property should have returned us a list of atoms
567 * corresponding to each selection target format supported.
569 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
573 /* Allocate the selection cache */
574 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
576 /* Cache these formats in the selection cache */
577 for (i = 0; i < cSelectionTargets; i++)
579 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
581 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
583 /* Populate the cache entry */
584 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
585 ERR("Failed to fill cache entry!\n");
591 /* Free the list of targets */
595 return cSelectionTargets;
598 /***********************************************************************
601 * Populates the specified cache entry
603 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
608 Atom atype=AnyPropertyType;
610 unsigned long nitems,remain,itemSize;
612 unsigned char* val=NULL;
615 TRACE("Requesting %s selection from %s...\n",
616 XGetAtomName(g_display, target),
617 XGetAtomName(g_display, SelectionSrc) );
619 /* Ask the selection owner to convert the selection to the target format */
620 XConvertSelection(g_display, SelectionSrc, target,
621 XInternAtom(g_display, "SELECTION_DATA", False),
624 /* wait until SelectionNotify is received */
625 if (!GetSelectionEvent(SelectionSrc,&xe))
628 /* Now proceed to retrieve the actual converted property from
629 * the SELECTION_DATA atom */
631 w = xe.xselection.requestor;
632 prop = xe.xselection.property;
633 reqType = xe.xselection.target;
637 TRACE("\tOwner failed to convert selection!\n");
641 TRACE("\tretrieving property %s from window %ld into %s\n",
642 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
645 * First request a zero length in order to figure out the request size.
647 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
648 &atype, &aformat, &nitems, &itemSize, &val) != Success)
650 WARN("\tcouldn't get property size\n");
654 /* Free zero length return data if any */
661 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
662 lRequestLength = (itemSize * aformat/8)/4 + 1;
665 * Retrieve the actual property in the required X format.
667 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
668 &atype, &aformat, &nitems, &remain, &val) != Success)
670 WARN("\tcouldn't read property\n");
674 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
675 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
679 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
684 * Populate the cache entry
686 pCacheEntry->target = target;
687 pCacheEntry->type = atype;
688 pCacheEntry->nFormat = aformat;
689 pCacheEntry->nElements = nitems;
691 if (atype == XA_PIXMAP)
693 Pixmap *pPixmap = (Pixmap *)val;
694 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
695 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
696 *pPixmap = newPixmap;
697 pCacheEntry->pData = pPixmap;
700 pCacheEntry->pData = val;
703 /* Delete the property on the window now that we are done
704 * This will send a PropertyNotify event to the selection owner. */
705 XDeleteProperty(g_display,w,prop);
711 /***********************************************************************
714 * Lookup a target atom in the cache and get the matching cache entry
716 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
719 int nCachetargets = 0;
720 PCACHEENTRY pCache = NULL;
721 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
723 /* Locate the cache to be used based on the selection type */
724 if ( selection == XA_PRIMARY )
726 pCache = g_pPrimaryCache;
727 nCachetargets = g_cPrimaryTargets;
729 else if ( selection == xaClipboard )
731 pCache = g_pClipboardCache;
732 nCachetargets = g_cClipboardTargets;
735 if (!pCache || !ppCacheEntry)
738 *ppCacheEntry = NULL;
740 /* Look for the target item in the cache */
741 for (i = 0; i < nCachetargets; i++)
743 if (pCache[i].target == target)
745 *ppCacheEntry = &pCache[i];
754 /***********************************************************************
757 * Empties the specified cache
759 void EmptyCache(PCACHEENTRY pCache, int nItems)
766 /* Release all items in the cache */
767 for (i = 0; i < nItems; i++)
769 if (pCache[i].target && pCache[i].pData)
771 /* If we have a Pixmap, free it first */
772 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
774 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
776 TRACE("Freeing %s (handle=%ld)...\n",
777 XGetAtomName(g_display, pCache[i].target), *pPixmap);
779 XFreePixmap(g_display, *pPixmap);
781 /* Free the cached data item (allocated by us) */
782 free(pCache[i].pData);
786 TRACE("Freeing %s (%p)...\n",
787 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
789 /* Free the cached data item (allocated by X) */
790 XFree(pCache[i].pData);
795 /* Destroy the cache */
800 /***********************************************************************
803 * Process an X event.
805 void EVENT_ProcessEvent( XEvent *event )
808 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
814 /* don't draw the window */
815 if (event->xexpose.count != 0)
818 /* Output something */
819 TextOut(g_win, g_gc, "Click here to terminate");
822 case ConfigureNotify:
826 /* fall into KeyPress (no break) */
831 case SelectionRequest:
832 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
836 EVENT_SelectionClear( (XSelectionClearEvent*)event );
841 EVENT_PropertyNotify( (XPropertyEvent *)event );
845 default: /* ignore all other events */
853 /***********************************************************************
854 * EVENT_SelectionRequest_MULTIPLE
855 * Service a MULTIPLE selection request event
856 * rprop contains a list of (target,property) atom pairs.
857 * The first atom names a target and the second names a property.
858 * The effect is as if we have received a sequence of SelectionRequest events
859 * (one for each atom pair) except that:
860 * 1. We reply with a SelectionNotify only when all the requested conversions
861 * have been performed.
862 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
863 * we replace the atom in the property by None.
865 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
868 Atom atype=AnyPropertyType;
870 unsigned long remain;
871 Atom* targetPropList=NULL;
872 unsigned long cTargetPropList = 0;
873 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
875 /* If the specified property is None the requestor is an obsolete client.
876 * We support these by using the specified target atom as the reply property.
878 rprop = pevent->property;
880 rprop = pevent->target;
884 /* Read the MULTIPLE property contents. This should contain a list of
885 * (target,property) atom pairs.
887 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
888 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
889 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
890 TRACE("\tCouldn't read MULTIPLE property\n");
893 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
894 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
897 * Make sure we got what we expect.
898 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
899 * in a MULTIPLE selection request should be of type ATOM_PAIR.
900 * However some X apps(such as XPaint) are not compliant with this and return
901 * a user defined atom in atype when XGetWindowProperty is called.
902 * The data *is* an atom pair but is not denoted as such.
904 if(aformat == 32 /* atype == xAtomPair */ )
908 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
909 * for each (target,property) pair */
911 for (i = 0; i < cTargetPropList; i+=2)
913 char *targetName = XGetAtomName(g_display, targetPropList[i]);
914 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
915 XSelectionRequestEvent event;
917 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
921 /* We must have a non "None" property to service a MULTIPLE target atom */
922 if ( !targetPropList[i+1] )
924 TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
928 /* Set up an XSelectionRequestEvent for this (target,property) pair */
929 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
930 event.target = targetPropList[i];
931 event.property = targetPropList[i+1];
933 /* Fire a SelectionRequest, informing the handler that we are processing
934 * a MULTIPLE selection request event.
936 EVENT_SelectionRequest( &event, TRUE );
940 /* Free the list of targets/properties */
941 XFree(targetPropList);
949 /***********************************************************************
950 * EVENT_SelectionRequest
951 * Process an event selection request event.
952 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
953 * recursively while servicing a "MULTIPLE" selection target.
956 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
958 XSelectionEvent result;
960 Window request = event->requestor;
961 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
962 PCACHEENTRY pCacheEntry = NULL;
966 /* If the specified property is None the requestor is an obsolete client.
967 * We support these by using the specified target atom as the reply property.
969 rprop = event->property;
971 rprop = event->target;
973 TRACE("Request for %s in selection %s\n",
974 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
976 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
977 if(event->target == xaMultiple)
979 /* MULTIPLE selection request - will call us back recursively */
980 rprop = EVENT_SelectionRequest_MULTIPLE( event );
984 /* Lookup the requested target property in the cache */
985 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
987 TRACE("Item not available in cache!\n");
991 /* Update the X property */
992 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
994 /* If we have a request for a pixmap, return a duplicate */
996 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
998 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
999 pixmap = DuplicatePixmap( *pPixmap );
1003 pData = pCacheEntry->pData;
1005 XChangeProperty(g_display, request, rprop,
1006 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
1007 (unsigned char *)pData, pCacheEntry->nElements);
1011 TRACE("\tRequest ignored\n");
1014 * SelectionNotify should be sent only at the end of a MULTIPLE request
1018 result.type = SelectionNotify;
1019 result.display = g_display;
1020 result.requestor = request;
1021 result.selection = event->selection;
1022 result.property = rprop;
1023 result.target = event->target;
1024 result.time = event->time;
1025 TRACE("Sending SelectionNotify event...\n");
1026 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
1031 /***********************************************************************
1032 * EVENT_SelectionClear
1033 * We receive this event when another client grabs the X selection.
1034 * If we lost both PRIMARY and CLIPBOARD we must terminate.
1036 void EVENT_SelectionClear( XSelectionClearEvent *event )
1038 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
1042 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
1043 * dictate that *all* selections should be cleared on loss of a selection,
1044 * we must give up all the selections we own.
1046 if ( g_clearAllSelections || (event->selection == xaClipboard) )
1048 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
1050 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
1051 if ( (event->selection == xaClipboard)
1052 && (g_selectionAcquired & S_PRIMARY) )
1054 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1057 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
1058 if ( (event->selection == XA_PRIMARY)
1059 && (g_selectionAcquired & S_CLIPBOARD) )
1061 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1064 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
1066 else if (event->selection == XA_PRIMARY)
1068 TRACE("Lost PRIMARY selection...\n");
1069 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
1072 /* Once we lose all our selections we have nothing more to do */
1073 if (g_selectionAcquired == S_NOSELECTION)
1077 /***********************************************************************
1078 * EVENT_PropertyNotify
1079 * We use this to release resources like Pixmaps when a selection
1080 * client no longer needs them.
1082 void EVENT_PropertyNotify( XPropertyEvent *event )
1086 /* Check if we have any resources to free */
1088 switch(event->state)
1090 case PropertyDelete:
1092 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1093 XGetAtomName(event->display, event->atom), (long)event->window);
1095 /* FreeResources( event->atom ); */
1099 case PropertyNewValue:
1101 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1102 XGetAtomName(event->display, event->atom), (long)event->window);
1111 /***********************************************************************
1114 Pixmap DuplicatePixmap(Pixmap pixmap)
1119 int x,y; /* Unused */
1120 unsigned border_width; /* Unused */
1121 unsigned int depth, width, height;
1123 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1125 /* Get the Pixmap dimensions and bit depth */
1126 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1127 &border_width, &depth) )
1130 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1131 width, height, depth);
1133 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1135 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1137 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1141 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1145 /***********************************************************************
1147 * Get a GC to use for drawing
1149 void getGC(Window win, GC *gc)
1151 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1153 unsigned int line_width = 6;
1154 int line_style = LineOnOffDash;
1155 int cap_style = CapRound;
1156 int join_style = JoinRound;
1157 int dash_offset = 0;
1158 static char dash_list[] = {12, 24};
1159 int list_length = 2;
1161 /* Create default Graphics Context */
1162 *gc = XCreateGC(g_display, win, valuemask, &values);
1164 /* specify black foreground since default window background is
1165 * white and default foreground is undefined. */
1166 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1168 /* set line attributes */
1169 XSetLineAttributes(g_display, *gc, line_width, line_style,
1170 cap_style, join_style);
1173 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1177 /***********************************************************************
1180 void TextOut(Window win, GC gc, char *pStr)
1182 int y_offset, x_offset;
1187 /* output text, centered on each line */
1188 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,