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