]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkasync.c
Change _gdk_x11_send_xevent_async() to
[~andy/gtk] / gdk / x11 / gdkasync.c
1 /* GTK - The GIMP Toolkit
2  * gdkasync.c: Utility functions using the Xlib asynchronous interfaces
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /* Portions of code in this file are based on code from Xlib
21  */
22 /*
23 Copyright 1986, 1998  The Open Group
24
25 Permission to use, copy, modify, distribute, and sell this software and its
26 documentation for any purpose is hereby granted without fee, provided that
27 the above copyright notice appear in all copies and that both that
28 copyright notice and this permission notice appear in supporting
29 documentation.
30
31 The above copyright notice and this permission notice shall be included in
32 all copies or substantial portions of the Software.
33
34 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
37 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
38 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40
41 Except as contained in this notice, the name of The Open Group shall not be
42 used in advertising or otherwise to promote the sale, use or other dealings
43 in this Software without prior written authorization from The Open Group.
44
45 */
46 #include <X11/Xlibint.h>
47 #include "gdkasync.h"
48 #include "gdkx.h"
49
50 typedef struct _ChildInfoChildState ChildInfoChildState;
51 typedef struct _ChildInfoState ChildInfoState;
52 typedef struct _ListChildrenState ListChildrenState;
53 typedef struct _SendEventState SendEventState;
54 typedef struct _SetInputFocusState SetInputFocusState;
55
56 typedef enum {
57   CHILD_INFO_GET_PROPERTY,
58   CHILD_INFO_GET_WA,
59   CHILD_INFO_GET_GEOMETRY
60 } ChildInfoReq;
61
62 struct _ChildInfoChildState
63 {
64   gulong seq[3];
65 };
66
67 struct _ChildInfoState
68 {
69   gboolean get_wm_state;
70   Window *children;
71   guint nchildren;
72   GdkChildInfoX11 *child_info;
73   ChildInfoChildState *child_states;
74
75   guint current_child;
76   guint n_children_found;
77   gint current_request;
78   gboolean have_error;
79   gboolean child_has_error;
80 };
81
82 struct _ListChildrenState
83 {
84   Display *dpy;
85   gulong get_property_req;
86   gboolean have_error;
87   gboolean has_wm_state;
88 };
89
90 struct _SendEventState
91 {
92   Display *dpy;
93   Window window;
94   _XAsyncHandler async;
95   gulong send_event_req;
96   gulong get_input_focus_req;
97   gboolean have_error;
98   GdkSendXEventCallback callback;
99   gpointer data;
100 };
101
102 struct _SetInputFocusState
103 {
104   Display *dpy;
105   _XAsyncHandler async;
106   gulong set_input_focus_req;
107   gulong get_input_focus_req;
108 };
109
110 static Bool
111 send_event_handler (Display *dpy,
112                     xReply  *rep,
113                     char    *buf,
114                     int      len,
115                     XPointer data)
116 {
117   SendEventState *state = (SendEventState *)data;  
118
119   if (dpy->last_request_read == state->send_event_req)
120     {
121       if (rep->generic.type == X_Error &&
122           rep->error.errorCode == BadWindow)
123         {
124           state->have_error = TRUE;
125           return True;
126         }
127     }
128   else if (dpy->last_request_read == state->get_input_focus_req)
129     {
130       xGetInputFocusReply replbuf;
131       xGetInputFocusReply *repl;
132       
133       if (rep->generic.type != X_Error)
134         {
135           /* Actually does nothing, since there are no additional bytes
136            * to read, but maintain good form.
137            */
138           repl = (xGetInputFocusReply *)
139             _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
140                             (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
141                             True);
142         }
143
144       if (state->callback)
145         state->callback (state->window, !state->have_error, state->data);
146
147       DeqAsyncHandler(state->dpy, &state->async);
148
149       return (rep->generic.type != X_Error);
150     }
151
152   return False;
153 }
154
155 static void
156 client_message_to_wire (XClientMessageEvent *ev,
157                         xEvent              *event)
158 {
159   int i;
160   event->u.clientMessage.window = ev->window;
161   event->u.u.type = ev->type;
162   event->u.u.detail = ev->format;
163   switch (ev->format)
164     {
165     case 8:     
166       event->u.clientMessage.u.b.type   = ev->message_type;
167       for (i = 0; i < 20; i++)
168         event->u.clientMessage.u.b.bytes[i] = ev->data.b[i];
169       break;
170     case 16:
171       event->u.clientMessage.u.s.type   = ev->message_type;
172       event->u.clientMessage.u.s.shorts0   = ev->data.s[0];
173       event->u.clientMessage.u.s.shorts1   = ev->data.s[1];
174       event->u.clientMessage.u.s.shorts2   = ev->data.s[2];
175       event->u.clientMessage.u.s.shorts3   = ev->data.s[3];
176       event->u.clientMessage.u.s.shorts4   = ev->data.s[4];
177       event->u.clientMessage.u.s.shorts5   = ev->data.s[5];
178       event->u.clientMessage.u.s.shorts6   = ev->data.s[6];
179       event->u.clientMessage.u.s.shorts7   = ev->data.s[7];
180       event->u.clientMessage.u.s.shorts8   = ev->data.s[8];
181       event->u.clientMessage.u.s.shorts9   = ev->data.s[9];
182       break;
183     case 32:
184       event->u.clientMessage.u.l.type   = ev->message_type;
185       event->u.clientMessage.u.l.longs0   = ev->data.l[0];
186       event->u.clientMessage.u.l.longs1   = ev->data.l[1];
187       event->u.clientMessage.u.l.longs2   = ev->data.l[2];
188       event->u.clientMessage.u.l.longs3   = ev->data.l[3];
189       event->u.clientMessage.u.l.longs4   = ev->data.l[4];
190       break;
191     default:
192       /* client passing bogus data, let server complain */
193       break;
194     }
195 }
196
197 void
198 _gdk_x11_send_client_message_async (GdkDisplay           *display, 
199                                     Window                window, 
200                                     gboolean              propagate,
201                                     glong                 event_mask,
202                                     XClientMessageEvent  *event_send,
203                                     GdkSendXEventCallback callback,
204                                     gpointer              data)
205 {
206   Display *dpy;
207   SendEventState *state;
208   
209   dpy = GDK_DISPLAY_XDISPLAY (display);
210
211   state = g_new (SendEventState, 1);
212
213   state->dpy = dpy;
214   state->window = window;
215   state->callback = callback;
216   state->data = data;
217   state->have_error = FALSE;
218   
219   LockDisplay(dpy);
220
221   state->async.next = dpy->async_handlers;
222   state->async.handler = send_event_handler;
223   state->async.data = (XPointer) state;
224   dpy->async_handlers = &state->async;
225
226   {
227     register xSendEventReq *req;
228     xEvent ev;
229     
230     client_message_to_wire (event_send, &ev);
231       
232     GetReq(SendEvent, req);
233     req->destination = window;
234     req->propagate = propagate;
235     req->eventMask = event_mask;
236     /* gross, matches Xproto.h */
237 #ifdef WORD64                   
238     memcpy ((char *) req->eventdata, (char *) &ev, SIZEOF(xEvent));
239 #else    
240     memcpy ((char *) &req->event, (char *) &ev, SIZEOF(xEvent));
241 #endif
242     
243     state->send_event_req = dpy->request;
244   }
245
246   /*
247    * XSync (dpy, 0)
248    */
249   {
250     xReq *req;
251     
252     GetEmptyReq(GetInputFocus, req);
253     state->get_input_focus_req = dpy->request;
254   }
255   
256   UnlockDisplay(dpy);
257   SyncHandle();
258 }
259
260 static Bool
261 set_input_focus_handler (Display *dpy,
262                          xReply  *rep,
263                          char    *buf,
264                          int      len,
265                          XPointer data)
266 {
267   SetInputFocusState *state = (SetInputFocusState *)data;  
268
269   if (dpy->last_request_read == state->set_input_focus_req)
270     {
271       if (rep->generic.type == X_Error &&
272           rep->error.errorCode == BadMatch)
273         {
274           /* Consume BadMatch errors, since we have no control
275            * over them.
276            */
277           return True;
278         }
279     }
280   
281   if (dpy->last_request_read == state->get_input_focus_req)
282     {
283       xGetInputFocusReply replbuf;
284       xGetInputFocusReply *repl;
285       
286       if (rep->generic.type != X_Error)
287         {
288           /* Actually does nothing, since there are no additional bytes
289            * to read, but maintain good form.
290            */
291           repl = (xGetInputFocusReply *)
292             _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
293                             (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
294                             True);
295         }
296
297       DeqAsyncHandler(state->dpy, &state->async);
298
299       g_free (state);
300       
301       return (rep->generic.type != X_Error);
302     }
303
304   return False;
305 }
306
307 void
308 _gdk_x11_set_input_focus_safe (GdkDisplay             *display,
309                                Window                  window,
310                                int                     revert_to,
311                                Time                    time)
312 {
313   Display *dpy;
314   SetInputFocusState *state;
315   
316   dpy = GDK_DISPLAY_XDISPLAY (display);
317
318   state = g_new (SetInputFocusState, 1);
319
320   state->dpy = dpy;
321   
322   LockDisplay(dpy);
323
324   state->async.next = dpy->async_handlers;
325   state->async.handler = set_input_focus_handler;
326   state->async.data = (XPointer) state;
327   dpy->async_handlers = &state->async;
328
329   {
330     xSetInputFocusReq *req;
331     
332     GetReq(SetInputFocus, req);
333     req->focus = window;
334     req->revertTo = revert_to;
335     req->time = time;
336     state->set_input_focus_req = dpy->request;
337   }
338
339   /*
340    * XSync (dpy, 0)
341    */
342   {
343     xReq *req;
344     
345     GetEmptyReq(GetInputFocus, req);
346     state->get_input_focus_req = dpy->request;
347   }
348   
349   UnlockDisplay(dpy);
350   SyncHandle();
351 }
352
353 static Bool
354 list_children_handler (Display *dpy,
355                        xReply  *rep,
356                        char    *buf,
357                        int      len,
358                        XPointer data)
359 {
360   ListChildrenState *state = (ListChildrenState *)data;
361
362   if (dpy->last_request_read != state->get_property_req)
363     return False;
364   
365   if (rep->generic.type == X_Error)
366     {
367       state->have_error = TRUE;
368       return False;
369     }
370   else
371     {
372       xGetPropertyReply replbuf;
373       xGetPropertyReply *repl;
374             
375       repl = (xGetPropertyReply *)
376         _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
377                         (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
378                         True);
379
380       state->has_wm_state = repl->propertyType != None;
381       /* Since we called GetProperty with longLength of 0, we don't
382        * have to worry about consuming the property data that would
383        * normally follow after the reply
384        */
385
386       return True;
387     }
388 }
389
390 static gboolean
391 list_children_and_wm_state (Display      *dpy,
392                             Window        w,
393                             Atom          wm_state_atom,
394                             gboolean     *has_wm_state,
395                             Window      **children,
396                             unsigned int *nchildren)
397 {
398   ListChildrenState state;
399   _XAsyncHandler async;
400   long nbytes;
401   xQueryTreeReply rep;
402   register xResourceReq *req;
403   xGetPropertyReq *prop_req;
404
405   LockDisplay(dpy);
406
407   *children = NULL;
408   *nchildren = 0;
409   *has_wm_state = FALSE;
410   
411   state.have_error = FALSE;
412   state.has_wm_state = FALSE;
413
414   if (wm_state_atom)
415     {
416       async.next = dpy->async_handlers;
417       async.handler = list_children_handler;
418       async.data = (XPointer) &state;
419       dpy->async_handlers = &async;
420
421       GetReq (GetProperty, prop_req);
422       prop_req->window = w;
423       prop_req->property = wm_state_atom;
424       prop_req->type = AnyPropertyType;
425       prop_req->delete = False;
426       prop_req->longOffset = 0;
427       prop_req->longLength = 0;
428       
429       state.get_property_req = dpy->request;
430     }
431   
432   GetResReq(QueryTree, w, req);
433   if (!_XReply(dpy, (xReply *)&rep, 0, xFalse))
434     {
435       state.have_error = TRUE;
436       goto out;
437     }
438
439   if (rep.nChildren != 0)
440     {
441       nbytes = rep.nChildren << 2;
442       if (state.have_error)
443         {
444           _XEatData(dpy, (unsigned long) nbytes);
445           goto out;
446         }
447       *children = g_new (Window, rep.nChildren);
448       _XRead32 (dpy, (long *) *children, nbytes);
449     }
450
451   *nchildren = rep.nChildren;
452   *has_wm_state = state.has_wm_state;
453
454  out:
455   if (wm_state_atom)
456     DeqAsyncHandler(dpy, &async);
457   UnlockDisplay(dpy);
458   SyncHandle();
459   
460   return !state.have_error;
461 }
462
463 static void
464 handle_get_wa_reply (Display                   *dpy,
465                      ChildInfoState            *state,
466                      xGetWindowAttributesReply *repl)
467 {
468   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
469   child->is_mapped = repl->mapState != IsUnmapped;
470   child->window_class = repl->class;
471 }
472
473 static void
474 handle_get_geometry_reply (Display           *dpy,
475                            ChildInfoState    *state,
476                            xGetGeometryReply *repl)
477 {
478   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
479   
480   child->x = cvtINT16toInt (repl->x);
481   child->y = cvtINT16toInt (repl->y);
482   child->width = repl->width;
483   child->height = repl->height;
484 }
485
486 static void
487 handle_get_property_reply (Display           *dpy,
488                            ChildInfoState    *state,
489                            xGetPropertyReply *repl)
490 {
491   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
492   child->has_wm_state = repl->propertyType != None;
493
494   /* Since we called GetProperty with longLength of 0, we don't
495    * have to worry about consuming the property data that would
496    * normally follow after the reply
497    */
498 }
499
500 static void
501 next_child (ChildInfoState *state)
502 {
503   if (state->current_request == CHILD_INFO_GET_GEOMETRY)
504     {
505       if (!state->have_error && !state->child_has_error)
506         {
507           state->child_info[state->n_children_found].window = state->children[state->current_child];
508           state->n_children_found++;
509         }
510       state->current_child++;
511       if (state->get_wm_state)
512         state->current_request = CHILD_INFO_GET_PROPERTY;
513       else
514         state->current_request = CHILD_INFO_GET_WA;
515       state->child_has_error = FALSE;
516       state->have_error = FALSE;
517     }
518   else
519     state->current_request++;
520 }
521
522 static Bool
523 get_child_info_handler (Display *dpy,
524                         xReply  *rep,
525                         char    *buf,
526                         int      len,
527                         XPointer data)
528 {
529   Bool result = True;
530   
531   ChildInfoState *state = (ChildInfoState *)data;
532
533   if (dpy->last_request_read != state->child_states[state->current_child].seq[state->current_request])
534     return False;
535   
536   if (rep->generic.type == X_Error)
537     {
538       state->child_has_error = TRUE;
539       if (rep->error.errorCode != BadDrawable ||
540           rep->error.errorCode != BadWindow)
541         {
542           state->have_error = TRUE;
543           result = False;
544         }
545     }
546   else
547     {
548       switch (state->current_request)
549         {
550         case CHILD_INFO_GET_PROPERTY:
551           {
552             xGetPropertyReply replbuf;
553             xGetPropertyReply *repl;
554             
555             repl = (xGetPropertyReply *)
556               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
557                               (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
558                               True);
559             
560             handle_get_property_reply (dpy, state, repl);
561           }
562           break;
563         case CHILD_INFO_GET_WA:
564           {
565             xGetWindowAttributesReply replbuf;
566             xGetWindowAttributesReply *repl;
567             
568             repl = (xGetWindowAttributesReply *)
569               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
570                               (sizeof(xGetWindowAttributesReply) - sizeof(xReply)) >> 2,
571                               True);
572             
573             handle_get_wa_reply (dpy, state, repl);
574           }
575           break;
576         case CHILD_INFO_GET_GEOMETRY:
577           {
578             xGetGeometryReply replbuf;
579             xGetGeometryReply *repl;
580             
581             repl = (xGetGeometryReply *)
582               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
583                               (sizeof(xGetGeometryReply) - sizeof(xReply)) >> 2,
584                               True);
585             
586             handle_get_geometry_reply (dpy, state, repl);
587           }
588           break;
589         }
590     }
591
592   next_child (state);
593
594   return result;
595 }
596
597 gboolean
598 _gdk_x11_get_window_child_info (GdkDisplay       *display,
599                                 Window            window,
600                                 gboolean          get_wm_state,
601                                 gboolean         *win_has_wm_state,
602                                 GdkChildInfoX11 **children,
603                                 guint            *nchildren)
604 {
605   Display *dpy;
606   _XAsyncHandler async;
607   ChildInfoState state;
608   Atom wm_state_atom;
609   gboolean has_wm_state;
610   Bool result;
611   guint i;
612
613   *children = NULL;
614   *nchildren = 0;
615   
616   dpy = GDK_DISPLAY_XDISPLAY (display);
617   if (get_wm_state)
618     wm_state_atom = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
619   else
620     wm_state_atom = None;
621
622   gdk_error_trap_push ();
623   result = list_children_and_wm_state (dpy, window,
624                                        win_has_wm_state ? wm_state_atom : None,
625                                        &has_wm_state,
626                                        &state.children, &state.nchildren);
627   gdk_error_trap_pop ();
628   if (!result)
629     return FALSE;
630
631   if (has_wm_state)
632     {
633       if (win_has_wm_state)
634         *win_has_wm_state = TRUE;
635       return TRUE;
636     }
637   else
638     {
639       if (win_has_wm_state)
640         *win_has_wm_state = FALSE;
641     }
642
643   state.get_wm_state = get_wm_state;
644   state.child_info = g_new (GdkChildInfoX11, state.nchildren);
645   state.child_states = g_new (ChildInfoChildState, state.nchildren);
646   state.current_child = 0;
647   state.n_children_found = 0;
648   if (get_wm_state)
649     state.current_request = CHILD_INFO_GET_PROPERTY;
650   else
651     state.current_request = CHILD_INFO_GET_WA;
652   state.have_error = FALSE;
653   state.child_has_error = FALSE;
654
655   LockDisplay(dpy);
656
657   async.next = dpy->async_handlers;
658   async.handler = get_child_info_handler;
659   async.data = (XPointer) &state;
660   dpy->async_handlers = &async;
661   
662   for (i = 0; i < state.nchildren; i++)
663     {
664       xResourceReq *resource_req;
665       xGetPropertyReq *prop_req;
666       Window window = state.children[i];
667       
668       if (get_wm_state)
669         {
670           GetReq (GetProperty, prop_req);
671           prop_req->window = window;
672           prop_req->property = wm_state_atom;
673           prop_req->type = AnyPropertyType;
674           prop_req->delete = False;
675           prop_req->longOffset = 0;
676           prop_req->longLength = 0;
677
678           state.child_states[i].seq[CHILD_INFO_GET_PROPERTY] = dpy->request;
679         }
680       
681       GetResReq(GetWindowAttributes, window, resource_req);
682       state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
683       
684       GetResReq(GetGeometry, window, resource_req);
685       state.child_states[i].seq[CHILD_INFO_GET_GEOMETRY] = dpy->request;
686     }
687
688   if (i != 0)
689     {
690       /* Wait for the last reply
691        */
692       xGetGeometryReply rep;
693
694       /* On error, our async handler will get called
695        */
696       if (_XReply (dpy, (xReply *)&rep, 0, xTrue))
697         handle_get_geometry_reply (dpy, &state, &rep);
698
699       next_child (&state);
700     }
701
702   if (!state.have_error)
703     {
704       *children = state.child_info;
705       *nchildren = state.n_children_found;
706     }
707   else
708     {
709       g_free (state.child_info);
710     }
711
712   g_free (state.children);
713   g_free (state.child_states);
714   
715   DeqAsyncHandler(dpy, &async);
716   UnlockDisplay(dpy);
717   SyncHandle();
718
719   return !state.have_error;
720 }
721