Keyboard shortcut to raise the dock
[wmaker-crm.git] / WINGs / selection.c
blobce93d2254a44ec0b0640ac287597cd05ad9d47f5
2 #include <stdlib.h>
4 #include <X11/Xatom.h>
6 #include "WINGsP.h"
8 #define MAX_PROPERTY_SIZE 8*1024
10 char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
12 typedef struct SelectionHandler {
13 WMView *view;
14 Atom selection;
15 Time timestamp;
16 WMSelectionProcs procs;
17 void *data;
19 struct {
20 unsigned delete_pending:1;
21 unsigned done_pending:1;
22 } flags;
23 } SelectionHandler;
25 typedef struct SelectionCallback {
26 WMView *view;
27 Atom selection;
28 Atom target;
29 Time timestamp;
30 WMSelectionCallback *callback;
31 void *data;
33 struct {
34 unsigned delete_pending:1;
35 unsigned done_pending:1;
36 } flags;
37 } SelectionCallback;
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);
50 WMArrayIterator iter;
52 if (!selHandlers)
53 return;
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"); */
64 return;
66 /*//printf(": found & removed"); */
67 WMRemoveFromArray(selHandlers, handler);
68 break;
72 /*//printf("\n"); */
74 XGrabServer(dpy);
75 if (XGetSelectionOwner(dpy, selection) == win) {
76 XSetSelectionOwner(dpy, selection, None, timestamp);
78 XUngrabServer(dpy);
81 void WMDeleteSelectionCallback(WMView * view, Atom selection, Time timestamp)
83 SelectionCallback *handler;
84 WMArrayIterator iter;
86 if (!selCallbacks)
87 return;
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;
95 return;
97 WMRemoveFromArray(selCallbacks, handler);
98 break;
103 static int handleXError(Display * dpy, XErrorEvent * ev)
105 gotXError = True;
107 return 1;
110 static Bool writeSelection(Display * dpy, Window requestor, Atom property, Atom type, WMData * data)
112 static void *oldHandler;
113 int format, bpi;
115 format = WMGetDataFormat(data);
116 if (format == 0)
117 format = 8;
119 bpi = format / 8;
121 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
123 oldHandler = XSetErrorHandler(handleXError);
125 gotXError = False;
127 XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
128 WMDataBytes(data), WMGetDataLength(data) / bpi);
130 XFlush(dpy);
132 XSetErrorHandler(oldHandler);
134 return !gotXError;
137 static void notifySelection(XEvent * event, Atom prop)
139 XEvent ev;
141 /* printf("event to %x\n", event->xselectionrequest.requestor); */
143 ev.xselection.type = SelectionNotify;
144 ev.xselection.serial = 0;
145 ev.xselection.send_event = True;
146 ev.xselection.display = event->xselectionrequest.display;
147 ev.xselection.requestor = event->xselectionrequest.requestor;
148 ev.xselection.target = event->xselectionrequest.target;
149 ev.xselection.selection = event->xselectionrequest.selection;
150 ev.xselection.property = prop;
151 ev.xselection.time = event->xselectionrequest.time;
153 XSendEvent(event->xany.display, event->xselectionrequest.requestor, False, 0, &ev);
154 XFlush(event->xany.display);
157 static void handleRequestEvent(XEvent * event)
159 SelectionHandler *handler;
160 WMArrayIterator iter;
161 WMArray *copy;
162 Bool handledRequest;
164 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
166 switch (event->type) {
167 case SelectionClear:
168 if (W_VIEW_DRAWABLE(handler->view)
169 != event->xselectionclear.window) {
170 break;
173 handler->flags.done_pending = 1;
174 if (handler->procs.selectionLost)
175 handler->procs.selectionLost(handler->view, handler->selection, handler->data);
176 handler->flags.done_pending = 0;
177 handler->flags.delete_pending = 1;
178 break;
180 case SelectionRequest:
181 if (W_VIEW_DRAWABLE(handler->view) != event->xselectionrequest.owner) {
182 break;
185 if (handler->procs.convertSelection != NULL
186 && handler->selection == event->xselectionrequest.selection) {
187 Atom atom;
188 WMData *data;
189 Atom prop;
191 /* they're requesting for something old.. maybe another handler
192 * can handle it */
193 if (event->xselectionrequest.time < handler->timestamp
194 && event->xselectionrequest.time != CurrentTime) {
195 break;
198 handledRequest = False;
200 handler->flags.done_pending = 1;
202 data = handler->procs.convertSelection(handler->view,
203 handler->selection,
204 event->xselectionrequest.target,
205 handler->data, &atom);
207 prop = event->xselectionrequest.property;
208 /* obsolete clients that don't set the property field */
209 if (prop == None)
210 prop = event->xselectionrequest.target;
212 if (data) {
213 if (writeSelection(event->xselectionrequest.display,
214 event->xselectionrequest.requestor, prop, atom, data)) {
215 handledRequest = True;
217 WMReleaseData(data);
220 notifySelection(event, (handledRequest == True ? prop : None));
222 if (handler->procs.selectionDone != NULL) {
223 handler->procs.selectionDone(handler->view,
224 handler->selection,
225 event->xselectionrequest.target,
226 handler->data);
229 handler->flags.done_pending = 0;
231 break;
235 /* delete handlers */
236 copy = WMDuplicateArray(selHandlers);
237 WM_ITERATE_ARRAY(copy, handler, iter) {
238 if (handler && handler->flags.delete_pending) {
239 WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
242 WMFreeArray(copy);
245 static WMData *getSelectionData(Display * dpy, Window win, Atom where)
247 WMData *wdata;
248 unsigned char *data;
249 Atom rtype;
250 int bits, bpi;
251 unsigned long len, bytes;
253 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
254 False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) != Success) {
255 return NULL;
258 bpi = bits / 8;
260 wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (WMFreeDataProc *) XFree);
261 WMSetDataFormat(wdata, bits);
263 return wdata;
266 static void handleNotifyEvent(XEvent * event)
268 SelectionCallback *handler;
269 WMArrayIterator iter;
270 WMArray *copy;
271 WMData *data;
273 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
275 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
276 || handler->selection != event->xselection.selection) {
277 continue;
279 handler->flags.done_pending = 1;
281 if (event->xselection.property == None) {
282 data = NULL;
283 } else {
284 data = getSelectionData(event->xselection.display,
285 event->xselection.requestor, event->xselection.property);
288 (*handler->callback) (handler->view, handler->selection,
289 handler->target, handler->timestamp, handler->data, data);
291 if (data != NULL) {
292 WMReleaseData(data);
294 handler->flags.done_pending = 0;
295 handler->flags.delete_pending = 1;
298 /* delete callbacks */
299 copy = WMDuplicateArray(selCallbacks);
300 WM_ITERATE_ARRAY(copy, handler, iter) {
301 if (handler && handler->flags.delete_pending) {
302 WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);
305 WMFreeArray(copy);
308 void W_HandleSelectionEvent(XEvent * event)
310 /*//printf("%d received selection ", event->xany.window); */
311 /*//switch(event->type) {
312 case SelectionNotify:
313 puts("notify"); break;
314 case SelectionRequest:
315 puts("request"); break;
316 case SelectionClear:
317 puts("clear"); break;
318 default:
319 puts("unknown"); break;
320 } */
322 if (event->type == SelectionNotify) {
323 handleNotifyEvent(event);
324 } else {
325 handleRequestEvent(event);
329 Bool WMCreateSelectionHandler(WMView * view, Atom selection, Time timestamp, WMSelectionProcs * procs, void *cdata)
331 SelectionHandler *handler;
332 Display *dpy = W_VIEW_SCREEN(view)->display;
334 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
335 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
336 return False;
339 WMPostNotificationName(WMSelectionOwnerDidChangeNotification, (void *)selection, (void *)view);
341 /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
343 handler = wmalloc(sizeof(SelectionHandler));
345 handler->view = view;
346 handler->selection = selection;
347 handler->timestamp = timestamp;
348 handler->procs = *procs;
349 handler->data = cdata;
350 memset(&handler->flags, 0, sizeof(handler->flags));
352 if (selHandlers == NULL) {
353 selHandlers = WMCreateArrayWithDestructor(4, wfree);
356 WMAddToArray(selHandlers, handler);
358 return True;
361 Bool
362 WMRequestSelection(WMView * view, Atom selection, Atom target, Time timestamp,
363 WMSelectionCallback * callback, void *cdata)
365 SelectionCallback *handler;
367 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
368 return False;
370 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
371 W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) {
372 return False;
375 handler = wmalloc(sizeof(SelectionCallback));
377 handler->view = view;
378 handler->selection = selection;
379 handler->target = target;
380 handler->timestamp = timestamp;
381 handler->callback = callback;
382 handler->data = cdata;
383 memset(&handler->flags, 0, sizeof(handler->flags));
385 if (selCallbacks == NULL) {
386 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
389 WMAddToArray(selCallbacks, handler);
391 return True;