9 #define MAX_PROPERTY_SIZE 8*1024
12 char *WMSelectionOwnerDidChangeNotification
= "WMSelectionOwnerDidChange";
15 typedef struct SelectionHandler
{
19 WMSelectionProcs procs
;
23 unsigned delete_pending
:1;
24 unsigned done_pending
:1;
29 typedef struct SelectionCallback
{
34 WMSelectionCallback
*callback
;
38 unsigned delete_pending
:1;
39 unsigned done_pending
:1;
45 static WMArray
*selCallbacks
= NULL
;
47 static WMArray
*selHandlers
= NULL
;
49 static Bool gotXError
= False
;
55 WMDeleteSelectionHandler(WMView
*view
, Atom selection
, Time timestamp
)
57 SelectionHandler
*handler
;
58 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
59 Window win
= W_VIEW_DRAWABLE(view
);
65 /*//printf("deleting selection handler for %d", win);*/
67 WM_ITERATE_ARRAY(selHandlers
, handler
, iter
) {
68 if (handler
->view
== view
69 && (handler
->selection
== selection
|| selection
== None
)
70 && (handler
->timestamp
== timestamp
|| timestamp
== CurrentTime
)) {
72 if (handler
->flags
.done_pending
) {
73 handler
->flags
.delete_pending
= 1;
74 /*//puts(": postponed because still pending");*/
77 /*//printf(": found & removed");*/
78 WMRemoveFromArray(selHandlers
, handler
);
86 if (XGetSelectionOwner(dpy
, selection
) == win
) {
87 XSetSelectionOwner(dpy
, selection
, None
, timestamp
);
95 WMDeleteSelectionCallback(WMView
*view
, Atom selection
, Time timestamp
)
97 SelectionCallback
*handler
;
103 WM_ITERATE_ARRAY(selCallbacks
, handler
, iter
) {
104 if (handler
->view
== view
105 && (handler
->selection
== selection
|| selection
== None
)
106 && (handler
->timestamp
== timestamp
|| timestamp
== CurrentTime
)) {
108 if (handler
->flags
.done_pending
) {
109 handler
->flags
.delete_pending
= 1;
112 WMRemoveFromArray(selCallbacks
, handler
);
120 handleXError(Display
*dpy
, XErrorEvent
*ev
)
129 writeSelection(Display
*dpy
, Window requestor
, Atom property
, Atom type
,
132 static void *oldHandler
;
135 format
= WMGetDataFormat(data
);
141 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
143 oldHandler
= XSetErrorHandler(handleXError
);
147 XChangeProperty(dpy
, requestor
, property
, type
, format
, PropModeReplace
,
148 WMDataBytes(data
), WMGetDataLength(data
)/bpi
);
152 XSetErrorHandler(oldHandler
);
159 notifySelection(XEvent
*event
, Atom prop
)
163 /* printf("event to %x\n", event->xselectionrequest.requestor); */
165 ev
.xselection
.type
= SelectionNotify
;
166 ev
.xselection
.serial
= 0;
167 ev
.xselection
.send_event
= True
;
168 ev
.xselection
.display
= event
->xselectionrequest
.display
;
169 ev
.xselection
.requestor
= event
->xselectionrequest
.requestor
;
170 ev
.xselection
.target
= event
->xselectionrequest
.target
;
171 ev
.xselection
.selection
= event
->xselectionrequest
.selection
;
172 ev
.xselection
.property
= prop
;
173 ev
.xselection
.time
= event
->xselectionrequest
.time
;
175 XSendEvent(event
->xany
.display
, event
->xselectionrequest
.requestor
,
177 XFlush(event
->xany
.display
);
182 handleRequestEvent(XEvent
*event
)
184 SelectionHandler
*handler
;
185 WMArrayIterator iter
;
189 WM_ITERATE_ARRAY(selHandlers
, handler
, iter
) {
191 switch (event
->type
) {
193 if (W_VIEW_DRAWABLE(handler
->view
)
194 != event
->xselectionclear
.window
) {
198 handler
->flags
.done_pending
= 1;
199 if (handler
->procs
.selectionLost
)
200 handler
->procs
.selectionLost(handler
->view
,
203 handler
->flags
.done_pending
= 0;
204 handler
->flags
.delete_pending
= 1;
207 case SelectionRequest
:
208 if (W_VIEW_DRAWABLE(handler
->view
)!=event
->xselectionrequest
.owner
) {
212 if (handler
->procs
.convertSelection
!= NULL
213 && handler
->selection
== event
->xselectionrequest
.selection
) {
218 /* they're requesting for something old.. maybe another handler
220 if (event
->xselectionrequest
.time
< handler
->timestamp
221 && event
->xselectionrequest
.time
!= CurrentTime
) {
225 handledRequest
= False
;
227 handler
->flags
.done_pending
= 1;
229 data
= handler
->procs
.convertSelection(handler
->view
,
231 event
->xselectionrequest
.target
,
235 prop
= event
->xselectionrequest
.property
;
236 /* obsolete clients that don't set the property field */
238 prop
= event
->xselectionrequest
.target
;
241 if (writeSelection(event
->xselectionrequest
.display
,
242 event
->xselectionrequest
.requestor
,
244 handledRequest
= True
;
249 notifySelection(event
, (handledRequest
==True
? prop
: None
));
251 if (handler
->procs
.selectionDone
!= NULL
) {
252 handler
->procs
.selectionDone(handler
->view
,
254 event
->xselectionrequest
.target
,
258 handler
->flags
.done_pending
= 0;
264 /* delete handlers */
265 copy
= WMDuplicateArray(selHandlers
);
266 WM_ITERATE_ARRAY(copy
, handler
, iter
) {
267 if (handler
&& handler
->flags
.delete_pending
) {
268 WMDeleteSelectionHandler(handler
->view
, handler
->selection
,
277 getSelectionData(Display
*dpy
, Window win
, Atom where
)
283 unsigned long len
, bytes
;
286 if (XGetWindowProperty(dpy
, win
, where
, 0, MAX_PROPERTY_SIZE
,
287 False
, AnyPropertyType
, &rtype
, &bits
, &len
,
288 &bytes
, &data
)!=Success
) {
294 wdata
= WMCreateDataWithBytesNoCopy(data
, len
*bpi
, (WMFreeDataProc
*)XFree
);
295 WMSetDataFormat(wdata
, bits
);
302 handleNotifyEvent(XEvent
*event
)
304 SelectionCallback
*handler
;
305 WMArrayIterator iter
;
309 WM_ITERATE_ARRAY(selCallbacks
, handler
, iter
) {
311 if (W_VIEW_DRAWABLE(handler
->view
) != event
->xselection
.requestor
312 || handler
->selection
!= event
->xselection
.selection
) {
315 handler
->flags
.done_pending
= 1;
317 if (event
->xselection
.property
== None
) {
320 data
= getSelectionData(event
->xselection
.display
,
321 event
->xselection
.requestor
,
322 event
->xselection
.property
);
325 (*handler
->callback
)(handler
->view
, handler
->selection
,
326 handler
->target
, handler
->timestamp
,
327 handler
->data
, data
);
332 handler
->flags
.done_pending
= 0;
333 handler
->flags
.delete_pending
= 1;
336 /* delete callbacks */
337 copy
= WMDuplicateArray(selCallbacks
);
338 WM_ITERATE_ARRAY(copy
, handler
, iter
) {
339 if (handler
&& handler
->flags
.delete_pending
) {
340 WMDeleteSelectionCallback(handler
->view
, handler
->selection
,
350 W_HandleSelectionEvent(XEvent
*event
)
352 /*//printf("%d received selection ", event->xany.window);*/
353 /*//switch(event->type) {
354 case SelectionNotify:
355 puts("notify"); break;
356 case SelectionRequest:
357 puts("request"); break;
359 puts("clear"); break;
361 puts("unknown"); break;
364 if (event
->type
== SelectionNotify
) {
365 handleNotifyEvent(event
);
367 handleRequestEvent(event
);
374 WMCreateSelectionHandler(WMView
*view
, Atom selection
, Time timestamp
,
375 WMSelectionProcs
*procs
, void *cdata
)
377 SelectionHandler
*handler
;
378 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
380 XSetSelectionOwner(dpy
, selection
, W_VIEW_DRAWABLE(view
), timestamp
);
381 if (XGetSelectionOwner(dpy
, selection
) != W_VIEW_DRAWABLE(view
)) {
385 WMPostNotificationName(WMSelectionOwnerDidChangeNotification
,
386 (void*)selection
, (void*)view
);
388 /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view));*/
390 handler
= wmalloc(sizeof(SelectionHandler
));
392 handler
->view
= view
;
393 handler
->selection
= selection
;
394 handler
->timestamp
= timestamp
;
395 handler
->procs
= *procs
;
396 handler
->data
= cdata
;
397 memset(&handler
->flags
, 0, sizeof(handler
->flags
));
399 if (selHandlers
== NULL
) {
400 selHandlers
= WMCreateArrayWithDestructor(4, wfree
);
403 WMAddToArray(selHandlers
, handler
);
411 WMRequestSelection(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
412 WMSelectionCallback
*callback
, void *cdata
)
414 SelectionCallback
*handler
;
416 if (XGetSelectionOwner(W_VIEW_SCREEN(view
)->display
, selection
) == None
)
419 if (!XConvertSelection(W_VIEW_SCREEN(view
)->display
, selection
, target
,
420 W_VIEW_SCREEN(view
)->clipboardAtom
,
421 W_VIEW_DRAWABLE(view
), timestamp
)) {
425 handler
= wmalloc(sizeof(SelectionCallback
));
427 handler
->view
= view
;
428 handler
->selection
= selection
;
429 handler
->target
= target
;
430 handler
->timestamp
= timestamp
;
431 handler
->callback
= callback
;
432 handler
->data
= cdata
;
433 memset(&handler
->flags
, 0, sizeof(handler
->flags
));
435 if (selCallbacks
== NULL
) {
436 selCallbacks
= WMCreateArrayWithDestructor(4, wfree
);
439 WMAddToArray(selCallbacks
, handler
);