Fixed some issues found by winapi_check.
[wine/dcerpc.git] / dlls / x11drv / wineclipsrv.c
blobf3d65f3cc173bf59ddac5fbd70af3146a6d466cc
1 /*
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
20 * USAGE:
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
33 * event is received.
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.
39 * NOTES:
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.
58 * TODO:
61 #include "config.h"
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <X11/Xlib.h>
66 #include <X11/Xutil.h>
67 #include <X11/Xos.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) */
80 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
82 extern char __debug_msg_enabled[__DBCL_COUNT];
84 extern const char * const debug_cl_name[__DBCL_COUNT];
86 #define DEBUG_CLASS_COUNT __DBCL_COUNT
88 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
89 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
92 #define __DPRINTF(dbcl) \
93 (!__GET_DEBUGGING(dbcl) || \
94 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
95 ? 0 : printf
97 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
99 /* use configure to allow user to compile out debugging messages */
100 #ifndef NO_TRACE_MSGS
101 #define TRACE __DPRINTF(__DBCL_TRACE)
102 #else
103 #define TRACE __DUMMY_DPRINTF
104 #endif /* NO_TRACE_MSGS */
106 #ifndef NO_DEBUG_MSGS
107 #define WARN __DPRINTF(__DBCL_WARN)
108 #define FIXME __DPRINTF(__DBCL_FIXME)
109 #else
110 #define WARN __DUMMY_DPRINTF
111 #define FIXME __DUMMY_DPRINTF
112 #endif /* NO_DEBUG_MSGS */
114 /* define error macro regardless of what is configured */
115 #define ERR __DPRINTF(__DBCL_ERR)
118 #define TRUE 1
119 #define FALSE 0
120 typedef int BOOL;
122 /* Internal definitions for debugging messages(do not use these directly) */
123 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
124 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
127 /* Selection masks */
129 #define S_NOSELECTION 0
130 #define S_PRIMARY 1
131 #define S_CLIPBOARD 2
133 /* Debugging class masks */
135 #define C_FIXME 1
136 #define C_ERR 2
137 #define C_WARN 4
138 #define C_TRACE 8
141 * Global variables
144 static Display *g_display = NULL;
145 static int screen_num;
146 static char *progname; /* name this program was invoked by */
147 static Window g_win = 0; /* the hidden clipboard server window */
148 static GC g_gc = 0;
150 static char *g_szOutOfMemory = "Insufficient memory!\n";
152 /* X selection context info */
153 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
154 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
155 static int g_selectionAcquired = 0; /* Contains the current selection masks */
156 static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
158 /* Selection cache */
159 typedef struct tag_CACHEENTRY
161 Atom target;
162 Atom type;
163 int nFormat;
164 int nElements;
165 void *pData;
166 } CACHEENTRY, *PCACHEENTRY;
168 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
169 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
170 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
171 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
173 /* Event names */
174 static const char * const event_names[] =
176 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
177 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
178 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
179 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
180 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
181 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
182 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
183 "ClientMessage", "MappingNotify"
188 * Prototypes
191 int RunAsDaemon( void );
192 BOOL Init(int argc, char **argv);
193 void TerminateServer( int ret );
194 int AcquireSelection();
195 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
196 void EmptyCache(PCACHEENTRY pCache, int nItems);
197 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
198 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
199 void EVENT_ProcessEvent( XEvent *event );
200 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
201 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
202 void EVENT_SelectionClear( XSelectionClearEvent *event );
203 void EVENT_PropertyNotify( XPropertyEvent *event );
204 Pixmap DuplicatePixmap(Pixmap pixmap);
205 void TextOut(Window win, GC gc, char *pStr);
206 void getGC(Window win, GC *gc);
209 int main(int argc, char **argv)
211 XEvent event;
213 if ( RunAsDaemon() == -1 )
215 ERR("could not run as daemon\n");
216 exit(1);
219 if ( !Init(argc, argv) )
220 exit(0);
222 /* Acquire the selection after retrieving all clipboard data
223 * owned by the current selection owner. If we were unable to
224 * Acquire any selection, terminate right away.
226 if ( AcquireSelection() == S_NOSELECTION )
227 TerminateServer(0);
229 TRACE("Clipboard server running...\n");
231 /* Start an X event loop */
232 while (1)
234 XNextEvent(g_display, &event);
236 EVENT_ProcessEvent( &event );
241 /**************************************************************************
242 * RunAsDaemon()
244 int RunAsDaemon( void )
246 int i;
248 /* fork child process and let parent exit ; gets rid of original PID */
249 switch( fork() )
251 case -1:
252 ERR("fork failed\n");
253 return(-1);
254 case 0:
255 exit(0);
256 break;
259 /* below is child process w/ new PID, set as session leader */
260 setsid();
262 /* close stdin,stdout,stderr and file descriptors (overkill method) */
263 for ( i = 0; i < 256 ; i++ )
264 close(i);
266 TRACE("now running as daemon...\n");
267 return 0;
271 /**************************************************************************
272 * Init()
273 * Initialize the clipboard server
275 BOOL Init(int argc, char **argv)
277 unsigned int width, height; /* window size */
278 unsigned int border_width = 4; /* four pixels */
279 unsigned int display_width, display_height;
280 char *window_name = "Wine Clipboard Server";
281 XSizeHints *size_hints = NULL;
282 XWMHints *wm_hints = NULL;
283 XClassHint *class_hints = NULL;
284 XTextProperty windowName;
285 char *display_name = NULL;
287 progname = argv[0];
289 if (!(size_hints = XAllocSizeHints()))
291 ERR(g_szOutOfMemory);
292 return 0;
294 if (!(wm_hints = XAllocWMHints()))
296 ERR(g_szOutOfMemory);
297 return 0;
299 if (!(class_hints = XAllocClassHint()))
301 ERR(g_szOutOfMemory);
302 return 0;
305 /* connect to X server */
306 if ( (g_display=XOpenDisplay(display_name)) == NULL )
308 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
309 return 0;
312 /* get screen size from display structure macro */
313 screen_num = DefaultScreen(g_display);
314 display_width = DisplayWidth(g_display, screen_num);
315 display_height = DisplayHeight(g_display, screen_num);
317 /* size window with enough room for text */
318 width = display_width/3, height = display_height/4;
320 /* create opaque window */
321 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
322 0, 0, width, height, border_width, BlackPixel(g_display,
323 screen_num), WhitePixel(g_display,screen_num));
326 /* Set size hints for window manager. The window manager may
327 * override these settings. */
329 /* x, y, width, and height hints are now taken from
330 * the actual settings of the window when mapped. Note
331 * that PPosition and PSize must be specified anyway. */
333 size_hints->flags = PPosition | PSize | PMinSize;
334 size_hints->min_width = 300;
335 size_hints->min_height = 200;
337 /* These calls store window_name into XTextProperty structures
338 * and sets the other fields properly. */
339 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
341 ERR( "structure allocation for windowName failed.\n");
342 TerminateServer(-1);
345 wm_hints->initial_state = NormalState;
346 wm_hints->input = True;
347 wm_hints->flags = StateHint | InputHint;
349 class_hints->res_name = progname;
350 class_hints->res_class = "WineClipSrv";
352 XSetWMProperties(g_display, g_win, &windowName, NULL,
353 argv, argc, size_hints, wm_hints,
354 class_hints);
356 /* Select event types wanted */
357 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
358 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
360 /* create GC for text and drawing */
361 getGC(g_win, &g_gc);
363 /* Display window */
364 /* XMapWindow(g_display, g_win); */
366 /* Set the selections to be acquired from the command line argument.
367 * If none specified, default to all selections we understand.
369 if (argc > 1)
370 g_selectionToAcquire = atoi(argv[1]);
371 else
372 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
374 /* Set the debugging class state from the command line argument */
375 if (argc > 2)
377 int dbgClasses = atoi(argv[2]);
379 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
380 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
381 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
382 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
385 /* Set the "ClearSelections" state from the command line argument */
386 if (argc > 3)
387 g_clearAllSelections = atoi(argv[3]);
389 return TRUE;
393 /**************************************************************************
394 * TerminateServer()
396 void TerminateServer( int ret )
398 TRACE("Terminating Wine clipboard server...\n");
400 /* Free Primary and Clipboard selection caches */
401 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
402 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
404 if (g_gc)
405 XFreeGC(g_display, g_gc);
407 if (g_display)
408 XCloseDisplay(g_display);
410 exit(ret);
414 /**************************************************************************
415 * AcquireSelection()
417 * Acquire the selection after retrieving all clipboard data owned by
418 * the current selection owner.
420 int AcquireSelection()
422 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
425 * For all selections we need to acquire, get a list of all targets
426 * supplied by the current selection owner.
428 if (g_selectionToAcquire & S_PRIMARY)
430 TRACE("Acquiring PRIMARY selection...\n");
431 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
432 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
434 if (g_selectionToAcquire & S_CLIPBOARD)
436 TRACE("Acquiring CLIPBOARD selection...\n");
437 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
438 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
442 * Now that we have cached the data, we proceed to acquire the selections
444 if (g_cPrimaryTargets)
446 /* Acquire the PRIMARY selection */
447 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
448 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
450 g_selectionAcquired |= S_PRIMARY;
452 else
453 TRACE("No PRIMARY targets - ownership not acquired.\n");
455 if (g_cClipboardTargets)
457 /* Acquire the CLIPBOARD selection */
458 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
459 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
461 g_selectionAcquired |= S_CLIPBOARD;
463 else
464 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
466 return g_selectionAcquired;
469 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
471 time_t end_time;
473 /* Set up a 10 second time out */
474 end_time=time(NULL)+10;
478 struct timeval nap;
480 if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
482 if( xe->xselection.selection == SelectionSrc )
483 return TRUE;
486 if (time(NULL)>end_time)
487 break;
489 /* Sleep a bit to make this busy wait less brutal */
490 nap.tv_sec = 0;
491 nap.tv_usec = 10;
492 select(0, NULL, NULL, NULL, &nap);
494 while (TRUE);
496 return FALSE;
499 /**************************************************************************
500 * CacheDataFormats
502 * Allocates and caches the list of data formats available from the current selection.
503 * This queries the selection owner for the TARGETS property and saves all
504 * reported property types.
506 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
508 XEvent xe;
509 Atom aTargets;
510 Atom atype=AnyPropertyType;
511 int aformat;
512 unsigned long remain;
513 unsigned long cSelectionTargets = 0;
514 Atom* targetList=NULL;
515 Window ownerSelection = 0;
517 if (!ppCache)
518 return 0;
519 *ppCache = NULL;
521 /* Get the selection owner */
522 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
523 if ( ownerSelection == None )
524 return cSelectionTargets;
527 * Query the selection owner for the TARGETS property
529 aTargets = XInternAtom(g_display, "TARGETS", False);
531 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
532 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
534 XConvertSelection(g_display, SelectionSrc, aTargets,
535 XInternAtom(g_display, "SELECTION_DATA", False),
536 g_win, CurrentTime);
539 * Wait until SelectionNotify is received
541 if (!GetSelectionEvent(SelectionSrc, &xe))
542 return 0;
544 /* Verify that the selection returned a valid TARGETS property */
545 if ( (xe.xselection.target != aTargets)
546 || (xe.xselection.property == None) )
548 TRACE("\tCould not retrieve TARGETS\n");
549 return cSelectionTargets;
552 /* Read the TARGETS property contents */
553 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
554 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
555 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
556 TRACE("\tCouldn't read TARGETS property\n");
557 else
559 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
560 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
562 * The TARGETS property should have returned us a list of atoms
563 * corresponding to each selection target format supported.
565 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
567 int i;
569 /* Allocate the selection cache */
570 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
572 /* Cache these formats in the selection cache */
573 for (i = 0; i < cSelectionTargets; i++)
575 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
577 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
579 /* Populate the cache entry */
580 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
581 ERR("Failed to fill cache entry!\n");
583 XFree(itemFmtName);
587 /* Free the list of targets */
588 XFree(targetList);
591 return cSelectionTargets;
594 /***********************************************************************
595 * FillCacheEntry
597 * Populates the specified cache entry
599 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
601 XEvent xe;
602 Window w;
603 Atom prop, reqType;
604 Atom atype=AnyPropertyType;
605 int aformat;
606 unsigned long nitems,remain,itemSize;
607 long lRequestLength;
608 unsigned char* val=NULL;
609 BOOL bRet = FALSE;
611 TRACE("Requesting %s selection from %s...\n",
612 XGetAtomName(g_display, target),
613 XGetAtomName(g_display, SelectionSrc) );
615 /* Ask the selection owner to convert the selection to the target format */
616 XConvertSelection(g_display, SelectionSrc, target,
617 XInternAtom(g_display, "SELECTION_DATA", False),
618 g_win, CurrentTime);
620 /* wait until SelectionNotify is received */
621 if (!GetSelectionEvent(SelectionSrc,&xe))
622 return bRet;
624 /* Now proceed to retrieve the actual converted property from
625 * the SELECTION_DATA atom */
627 w = xe.xselection.requestor;
628 prop = xe.xselection.property;
629 reqType = xe.xselection.target;
631 if(prop == None)
633 TRACE("\tOwner failed to convert selection!\n");
634 return bRet;
637 TRACE("\tretrieving property %s from window %ld into %s\n",
638 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
641 * First request a zero length in order to figure out the request size.
643 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
644 &atype, &aformat, &nitems, &itemSize, &val) != Success)
646 WARN("\tcouldn't get property size\n");
647 return bRet;
650 /* Free zero length return data if any */
651 if ( val )
653 XFree(val);
654 val = NULL;
657 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
658 lRequestLength = (itemSize * aformat/8)/4 + 1;
661 * Retrieve the actual property in the required X format.
663 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
664 &atype, &aformat, &nitems, &remain, &val) != Success)
666 WARN("\tcouldn't read property\n");
667 return bRet;
670 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
671 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
673 if (remain)
675 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
676 goto END;
680 * Populate the cache entry
682 pCacheEntry->target = target;
683 pCacheEntry->type = atype;
684 pCacheEntry->nFormat = aformat;
685 pCacheEntry->nElements = nitems;
687 if (atype == XA_PIXMAP)
689 Pixmap *pPixmap = (Pixmap *)val;
690 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
691 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
692 *pPixmap = newPixmap;
693 pCacheEntry->pData = pPixmap;
695 else
696 pCacheEntry->pData = val;
698 END:
699 /* Delete the property on the window now that we are done
700 * This will send a PropertyNotify event to the selection owner. */
701 XDeleteProperty(g_display,w,prop);
703 return TRUE;
707 /***********************************************************************
708 * LookupCacheItem
710 * Lookup a target atom in the cache and get the matching cache entry
712 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
714 int i;
715 int nCachetargets = 0;
716 PCACHEENTRY pCache = NULL;
717 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
719 /* Locate the cache to be used based on the selection type */
720 if ( selection == XA_PRIMARY )
722 pCache = g_pPrimaryCache;
723 nCachetargets = g_cPrimaryTargets;
725 else if ( selection == xaClipboard )
727 pCache = g_pClipboardCache;
728 nCachetargets = g_cClipboardTargets;
731 if (!pCache || !ppCacheEntry)
732 return FALSE;
734 *ppCacheEntry = NULL;
736 /* Look for the target item in the cache */
737 for (i = 0; i < nCachetargets; i++)
739 if (pCache[i].target == target)
741 *ppCacheEntry = &pCache[i];
742 return TRUE;
746 return FALSE;
750 /***********************************************************************
751 * EmptyCache
753 * Empties the specified cache
755 void EmptyCache(PCACHEENTRY pCache, int nItems)
757 int i;
759 if (!pCache)
760 return;
762 /* Release all items in the cache */
763 for (i = 0; i < nItems; i++)
765 if (pCache[i].target && pCache[i].pData)
767 /* If we have a Pixmap, free it first */
768 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
770 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
772 TRACE("Freeing %s (handle=%ld)...\n",
773 XGetAtomName(g_display, pCache[i].target), *pPixmap);
775 XFreePixmap(g_display, *pPixmap);
777 /* Free the cached data item (allocated by us) */
778 free(pCache[i].pData);
780 else
782 TRACE("Freeing %s (%p)...\n",
783 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
785 /* Free the cached data item (allocated by X) */
786 XFree(pCache[i].pData);
791 /* Destroy the cache */
792 free(pCache);
796 /***********************************************************************
797 * EVENT_ProcessEvent
799 * Process an X event.
801 void EVENT_ProcessEvent( XEvent *event )
804 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
807 switch (event->type)
809 case Expose:
810 /* don't draw the window */
811 if (event->xexpose.count != 0)
812 break;
814 /* Output something */
815 TextOut(g_win, g_gc, "Click here to terminate");
816 break;
818 case ConfigureNotify:
819 break;
821 case ButtonPress:
822 /* fall into KeyPress (no break) */
823 case KeyPress:
824 TerminateServer(1);
825 break;
827 case SelectionRequest:
828 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
829 break;
831 case SelectionClear:
832 EVENT_SelectionClear( (XSelectionClearEvent*)event );
833 break;
835 case PropertyNotify:
836 #if 0
837 EVENT_PropertyNotify( (XPropertyEvent *)event );
838 #endif
839 break;
841 default: /* ignore all other events */
842 break;
844 } /* end switch */
849 /***********************************************************************
850 * EVENT_SelectionRequest_MULTIPLE
851 * Service a MULTIPLE selection request event
852 * rprop contains a list of (target,property) atom pairs.
853 * The first atom names a target and the second names a property.
854 * The effect is as if we have received a sequence of SelectionRequest events
855 * (one for each atom pair) except that:
856 * 1. We reply with a SelectionNotify only when all the requested conversions
857 * have been performed.
858 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
859 * we replace the atom in the property by None.
861 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
863 Atom rprop;
864 Atom atype=AnyPropertyType;
865 int aformat;
866 unsigned long remain;
867 Atom* targetPropList=NULL;
868 unsigned long cTargetPropList = 0;
869 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
871 /* If the specified property is None the requestor is an obsolete client.
872 * We support these by using the specified target atom as the reply property.
874 rprop = pevent->property;
875 if( rprop == None )
876 rprop = pevent->target;
877 if (!rprop)
878 goto END;
880 /* Read the MULTIPLE property contents. This should contain a list of
881 * (target,property) atom pairs.
883 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
884 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
885 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
886 TRACE("\tCouldn't read MULTIPLE property\n");
887 else
889 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
890 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
893 * Make sure we got what we expect.
894 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
895 * in a MULTIPLE selection request should be of type ATOM_PAIR.
896 * However some X apps(such as XPaint) are not compliant with this and return
897 * a user defined atom in atype when XGetWindowProperty is called.
898 * The data *is* an atom pair but is not denoted as such.
900 if(aformat == 32 /* atype == xAtomPair */ )
902 int i;
904 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
905 * for each (target,property) pair */
907 for (i = 0; i < cTargetPropList; i+=2)
909 char *targetName = XGetAtomName(g_display, targetPropList[i]);
910 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
911 XSelectionRequestEvent event;
913 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
914 XFree(targetName);
915 XFree(propName);
917 /* We must have a non "None" property to service a MULTIPLE target atom */
918 if ( !targetPropList[i+1] )
920 TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
921 continue;
924 /* Set up an XSelectionRequestEvent for this (target,property) pair */
925 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
926 event.target = targetPropList[i];
927 event.property = targetPropList[i+1];
929 /* Fire a SelectionRequest, informing the handler that we are processing
930 * a MULTIPLE selection request event.
932 EVENT_SelectionRequest( &event, TRUE );
936 /* Free the list of targets/properties */
937 XFree(targetPropList);
940 END:
941 return rprop;
945 /***********************************************************************
946 * EVENT_SelectionRequest
947 * Process an event selection request event.
948 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
949 * recursively while servicing a "MULTIPLE" selection target.
952 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
954 XSelectionEvent result;
955 Atom rprop = None;
956 Window request = event->requestor;
957 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
958 PCACHEENTRY pCacheEntry = NULL;
959 void *pData = NULL;
960 Pixmap pixmap;
962 /* If the specified property is None the requestor is an obsolete client.
963 * We support these by using the specified target atom as the reply property.
965 rprop = event->property;
966 if( rprop == None )
967 rprop = event->target;
969 TRACE("Request for %s in selection %s\n",
970 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
972 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
973 if(event->target == xaMultiple)
975 /* MULTIPLE selection request - will call us back recursively */
976 rprop = EVENT_SelectionRequest_MULTIPLE( event );
977 goto END;
980 /* Lookup the requested target property in the cache */
981 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
983 TRACE("Item not available in cache!\n");
984 goto END;
987 /* Update the X property */
988 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
990 /* If we have a request for a pixmap, return a duplicate */
992 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
994 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
995 pixmap = DuplicatePixmap( *pPixmap );
996 pData = &pixmap;
998 else
999 pData = pCacheEntry->pData;
1001 XChangeProperty(g_display, request, rprop,
1002 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
1003 (unsigned char *)pData, pCacheEntry->nElements);
1005 END:
1006 if( rprop == None)
1007 TRACE("\tRequest ignored\n");
1009 /* reply to sender
1010 * SelectionNotify should be sent only at the end of a MULTIPLE request
1012 if ( !bIsMultiple )
1014 result.type = SelectionNotify;
1015 result.display = g_display;
1016 result.requestor = request;
1017 result.selection = event->selection;
1018 result.property = rprop;
1019 result.target = event->target;
1020 result.time = event->time;
1021 TRACE("Sending SelectionNotify event...\n");
1022 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
1027 /***********************************************************************
1028 * EVENT_SelectionClear
1029 * We receive this event when another client grabs the X selection.
1030 * If we lost both PRIMARY and CLIPBOARD we must terminate.
1032 void EVENT_SelectionClear( XSelectionClearEvent *event )
1034 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
1036 TRACE("()\n");
1038 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
1039 * dictate that *all* selections should be cleared on loss of a selection,
1040 * we must give up all the selections we own.
1042 if ( g_clearAllSelections || (event->selection == xaClipboard) )
1044 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
1046 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
1047 if ( (event->selection == xaClipboard)
1048 && (g_selectionAcquired & S_PRIMARY) )
1050 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1053 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
1054 if ( (event->selection == XA_PRIMARY)
1055 && (g_selectionAcquired & S_CLIPBOARD) )
1057 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1060 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
1062 else if (event->selection == XA_PRIMARY)
1064 TRACE("Lost PRIMARY selection...\n");
1065 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
1068 /* Once we lose all our selections we have nothing more to do */
1069 if (g_selectionAcquired == S_NOSELECTION)
1070 TerminateServer(1);
1073 /***********************************************************************
1074 * EVENT_PropertyNotify
1075 * We use this to release resources like Pixmaps when a selection
1076 * client no longer needs them.
1078 void EVENT_PropertyNotify( XPropertyEvent *event )
1080 TRACE("()\n");
1082 /* Check if we have any resources to free */
1084 switch(event->state)
1086 case PropertyDelete:
1088 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1089 XGetAtomName(event->display, event->atom), (long)event->window);
1091 /* FreeResources( event->atom ); */
1092 break;
1095 case PropertyNewValue:
1097 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1098 XGetAtomName(event->display, event->atom), (long)event->window);
1099 break;
1102 default:
1103 break;
1107 /***********************************************************************
1108 * DuplicatePixmap
1110 Pixmap DuplicatePixmap(Pixmap pixmap)
1112 Pixmap newPixmap;
1113 XImage *xi;
1114 Window root;
1115 int x,y; /* Unused */
1116 unsigned border_width; /* Unused */
1117 unsigned int depth, width, height;
1119 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1121 /* Get the Pixmap dimensions and bit depth */
1122 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1123 &border_width, &depth) )
1124 return 0;
1126 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1127 width, height, depth);
1129 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1131 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1133 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1135 XDestroyImage(xi);
1137 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1138 return newPixmap;
1141 /***********************************************************************
1142 * getGC
1143 * Get a GC to use for drawing
1145 void getGC(Window win, GC *gc)
1147 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1148 XGCValues values;
1149 unsigned int line_width = 6;
1150 int line_style = LineOnOffDash;
1151 int cap_style = CapRound;
1152 int join_style = JoinRound;
1153 int dash_offset = 0;
1154 static char dash_list[] = {12, 24};
1155 int list_length = 2;
1157 /* Create default Graphics Context */
1158 *gc = XCreateGC(g_display, win, valuemask, &values);
1160 /* specify black foreground since default window background is
1161 * white and default foreground is undefined. */
1162 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1164 /* set line attributes */
1165 XSetLineAttributes(g_display, *gc, line_width, line_style,
1166 cap_style, join_style);
1168 /* set dashes */
1169 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1173 /***********************************************************************
1174 * TextOut
1176 void TextOut(Window win, GC gc, char *pStr)
1178 int y_offset, x_offset;
1180 y_offset = 10;
1181 x_offset = 2;
1183 /* output text, centered on each line */
1184 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
1185 strlen(pStr));