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