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 */
181 int RunAsDaemon( void );
182 BOOL Init(int argc, char **argv);
183 void TerminateServer( int ret );
184 int AcquireSelection();
185 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
186 void EmptyCache(PCACHEENTRY pCache, int nItems);
187 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
188 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
189 void EVENT_ProcessEvent( XEvent *event );
190 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
191 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
192 void EVENT_SelectionClear( XSelectionClearEvent *event );
193 void EVENT_PropertyNotify( XPropertyEvent *event );
194 Pixmap DuplicatePixmap(Pixmap pixmap);
195 void TextOut(Window win, GC gc, char *pStr);
196 void getGC(Window win, GC *gc);
199 int main(int argc, char **argv)
203 if ( RunAsDaemon() == -1 )
205 ERR("could not run as daemon\n");
209 if ( !Init(argc, argv) )
212 /* Acquire the selection after retrieving all clipboard data
213 * owned by the current selection owner. If we were unable to
214 * Acquire any selection, terminate right away.
216 if ( AcquireSelection() == S_NOSELECTION )
219 TRACE("Clipboard server running...\n");
221 /* Start an X event loop */
224 XNextEvent(g_display, &event);
226 EVENT_ProcessEvent( &event );
231 /**************************************************************************
234 int RunAsDaemon( void )
238 /* fork child process and let parent exit ; gets rid of original PID */
242 ERR("fork failed\n");
249 /* below is child process w/ new PID, set as session leader */
252 /* close stdin,stdout,stderr and file descriptors (overkill method) */
253 for ( i = 0; i < 256 ; i++ )
256 TRACE("now running as daemon...\n");
261 /**************************************************************************
263 * Initialize the clipboard server
265 BOOL Init(int argc, char **argv)
267 unsigned int width, height; /* window size */
268 unsigned int border_width = 4; /* four pixels */
269 unsigned int display_width, display_height;
270 char *window_name = "Wine Clipboard Server";
271 XSizeHints *size_hints = NULL;
272 XWMHints *wm_hints = NULL;
273 XClassHint *class_hints = NULL;
274 XTextProperty windowName;
275 char *display_name = NULL;
279 if (!(size_hints = XAllocSizeHints()))
281 ERR(g_szOutOfMemory);
284 if (!(wm_hints = XAllocWMHints()))
286 ERR(g_szOutOfMemory);
289 if (!(class_hints = XAllocClassHint()))
291 ERR(g_szOutOfMemory);
295 /* connect to X server */
296 if ( (g_display=XOpenDisplay(display_name)) == NULL )
298 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
302 /* get screen size from display structure macro */
303 screen_num = DefaultScreen(g_display);
304 display_width = DisplayWidth(g_display, screen_num);
305 display_height = DisplayHeight(g_display, screen_num);
307 /* size window with enough room for text */
308 width = display_width/3, height = display_height/4;
310 /* create opaque window */
311 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
312 0, 0, width, height, border_width, BlackPixel(g_display,
313 screen_num), WhitePixel(g_display,screen_num));
316 /* Set size hints for window manager. The window manager may
317 * override these settings. */
319 /* x, y, width, and height hints are now taken from
320 * the actual settings of the window when mapped. Note
321 * that PPosition and PSize must be specified anyway. */
323 size_hints->flags = PPosition | PSize | PMinSize;
324 size_hints->min_width = 300;
325 size_hints->min_height = 200;
327 /* These calls store window_name into XTextProperty structures
328 * and sets the other fields properly. */
329 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
331 ERR( "structure allocation for windowName failed.\n");
335 wm_hints->initial_state = NormalState;
336 wm_hints->input = True;
337 wm_hints->flags = StateHint | InputHint;
339 class_hints->res_name = progname;
340 class_hints->res_class = "WineClipSrv";
342 XSetWMProperties(g_display, g_win, &windowName, NULL,
343 argv, argc, size_hints, wm_hints,
346 /* Select event types wanted */
347 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
348 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
350 /* create GC for text and drawing */
354 /* XMapWindow(g_display, g_win); */
356 /* Set the selections to be acquired from the command line argument.
357 * If none specified, default to all selections we understand.
360 g_selectionToAcquire = atoi(argv[1]);
362 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
364 /* Set the debugging class state from the command line argument */
367 int dbgClasses = atoi(argv[2]);
369 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
370 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
371 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
372 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
375 /* Set the "ClearSelections" state from the command line argument */
377 g_clearAllSelections = atoi(argv[3]);
383 /**************************************************************************
386 void TerminateServer( int ret )
388 TRACE("Terminating Wine clipboard server...\n");
390 /* Free Primary and Clipboard selection caches */
391 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
392 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
395 XFreeGC(g_display, g_gc);
398 XCloseDisplay(g_display);
404 /**************************************************************************
407 * Acquire the selection after retrieving all clipboard data owned by
408 * the current selection owner.
410 int AcquireSelection()
412 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
415 * For all selections we need to acquire, get a list of all targets
416 * supplied by the current selection owner.
418 if (g_selectionToAcquire & S_PRIMARY)
420 TRACE("Acquiring PRIMARY selection...\n");
421 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
422 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
424 if (g_selectionToAcquire & S_CLIPBOARD)
426 TRACE("Acquiring CLIPBOARD selection...\n");
427 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
428 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
432 * Now that we have cached the data, we proceed to acquire the selections
434 if (g_cPrimaryTargets)
436 /* Acquire the PRIMARY selection */
437 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
438 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
440 g_selectionAcquired |= S_PRIMARY;
443 TRACE("No PRIMARY targets - ownership not acquired.\n");
445 if (g_cClipboardTargets)
447 /* Acquire the CLIPBOARD selection */
448 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
449 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
451 g_selectionAcquired |= S_CLIPBOARD;
454 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
456 return g_selectionAcquired;
459 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
463 /* Set up a 10 second time out */
464 end_time=time(NULL)+10;
470 if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
472 if( xe->xselection.selection == SelectionSrc )
476 if (time(NULL)>end_time)
479 /* Sleep a bit to make this busy wait less brutal */
482 select(0, NULL, NULL, NULL, &nap);
489 /**************************************************************************
492 * Allocates and caches the list of data formats available from the current selection.
493 * This queries the selection owner for the TARGETS property and saves all
494 * reported property types.
496 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
500 Atom atype=AnyPropertyType;
502 unsigned long remain;
503 unsigned long cSelectionTargets = 0;
504 Atom* targetList=NULL;
505 Window ownerSelection = 0;
511 /* Get the selection owner */
512 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
513 if ( ownerSelection == None )
514 return cSelectionTargets;
517 * Query the selection owner for the TARGETS property
519 aTargets = XInternAtom(g_display, "TARGETS", False);
521 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
522 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
524 XConvertSelection(g_display, SelectionSrc, aTargets,
525 XInternAtom(g_display, "SELECTION_DATA", False),
529 * Wait until SelectionNotify is received
531 if (!GetSelectionEvent(SelectionSrc, &xe))
534 /* Verify that the selection returned a valid TARGETS property */
535 if ( (xe.xselection.target != aTargets)
536 || (xe.xselection.property == None) )
538 TRACE("\tCould not retrieve TARGETS\n");
539 return cSelectionTargets;
542 /* Read the TARGETS property contents */
543 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
544 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
545 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
546 TRACE("\tCouldn't read TARGETS property\n");
549 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
550 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
552 * The TARGETS property should have returned us a list of atoms
553 * corresponding to each selection target format supported.
555 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
559 /* Allocate the selection cache */
560 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
562 /* Cache these formats in the selection cache */
563 for (i = 0; i < cSelectionTargets; i++)
565 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
567 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
569 /* Populate the cache entry */
570 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
571 ERR("Failed to fill cache entry!\n");
577 /* Free the list of targets */
581 return cSelectionTargets;
584 /***********************************************************************
587 * Populates the specified cache entry
589 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
594 Atom atype=AnyPropertyType;
596 unsigned long nitems,remain,itemSize;
598 unsigned char* val=NULL;
601 TRACE("Requesting %s selection from %s...\n",
602 XGetAtomName(g_display, target),
603 XGetAtomName(g_display, SelectionSrc) );
605 /* Ask the selection owner to convert the selection to the target format */
606 XConvertSelection(g_display, SelectionSrc, target,
607 XInternAtom(g_display, "SELECTION_DATA", False),
610 /* wait until SelectionNotify is received */
611 if (!GetSelectionEvent(SelectionSrc,&xe))
614 /* Now proceed to retrieve the actual converted property from
615 * the SELECTION_DATA atom */
617 w = xe.xselection.requestor;
618 prop = xe.xselection.property;
619 reqType = xe.xselection.target;
623 TRACE("\tOwner failed to convert selection!\n");
627 TRACE("\tretrieving property %s from window %ld into %s\n",
628 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
631 * First request a zero length in order to figure out the request size.
633 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
634 &atype, &aformat, &nitems, &itemSize, &val) != Success)
636 WARN("\tcouldn't get property size\n");
640 /* Free zero length return data if any */
647 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
648 lRequestLength = (itemSize * aformat/8)/4 + 1;
651 * Retrieve the actual property in the required X format.
653 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
654 &atype, &aformat, &nitems, &remain, &val) != Success)
656 WARN("\tcouldn't read property\n");
660 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
661 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
665 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
670 * Populate the cache entry
672 pCacheEntry->target = target;
673 pCacheEntry->type = atype;
674 pCacheEntry->nFormat = aformat;
675 pCacheEntry->nElements = nitems;
677 if (atype == XA_PIXMAP)
679 Pixmap *pPixmap = (Pixmap *)val;
680 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
681 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
682 *pPixmap = newPixmap;
683 pCacheEntry->pData = pPixmap;
686 pCacheEntry->pData = val;
689 /* Delete the property on the window now that we are done
690 * This will send a PropertyNotify event to the selection owner. */
691 XDeleteProperty(g_display,w,prop);
697 /***********************************************************************
700 * Lookup a target atom in the cache and get the matching cache entry
702 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
705 int nCachetargets = 0;
706 PCACHEENTRY pCache = NULL;
707 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
709 /* Locate the cache to be used based on the selection type */
710 if ( selection == XA_PRIMARY )
712 pCache = g_pPrimaryCache;
713 nCachetargets = g_cPrimaryTargets;
715 else if ( selection == xaClipboard )
717 pCache = g_pClipboardCache;
718 nCachetargets = g_cClipboardTargets;
721 if (!pCache || !ppCacheEntry)
724 *ppCacheEntry = NULL;
726 /* Look for the target item in the cache */
727 for (i = 0; i < nCachetargets; i++)
729 if (pCache[i].target == target)
731 *ppCacheEntry = &pCache[i];
740 /***********************************************************************
743 * Empties the specified cache
745 void EmptyCache(PCACHEENTRY pCache, int nItems)
752 /* Release all items in the cache */
753 for (i = 0; i < nItems; i++)
755 if (pCache[i].target && pCache[i].pData)
757 /* If we have a Pixmap, free it first */
758 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
760 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
762 TRACE("Freeing %s (handle=%ld)...\n",
763 XGetAtomName(g_display, pCache[i].target), *pPixmap);
765 XFreePixmap(g_display, *pPixmap);
767 /* Free the cached data item (allocated by us) */
768 free(pCache[i].pData);
772 TRACE("Freeing %s (%p)...\n",
773 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
775 /* Free the cached data item (allocated by X) */
776 XFree(pCache[i].pData);
781 /* Destroy the cache */
786 /***********************************************************************
789 * Process an X event.
791 void EVENT_ProcessEvent( XEvent *event )
794 static const char * const event_names[] =
796 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
797 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
798 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
799 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
800 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
801 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
802 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
803 "ClientMessage", "MappingNotify"
806 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
812 /* don't draw the window */
813 if (event->xexpose.count != 0)
816 /* Output something */
817 TextOut(g_win, g_gc, "Click here to terminate");
820 case ConfigureNotify:
824 /* fall into KeyPress (no break) */
829 case SelectionRequest:
830 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
834 EVENT_SelectionClear( (XSelectionClearEvent*)event );
839 EVENT_PropertyNotify( (XPropertyEvent *)event );
843 default: /* ignore all other events */
851 /***********************************************************************
852 * EVENT_SelectionRequest_MULTIPLE
853 * Service a MULTIPLE selection request event
854 * rprop contains a list of (target,property) atom pairs.
855 * The first atom names a target and the second names a property.
856 * The effect is as if we have received a sequence of SelectionRequest events
857 * (one for each atom pair) except that:
858 * 1. We reply with a SelectionNotify only when all the requested conversions
859 * have been performed.
860 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
861 * we replace the atom in the property by None.
863 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
866 Atom atype=AnyPropertyType;
868 unsigned long remain;
869 Atom* targetPropList=NULL;
870 unsigned long cTargetPropList = 0;
871 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
873 /* If the specified property is None the requestor is an obsolete client.
874 * We support these by using the specified target atom as the reply property.
876 rprop = pevent->property;
878 rprop = pevent->target;
882 /* Read the MULTIPLE property contents. This should contain a list of
883 * (target,property) atom pairs.
885 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
886 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
887 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
888 TRACE("\tCouldn't read MULTIPLE property\n");
891 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
892 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
895 * Make sure we got what we expect.
896 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
897 * in a MULTIPLE selection request should be of type ATOM_PAIR.
898 * However some X apps(such as XPaint) are not compliant with this and return
899 * a user defined atom in atype when XGetWindowProperty is called.
900 * The data *is* an atom pair but is not denoted as such.
902 if(aformat == 32 /* atype == xAtomPair */ )
906 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
907 * for each (target,property) pair */
909 for (i = 0; i < cTargetPropList; i+=2)
911 char *targetName = XGetAtomName(g_display, targetPropList[i]);
912 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
913 XSelectionRequestEvent event;
915 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
919 /* We must have a non "None" property to service a MULTIPLE target atom */
920 if ( !targetPropList[i+1] )
922 TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
926 /* Set up an XSelectionRequestEvent for this (target,property) pair */
927 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
928 event.target = targetPropList[i];
929 event.property = targetPropList[i+1];
931 /* Fire a SelectionRequest, informing the handler that we are processing
932 * a MULTIPLE selection request event.
934 EVENT_SelectionRequest( &event, TRUE );
938 /* Free the list of targets/properties */
939 XFree(targetPropList);
947 /***********************************************************************
948 * EVENT_SelectionRequest
949 * Process an event selection request event.
950 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
951 * recursively while servicing a "MULTIPLE" selection target.
954 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
956 XSelectionEvent result;
958 Window request = event->requestor;
959 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
960 PCACHEENTRY pCacheEntry = NULL;
964 /* If the specified property is None the requestor is an obsolete client.
965 * We support these by using the specified target atom as the reply property.
967 rprop = event->property;
969 rprop = event->target;
971 TRACE("Request for %s in selection %s\n",
972 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
974 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
975 if(event->target == xaMultiple)
977 /* MULTIPLE selection request - will call us back recursively */
978 rprop = EVENT_SelectionRequest_MULTIPLE( event );
982 /* Lookup the requested target property in the cache */
983 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
985 TRACE("Item not available in cache!\n");
989 /* Update the X property */
990 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
992 /* If we have a request for a pixmap, return a duplicate */
994 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
996 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
997 pixmap = DuplicatePixmap( *pPixmap );
1001 pData = pCacheEntry->pData;
1003 XChangeProperty(g_display, request, rprop,
1004 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
1005 (unsigned char *)pData, pCacheEntry->nElements);
1009 TRACE("\tRequest ignored\n");
1012 * SelectionNotify should be sent only at the end of a MULTIPLE request
1016 result.type = SelectionNotify;
1017 result.display = g_display;
1018 result.requestor = request;
1019 result.selection = event->selection;
1020 result.property = rprop;
1021 result.target = event->target;
1022 result.time = event->time;
1023 TRACE("Sending SelectionNotify event...\n");
1024 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
1029 /***********************************************************************
1030 * EVENT_SelectionClear
1031 * We receive this event when another client grabs the X selection.
1032 * If we lost both PRIMARY and CLIPBOARD we must terminate.
1034 void EVENT_SelectionClear( XSelectionClearEvent *event )
1036 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
1040 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
1041 * dictate that *all* selections should be cleared on loss of a selection,
1042 * we must give up all the selections we own.
1044 if ( g_clearAllSelections || (event->selection == xaClipboard) )
1046 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
1048 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
1049 if ( (event->selection == xaClipboard)
1050 && (g_selectionAcquired & S_PRIMARY) )
1052 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1055 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
1056 if ( (event->selection == XA_PRIMARY)
1057 && (g_selectionAcquired & S_CLIPBOARD) )
1059 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1062 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
1064 else if (event->selection == XA_PRIMARY)
1066 TRACE("Lost PRIMARY selection...\n");
1067 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
1070 /* Once we lose all our selections we have nothing more to do */
1071 if (g_selectionAcquired == S_NOSELECTION)
1075 /***********************************************************************
1076 * EVENT_PropertyNotify
1077 * We use this to release resources like Pixmaps when a selection
1078 * client no longer needs them.
1080 void EVENT_PropertyNotify( XPropertyEvent *event )
1084 /* Check if we have any resources to free */
1086 switch(event->state)
1088 case PropertyDelete:
1090 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1091 XGetAtomName(event->display, event->atom), (long)event->window);
1093 /* FreeResources( event->atom ); */
1097 case PropertyNewValue:
1099 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1100 XGetAtomName(event->display, event->atom), (long)event->window);
1109 /***********************************************************************
1112 Pixmap DuplicatePixmap(Pixmap pixmap)
1117 int x,y; /* Unused */
1118 unsigned border_width; /* Unused */
1119 unsigned int depth, width, height;
1121 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1123 /* Get the Pixmap dimensions and bit depth */
1124 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1125 &border_width, &depth) )
1128 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1129 width, height, depth);
1131 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1133 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1135 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1139 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1143 /***********************************************************************
1145 * Get a GC to use for drawing
1147 void getGC(Window win, GC *gc)
1149 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1151 unsigned int line_width = 6;
1152 int line_style = LineOnOffDash;
1153 int cap_style = CapRound;
1154 int join_style = JoinRound;
1155 int dash_offset = 0;
1156 static char dash_list[] = {12, 24};
1157 int list_length = 2;
1159 /* Create default Graphics Context */
1160 *gc = XCreateGC(g_display, win, valuemask, &values);
1162 /* specify black foreground since default window background is
1163 * white and default foreground is undefined. */
1164 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1166 /* set line attributes */
1167 XSetLineAttributes(g_display, *gc, line_width, line_style,
1168 cap_style, join_style);
1171 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1175 /***********************************************************************
1178 void TextOut(Window win, GC gc, char *pStr)
1180 int y_offset, x_offset;
1185 /* output text, centered on each line */
1186 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,