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