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