2 * Wine Clipboard Server
4 * Copyright 1999 Noel Borthwick
7 * wineclipsrv [selection_mask] [debugClass_mask] [clearAllSelections]
9 * The optional selection-mask argument is a bit mask of the selection
10 * types to be acquired. Currently two selections are supported:
11 * 1. PRIMARY (mask value 1)
12 * 2. CLIPBOARD (mask value 2).
14 * debugClass_mask is a bit mask of all debugging classes for which messages
15 * are to be output. The standard Wine debug class set FIXME(1), ERR(2),
16 * WARN(4) and TRACE(8) are supported.
18 * If clearAllSelections == 1 *all* selections are lost whenever a SelectionClear
21 * If no arguments are supplied the server aquires all selections. (mask value 3)
22 * and defaults to output of only FIXME(1) and ERR(2) messages. The default for
23 * clearAllSelections is 0.
27 * The Wine Clipboard Server is a standalone XLib application whose
28 * purpose is to manage the X selection when Wine exits.
29 * The server itself is started automatically with the appropriate
30 * selection masks, whenever Wine exits after acquiring the PRIMARY and/or
31 * CLIPBOARD selection. (See X11DRV_CLIPBOARD_ResetOwner)
32 * When the server starts, it first proceeds to capture the selection data from
33 * Wine and then takes over the selection ownership. It does this by querying
34 * the current selection owner(of the specified selections) for the TARGETS
35 * selection target. It then proceeds to cache all the formats exposed by
36 * TARGETS. If the selection does not support the TARGETS target, or if no
37 * target formats are exposed, the server simply exits.
38 * Once the cache has been filled, the server then actually acquires ownership
39 * of the respective selection and begins fielding selection requests.
40 * Selection requests are serviced from the cache. If a selection is lost the
41 * server flushes its internal cache, destroying all data previously saved.
42 * Once ALL selections have been lost the server terminates.
50 #include <X11/Xutil.h>
52 #include <X11/Xatom.h>
57 * Lightweight debug definitions for Wine Clipboard Server.
58 * The standard FIXME, ERR, WARN & TRACE classes are supported
59 * without debug channels.
60 * The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
61 * TRACE, WARN and ERR and FIXME message displays.
64 /* Internal definitions (do not use these directly) */
66 enum __DEBUG_CLASS
{ __DBCL_FIXME
, __DBCL_ERR
, __DBCL_WARN
, __DBCL_TRACE
, __DBCL_COUNT
};
68 extern char __debug_msg_enabled
[__DBCL_COUNT
];
70 extern const char * const debug_cl_name
[__DBCL_COUNT
];
72 #define DEBUG_CLASS_COUNT __DBCL_COUNT
74 #define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
75 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
78 #define __DPRINTF(dbcl) \
79 (!__GET_DEBUGGING(dbcl) || \
80 (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
83 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
85 /* use configure to allow user to compile out debugging messages */
87 #define TRACE __DPRINTF(__DBCL_TRACE)
89 #define TRACE __DUMMY_DPRINTF
90 #endif /* NO_TRACE_MSGS */
93 #define WARN __DPRINTF(__DBCL_WARN)
94 #define FIXME __DPRINTF(__DBCL_FIXME)
96 #define WARN __DUMMY_DPRINTF
97 #define FIXME __DUMMY_DPRINTF
98 #endif /* NO_DEBUG_MSGS */
100 /* define error macro regardless of what is configured */
101 #define ERR __DPRINTF(__DBCL_ERR)
108 /* Internal definitions for debugging messages(do not use these directly) */
109 const char * const debug_cl_name
[] = { "fixme", "err", "warn", "trace" };
110 char __debug_msg_enabled
[DEBUG_CLASS_COUNT
] = {1, 1, 0, 0};
113 /* Selection masks */
115 #define S_NOSELECTION 0
117 #define S_CLIPBOARD 2
119 /* Debugging class masks */
130 static Display
*g_display
= NULL
;
131 static int screen_num
;
132 static char *progname
; /* name this program was invoked by */
133 static Window g_win
= 0; /* the hidden clipboard server window */
136 static char *g_szOutOfMemory
= "Insufficient memory!\n";
138 /* X selection context info */
139 static char _CLIPBOARD
[] = "CLIPBOARD"; /* CLIPBOARD atom name */
140 static int g_selectionToAcquire
= 0; /* Masks for the selection to be acquired */
141 static int g_selectionAcquired
= 0; /* Contains the current selection masks */
142 static int g_clearAllSelections
= 0; /* If TRUE *all* selections are lost on SelectionClear */
144 /* Selection cache */
145 typedef struct tag_CACHEENTRY
152 } CACHEENTRY
, *PCACHEENTRY
;
154 static PCACHEENTRY g_pPrimaryCache
= NULL
; /* Primary selection cache */
155 static PCACHEENTRY g_pClipboardCache
= NULL
; /* Clipboard selection cache */
156 static unsigned long g_cPrimaryTargets
= 0; /* Number of TARGETS reported by PRIMARY selection */
157 static unsigned long g_cClipboardTargets
= 0; /* Number of TARGETS reported by CLIPBOARD selection */
160 static const char * const event_names
[] =
162 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
163 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
164 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
165 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
166 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
167 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
168 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
169 "ClientMessage", "MappingNotify"
177 BOOL
Init(int argc
, char **argv
);
178 void TerminateServer( int ret
);
179 int AcquireSelection();
180 int CacheDataFormats( Atom SelectionSrc
, PCACHEENTRY
*ppCache
);
181 void EmptyCache(PCACHEENTRY pCache
, int nItems
);
182 BOOL
FillCacheEntry( Atom SelectionSrc
, Atom target
, PCACHEENTRY pCacheEntry
);
183 BOOL
LookupCacheItem( Atom selection
, Atom target
, PCACHEENTRY
*ppCacheEntry
);
184 void EVENT_ProcessEvent( XEvent
*event
);
185 Atom
EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent
*pevent
);
186 void EVENT_SelectionRequest( XSelectionRequestEvent
*event
, BOOL bIsMultiple
);
187 void EVENT_SelectionClear( XSelectionClearEvent
*event
);
188 void EVENT_PropertyNotify( XPropertyEvent
*event
);
189 Pixmap
DuplicatePixmap(Pixmap pixmap
);
190 void TextOut(Window win
, GC gc
, char *pStr
);
191 void getGC(Window win
, GC
*gc
);
194 int main(int argc
, char **argv
)
198 if ( !Init(argc
, argv
) )
201 /* Acquire the selection after retrieving all clipboard data
202 * owned by the current selection owner. If we were unable to
203 * Acquire any selection, terminate right away.
205 if ( AcquireSelection() == S_NOSELECTION
)
208 TRACE("Clipboard server running...\n");
210 /* Start an X event loop */
213 XNextEvent(g_display
, &event
);
215 EVENT_ProcessEvent( &event
);
220 /**************************************************************************
222 * Initialize the clipboard server
224 BOOL
Init(int argc
, char **argv
)
226 unsigned int width
, height
; /* window size */
227 unsigned int border_width
= 4; /* four pixels */
228 unsigned int display_width
, display_height
;
229 char *window_name
= "Wine Clipboard Server";
230 XSizeHints
*size_hints
= NULL
;
231 XWMHints
*wm_hints
= NULL
;
232 XClassHint
*class_hints
= NULL
;
233 XTextProperty windowName
;
234 char *display_name
= NULL
;
238 if (!(size_hints
= XAllocSizeHints()))
240 ERR(g_szOutOfMemory
);
243 if (!(wm_hints
= XAllocWMHints()))
245 ERR(g_szOutOfMemory
);
248 if (!(class_hints
= XAllocClassHint()))
250 ERR(g_szOutOfMemory
);
254 /* connect to X server */
255 if ( (g_display
=XOpenDisplay(display_name
)) == NULL
)
257 ERR( "cannot connect to X server %s\n", XDisplayName(display_name
));
261 /* get screen size from display structure macro */
262 screen_num
= DefaultScreen(g_display
);
263 display_width
= DisplayWidth(g_display
, screen_num
);
264 display_height
= DisplayHeight(g_display
, screen_num
);
266 /* size window with enough room for text */
267 width
= display_width
/3, height
= display_height
/4;
269 /* create opaque window */
270 g_win
= XCreateSimpleWindow(g_display
, RootWindow(g_display
,screen_num
),
271 0, 0, width
, height
, border_width
, BlackPixel(g_display
,
272 screen_num
), WhitePixel(g_display
,screen_num
));
275 /* Set size hints for window manager. The window manager may
276 * override these settings. */
278 /* x, y, width, and height hints are now taken from
279 * the actual settings of the window when mapped. Note
280 * that PPosition and PSize must be specified anyway. */
282 size_hints
->flags
= PPosition
| PSize
| PMinSize
;
283 size_hints
->min_width
= 300;
284 size_hints
->min_height
= 200;
286 /* These calls store window_name into XTextProperty structures
287 * and sets the other fields properly. */
288 if (XStringListToTextProperty(&window_name
, 1, &windowName
) == 0)
290 ERR( "structure allocation for windowName failed.\n");
294 wm_hints
->initial_state
= NormalState
;
295 wm_hints
->input
= True
;
296 wm_hints
->flags
= StateHint
| InputHint
;
298 class_hints
->res_name
= progname
;
299 class_hints
->res_class
= "WineClipSrv";
301 XSetWMProperties(g_display
, g_win
, &windowName
, NULL
,
302 argv
, argc
, size_hints
, wm_hints
,
305 /* Select event types wanted */
306 XSelectInput(g_display
, g_win
, ExposureMask
| KeyPressMask
|
307 ButtonPressMask
| StructureNotifyMask
| PropertyChangeMask
);
309 /* create GC for text and drawing */
313 /* XMapWindow(g_display, g_win); */
315 /* Set the selections to be acquired from the command line argument.
316 * If none specified, default to all selections we understand.
319 g_selectionToAcquire
= atoi(argv
[1]);
321 g_selectionToAcquire
= S_PRIMARY
| S_CLIPBOARD
;
323 /* Set the debugging class state from the command line argument */
326 int dbgClasses
= atoi(argv
[2]);
328 __SET_DEBUGGING(__DBCL_FIXME
, dbgClasses
& C_FIXME
);
329 __SET_DEBUGGING(__DBCL_ERR
, dbgClasses
& C_ERR
);
330 __SET_DEBUGGING(__DBCL_WARN
, dbgClasses
& C_WARN
);
331 __SET_DEBUGGING(__DBCL_TRACE
, dbgClasses
& C_TRACE
);
334 /* Set the "ClearSelections" state from the command line argument */
336 g_clearAllSelections
= atoi(argv
[3]);
342 /**************************************************************************
345 void TerminateServer( int ret
)
347 TRACE("Terminating Wine clipboard server...\n");
349 /* Free Primary and Clipboard selection caches */
350 EmptyCache(g_pPrimaryCache
, g_cPrimaryTargets
);
351 EmptyCache(g_pClipboardCache
, g_cClipboardTargets
);
354 XFreeGC(g_display
, g_gc
);
357 XCloseDisplay(g_display
);
363 /**************************************************************************
366 * Acquire the selection after retrieving all clipboard data owned by
367 * the current selection owner.
369 int AcquireSelection()
371 Atom xaClipboard
= XInternAtom(g_display
, _CLIPBOARD
, False
);
374 * For all selections we need to acquire, get a list of all targets
375 * supplied by the current selection owner.
377 if (g_selectionToAcquire
& S_PRIMARY
)
379 TRACE("Acquiring PRIMARY selection...\n");
380 g_cPrimaryTargets
= CacheDataFormats( XA_PRIMARY
, &g_pPrimaryCache
);
381 TRACE("Cached %ld formats...\n", g_cPrimaryTargets
);
383 if (g_selectionToAcquire
& S_CLIPBOARD
)
385 TRACE("Acquiring CLIPBOARD selection...\n");
386 g_cClipboardTargets
= CacheDataFormats( xaClipboard
, &g_pClipboardCache
);
387 TRACE("Cached %ld formats...\n", g_cClipboardTargets
);
391 * Now that we have cached the data, we proceed to acquire the selections
393 if (g_cPrimaryTargets
)
395 /* Acquire the PRIMARY selection */
396 while (XGetSelectionOwner(g_display
,XA_PRIMARY
) != g_win
)
397 XSetSelectionOwner(g_display
, XA_PRIMARY
, g_win
, CurrentTime
);
399 g_selectionAcquired
|= S_PRIMARY
;
402 TRACE("No PRIMARY targets - ownership not acquired.\n");
404 if (g_cClipboardTargets
)
406 /* Acquire the CLIPBOARD selection */
407 while (XGetSelectionOwner(g_display
,xaClipboard
) != g_win
)
408 XSetSelectionOwner(g_display
, xaClipboard
, g_win
, CurrentTime
);
410 g_selectionAcquired
|= S_CLIPBOARD
;
413 TRACE("No CLIPBOARD targets - ownership not acquired.\n");
415 return g_selectionAcquired
;
418 /**************************************************************************
421 * Allocates and caches the list of data formats available from the current selection.
422 * This queries the selection owner for the TARGETS property and saves all
423 * reported property types.
425 int CacheDataFormats( Atom SelectionSrc
, PCACHEENTRY
*ppCache
)
429 Atom atype
=AnyPropertyType
;
431 unsigned long remain
;
432 unsigned long cSelectionTargets
= 0;
433 Atom
* targetList
=NULL
;
434 Window ownerSelection
= 0;
440 /* Get the selection owner */
441 ownerSelection
= XGetSelectionOwner(g_display
, SelectionSrc
);
442 if ( ownerSelection
== None
)
443 return cSelectionTargets
;
446 * Query the selection owner for the TARGETS property
448 aTargets
= XInternAtom(g_display
, "TARGETS", False
);
450 TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
451 XGetAtomName(g_display
, SelectionSrc
), (unsigned)ownerSelection
);
453 XConvertSelection(g_display
, SelectionSrc
, aTargets
,
454 XInternAtom(g_display
, "SELECTION_DATA", False
),
458 * Wait until SelectionNotify is received
462 if( XCheckTypedWindowEvent(g_display
, g_win
, SelectionNotify
, &xe
) )
463 if( xe
.xselection
.selection
== SelectionSrc
)
467 /* Verify that the selection returned a valid TARGETS property */
468 if ( (xe
.xselection
.target
!= aTargets
)
469 || (xe
.xselection
.property
== None
) )
471 TRACE("\tCould not retrieve TARGETS\n");
472 return cSelectionTargets
;
475 /* Read the TARGETS property contents */
476 if(XGetWindowProperty(g_display
, xe
.xselection
.requestor
, xe
.xselection
.property
,
477 0, 0x3FFF, True
, AnyPropertyType
/*XA_ATOM*/, &atype
, &aformat
,
478 &cSelectionTargets
, &remain
, (unsigned char**)&targetList
) != Success
)
479 TRACE("\tCouldn't read TARGETS property\n");
482 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
483 XGetAtomName(g_display
,atype
),aformat
,cSelectionTargets
, remain
);
485 * The TARGETS property should have returned us a list of atoms
486 * corresponding to each selection target format supported.
488 if( (atype
== XA_ATOM
|| atype
== aTargets
) && aformat
== 32 )
492 /* Allocate the selection cache */
493 *ppCache
= (PCACHEENTRY
)calloc(cSelectionTargets
, sizeof(CACHEENTRY
));
495 /* Cache these formats in the selection cache */
496 for (i
= 0; i
< cSelectionTargets
; i
++)
498 char *itemFmtName
= XGetAtomName(g_display
, targetList
[i
]);
500 TRACE("\tAtom# %d: '%s'\n", i
, itemFmtName
);
502 /* Populate the cache entry */
503 if (!FillCacheEntry( SelectionSrc
, targetList
[i
], &((*ppCache
)[i
])))
504 ERR("Failed to fill cache entry!\n");
510 /* Free the list of targets */
514 return cSelectionTargets
;
517 /***********************************************************************
520 * Populates the specified cache entry
522 BOOL
FillCacheEntry( Atom SelectionSrc
, Atom target
, PCACHEENTRY pCacheEntry
)
527 Atom atype
=AnyPropertyType
;
529 unsigned long nitems
,remain
,itemSize
;
531 unsigned char* val
=NULL
;
534 TRACE("Requesting %s selection from %s...\n",
535 XGetAtomName(g_display
, target
),
536 XGetAtomName(g_display
, SelectionSrc
) );
538 /* Ask the selection owner to convert the selection to the target format */
539 XConvertSelection(g_display
, SelectionSrc
, target
,
540 XInternAtom(g_display
, "SELECTION_DATA", False
),
543 /* wait until SelectionNotify is received */
546 if( XCheckTypedWindowEvent(g_display
, g_win
, SelectionNotify
, &xe
) )
547 if( xe
.xselection
.selection
== SelectionSrc
)
551 /* Now proceed to retrieve the actual converted property from
552 * the SELECTION_DATA atom */
554 w
= xe
.xselection
.requestor
;
555 prop
= xe
.xselection
.property
;
556 reqType
= xe
.xselection
.target
;
560 TRACE("\tOwner failed to convert selection!\n");
564 TRACE("\tretrieving property %s from window %ld into %s\n",
565 XGetAtomName(g_display
,reqType
), (long)w
, XGetAtomName(g_display
,prop
) );
568 * First request a zero length in order to figure out the request size.
570 if(XGetWindowProperty(g_display
,w
,prop
,0,0,False
, AnyPropertyType
/*reqType*/,
571 &atype
, &aformat
, &nitems
, &itemSize
, &val
) != Success
)
573 WARN("\tcouldn't get property size\n");
577 /* Free zero length return data if any */
584 TRACE("\tretrieving %ld bytes...\n", itemSize
* aformat
/8);
585 lRequestLength
= (itemSize
* aformat
/8)/4 + 1;
588 * Retrieve the actual property in the required X format.
590 if(XGetWindowProperty(g_display
,w
,prop
,0,lRequestLength
,False
,AnyPropertyType
/*reqType*/,
591 &atype
, &aformat
, &nitems
, &remain
, &val
) != Success
)
593 WARN("\tcouldn't read property\n");
597 TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
598 atype
? XGetAtomName(g_display
,atype
) : NULL
, aformat
,nitems
,remain
,val
);
602 WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain
);
607 * Populate the cache entry
609 pCacheEntry
->target
= target
;
610 pCacheEntry
->type
= atype
;
611 pCacheEntry
->nFormat
= aformat
;
612 pCacheEntry
->nElements
= nitems
;
614 if (atype
== XA_PIXMAP
)
616 Pixmap
*pPixmap
= (Pixmap
*)val
;
617 Pixmap newPixmap
= DuplicatePixmap( *pPixmap
);
618 pPixmap
= (Pixmap
*)calloc(1, sizeof(Pixmap
));
619 *pPixmap
= newPixmap
;
620 pCacheEntry
->pData
= pPixmap
;
623 pCacheEntry
->pData
= val
;
626 /* Delete the property on the window now that we are done
627 * This will send a PropertyNotify event to the selection owner. */
628 XDeleteProperty(g_display
,w
,prop
);
634 /***********************************************************************
637 * Lookup a target atom in the cache and get the matching cache entry
639 BOOL
LookupCacheItem( Atom selection
, Atom target
, PCACHEENTRY
*ppCacheEntry
)
642 int nCachetargets
= 0;
643 PCACHEENTRY pCache
= NULL
;
644 Atom xaClipboard
= XInternAtom(g_display
, _CLIPBOARD
, False
);
646 /* Locate the cache to be used based on the selection type */
647 if ( selection
== XA_PRIMARY
)
649 pCache
= g_pPrimaryCache
;
650 nCachetargets
= g_cPrimaryTargets
;
652 else if ( selection
== xaClipboard
)
654 pCache
= g_pClipboardCache
;
655 nCachetargets
= g_cClipboardTargets
;
658 if (!pCache
|| !ppCacheEntry
)
661 *ppCacheEntry
= NULL
;
663 /* Look for the target item in the cache */
664 for (i
= 0; i
< nCachetargets
; i
++)
666 if (pCache
[i
].target
== target
)
668 *ppCacheEntry
= &pCache
[i
];
677 /***********************************************************************
680 * Empties the specified cache
682 void EmptyCache(PCACHEENTRY pCache
, int nItems
)
689 /* Release all items in the cache */
690 for (i
= 0; i
< nItems
; i
++)
692 if (pCache
[i
].target
&& pCache
[i
].pData
)
694 /* If we have a Pixmap, free it first */
695 if (pCache
[i
].target
== XA_PIXMAP
|| pCache
[i
].target
== XA_BITMAP
)
697 Pixmap
*pPixmap
= (Pixmap
*)pCache
[i
].pData
;
699 TRACE("Freeing %s (handle=%ld)...\n",
700 XGetAtomName(g_display
, pCache
[i
].target
), *pPixmap
);
702 XFreePixmap(g_display
, *pPixmap
);
704 /* Free the cached data item (allocated by us) */
705 free(pCache
[i
].pData
);
709 TRACE("Freeing %s (%p)...\n",
710 XGetAtomName(g_display
, pCache
[i
].target
), pCache
[i
].pData
);
712 /* Free the cached data item (allocated by X) */
713 XFree(pCache
[i
].pData
);
718 /* Destroy the cache */
723 /***********************************************************************
726 * Process an X event.
728 void EVENT_ProcessEvent( XEvent
*event
)
731 TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
737 /* don't draw the window */
738 if (event
->xexpose
.count
!= 0)
741 /* Output something */
742 TextOut(g_win
, g_gc
, "Click here to terminate");
745 case ConfigureNotify
:
749 /* fall into KeyPress (no break) */
754 case SelectionRequest
:
755 EVENT_SelectionRequest( (XSelectionRequestEvent
*)event
, FALSE
);
759 EVENT_SelectionClear( (XSelectionClearEvent
*)event
);
763 // EVENT_PropertyNotify( (XPropertyEvent *)event );
766 default: /* ignore all other events */
774 /***********************************************************************
775 * EVENT_SelectionRequest_MULTIPLE
776 * Service a MULTIPLE selection request event
777 * rprop contains a list of (target,property) atom pairs.
778 * The first atom names a target and the second names a property.
779 * The effect is as if we have received a sequence of SelectionRequest events
780 * (one for each atom pair) except that:
781 * 1. We reply with a SelectionNotify only when all the requested conversions
782 * have been performed.
783 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
784 * we replace the atom in the property by None.
786 Atom
EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent
*pevent
)
789 Atom atype
=AnyPropertyType
;
791 unsigned long remain
;
792 Atom
* targetPropList
=NULL
;
793 unsigned long cTargetPropList
= 0;
794 /* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
796 /* If the specified property is None the requestor is an obsolete client.
797 * We support these by using the specified target atom as the reply property.
799 rprop
= pevent
->property
;
801 rprop
= pevent
->target
;
805 /* Read the MULTIPLE property contents. This should contain a list of
806 * (target,property) atom pairs.
808 if(XGetWindowProperty(g_display
, pevent
->requestor
, rprop
,
809 0, 0x3FFF, False
, AnyPropertyType
, &atype
, &aformat
,
810 &cTargetPropList
, &remain
, (unsigned char**)&targetPropList
) != Success
)
811 TRACE("\tCouldn't read MULTIPLE property\n");
814 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
815 XGetAtomName(g_display
,atype
),aformat
,cTargetPropList
,remain
);
818 * Make sure we got what we expect.
819 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
820 * in a MULTIPLE selection request should be of type ATOM_PAIR.
821 * However some X apps(such as XPaint) are not compliant with this and return
822 * a user defined atom in atype when XGetWindowProperty is called.
823 * The data *is* an atom pair but is not denoted as such.
825 if(aformat
== 32 /* atype == xAtomPair */ )
829 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
830 * for each (target,property) pair */
832 for (i
= 0; i
< cTargetPropList
; i
+=2)
834 char *targetName
= XGetAtomName(g_display
, targetPropList
[i
]);
835 char *propName
= XGetAtomName(g_display
, targetPropList
[i
+1]);
836 XSelectionRequestEvent event
;
838 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i
/2, targetName
, propName
);
842 /* We must have a non "None" property to service a MULTIPLE target atom */
843 if ( !targetPropList
[i
+1] )
845 TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i
);
849 /* Set up an XSelectionRequestEvent for this (target,property) pair */
850 memcpy( &event
, pevent
, sizeof(XSelectionRequestEvent
) );
851 event
.target
= targetPropList
[i
];
852 event
.property
= targetPropList
[i
+1];
854 /* Fire a SelectionRequest, informing the handler that we are processing
855 * a MULTIPLE selection request event.
857 EVENT_SelectionRequest( &event
, TRUE
);
861 /* Free the list of targets/properties */
862 XFree(targetPropList
);
870 /***********************************************************************
871 * EVENT_SelectionRequest
872 * Process an event selection request event.
873 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
874 * recursively while servicing a "MULTIPLE" selection target.
877 void EVENT_SelectionRequest( XSelectionRequestEvent
*event
, BOOL bIsMultiple
)
879 XSelectionEvent result
;
881 Window request
= event
->requestor
;
882 Atom xaMultiple
= XInternAtom(g_display
, "MULTIPLE", False
);
883 PCACHEENTRY pCacheEntry
= NULL
;
887 /* If the specified property is None the requestor is an obsolete client.
888 * We support these by using the specified target atom as the reply property.
890 rprop
= event
->property
;
892 rprop
= event
->target
;
894 TRACE("Request for %s in selection %s\n",
895 XGetAtomName(g_display
, event
->target
), XGetAtomName(g_display
, event
->selection
));
897 /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
898 if(event
->target
== xaMultiple
)
900 /* MULTIPLE selection request - will call us back recursively */
901 rprop
= EVENT_SelectionRequest_MULTIPLE( event
);
905 /* Lookup the requested target property in the cache */
906 if ( !LookupCacheItem(event
->selection
, event
->target
, &pCacheEntry
) )
908 TRACE("Item not available in cache!\n");
912 /* Update the X property */
913 TRACE("\tUpdating property %s...\n", XGetAtomName(g_display
, rprop
));
915 /* If we have a request for a pixmap, return a duplicate */
917 if(event
->target
== XA_PIXMAP
|| event
->target
== XA_BITMAP
)
919 Pixmap
*pPixmap
= (Pixmap
*)pCacheEntry
->pData
;
920 pixmap
= DuplicatePixmap( *pPixmap
);
924 pData
= pCacheEntry
->pData
;
926 XChangeProperty(g_display
, request
, rprop
,
927 pCacheEntry
->type
, pCacheEntry
->nFormat
, PropModeReplace
,
928 (unsigned char *)pData
, pCacheEntry
->nElements
);
932 TRACE("\tRequest ignored\n");
935 * SelectionNotify should be sent only at the end of a MULTIPLE request
939 result
.type
= SelectionNotify
;
940 result
.display
= g_display
;
941 result
.requestor
= request
;
942 result
.selection
= event
->selection
;
943 result
.property
= rprop
;
944 result
.target
= event
->target
;
945 result
.time
= event
->time
;
946 TRACE("Sending SelectionNotify event...\n");
947 XSendEvent(g_display
,event
->requestor
,False
,NoEventMask
,(XEvent
*)&result
);
952 /***********************************************************************
953 * EVENT_SelectionClear
954 * We receive this event when another client grabs the X selection.
955 * If we lost both PRIMARY and CLIPBOARD we must terminate.
957 void EVENT_SelectionClear( XSelectionClearEvent
*event
)
959 Atom xaClipboard
= XInternAtom(g_display
, _CLIPBOARD
, False
);
963 /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
964 * dictate that *all* selections should be cleared on loss of a selection,
965 * we must give up all the selections we own.
967 if ( g_clearAllSelections
|| (event
->selection
== xaClipboard
) )
969 TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
971 /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
972 if ( (event
->selection
== xaClipboard
)
973 && (g_selectionAcquired
& S_PRIMARY
) )
975 XSetSelectionOwner(g_display
, XA_PRIMARY
, None
, CurrentTime
);
978 /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
979 if ( (event
->selection
== XA_PRIMARY
)
980 && (g_selectionAcquired
& S_CLIPBOARD
) )
982 XSetSelectionOwner(g_display
, xaClipboard
, None
, CurrentTime
);
985 g_selectionAcquired
= S_NOSELECTION
; /* Clear the selection masks */
987 else if (event
->selection
== XA_PRIMARY
)
989 TRACE("Lost PRIMARY selection...\n");
990 g_selectionAcquired
&= ~S_PRIMARY
; /* Clear the PRIMARY flag */
993 /* Once we lose all our selections we have nothing more to do */
994 if (g_selectionAcquired
== S_NOSELECTION
)
998 /***********************************************************************
999 * EVENT_PropertyNotify
1000 * We use this to release resources like Pixmaps when a selection
1001 * client no longer needs them.
1003 void EVENT_PropertyNotify( XPropertyEvent
*event
)
1007 /* Check if we have any resources to free */
1009 switch(event
->state
)
1011 case PropertyDelete
:
1013 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1014 XGetAtomName(event
->display
, event
->atom
), (long)event
->window
);
1016 /* FreeResources( event->atom ); */
1020 case PropertyNewValue
:
1022 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1023 XGetAtomName(event
->display
, event
->atom
), (long)event
->window
);
1032 /***********************************************************************
1035 Pixmap
DuplicatePixmap(Pixmap pixmap
)
1040 int x
,y
; /* Unused */
1041 unsigned border_width
; /* Unused */
1042 unsigned int depth
, width
, height
;
1044 TRACE("\t() Pixmap=%ld\n", (long)pixmap
);
1046 /* Get the Pixmap dimensions and bit depth */
1047 if ( 0 == XGetGeometry(g_display
, pixmap
, &root
, &x
, &y
, &width
, &height
,
1048 &border_width
, &depth
) )
1051 TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1052 width
, height
, depth
);
1054 newPixmap
= XCreatePixmap(g_display
, g_win
, width
, height
, depth
);
1056 xi
= XGetImage(g_display
, pixmap
, 0, 0, width
, height
, AllPlanes
, XYPixmap
);
1058 XPutImage(g_display
, newPixmap
, g_gc
, xi
, 0, 0, 0, 0, width
, height
);
1062 TRACE("\t() New Pixmap=%ld\n", (long)newPixmap
);
1066 /***********************************************************************
1068 * Get a GC to use for drawing
1070 void getGC(Window win
, GC
*gc
)
1072 unsigned long valuemask
= 0; /* ignore XGCvalues and use defaults */
1074 unsigned int line_width
= 6;
1075 int line_style
= LineOnOffDash
;
1076 int cap_style
= CapRound
;
1077 int join_style
= JoinRound
;
1078 int dash_offset
= 0;
1079 static char dash_list
[] = {12, 24};
1080 int list_length
= 2;
1082 /* Create default Graphics Context */
1083 *gc
= XCreateGC(g_display
, win
, valuemask
, &values
);
1085 /* specify black foreground since default window background is
1086 * white and default foreground is undefined. */
1087 XSetForeground(g_display
, *gc
, BlackPixel(g_display
,screen_num
));
1089 /* set line attributes */
1090 XSetLineAttributes(g_display
, *gc
, line_width
, line_style
,
1091 cap_style
, join_style
);
1094 XSetDashes(g_display
, *gc
, dash_offset
, dash_list
, list_length
);
1098 /***********************************************************************
1101 void TextOut(Window win
, GC gc
, char *pStr
)
1103 int y_offset
, x_offset
;
1108 /* output text, centered on each line */
1109 XDrawString(g_display
, win
, gc
, x_offset
, y_offset
, pStr
,