Removed obsolete INT_Int31Handler.
[wine/multimedia.git] / dlls / x11drv / wineclipsrv.c
blobac70e48ac10923791c84bb08efec91bd7fefcae4
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 #ifdef __SUNPRO_C
81 #define __FUNCTION__ __func__
82 #endif
84 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
86 extern char __debug_msg_enabled[__DBCL_COUNT];
88 extern const char * const debug_cl_name[__DBCL_COUNT];
90 #define DEBUG_CLASS_COUNT __DBCL_COUNT
92 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
93 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
96 #define __DPRINTF(dbcl) \
97 (!__GET_DEBUGGING(dbcl) || \
98 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
99 ? 0 : printf
101 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
103 /* use configure to allow user to compile out debugging messages */
104 #ifndef NO_TRACE_MSGS
105 #define TRACE __DPRINTF(__DBCL_TRACE)
106 #else
107 #define TRACE __DUMMY_DPRINTF
108 #endif /* NO_TRACE_MSGS */
110 #ifndef NO_DEBUG_MSGS
111 #define WARN __DPRINTF(__DBCL_WARN)
112 #define FIXME __DPRINTF(__DBCL_FIXME)
113 #else
114 #define WARN __DUMMY_DPRINTF
115 #define FIXME __DUMMY_DPRINTF
116 #endif /* NO_DEBUG_MSGS */
118 /* define error macro regardless of what is configured */
119 #define ERR __DPRINTF(__DBCL_ERR)
122 #define TRUE 1
123 #define FALSE 0
124 typedef int BOOL;
126 /* Internal definitions for debugging messages(do not use these directly) */
127 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
128 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
131 /* Selection masks */
133 #define S_NOSELECTION 0
134 #define S_PRIMARY 1
135 #define S_CLIPBOARD 2
137 /* Debugging class masks */
139 #define C_FIXME 1
140 #define C_ERR 2
141 #define C_WARN 4
142 #define C_TRACE 8
145 * Global variables
148 static Display *g_display = NULL;
149 static int screen_num;
150 static char *progname; /* name this program was invoked by */
151 static Window g_win = 0; /* the hidden clipboard server window */
152 static GC g_gc = 0;
154 static char *g_szOutOfMemory = "Insufficient memory!\n";
156 /* X selection context info */
157 static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
158 static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
159 static int g_selectionAcquired = 0; /* Contains the current selection masks */
160 static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
162 /* Selection cache */
163 typedef struct tag_CACHEENTRY
165 Atom target;
166 Atom type;
167 int nFormat;
168 int nElements;
169 void *pData;
170 } CACHEENTRY, *PCACHEENTRY;
172 static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
173 static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
174 static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
175 static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
177 /* Event names */
178 static const char * const event_names[] =
180 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
181 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
182 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
183 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
184 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
185 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
186 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
187 "ClientMessage", "MappingNotify"
192 * Prototypes
195 int RunAsDaemon( void );
196 BOOL Init(int argc, char **argv);
197 void TerminateServer( int ret );
198 int AcquireSelection();
199 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
200 void EmptyCache(PCACHEENTRY pCache, int nItems);
201 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
202 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
203 void EVENT_ProcessEvent( XEvent *event );
204 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
205 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
206 void EVENT_SelectionClear( XSelectionClearEvent *event );
207 void EVENT_PropertyNotify( XPropertyEvent *event );
208 Pixmap DuplicatePixmap(Pixmap pixmap);
209 void TextOut(Window win, GC gc, char *pStr);
210 void getGC(Window win, GC *gc);
213 int main(int argc, char **argv)
215 XEvent event;
217 if ( RunAsDaemon() == -1 )
219 ERR("could not run as daemon\n");
220 exit(1);
223 if ( !Init(argc, argv) )
224 exit(0);
226 /* Acquire the selection after retrieving all clipboard data
227 * owned by the current selection owner. If we were unable to
228 * Acquire any selection, terminate right away.
230 if ( AcquireSelection() == S_NOSELECTION )
231 TerminateServer(0);
233 TRACE("Clipboard server running...\n");
235 /* Start an X event loop */
236 while (1)
238 XNextEvent(g_display, &event);
240 EVENT_ProcessEvent( &event );
245 /**************************************************************************
246 * RunAsDaemon()
248 int RunAsDaemon( void )
250 int i;
252 /* fork child process and let parent exit ; gets rid of original PID */
253 switch( fork() )
255 case -1:
256 ERR("fork failed\n");
257 return(-1);
258 case 0:
259 exit(0);
260 break;
263 /* below is child process w/ new PID, set as session leader */
264 setsid();
266 /* close stdin,stdout,stderr and file descriptors (overkill method) */
267 for ( i = 0; i < 256 ; i++ )
268 close(i);
270 TRACE("now running as daemon...\n");
271 return 0;
275 /**************************************************************************
276 * Init()
277 * Initialize the clipboard server
279 BOOL Init(int argc, char **argv)
281 unsigned int width, height; /* window size */
282 unsigned int border_width = 4; /* four pixels */
283 unsigned int display_width, display_height;
284 char *window_name = "Wine Clipboard Server";
285 XSizeHints *size_hints = NULL;
286 XWMHints *wm_hints = NULL;
287 XClassHint *class_hints = NULL;
288 XTextProperty windowName;
289 char *display_name = NULL;
291 progname = argv[0];
293 if (!(size_hints = XAllocSizeHints()))
295 ERR(g_szOutOfMemory);
296 return 0;
298 if (!(wm_hints = XAllocWMHints()))
300 ERR(g_szOutOfMemory);
301 return 0;
303 if (!(class_hints = XAllocClassHint()))
305 ERR(g_szOutOfMemory);
306 return 0;
309 /* connect to X server */
310 if ( (g_display=XOpenDisplay(display_name)) == NULL )
312 ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
313 return 0;
316 /* get screen size from display structure macro */
317 screen_num = DefaultScreen(g_display);
318 display_width = DisplayWidth(g_display, screen_num);
319 display_height = DisplayHeight(g_display, screen_num);
321 /* size window with enough room for text */
322 width = display_width/3, height = display_height/4;
324 /* create opaque window */
325 g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
326 0, 0, width, height, border_width, BlackPixel(g_display,
327 screen_num), WhitePixel(g_display,screen_num));
330 /* Set size hints for window manager. The window manager may
331 * override these settings. */
333 /* x, y, width, and height hints are now taken from
334 * the actual settings of the window when mapped. Note
335 * that PPosition and PSize must be specified anyway. */
337 size_hints->flags = PPosition | PSize | PMinSize;
338 size_hints->min_width = 300;
339 size_hints->min_height = 200;
341 /* These calls store window_name into XTextProperty structures
342 * and sets the other fields properly. */
343 if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
345 ERR( "structure allocation for windowName failed.\n");
346 TerminateServer(-1);
349 wm_hints->initial_state = NormalState;
350 wm_hints->input = True;
351 wm_hints->flags = StateHint | InputHint;
353 class_hints->res_name = progname;
354 class_hints->res_class = "WineClipSrv";
356 XSetWMProperties(g_display, g_win, &windowName, NULL,
357 argv, argc, size_hints, wm_hints,
358 class_hints);
360 /* Select event types wanted */
361 XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
362 ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
364 /* create GC for text and drawing */
365 getGC(g_win, &g_gc);
367 /* Display window */
368 /* XMapWindow(g_display, g_win); */
370 /* Set the selections to be acquired from the command line argument.
371 * If none specified, default to all selections we understand.
373 if (argc > 1)
374 g_selectionToAcquire = atoi(argv[1]);
375 else
376 g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
378 /* Set the debugging class state from the command line argument */
379 if (argc > 2)
381 int dbgClasses = atoi(argv[2]);
383 __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
384 __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
385 __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
386 __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
389 /* Set the "ClearSelections" state from the command line argument */
390 if (argc > 3)
391 g_clearAllSelections = atoi(argv[3]);
393 return TRUE;
397 /**************************************************************************
398 * TerminateServer()
400 void TerminateServer( int ret )
402 TRACE("Terminating Wine clipboard server...\n");
404 /* Free Primary and Clipboard selection caches */
405 EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
406 EmptyCache(g_pClipboardCache, g_cClipboardTargets);
408 if (g_gc)
409 XFreeGC(g_display, g_gc);
411 if (g_display)
412 XCloseDisplay(g_display);
414 exit(ret);
418 /**************************************************************************
419 * AcquireSelection()
421 * Acquire the selection after retrieving all clipboard data owned by
422 * the current selection owner.
424 int AcquireSelection()
426 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
429 * For all selections we need to acquire, get a list of all targets
430 * supplied by the current selection owner.
432 if (g_selectionToAcquire & S_PRIMARY)
434 TRACE("Acquiring PRIMARY selection...\n");
435 g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
436 TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
438 if (g_selectionToAcquire & S_CLIPBOARD)
440 TRACE("Acquiring CLIPBOARD selection...\n");
441 g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
442 TRACE("Cached %ld formats...\n", g_cClipboardTargets);
446 * Now that we have cached the data, we proceed to acquire the selections
448 if (g_cPrimaryTargets)
450 /* Acquire the PRIMARY selection */
451 while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
452 XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
454 g_selectionAcquired |= S_PRIMARY;
456 else
457 TRACE("No PRIMARY targets - ownership not acquired.\n");
459 if (g_cClipboardTargets)
461 /* Acquire the CLIPBOARD selection */
462 while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
463 XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
465 g_selectionAcquired |= S_CLIPBOARD;
467 else
468 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
470 return g_selectionAcquired;
473 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
475 time_t end_time;
477 /* Set up a 10 second time out */
478 end_time=time(NULL)+10;
482 struct timeval nap;
484 if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
486 if( xe->xselection.selection == SelectionSrc )
487 return TRUE;
490 if (time(NULL)>end_time)
491 break;
493 /* Sleep a bit to make this busy wait less brutal */
494 nap.tv_sec = 0;
495 nap.tv_usec = 10;
496 select(0, NULL, NULL, NULL, &nap);
498 while (TRUE);
500 return FALSE;
503 /**************************************************************************
504 * CacheDataFormats
506 * Allocates and caches the list of data formats available from the current selection.
507 * This queries the selection owner for the TARGETS property and saves all
508 * reported property types.
510 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
512 XEvent xe;
513 Atom aTargets;
514 Atom atype=AnyPropertyType;
515 int aformat;
516 unsigned long remain;
517 unsigned long cSelectionTargets = 0;
518 Atom* targetList=NULL;
519 Window ownerSelection = 0;
521 if (!ppCache)
522 return 0;
523 *ppCache = NULL;
525 /* Get the selection owner */
526 ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
527 if ( ownerSelection == None )
528 return cSelectionTargets;
531 * Query the selection owner for the TARGETS property
533 aTargets = XInternAtom(g_display, "TARGETS", False);
535 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
536 XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
538 XConvertSelection(g_display, SelectionSrc, aTargets,
539 XInternAtom(g_display, "SELECTION_DATA", False),
540 g_win, CurrentTime);
543 * Wait until SelectionNotify is received
545 if (!GetSelectionEvent(SelectionSrc, &xe))
546 return 0;
548 /* Verify that the selection returned a valid TARGETS property */
549 if ( (xe.xselection.target != aTargets)
550 || (xe.xselection.property == None) )
552 TRACE("\tCould not retrieve TARGETS\n");
553 return cSelectionTargets;
556 /* Read the TARGETS property contents */
557 if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
558 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
559 &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
560 TRACE("\tCouldn't read TARGETS property\n");
561 else
563 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
564 XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
566 * The TARGETS property should have returned us a list of atoms
567 * corresponding to each selection target format supported.
569 if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
571 int i;
573 /* Allocate the selection cache */
574 *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
576 /* Cache these formats in the selection cache */
577 for (i = 0; i < cSelectionTargets; i++)
579 char *itemFmtName = XGetAtomName(g_display, targetList[i]);
581 TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
583 /* Populate the cache entry */
584 if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
585 ERR("Failed to fill cache entry!\n");
587 XFree(itemFmtName);
591 /* Free the list of targets */
592 XFree(targetList);
595 return cSelectionTargets;
598 /***********************************************************************
599 * FillCacheEntry
601 * Populates the specified cache entry
603 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
605 XEvent xe;
606 Window w;
607 Atom prop, reqType;
608 Atom atype=AnyPropertyType;
609 int aformat;
610 unsigned long nitems,remain,itemSize;
611 long lRequestLength;
612 unsigned char* val=NULL;
613 BOOL bRet = FALSE;
615 TRACE("Requesting %s selection from %s...\n",
616 XGetAtomName(g_display, target),
617 XGetAtomName(g_display, SelectionSrc) );
619 /* Ask the selection owner to convert the selection to the target format */
620 XConvertSelection(g_display, SelectionSrc, target,
621 XInternAtom(g_display, "SELECTION_DATA", False),
622 g_win, CurrentTime);
624 /* wait until SelectionNotify is received */
625 if (!GetSelectionEvent(SelectionSrc,&xe))
626 return bRet;
628 /* Now proceed to retrieve the actual converted property from
629 * the SELECTION_DATA atom */
631 w = xe.xselection.requestor;
632 prop = xe.xselection.property;
633 reqType = xe.xselection.target;
635 if(prop == None)
637 TRACE("\tOwner failed to convert selection!\n");
638 return bRet;
641 TRACE("\tretrieving property %s from window %ld into %s\n",
642 XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
645 * First request a zero length in order to figure out the request size.
647 if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
648 &atype, &aformat, &nitems, &itemSize, &val) != Success)
650 WARN("\tcouldn't get property size\n");
651 return bRet;
654 /* Free zero length return data if any */
655 if ( val )
657 XFree(val);
658 val = NULL;
661 TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
662 lRequestLength = (itemSize * aformat/8)/4 + 1;
665 * Retrieve the actual property in the required X format.
667 if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
668 &atype, &aformat, &nitems, &remain, &val) != Success)
670 WARN("\tcouldn't read property\n");
671 return bRet;
674 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
675 atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
677 if (remain)
679 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
680 goto END;
684 * Populate the cache entry
686 pCacheEntry->target = target;
687 pCacheEntry->type = atype;
688 pCacheEntry->nFormat = aformat;
689 pCacheEntry->nElements = nitems;
691 if (atype == XA_PIXMAP)
693 Pixmap *pPixmap = (Pixmap *)val;
694 Pixmap newPixmap = DuplicatePixmap( *pPixmap );
695 pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
696 *pPixmap = newPixmap;
697 pCacheEntry->pData = pPixmap;
699 else
700 pCacheEntry->pData = val;
702 END:
703 /* Delete the property on the window now that we are done
704 * This will send a PropertyNotify event to the selection owner. */
705 XDeleteProperty(g_display,w,prop);
707 return TRUE;
711 /***********************************************************************
712 * LookupCacheItem
714 * Lookup a target atom in the cache and get the matching cache entry
716 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
718 int i;
719 int nCachetargets = 0;
720 PCACHEENTRY pCache = NULL;
721 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
723 /* Locate the cache to be used based on the selection type */
724 if ( selection == XA_PRIMARY )
726 pCache = g_pPrimaryCache;
727 nCachetargets = g_cPrimaryTargets;
729 else if ( selection == xaClipboard )
731 pCache = g_pClipboardCache;
732 nCachetargets = g_cClipboardTargets;
735 if (!pCache || !ppCacheEntry)
736 return FALSE;
738 *ppCacheEntry = NULL;
740 /* Look for the target item in the cache */
741 for (i = 0; i < nCachetargets; i++)
743 if (pCache[i].target == target)
745 *ppCacheEntry = &pCache[i];
746 return TRUE;
750 return FALSE;
754 /***********************************************************************
755 * EmptyCache
757 * Empties the specified cache
759 void EmptyCache(PCACHEENTRY pCache, int nItems)
761 int i;
763 if (!pCache)
764 return;
766 /* Release all items in the cache */
767 for (i = 0; i < nItems; i++)
769 if (pCache[i].target && pCache[i].pData)
771 /* If we have a Pixmap, free it first */
772 if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
774 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
776 TRACE("Freeing %s (handle=%ld)...\n",
777 XGetAtomName(g_display, pCache[i].target), *pPixmap);
779 XFreePixmap(g_display, *pPixmap);
781 /* Free the cached data item (allocated by us) */
782 free(pCache[i].pData);
784 else
786 TRACE("Freeing %s (%p)...\n",
787 XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
789 /* Free the cached data item (allocated by X) */
790 XFree(pCache[i].pData);
795 /* Destroy the cache */
796 free(pCache);
800 /***********************************************************************
801 * EVENT_ProcessEvent
803 * Process an X event.
805 void EVENT_ProcessEvent( XEvent *event )
808 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
811 switch (event->type)
813 case Expose:
814 /* don't draw the window */
815 if (event->xexpose.count != 0)
816 break;
818 /* Output something */
819 TextOut(g_win, g_gc, "Click here to terminate");
820 break;
822 case ConfigureNotify:
823 break;
825 case ButtonPress:
826 /* fall into KeyPress (no break) */
827 case KeyPress:
828 TerminateServer(1);
829 break;
831 case SelectionRequest:
832 EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
833 break;
835 case SelectionClear:
836 EVENT_SelectionClear( (XSelectionClearEvent*)event );
837 break;
839 case PropertyNotify:
840 #if 0
841 EVENT_PropertyNotify( (XPropertyEvent *)event );
842 #endif
843 break;
845 default: /* ignore all other events */
846 break;
848 } /* end switch */
853 /***********************************************************************
854 * EVENT_SelectionRequest_MULTIPLE
855 * Service a MULTIPLE selection request event
856 * rprop contains a list of (target,property) atom pairs.
857 * The first atom names a target and the second names a property.
858 * The effect is as if we have received a sequence of SelectionRequest events
859 * (one for each atom pair) except that:
860 * 1. We reply with a SelectionNotify only when all the requested conversions
861 * have been performed.
862 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
863 * we replace the atom in the property by None.
865 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
867 Atom rprop;
868 Atom atype=AnyPropertyType;
869 int aformat;
870 unsigned long remain;
871 Atom* targetPropList=NULL;
872 unsigned long cTargetPropList = 0;
873 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
875 /* If the specified property is None the requestor is an obsolete client.
876 * We support these by using the specified target atom as the reply property.
878 rprop = pevent->property;
879 if( rprop == None )
880 rprop = pevent->target;
881 if (!rprop)
882 goto END;
884 /* Read the MULTIPLE property contents. This should contain a list of
885 * (target,property) atom pairs.
887 if(XGetWindowProperty(g_display, pevent->requestor, rprop,
888 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
889 &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
890 TRACE("\tCouldn't read MULTIPLE property\n");
891 else
893 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
894 XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
897 * Make sure we got what we expect.
898 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
899 * in a MULTIPLE selection request should be of type ATOM_PAIR.
900 * However some X apps(such as XPaint) are not compliant with this and return
901 * a user defined atom in atype when XGetWindowProperty is called.
902 * The data *is* an atom pair but is not denoted as such.
904 if(aformat == 32 /* atype == xAtomPair */ )
906 int i;
908 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
909 * for each (target,property) pair */
911 for (i = 0; i < cTargetPropList; i+=2)
913 char *targetName = XGetAtomName(g_display, targetPropList[i]);
914 char *propName = XGetAtomName(g_display, targetPropList[i+1]);
915 XSelectionRequestEvent event;
917 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
918 XFree(targetName);
919 XFree(propName);
921 /* We must have a non "None" property to service a MULTIPLE target atom */
922 if ( !targetPropList[i+1] )
924 TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
925 continue;
928 /* Set up an XSelectionRequestEvent for this (target,property) pair */
929 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
930 event.target = targetPropList[i];
931 event.property = targetPropList[i+1];
933 /* Fire a SelectionRequest, informing the handler that we are processing
934 * a MULTIPLE selection request event.
936 EVENT_SelectionRequest( &event, TRUE );
940 /* Free the list of targets/properties */
941 XFree(targetPropList);
944 END:
945 return rprop;
949 /***********************************************************************
950 * EVENT_SelectionRequest
951 * Process an event selection request event.
952 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
953 * recursively while servicing a "MULTIPLE" selection target.
956 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
958 XSelectionEvent result;
959 Atom rprop = None;
960 Window request = event->requestor;
961 Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
962 PCACHEENTRY pCacheEntry = NULL;
963 void *pData = NULL;
964 Pixmap pixmap;
966 /* If the specified property is None the requestor is an obsolete client.
967 * We support these by using the specified target atom as the reply property.
969 rprop = event->property;
970 if( rprop == None )
971 rprop = event->target;
973 TRACE("Request for %s in selection %s\n",
974 XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
976 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
977 if(event->target == xaMultiple)
979 /* MULTIPLE selection request - will call us back recursively */
980 rprop = EVENT_SelectionRequest_MULTIPLE( event );
981 goto END;
984 /* Lookup the requested target property in the cache */
985 if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
987 TRACE("Item not available in cache!\n");
988 goto END;
991 /* Update the X property */
992 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
994 /* If we have a request for a pixmap, return a duplicate */
996 if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
998 Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
999 pixmap = DuplicatePixmap( *pPixmap );
1000 pData = &pixmap;
1002 else
1003 pData = pCacheEntry->pData;
1005 XChangeProperty(g_display, request, rprop,
1006 pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
1007 (unsigned char *)pData, pCacheEntry->nElements);
1009 END:
1010 if( rprop == None)
1011 TRACE("\tRequest ignored\n");
1013 /* reply to sender
1014 * SelectionNotify should be sent only at the end of a MULTIPLE request
1016 if ( !bIsMultiple )
1018 result.type = SelectionNotify;
1019 result.display = g_display;
1020 result.requestor = request;
1021 result.selection = event->selection;
1022 result.property = rprop;
1023 result.target = event->target;
1024 result.time = event->time;
1025 TRACE("Sending SelectionNotify event...\n");
1026 XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
1031 /***********************************************************************
1032 * EVENT_SelectionClear
1033 * We receive this event when another client grabs the X selection.
1034 * If we lost both PRIMARY and CLIPBOARD we must terminate.
1036 void EVENT_SelectionClear( XSelectionClearEvent *event )
1038 Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
1040 TRACE("()\n");
1042 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
1043 * dictate that *all* selections should be cleared on loss of a selection,
1044 * we must give up all the selections we own.
1046 if ( g_clearAllSelections || (event->selection == xaClipboard) )
1048 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
1050 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
1051 if ( (event->selection == xaClipboard)
1052 && (g_selectionAcquired & S_PRIMARY) )
1054 XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1057 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
1058 if ( (event->selection == XA_PRIMARY)
1059 && (g_selectionAcquired & S_CLIPBOARD) )
1061 XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1064 g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
1066 else if (event->selection == XA_PRIMARY)
1068 TRACE("Lost PRIMARY selection...\n");
1069 g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
1072 /* Once we lose all our selections we have nothing more to do */
1073 if (g_selectionAcquired == S_NOSELECTION)
1074 TerminateServer(1);
1077 /***********************************************************************
1078 * EVENT_PropertyNotify
1079 * We use this to release resources like Pixmaps when a selection
1080 * client no longer needs them.
1082 void EVENT_PropertyNotify( XPropertyEvent *event )
1084 TRACE("()\n");
1086 /* Check if we have any resources to free */
1088 switch(event->state)
1090 case PropertyDelete:
1092 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1093 XGetAtomName(event->display, event->atom), (long)event->window);
1095 /* FreeResources( event->atom ); */
1096 break;
1099 case PropertyNewValue:
1101 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1102 XGetAtomName(event->display, event->atom), (long)event->window);
1103 break;
1106 default:
1107 break;
1111 /***********************************************************************
1112 * DuplicatePixmap
1114 Pixmap DuplicatePixmap(Pixmap pixmap)
1116 Pixmap newPixmap;
1117 XImage *xi;
1118 Window root;
1119 int x,y; /* Unused */
1120 unsigned border_width; /* Unused */
1121 unsigned int depth, width, height;
1123 TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1125 /* Get the Pixmap dimensions and bit depth */
1126 if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1127 &border_width, &depth) )
1128 return 0;
1130 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1131 width, height, depth);
1133 newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1135 xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1137 XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1139 XDestroyImage(xi);
1141 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1142 return newPixmap;
1145 /***********************************************************************
1146 * getGC
1147 * Get a GC to use for drawing
1149 void getGC(Window win, GC *gc)
1151 unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1152 XGCValues values;
1153 unsigned int line_width = 6;
1154 int line_style = LineOnOffDash;
1155 int cap_style = CapRound;
1156 int join_style = JoinRound;
1157 int dash_offset = 0;
1158 static char dash_list[] = {12, 24};
1159 int list_length = 2;
1161 /* Create default Graphics Context */
1162 *gc = XCreateGC(g_display, win, valuemask, &values);
1164 /* specify black foreground since default window background is
1165 * white and default foreground is undefined. */
1166 XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1168 /* set line attributes */
1169 XSetLineAttributes(g_display, *gc, line_width, line_style,
1170 cap_style, join_style);
1172 /* set dashes */
1173 XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1177 /***********************************************************************
1178 * TextOut
1180 void TextOut(Window win, GC gc, char *pStr)
1182 int y_offset, x_offset;
1184 y_offset = 10;
1185 x_offset = 2;
1187 /* output text, centered on each line */
1188 XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
1189 strlen(pStr));