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