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