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