8 #define MAX_PROPERTY_SIZE 8*1024
10 char *WMSelectionOwnerDidChangeNotification
= "WMSelectionOwnerDidChange";
12 typedef struct SelectionHandler
{
16 WMSelectionProcs procs
;
20 unsigned delete_pending
:1;
21 unsigned done_pending
:1;
25 typedef struct SelectionCallback
{
30 WMSelectionCallback
*callback
;
34 unsigned delete_pending
:1;
35 unsigned done_pending
:1;
39 static WMArray
*selCallbacks
= NULL
;
41 static WMArray
*selHandlers
= NULL
;
43 static Bool gotXError
= False
;
45 void WMDeleteSelectionHandler(WMView
* view
, Atom selection
, Time timestamp
)
47 SelectionHandler
*handler
;
48 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
49 Window win
= W_VIEW_DRAWABLE(view
);
55 /*//printf("deleting selection handler for %d", win); */
57 WM_ITERATE_ARRAY(selHandlers
, handler
, iter
) {
58 if (handler
->view
== view
&& (handler
->selection
== selection
|| selection
== None
)
59 && (handler
->timestamp
== timestamp
|| timestamp
== CurrentTime
)) {
61 if (handler
->flags
.done_pending
) {
62 handler
->flags
.delete_pending
= 1;
63 /*//puts(": postponed because still pending"); */
66 /*//printf(": found & removed"); */
67 WMRemoveFromArray(selHandlers
, handler
);
75 if (XGetSelectionOwner(dpy
, selection
) == win
) {
76 XSetSelectionOwner(dpy
, selection
, None
, timestamp
);
81 static void WMDeleteSelectionCallback(WMView
* view
, Atom selection
, Time timestamp
)
83 SelectionCallback
*handler
;
89 WM_ITERATE_ARRAY(selCallbacks
, handler
, iter
) {
90 if (handler
->view
== view
&& (handler
->selection
== selection
|| selection
== None
)
91 && (handler
->timestamp
== timestamp
|| timestamp
== CurrentTime
)) {
93 if (handler
->flags
.done_pending
) {
94 handler
->flags
.delete_pending
= 1;
97 WMRemoveFromArray(selCallbacks
, handler
);
103 static int handleXError(Display
* dpy
, XErrorEvent
* ev
)
105 /* Parameter not used, but tell the compiler that it is ok */
114 static Bool
writeSelection(Display
* dpy
, Window requestor
, Atom property
, Atom type
, WMData
* data
)
116 static void *oldHandler
;
119 format
= WMGetDataFormat(data
);
125 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
127 oldHandler
= XSetErrorHandler(handleXError
);
131 XChangeProperty(dpy
, requestor
, property
, type
, format
, PropModeReplace
,
132 WMDataBytes(data
), WMGetDataLength(data
) / bpi
);
136 XSetErrorHandler(oldHandler
);
141 static void notifySelection(XEvent
* event
, Atom prop
)
145 /* printf("event to %x\n", event->xselectionrequest.requestor); */
147 ev
.xselection
.type
= SelectionNotify
;
148 ev
.xselection
.serial
= 0;
149 ev
.xselection
.send_event
= True
;
150 ev
.xselection
.display
= event
->xselectionrequest
.display
;
151 ev
.xselection
.requestor
= event
->xselectionrequest
.requestor
;
152 ev
.xselection
.target
= event
->xselectionrequest
.target
;
153 ev
.xselection
.selection
= event
->xselectionrequest
.selection
;
154 ev
.xselection
.property
= prop
;
155 ev
.xselection
.time
= event
->xselectionrequest
.time
;
157 XSendEvent(event
->xany
.display
, event
->xselectionrequest
.requestor
, False
, 0, &ev
);
158 XFlush(event
->xany
.display
);
161 static void handleRequestEvent(XEvent
* event
)
163 SelectionHandler
*handler
;
164 WMArrayIterator iter
;
168 WM_ITERATE_ARRAY(selHandlers
, handler
, iter
) {
170 switch (event
->type
) {
172 if (W_VIEW_DRAWABLE(handler
->view
)
173 != event
->xselectionclear
.window
) {
177 handler
->flags
.done_pending
= 1;
178 if (handler
->procs
.selectionLost
)
179 handler
->procs
.selectionLost(handler
->view
, handler
->selection
, handler
->data
);
180 handler
->flags
.done_pending
= 0;
181 handler
->flags
.delete_pending
= 1;
184 case SelectionRequest
:
185 if (W_VIEW_DRAWABLE(handler
->view
) != event
->xselectionrequest
.owner
) {
189 if (handler
->procs
.convertSelection
!= NULL
190 && handler
->selection
== event
->xselectionrequest
.selection
) {
195 /* they're requesting for something old.. maybe another handler
197 if (event
->xselectionrequest
.time
< handler
->timestamp
198 && event
->xselectionrequest
.time
!= CurrentTime
) {
202 handledRequest
= False
;
204 handler
->flags
.done_pending
= 1;
206 data
= handler
->procs
.convertSelection(handler
->view
,
208 event
->xselectionrequest
.target
,
209 handler
->data
, &atom
);
211 prop
= event
->xselectionrequest
.property
;
212 /* obsolete clients that don't set the property field */
214 prop
= event
->xselectionrequest
.target
;
217 if (writeSelection(event
->xselectionrequest
.display
,
218 event
->xselectionrequest
.requestor
, prop
, atom
, data
)) {
219 handledRequest
= True
;
224 notifySelection(event
, (handledRequest
== True
? prop
: None
));
226 if (handler
->procs
.selectionDone
!= NULL
) {
227 handler
->procs
.selectionDone(handler
->view
,
229 event
->xselectionrequest
.target
,
233 handler
->flags
.done_pending
= 0;
239 /* delete handlers */
240 copy
= WMDuplicateArray(selHandlers
);
241 WM_ITERATE_ARRAY(copy
, handler
, iter
) {
242 if (handler
&& handler
->flags
.delete_pending
) {
243 WMDeleteSelectionHandler(handler
->view
, handler
->selection
, handler
->timestamp
);
249 static WMData
*getSelectionData(Display
* dpy
, Window win
, Atom where
)
255 unsigned long len
, bytes
;
257 if (XGetWindowProperty(dpy
, win
, where
, 0, MAX_PROPERTY_SIZE
,
258 False
, AnyPropertyType
, &rtype
, &bits
, &len
, &bytes
, &data
) != Success
) {
264 wdata
= WMCreateDataWithBytesNoCopy(data
, len
* bpi
, (void *) XFree
);
265 WMSetDataFormat(wdata
, bits
);
270 static void handleNotifyEvent(XEvent
* event
)
272 SelectionCallback
*handler
;
273 WMArrayIterator iter
;
277 WM_ITERATE_ARRAY(selCallbacks
, handler
, iter
) {
279 if (W_VIEW_DRAWABLE(handler
->view
) != event
->xselection
.requestor
280 || handler
->selection
!= event
->xselection
.selection
) {
283 handler
->flags
.done_pending
= 1;
285 if (event
->xselection
.property
== None
) {
288 data
= getSelectionData(event
->xselection
.display
,
289 event
->xselection
.requestor
, event
->xselection
.property
);
292 (*handler
->callback
) (handler
->view
, handler
->selection
,
293 handler
->target
, handler
->timestamp
, handler
->data
, data
);
298 handler
->flags
.done_pending
= 0;
299 handler
->flags
.delete_pending
= 1;
302 /* delete callbacks */
303 copy
= WMDuplicateArray(selCallbacks
);
304 WM_ITERATE_ARRAY(copy
, handler
, iter
) {
305 if (handler
&& handler
->flags
.delete_pending
) {
306 WMDeleteSelectionCallback(handler
->view
, handler
->selection
, handler
->timestamp
);
312 void W_HandleSelectionEvent(XEvent
* event
)
314 /*//printf("%d received selection ", event->xany.window); */
315 /*//switch(event->type) {
316 case SelectionNotify:
317 puts("notify"); break;
318 case SelectionRequest:
319 puts("request"); break;
321 puts("clear"); break;
323 puts("unknown"); break;
326 if (event
->type
== SelectionNotify
) {
327 handleNotifyEvent(event
);
329 handleRequestEvent(event
);
333 Bool
WMCreateSelectionHandler(WMView
* view
, Atom selection
, Time timestamp
, WMSelectionProcs
* procs
, void *cdata
)
335 SelectionHandler
*handler
;
336 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
338 XSetSelectionOwner(dpy
, selection
, W_VIEW_DRAWABLE(view
), timestamp
);
339 if (XGetSelectionOwner(dpy
, selection
) != W_VIEW_DRAWABLE(view
)) {
343 WMPostNotificationName(WMSelectionOwnerDidChangeNotification
, (void *)selection
, (void *)view
);
345 /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
347 handler
= wmalloc(sizeof(SelectionHandler
));
348 handler
->view
= view
;
349 handler
->selection
= selection
;
350 handler
->timestamp
= timestamp
;
351 handler
->procs
= *procs
;
352 handler
->data
= cdata
;
353 memset(&handler
->flags
, 0, sizeof(handler
->flags
));
355 if (selHandlers
== NULL
) {
356 selHandlers
= WMCreateArrayWithDestructor(4, wfree
);
359 WMAddToArray(selHandlers
, handler
);
365 WMRequestSelection(WMView
* view
, Atom selection
, Atom target
, Time timestamp
,
366 WMSelectionCallback
* callback
, void *cdata
)
368 SelectionCallback
*handler
;
370 if (XGetSelectionOwner(W_VIEW_SCREEN(view
)->display
, selection
) == None
)
373 if (!XConvertSelection(W_VIEW_SCREEN(view
)->display
, selection
, target
,
374 W_VIEW_SCREEN(view
)->clipboardAtom
, W_VIEW_DRAWABLE(view
), timestamp
)) {
378 handler
= wmalloc(sizeof(SelectionCallback
));
379 handler
->view
= view
;
380 handler
->selection
= selection
;
381 handler
->target
= target
;
382 handler
->timestamp
= timestamp
;
383 handler
->callback
= callback
;
384 handler
->data
= cdata
;
386 if (selCallbacks
== NULL
) {
387 selCallbacks
= WMCreateArrayWithDestructor(4, wfree
);
390 WMAddToArray(selCallbacks
, handler
);