Changed the spec definition of *s*printf from *str to ptr, since that
[wine/testsucceed.git] / windows / x11drv / wineclipsrv.c
blobc54d2f5eb63226cd97fe7f57d3b55b950aa77b9b
1 /*
2 * Wine Clipboard Server
4 * Copyright 1999 Noel Borthwick
6 * USAGE:
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
19 * event is received.
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.
25 * NOTES:
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.
44 * TODO:
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <X11/Xlib.h>
50 #include <X11/Xutil.h>
51 #include <X11/Xos.h>
52 #include <X11/Xatom.h>
54 #include "config.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)) \
81 ? 0 : printf
83 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
85 /* use configure to allow user to compile out debugging messages */
86 #ifndef NO_TRACE_MSGS
87 #define TRACE __DPRINTF(__DBCL_TRACE)
88 #else
89 #define TRACE __DUMMY_DPRINTF
90 #endif /* NO_TRACE_MSGS */
92 #ifndef NO_DEBUG_MSGS
93 #define WARN __DPRINTF(__DBCL_WARN)
94 #define FIXME __DPRINTF(__DBCL_FIXME)
95 #else
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)
104 #define TRUE 1
105 #define FALSE 0
106 typedef int BOOL;
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
116 #define S_PRIMARY 1
117 #define S_CLIPBOARD 2
119 /* Debugging class masks */
121 #define C_FIXME 1
122 #define C_ERR 2
123 #define C_WARN 4
124 #define C_TRACE 8
127 * Global variables
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 */
134 static GC g_gc = 0;
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
147 Atom target;
148 Atom type;
149 int nFormat;
150 int nElements;
151 void *pData;
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 */
159 /* Event names */
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"
174 * Prototypes
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)
196 XEvent event;
198 if ( !Init(argc, argv) )
199 exit(0);
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 )
206 TerminateServer(0);
208 TRACE("Clipboard server running...\n");
210 /* Start an X event loop */
211 while (1)
213 XNextEvent(g_display, &event);
215 EVENT_ProcessEvent( &event );
220 /**************************************************************************
221 * Init()
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;
236 progname = argv[0];
238 if (!(size_hints = XAllocSizeHints()))
240 ERR(g_szOutOfMemory);
241 return 0;
243 if (!(wm_hints = XAllocWMHints()))
245 ERR(g_szOutOfMemory);
246 return 0;
248 if (!(class_hints = XAllocClassHint()))
250 ERR(g_szOutOfMemory);
251 return 0;
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));
258 return 0;
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");
291 TerminateServer(-1);
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,
303 class_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 */
310 getGC(g_win, &g_gc);
312 /* Display window */
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.
318 if (argc > 1)
319 g_selectionToAcquire = atoi(argv[1]);
320 else
321 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
323 /* Set the debugging class state from the command line argument */
324 if (argc > 2)
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 */
335 if (argc > 3)
336 g_clearAllSelections = atoi(argv[3]);
338 return TRUE;
342 /**************************************************************************
343 * TerminateServer()
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);
353 if (g_gc)
354 XFreeGC(g_display, g_gc);
356 if (g_display)
357 XCloseDisplay(g_display);
359 exit(ret);
363 /**************************************************************************
364 * AcquireSelection()
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;
401 else
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;
412 else
413 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
415 return g_selectionAcquired;
418 /**************************************************************************
419 * CacheDataFormats
421 * Allocates and caches the list of data formats available from the current selection.
422 * This queries the selection owner for the TARGETS property and saves all
423 * reported property types.
425 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
427 XEvent xe;
428 Atom aTargets;
429 Atom atype=AnyPropertyType;
430 int aformat;
431 unsigned long remain;
432 unsigned long cSelectionTargets = 0;
433 Atom* targetList=NULL;
434 Window ownerSelection = 0;
436 if (!ppCache)
437 return 0;
438 *ppCache = NULL;
440 /* Get the selection owner */
441 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
442 if ( ownerSelection == None )
443 return cSelectionTargets;
446 * Query the selection owner for the TARGETS property
448 aTargets = XInternAtom(g_display, "TARGETS", False);
450 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
451 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
453 XConvertSelection(g_display, SelectionSrc, aTargets,
454 XInternAtom(g_display, "SELECTION_DATA", False),
455 g_win, CurrentTime);
458 * Wait until SelectionNotify is received
460 while( TRUE )
462 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
463 if( xe.xselection.selection == SelectionSrc )
464 break;
467 /* Verify that the selection returned a valid TARGETS property */
468 if ( (xe.xselection.target != aTargets)
469 || (xe.xselection.property == None) )
471 TRACE("\tCould not retrieve TARGETS\n");
472 return cSelectionTargets;
475 /* Read the TARGETS property contents */
476 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
477 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
478 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
479 TRACE("\tCouldn't read TARGETS property\n");
480 else
482 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
483 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
485 * The TARGETS property should have returned us a list of atoms
486 * corresponding to each selection target format supported.
488 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
490 int i;
492 /* Allocate the selection cache */
493 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
495 /* Cache these formats in the selection cache */
496 for (i = 0; i < cSelectionTargets; i++)
498 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
500 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
502 /* Populate the cache entry */
503 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
504 ERR("Failed to fill cache entry!\n");
506 XFree(itemFmtName);
510 /* Free the list of targets */
511 XFree(targetList);
514 return cSelectionTargets;
517 /***********************************************************************
518 * FillCacheEntry
520 * Populates the specified cache entry
522 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
524 XEvent xe;
525 Window w;
526 Atom prop, reqType;
527 Atom atype=AnyPropertyType;
528 int aformat;
529 unsigned long nitems,remain,itemSize;
530 long lRequestLength;
531 unsigned char* val=NULL;
532 BOOL bRet = FALSE;
534 TRACE("Requesting %s selection from %s...\n",
535 XGetAtomName(g_display, target),
536 XGetAtomName(g_display, SelectionSrc) );
538 /* Ask the selection owner to convert the selection to the target format */
539 XConvertSelection(g_display, SelectionSrc, target,
540 XInternAtom(g_display, "SELECTION_DATA", False),
541 g_win, CurrentTime);
543 /* wait until SelectionNotify is received */
544 while( TRUE )
546 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
547 if( xe.xselection.selection == SelectionSrc )
548 break;
551 /* Now proceed to retrieve the actual converted property from
552 * the SELECTION_DATA atom */
554 w = xe.xselection.requestor;
555 prop = xe.xselection.property;
556 reqType = xe.xselection.target;
558 if(prop == None)
560 TRACE("\tOwner failed to convert selection!\n");
561 return bRet;
564 TRACE("\tretrieving property %s from window %ld into %s\n",
565 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
568 * First request a zero length in order to figure out the request size.
570 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
571 &atype, &aformat, &nitems, &itemSize, &val) != Success)
573 WARN("\tcouldn't get property size\n");
574 return bRet;
577 /* Free zero length return data if any */
578 if ( val )
580 XFree(val);
581 val = NULL;
584 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
585 lRequestLength = (itemSize * aformat/8)/4 + 1;
588 * Retrieve the actual property in the required X format.
590 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
591 &atype, &aformat, &nitems, &remain, &val) != Success)
593 WARN("\tcouldn't read property\n");
594 return bRet;
597 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
598 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
600 if (remain)
602 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
603 goto END;
607 * Populate the cache entry
609 pCacheEntry->target = target;
610 pCacheEntry->type = atype;
611 pCacheEntry->nFormat = aformat;
612 pCacheEntry->nElements = nitems;
614 if (atype == XA_PIXMAP)
616 Pixmap *pPixmap = (Pixmap *)val;
617 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
618 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
619 *pPixmap = newPixmap;
620 pCacheEntry->pData = pPixmap;
622 else
623 pCacheEntry->pData = val;
625 END:
626 /* Delete the property on the window now that we are done
627 * This will send a PropertyNotify event to the selection owner. */
628 XDeleteProperty(g_display,w,prop);
630 return TRUE;
634 /***********************************************************************
635 * LookupCacheItem
637 * Lookup a target atom in the cache and get the matching cache entry
639 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
641 int i;
642 int nCachetargets = 0;
643 PCACHEENTRY pCache = NULL;
644 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
646 /* Locate the cache to be used based on the selection type */
647 if ( selection == XA_PRIMARY )
649 pCache = g_pPrimaryCache;
650 nCachetargets = g_cPrimaryTargets;
652 else if ( selection == xaClipboard )
654 pCache = g_pClipboardCache;
655 nCachetargets = g_cClipboardTargets;
658 if (!pCache || !ppCacheEntry)
659 return FALSE;
661 *ppCacheEntry = NULL;
663 /* Look for the target item in the cache */
664 for (i = 0; i < nCachetargets; i++)
666 if (pCache[i].target == target)
668 *ppCacheEntry = &pCache[i];
669 return TRUE;
673 return FALSE;
677 /***********************************************************************
678 * EmptyCache
680 * Empties the specified cache
682 void EmptyCache(PCACHEENTRY pCache, int nItems)
684 int i;
686 if (!pCache)
687 return;
689 /* Release all items in the cache */
690 for (i = 0; i < nItems; i++)
692 if (pCache[i].target && pCache[i].pData)
694 /* If we have a Pixmap, free it first */
695 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
697 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
699 TRACE("Freeing %s (handle=%ld)...\n",
700 XGetAtomName(g_display, pCache[i].target), *pPixmap);
702 XFreePixmap(g_display, *pPixmap);
704 /* Free the cached data item (allocated by us) */
705 free(pCache[i].pData);
707 else
709 TRACE("Freeing %s (%p)...\n",
710 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
712 /* Free the cached data item (allocated by X) */
713 XFree(pCache[i].pData);
718 /* Destroy the cache */
719 free(pCache);
723 /***********************************************************************
724 * EVENT_ProcessEvent
726 * Process an X event.
728 void EVENT_ProcessEvent( XEvent *event )
731 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
734 switch (event->type)
736 case Expose:
737 /* don't draw the window */
738 if (event->xexpose.count != 0)
739 break;
741 /* Output something */
742 TextOut(g_win, g_gc, "Click here to terminate");
743 break;
745 case ConfigureNotify:
746 break;
748 case ButtonPress:
749 /* fall into KeyPress (no break) */
750 case KeyPress:
751 TerminateServer(1);
752 break;
754 case SelectionRequest:
755 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
756 break;
758 case SelectionClear:
759 EVENT_SelectionClear( (XSelectionClearEvent*)event );
760 break;
762 case PropertyNotify:
763 // EVENT_PropertyNotify( (XPropertyEvent *)event );
764 break;
766 default: /* ignore all other events */
767 break;
769 } /* end switch */
774 /***********************************************************************
775 * EVENT_SelectionRequest_MULTIPLE
776 * Service a MULTIPLE selection request event
777 * rprop contains a list of (target,property) atom pairs.
778 * The first atom names a target and the second names a property.
779 * The effect is as if we have received a sequence of SelectionRequest events
780 * (one for each atom pair) except that:
781 * 1. We reply with a SelectionNotify only when all the requested conversions
782 * have been performed.
783 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
784 * we replace the atom in the property by None.
786 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
788 Atom rprop;
789 Atom atype=AnyPropertyType;
790 int aformat;
791 unsigned long remain;
792 Atom* targetPropList=NULL;
793 unsigned long cTargetPropList = 0;
794 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
796 /* If the specified property is None the requestor is an obsolete client.
797 * We support these by using the specified target atom as the reply property.
799 rprop = pevent->property;
800 if( rprop == None )
801 rprop = pevent->target;
802 if (!rprop)
803 goto END;
805 /* Read the MULTIPLE property contents. This should contain a list of
806 * (target,property) atom pairs.
808 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
809 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
810 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
811 TRACE("\tCouldn't read MULTIPLE property\n");
812 else
814 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
815 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
818 * Make sure we got what we expect.
819 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
820 * in a MULTIPLE selection request should be of type ATOM_PAIR.
821 * However some X apps(such as XPaint) are not compliant with this and return
822 * a user defined atom in atype when XGetWindowProperty is called.
823 * The data *is* an atom pair but is not denoted as such.
825 if(aformat == 32 /* atype == xAtomPair */ )
827 int i;
829 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
830 * for each (target,property) pair */
832 for (i = 0; i < cTargetPropList; i+=2)
834 char *targetName = XGetAtomName(g_display, targetPropList[i]);
835 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
836 XSelectionRequestEvent event;
838 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
839 XFree(targetName);
840 XFree(propName);
842 /* We must have a non "None" property to service a MULTIPLE target atom */
843 if ( !targetPropList[i+1] )
845 TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
846 continue;
849 /* Set up an XSelectionRequestEvent for this (target,property) pair */
850 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
851 event.target = targetPropList[i];
852 event.property = targetPropList[i+1];
854 /* Fire a SelectionRequest, informing the handler that we are processing
855 * a MULTIPLE selection request event.
857 EVENT_SelectionRequest( &event, TRUE );
861 /* Free the list of targets/properties */
862 XFree(targetPropList);
865 END:
866 return rprop;
870 /***********************************************************************
871 * EVENT_SelectionRequest
872 * Process an event selection request event.
873 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
874 * recursively while servicing a "MULTIPLE" selection target.
877 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
879 XSelectionEvent result;
880 Atom rprop = None;
881 Window request = event->requestor;
882 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
883 PCACHEENTRY pCacheEntry = NULL;
884 void *pData = NULL;
885 Pixmap pixmap;
887 /* If the specified property is None the requestor is an obsolete client.
888 * We support these by using the specified target atom as the reply property.
890 rprop = event->property;
891 if( rprop == None )
892 rprop = event->target;
894 TRACE("Request for %s in selection %s\n",
895 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
897 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
898 if(event->target == xaMultiple)
900 /* MULTIPLE selection request - will call us back recursively */
901 rprop = EVENT_SelectionRequest_MULTIPLE( event );
902 goto END;
905 /* Lookup the requested target property in the cache */
906 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
908 TRACE("Item not available in cache!\n");
909 goto END;
912 /* Update the X property */
913 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
915 /* If we have a request for a pixmap, return a duplicate */
917 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
919 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
920 pixmap = DuplicatePixmap( *pPixmap );
921 pData = &pixmap;
923 else
924 pData = pCacheEntry->pData;
926 XChangeProperty(g_display, request, rprop,
927 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
928 (unsigned char *)pData, pCacheEntry->nElements);
930 END:
931 if( rprop == None)
932 TRACE("\tRequest ignored\n");
934 /* reply to sender
935 * SelectionNotify should be sent only at the end of a MULTIPLE request
937 if ( !bIsMultiple )
939 result.type = SelectionNotify;
940 result.display = g_display;
941 result.requestor = request;
942 result.selection = event->selection;
943 result.property = rprop;
944 result.target = event->target;
945 result.time = event->time;
946 TRACE("Sending SelectionNotify event...\n");
947 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
952 /***********************************************************************
953 * EVENT_SelectionClear
954 * We receive this event when another client grabs the X selection.
955 * If we lost both PRIMARY and CLIPBOARD we must terminate.
957 void EVENT_SelectionClear( XSelectionClearEvent *event )
959 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
961 TRACE("()\n");
963 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
964 * dictate that *all* selections should be cleared on loss of a selection,
965 * we must give up all the selections we own.
967 if ( g_clearAllSelections || (event->selection == xaClipboard) )
969 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
971 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
972 if ( (event->selection == xaClipboard)
973 && (g_selectionAcquired & S_PRIMARY) )
975 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
978 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
979 if ( (event->selection == XA_PRIMARY)
980 && (g_selectionAcquired & S_CLIPBOARD) )
982 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
985 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
987 else if (event->selection == XA_PRIMARY)
989 TRACE("Lost PRIMARY selection...\n");
990 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
993 /* Once we lose all our selections we have nothing more to do */
994 if (g_selectionAcquired == S_NOSELECTION)
995 TerminateServer(1);
998 /***********************************************************************
999 * EVENT_PropertyNotify
1000 * We use this to release resources like Pixmaps when a selection
1001 * client no longer needs them.
1003 void EVENT_PropertyNotify( XPropertyEvent *event )
1005 TRACE("()\n");
1007 /* Check if we have any resources to free */
1009 switch(event->state)
1011 case PropertyDelete:
1013 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1014 XGetAtomName(event->display, event->atom), (long)event->window);
1016 /* FreeResources( event->atom ); */
1017 break;
1020 case PropertyNewValue:
1022 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1023 XGetAtomName(event->display, event->atom), (long)event->window);
1024 break;
1027 default:
1028 break;
1032 /***********************************************************************
1033 * DuplicatePixmap
1035 Pixmap DuplicatePixmap(Pixmap pixmap)
1037 Pixmap newPixmap;
1038 XImage *xi;
1039 Window root;
1040 int x,y; /* Unused */
1041 unsigned border_width; /* Unused */
1042 unsigned int depth, width, height;
1044 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1046 /* Get the Pixmap dimensions and bit depth */
1047 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1048 &border_width, &depth) )
1049 return 0;
1051 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1052 width, height, depth);
1054 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1056 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1058 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1060 XDestroyImage(xi);
1062 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1063 return newPixmap;
1066 /***********************************************************************
1067 * getGC
1068 * Get a GC to use for drawing
1070 void getGC(Window win, GC *gc)
1072 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1073 XGCValues values;
1074 unsigned int line_width = 6;
1075 int line_style = LineOnOffDash;
1076 int cap_style = CapRound;
1077 int join_style = JoinRound;
1078 int dash_offset = 0;
1079 static char dash_list[] = {12, 24};
1080 int list_length = 2;
1082 /* Create default Graphics Context */
1083 *gc = XCreateGC(g_display, win, valuemask, &values);
1085 /* specify black foreground since default window background is
1086 * white and default foreground is undefined. */
1087 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1089 /* set line attributes */
1090 XSetLineAttributes(g_display, *gc, line_width, line_style,
1091 cap_style, join_style);
1093 /* set dashes */
1094 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1098 /***********************************************************************
1099 * TextOut
1101 void TextOut(Window win, GC gc, char *pStr)
1103 int y_offset, x_offset;
1105 y_offset = 10;
1106 x_offset = 2;
1108 /* output text, centered on each line */
1109 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
1110 strlen(pStr));