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