Clear the remainder of the page when mapping a section whose size on
[wine/multimedia.git] / windows / x11drv / wineclipsrv.c
blob8285ae9e58682e8edd37221f1c4794ce21b67617
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>
55 * Lightweight debug definitions for Wine Clipboard Server.
56 * The standard FIXME, ERR, WARN & TRACE classes are supported
57 * without debug channels.
58 * The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
59 * TRACE, WARN and ERR and FIXME message displays.
62 /* Internal definitions (do not use these directly) */
64 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
66 extern char __debug_msg_enabled[__DBCL_COUNT];
68 extern const char * const debug_cl_name[__DBCL_COUNT];
70 #define DEBUG_CLASS_COUNT __DBCL_COUNT
72 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
73 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
76 #define __DPRINTF(dbcl) \
77 (!__GET_DEBUGGING(dbcl) || \
78 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
79 ? 0 : printf
81 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
83 /* use configure to allow user to compile out debugging messages */
84 #ifndef NO_TRACE_MSGS
85 #define TRACE __DPRINTF(__DBCL_TRACE)
86 #else
87 #define TRACE __DUMMY_DPRINTF
88 #endif /* NO_TRACE_MSGS */
90 #ifndef NO_DEBUG_MSGS
91 #define WARN __DPRINTF(__DBCL_WARN)
92 #define FIXME __DPRINTF(__DBCL_FIXME)
93 #else
94 #define WARN __DUMMY_DPRINTF
95 #define FIXME __DUMMY_DPRINTF
96 #endif /* NO_DEBUG_MSGS */
98 /* define error macro regardless of what is configured */
99 #define ERR __DPRINTF(__DBCL_ERR)
102 #define TRUE 1
103 #define FALSE 0
104 typedef int BOOL;
106 /* Internal definitions for debugging messages(do not use these directly) */
107 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
108 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
111 /* Selection masks */
113 #define S_NOSELECTION 0
114 #define S_PRIMARY 1
115 #define S_CLIPBOARD 2
117 /* Debugging class masks */
119 #define C_FIXME 1
120 #define C_ERR 2
121 #define C_WARN 4
122 #define C_TRACE 8
125 * Global variables
128 static Display *g_display = NULL;
129 static int screen_num;
130 static char *progname; /* name this program was invoked by */
131 static Window g_win = 0; /* the hidden clipboard server window */
132 static GC g_gc = 0;
134 static char *g_szOutOfMemory = "Insufficient memory!\n";
136 /* X selection context info */
137 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
138 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
139 static int g_selectionAcquired = 0; /* Contains the current selection masks */
140 static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
142 /* Selection cache */
143 typedef struct tag_CACHEENTRY
145 Atom target;
146 Atom type;
147 int nFormat;
148 int nElements;
149 void *pData;
150 } CACHEENTRY, *PCACHEENTRY;
152 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
153 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
154 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
155 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
157 /* Event names */
158 static const char * const event_names[] =
160 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
161 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
162 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
163 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
164 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
165 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
166 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
167 "ClientMessage", "MappingNotify"
172 * Prototypes
175 BOOL Init(int argc, char **argv);
176 void TerminateServer( int ret );
177 int AcquireSelection();
178 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
179 void EmptyCache(PCACHEENTRY pCache, int nItems);
180 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
181 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
182 void EVENT_ProcessEvent( XEvent *event );
183 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
184 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
185 void EVENT_SelectionClear( XSelectionClearEvent *event );
186 void EVENT_PropertyNotify( XPropertyEvent *event );
187 Pixmap DuplicatePixmap(Pixmap pixmap);
188 void TextOut(Window win, GC gc, char *pStr);
189 void getGC(Window win, GC *gc);
192 int main(int argc, char **argv)
194 XEvent event;
196 if ( !Init(argc, argv) )
197 exit(0);
199 /* Acquire the selection after retrieving all clipboard data
200 * owned by the current selection owner. If we were unable to
201 * Acquire any selection, terminate right away.
203 if ( AcquireSelection() == S_NOSELECTION )
204 TerminateServer(0);
206 TRACE("Clipboard server running...\n");
208 /* Start an X event loop */
209 while (1)
211 XNextEvent(g_display, &event);
213 EVENT_ProcessEvent( &event );
218 /**************************************************************************
219 * Init()
220 * Initialize the clipboard server
222 BOOL Init(int argc, char **argv)
224 unsigned int width, height; /* window size */
225 unsigned int border_width = 4; /* four pixels */
226 unsigned int display_width, display_height;
227 char *window_name = "Wine Clipboard Server";
228 XSizeHints *size_hints = NULL;
229 XWMHints *wm_hints = NULL;
230 XClassHint *class_hints = NULL;
231 XTextProperty windowName;
232 char *display_name = NULL;
234 progname = argv[0];
236 if (!(size_hints = XAllocSizeHints()))
238 ERR(g_szOutOfMemory);
239 return 0;
241 if (!(wm_hints = XAllocWMHints()))
243 ERR(g_szOutOfMemory);
244 return 0;
246 if (!(class_hints = XAllocClassHint()))
248 ERR(g_szOutOfMemory);
249 return 0;
252 /* connect to X server */
253 if ( (g_display=XOpenDisplay(display_name)) == NULL )
255 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
256 return 0;
259 /* get screen size from display structure macro */
260 screen_num = DefaultScreen(g_display);
261 display_width = DisplayWidth(g_display, screen_num);
262 display_height = DisplayHeight(g_display, screen_num);
264 /* size window with enough room for text */
265 width = display_width/3, height = display_height/4;
267 /* create opaque window */
268 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
269 0, 0, width, height, border_width, BlackPixel(g_display,
270 screen_num), WhitePixel(g_display,screen_num));
273 /* Set size hints for window manager. The window manager may
274 * override these settings. */
276 /* x, y, width, and height hints are now taken from
277 * the actual settings of the window when mapped. Note
278 * that PPosition and PSize must be specified anyway. */
280 size_hints->flags = PPosition | PSize | PMinSize;
281 size_hints->min_width = 300;
282 size_hints->min_height = 200;
284 /* These calls store window_name into XTextProperty structures
285 * and sets the other fields properly. */
286 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
288 ERR( "structure allocation for windowName failed.\n");
289 TerminateServer(-1);
292 wm_hints->initial_state = NormalState;
293 wm_hints->input = True;
294 wm_hints->flags = StateHint | InputHint;
296 class_hints->res_name = progname;
297 class_hints->res_class = "WineClipSrv";
299 XSetWMProperties(g_display, g_win, &windowName, NULL,
300 argv, argc, size_hints, wm_hints,
301 class_hints);
303 /* Select event types wanted */
304 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
305 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
307 /* create GC for text and drawing */
308 getGC(g_win, &g_gc);
310 /* Display window */
311 /* XMapWindow(g_display, g_win); */
313 /* Set the selections to be acquired from the command line argument.
314 * If none specified, default to all selections we understand.
316 if (argc > 1)
317 g_selectionToAcquire = atoi(argv[1]);
318 else
319 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
321 /* Set the debugging class state from the command line argument */
322 if (argc > 2)
324 int dbgClasses = atoi(argv[2]);
326 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
327 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
328 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
329 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
332 /* Set the "ClearSelections" state from the command line argument */
333 if (argc > 3)
334 g_clearAllSelections = atoi(argv[3]);
336 return TRUE;
340 /**************************************************************************
341 * TerminateServer()
343 void TerminateServer( int ret )
345 TRACE("Terminating Wine clipboard server...\n");
347 /* Free Primary and Clipboard selection caches */
348 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
349 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
351 if (g_gc)
352 XFreeGC(g_display, g_gc);
354 if (g_display)
355 XCloseDisplay(g_display);
357 exit(ret);
361 /**************************************************************************
362 * AcquireSelection()
364 * Acquire the selection after retrieving all clipboard data owned by
365 * the current selection owner.
367 int AcquireSelection()
369 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
372 * For all selections we need to acquire, get a list of all targets
373 * supplied by the current selection owner.
375 if (g_selectionToAcquire & S_PRIMARY)
377 TRACE("Acquiring PRIMARY selection...\n");
378 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
379 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
381 if (g_selectionToAcquire & S_CLIPBOARD)
383 TRACE("Acquiring CLIPBOARD selection...\n");
384 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
385 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
389 * Now that we have cached the data, we proceed to acquire the selections
391 if (g_cPrimaryTargets)
393 /* Acquire the PRIMARY selection */
394 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
395 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
397 g_selectionAcquired |= S_PRIMARY;
399 else
400 TRACE("No PRIMARY targets - ownership not acquired.\n");
402 if (g_cClipboardTargets)
404 /* Acquire the CLIPBOARD selection */
405 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
406 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
408 g_selectionAcquired |= S_CLIPBOARD;
410 else
411 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
413 return g_selectionAcquired;
416 /**************************************************************************
417 * CacheDataFormats
419 * Allocates and caches the list of data formats available from the current selection.
420 * This queries the selection owner for the TARGETS property and saves all
421 * reported property types.
423 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
425 XEvent xe;
426 Atom aTargets;
427 Atom atype=AnyPropertyType;
428 int aformat;
429 unsigned long remain;
430 unsigned long cSelectionTargets = 0;
431 Atom* targetList=NULL;
432 Window ownerSelection = 0;
434 if (!ppCache)
435 return 0;
436 *ppCache = NULL;
438 /* Get the selection owner */
439 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
440 if ( ownerSelection == None )
441 return cSelectionTargets;
444 * Query the selection owner for the TARGETS property
446 aTargets = XInternAtom(g_display, "TARGETS", False);
448 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
449 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
451 XConvertSelection(g_display, SelectionSrc, aTargets,
452 XInternAtom(g_display, "SELECTION_DATA", False),
453 g_win, CurrentTime);
456 * Wait until SelectionNotify is received
458 while( TRUE )
460 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
461 if( xe.xselection.selection == SelectionSrc )
462 break;
465 /* Verify that the selection returned a valid TARGETS property */
466 if ( (xe.xselection.target != aTargets)
467 || (xe.xselection.property == None) )
469 TRACE("\tCould not retrieve TARGETS\n");
470 return cSelectionTargets;
473 /* Read the TARGETS property contents */
474 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
475 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
476 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
477 TRACE("\tCouldn't read TARGETS property\n");
478 else
480 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
481 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
483 * The TARGETS property should have returned us a list of atoms
484 * corresponding to each selection target format supported.
486 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
488 int i;
490 /* Allocate the selection cache */
491 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
493 /* Cache these formats in the selection cache */
494 for (i = 0; i < cSelectionTargets; i++)
496 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
498 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
500 /* Populate the cache entry */
501 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
502 ERR("Failed to fill cache entry!\n");
504 XFree(itemFmtName);
508 /* Free the list of targets */
509 XFree(targetList);
512 return cSelectionTargets;
515 /***********************************************************************
516 * FillCacheEntry
518 * Populates the specified cache entry
520 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
522 XEvent xe;
523 Window w;
524 Atom prop, reqType;
525 Atom atype=AnyPropertyType;
526 int aformat;
527 unsigned long nitems,remain,itemSize;
528 long lRequestLength;
529 unsigned char* val=NULL;
530 BOOL bRet = FALSE;
532 TRACE("Requesting %s selection from %s...\n",
533 XGetAtomName(g_display, target),
534 XGetAtomName(g_display, SelectionSrc) );
536 /* Ask the selection owner to convert the selection to the target format */
537 XConvertSelection(g_display, SelectionSrc, target,
538 XInternAtom(g_display, "SELECTION_DATA", False),
539 g_win, CurrentTime);
541 /* wait until SelectionNotify is received */
542 while( TRUE )
544 if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
545 if( xe.xselection.selection == SelectionSrc )
546 break;
549 /* Now proceed to retrieve the actual converted property from
550 * the SELECTION_DATA atom */
552 w = xe.xselection.requestor;
553 prop = xe.xselection.property;
554 reqType = xe.xselection.target;
556 if(prop == None)
558 TRACE("\tOwner failed to convert selection!\n");
559 return bRet;
562 TRACE("\tretrieving property %s from window %ld into %s\n",
563 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
566 * First request a zero length in order to figure out the request size.
568 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
569 &atype, &aformat, &nitems, &itemSize, &val) != Success)
571 WARN("\tcouldn't get property size\n");
572 return bRet;
575 /* Free zero length return data if any */
576 if ( val )
578 XFree(val);
579 val = NULL;
582 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
583 lRequestLength = (itemSize * aformat/8)/4 + 1;
586 * Retrieve the actual property in the required X format.
588 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
589 &atype, &aformat, &nitems, &remain, &val) != Success)
591 WARN("\tcouldn't read property\n");
592 return bRet;
595 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
596 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
598 if (remain)
600 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
601 goto END;
605 * Populate the cache entry
607 pCacheEntry->target = target;
608 pCacheEntry->type = atype;
609 pCacheEntry->nFormat = aformat;
610 pCacheEntry->nElements = nitems;
612 if (atype == XA_PIXMAP)
614 Pixmap *pPixmap = (Pixmap *)val;
615 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
616 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
617 *pPixmap = newPixmap;
618 pCacheEntry->pData = pPixmap;
620 else
621 pCacheEntry->pData = val;
623 END:
624 /* Delete the property on the window now that we are done
625 * This will send a PropertyNotify event to the selection owner. */
626 XDeleteProperty(g_display,w,prop);
628 return TRUE;
632 /***********************************************************************
633 * LookupCacheItem
635 * Lookup a target atom in the cache and get the matching cache entry
637 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
639 int i;
640 int nCachetargets = 0;
641 PCACHEENTRY pCache = NULL;
642 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
644 /* Locate the cache to be used based on the selection type */
645 if ( selection == XA_PRIMARY )
647 pCache = g_pPrimaryCache;
648 nCachetargets = g_cPrimaryTargets;
650 else if ( selection == xaClipboard )
652 pCache = g_pClipboardCache;
653 nCachetargets = g_cClipboardTargets;
656 if (!pCache || !ppCacheEntry)
657 return FALSE;
659 *ppCacheEntry = NULL;
661 /* Look for the target item in the cache */
662 for (i = 0; i < nCachetargets; i++)
664 if (pCache[i].target == target)
666 *ppCacheEntry = &pCache[i];
667 return TRUE;
671 return FALSE;
675 /***********************************************************************
676 * EmptyCache
678 * Empties the specified cache
680 void EmptyCache(PCACHEENTRY pCache, int nItems)
682 int i;
684 if (!pCache)
685 return;
687 /* Release all items in the cache */
688 for (i = 0; i < nItems; i++)
690 if (pCache[i].target && pCache[i].pData)
692 /* If we have a Pixmap, free it first */
693 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
695 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
697 TRACE("Freeing %s (handle=%ld)...\n",
698 XGetAtomName(g_display, pCache[i].target), *pPixmap);
700 XFreePixmap(g_display, *pPixmap);
702 /* Free the cached data item (allocated by us) */
703 free(pCache[i].pData);
705 else
707 TRACE("Freeing %s (%p)...\n",
708 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
710 /* Free the cached data item (allocated by X) */
711 XFree(pCache[i].pData);
716 /* Destroy the cache */
717 free(pCache);
721 /***********************************************************************
722 * EVENT_ProcessEvent
724 * Process an X event.
726 void EVENT_ProcessEvent( XEvent *event )
729 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
732 switch (event->type)
734 case Expose:
735 /* don't draw the window */
736 if (event->xexpose.count != 0)
737 break;
739 /* Output something */
740 TextOut(g_win, g_gc, "Click here to terminate");
741 break;
743 case ConfigureNotify:
744 break;
746 case ButtonPress:
747 /* fall into KeyPress (no break) */
748 case KeyPress:
749 TerminateServer(1);
750 break;
752 case SelectionRequest:
753 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
754 break;
756 case SelectionClear:
757 EVENT_SelectionClear( (XSelectionClearEvent*)event );
758 break;
760 case PropertyNotify:
761 // EVENT_PropertyNotify( (XPropertyEvent *)event );
762 break;
764 default: /* ignore all other events */
765 break;
767 } /* end switch */
772 /***********************************************************************
773 * EVENT_SelectionRequest_MULTIPLE
774 * Service a MULTIPLE selection request event
775 * rprop contains a list of (target,property) atom pairs.
776 * The first atom names a target and the second names a property.
777 * The effect is as if we have received a sequence of SelectionRequest events
778 * (one for each atom pair) except that:
779 * 1. We reply with a SelectionNotify only when all the requested conversions
780 * have been performed.
781 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
782 * we replace the atom in the property by None.
784 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
786 Atom rprop;
787 Atom atype=AnyPropertyType;
788 int aformat;
789 unsigned long remain;
790 Atom* targetPropList=NULL;
791 unsigned long cTargetPropList = 0;
792 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
794 /* If the specified property is None the requestor is an obsolete client.
795 * We support these by using the specified target atom as the reply property.
797 rprop = pevent->property;
798 if( rprop == None )
799 rprop = pevent->target;
800 if (!rprop)
801 goto END;
803 /* Read the MULTIPLE property contents. This should contain a list of
804 * (target,property) atom pairs.
806 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
807 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
808 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
809 TRACE("\tCouldn't read MULTIPLE property\n");
810 else
812 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
813 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
816 * Make sure we got what we expect.
817 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
818 * in a MULTIPLE selection request should be of type ATOM_PAIR.
819 * However some X apps(such as XPaint) are not compliant with this and return
820 * a user defined atom in atype when XGetWindowProperty is called.
821 * The data *is* an atom pair but is not denoted as such.
823 if(aformat == 32 /* atype == xAtomPair */ )
825 int i;
827 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
828 * for each (target,property) pair */
830 for (i = 0; i < cTargetPropList; i+=2)
832 char *targetName = XGetAtomName(g_display, targetPropList[i]);
833 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
834 XSelectionRequestEvent event;
836 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
837 XFree(targetName);
838 XFree(propName);
840 /* We must have a non "None" property to service a MULTIPLE target atom */
841 if ( !targetPropList[i+1] )
843 TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
844 continue;
847 /* Set up an XSelectionRequestEvent for this (target,property) pair */
848 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
849 event.target = targetPropList[i];
850 event.property = targetPropList[i+1];
852 /* Fire a SelectionRequest, informing the handler that we are processing
853 * a MULTIPLE selection request event.
855 EVENT_SelectionRequest( &event, TRUE );
859 /* Free the list of targets/properties */
860 XFree(targetPropList);
863 END:
864 return rprop;
868 /***********************************************************************
869 * EVENT_SelectionRequest
870 * Process an event selection request event.
871 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
872 * recursively while servicing a "MULTIPLE" selection target.
875 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
877 XSelectionEvent result;
878 Atom rprop = None;
879 Window request = event->requestor;
880 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
881 PCACHEENTRY pCacheEntry = NULL;
882 void *pData = NULL;
883 Pixmap pixmap;
885 /* If the specified property is None the requestor is an obsolete client.
886 * We support these by using the specified target atom as the reply property.
888 rprop = event->property;
889 if( rprop == None )
890 rprop = event->target;
892 TRACE("Request for %s in selection %s\n",
893 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
895 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
896 if(event->target == xaMultiple)
898 /* MULTIPLE selection request - will call us back recursively */
899 rprop = EVENT_SelectionRequest_MULTIPLE( event );
900 goto END;
903 /* Lookup the requested target property in the cache */
904 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
906 TRACE("Item not available in cache!\n");
907 goto END;
910 /* Update the X property */
911 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
913 /* If we have a request for a pixmap, return a duplicate */
915 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
917 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
918 pixmap = DuplicatePixmap( *pPixmap );
919 pData = &pixmap;
921 else
922 pData = pCacheEntry->pData;
924 XChangeProperty(g_display, request, rprop,
925 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
926 (unsigned char *)pData, pCacheEntry->nElements);
928 END:
929 if( rprop == None)
930 TRACE("\tRequest ignored\n");
932 /* reply to sender
933 * SelectionNotify should be sent only at the end of a MULTIPLE request
935 if ( !bIsMultiple )
937 result.type = SelectionNotify;
938 result.display = g_display;
939 result.requestor = request;
940 result.selection = event->selection;
941 result.property = rprop;
942 result.target = event->target;
943 result.time = event->time;
944 TRACE("Sending SelectionNotify event...\n");
945 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
950 /***********************************************************************
951 * EVENT_SelectionClear
952 * We receive this event when another client grabs the X selection.
953 * If we lost both PRIMARY and CLIPBOARD we must terminate.
955 void EVENT_SelectionClear( XSelectionClearEvent *event )
957 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
959 TRACE("()\n");
961 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
962 * dictate that *all* selections should be cleared on loss of a selection,
963 * we must give up all the selections we own.
965 if ( g_clearAllSelections || (event->selection == xaClipboard) )
967 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
969 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
970 if ( (event->selection == xaClipboard)
971 && (g_selectionAcquired & S_PRIMARY) )
973 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
976 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
977 if ( (event->selection == XA_PRIMARY)
978 && (g_selectionAcquired & S_CLIPBOARD) )
980 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
983 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
985 else if (event->selection == XA_PRIMARY)
987 TRACE("Lost PRIMARY selection...\n");
988 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
991 /* Once we lose all our selections we have nothing more to do */
992 if (g_selectionAcquired == S_NOSELECTION)
993 TerminateServer(1);
996 /***********************************************************************
997 * EVENT_PropertyNotify
998 * We use this to release resources like Pixmaps when a selection
999 * client no longer needs them.
1001 void EVENT_PropertyNotify( XPropertyEvent *event )
1003 TRACE("()\n");
1005 /* Check if we have any resources to free */
1007 switch(event->state)
1009 case PropertyDelete:
1011 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1012 XGetAtomName(event->display, event->atom), (long)event->window);
1014 /* FreeResources( event->atom ); */
1015 break;
1018 case PropertyNewValue:
1020 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1021 XGetAtomName(event->display, event->atom), (long)event->window);
1022 break;
1025 default:
1026 break;
1030 /***********************************************************************
1031 * DuplicatePixmap
1033 Pixmap DuplicatePixmap(Pixmap pixmap)
1035 Pixmap newPixmap;
1036 XImage *xi;
1037 Window root;
1038 int x,y; /* Unused */
1039 unsigned border_width; /* Unused */
1040 unsigned int depth, width, height;
1042 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1044 /* Get the Pixmap dimensions and bit depth */
1045 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1046 &border_width, &depth) )
1047 return 0;
1049 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1050 width, height, depth);
1052 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1054 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1056 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1058 XDestroyImage(xi);
1060 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1061 return newPixmap;
1064 /***********************************************************************
1065 * getGC
1066 * Get a GC to use for drawing
1068 void getGC(Window win, GC *gc)
1070 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1071 XGCValues values;
1072 unsigned int line_width = 6;
1073 int line_style = LineOnOffDash;
1074 int cap_style = CapRound;
1075 int join_style = JoinRound;
1076 int dash_offset = 0;
1077 static char dash_list[] = {12, 24};
1078 int list_length = 2;
1080 /* Create default Graphics Context */
1081 *gc = XCreateGC(g_display, win, valuemask, &values);
1083 /* specify black foreground since default window background is
1084 * white and default foreground is undefined. */
1085 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1087 /* set line attributes */
1088 XSetLineAttributes(g_display, *gc, line_width, line_style,
1089 cap_style, join_style);
1091 /* set dashes */
1092 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1096 /***********************************************************************
1097 * TextOut
1099 void TextOut(Window win, GC gc, char *pStr)
1101 int y_offset, x_offset;
1103 y_offset = 10;
1104 x_offset = 2;
1106 /* output text, centered on each line */
1107 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
1108 strlen(pStr));