]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkasync.c
Consistently use per-display error traps in the X11 backend
[~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 "gdkx.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;
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 set_input_focus_handler (Display *dpy,
293                          xReply  *rep,
294                          char    *buf,
295                          int      len,
296                          XPointer data)
297 {
298   SetInputFocusState *state = (SetInputFocusState *)data;  
299
300   if (dpy->last_request_read == state->set_input_focus_req)
301     {
302       if (rep->generic.type == X_Error &&
303           rep->error.errorCode == BadMatch)
304         {
305           /* Consume BadMatch errors, since we have no control
306            * over them.
307            */
308           return True;
309         }
310     }
311   
312   if (dpy->last_request_read == state->get_input_focus_req)
313     {
314       xGetInputFocusReply replbuf;
315       xGetInputFocusReply *repl;
316       
317       if (rep->generic.type != X_Error)
318         {
319           /* Actually does nothing, since there are no additional bytes
320            * to read, but maintain good form.
321            */
322           repl = (xGetInputFocusReply *)
323             _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
324                             (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
325                             True);
326         }
327
328       DeqAsyncHandler(state->dpy, &state->async);
329
330       g_free (state);
331       
332       return (rep->generic.type != X_Error);
333     }
334
335   return False;
336 }
337
338 void
339 _gdk_x11_set_input_focus_safe (GdkDisplay             *display,
340                                Window                  window,
341                                int                     revert_to,
342                                Time                    time)
343 {
344   Display *dpy;
345   SetInputFocusState *state;
346   
347   dpy = GDK_DISPLAY_XDISPLAY (display);
348
349   state = g_new (SetInputFocusState, 1);
350
351   state->dpy = dpy;
352   
353   LockDisplay(dpy);
354
355   state->async.next = dpy->async_handlers;
356   state->async.handler = set_input_focus_handler;
357   state->async.data = (XPointer) state;
358   dpy->async_handlers = &state->async;
359
360   {
361     xSetInputFocusReq *req;
362     
363     GetReq(SetInputFocus, req);
364     req->focus = window;
365     req->revertTo = revert_to;
366     req->time = time;
367     state->set_input_focus_req = dpy->request;
368   }
369
370   /*
371    * XSync (dpy, 0)
372    */
373   {
374     xReq *req;
375     
376     GetEmptyReq(GetInputFocus, req);
377     state->get_input_focus_req = dpy->request;
378   }
379   
380   UnlockDisplay(dpy);
381   SyncHandle();
382 }
383
384 static Bool
385 list_children_handler (Display *dpy,
386                        xReply  *rep,
387                        char    *buf,
388                        int      len,
389                        XPointer data)
390 {
391   ListChildrenState *state = (ListChildrenState *)data;
392
393   if (dpy->last_request_read != state->get_property_req)
394     return False;
395   
396   if (rep->generic.type == X_Error)
397     {
398       state->have_error = TRUE;
399       return False;
400     }
401   else
402     {
403       xGetPropertyReply replbuf;
404       xGetPropertyReply *repl;
405             
406       repl = (xGetPropertyReply *)
407         _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
408                         (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
409                         True);
410
411       state->has_wm_state = repl->propertyType != None;
412       /* Since we called GetProperty with longLength of 0, we don't
413        * have to worry about consuming the property data that would
414        * normally follow after the reply
415        */
416
417       return True;
418     }
419 }
420
421 static gboolean
422 list_children_and_wm_state (Display      *dpy,
423                             Window        w,
424                             Atom          wm_state_atom,
425                             gboolean     *has_wm_state,
426                             Window      **children,
427                             unsigned int *nchildren)
428 {
429   ListChildrenState state;
430   _XAsyncHandler async;
431   long nbytes;
432   xQueryTreeReply rep;
433   register xResourceReq *req;
434   xGetPropertyReq *prop_req;
435
436   LockDisplay(dpy);
437
438   *children = NULL;
439   *nchildren = 0;
440   *has_wm_state = FALSE;
441   
442   state.have_error = FALSE;
443   state.has_wm_state = FALSE;
444
445   if (wm_state_atom)
446     {
447       async.next = dpy->async_handlers;
448       async.handler = list_children_handler;
449       async.data = (XPointer) &state;
450       dpy->async_handlers = &async;
451
452       GetReq (GetProperty, prop_req);
453       prop_req->window = w;
454       prop_req->property = wm_state_atom;
455       prop_req->type = AnyPropertyType;
456       prop_req->delete = False;
457       prop_req->longOffset = 0;
458       prop_req->longLength = 0;
459       
460       state.get_property_req = dpy->request;
461     }
462   
463   GetResReq(QueryTree, w, req);
464   if (!_XReply(dpy, (xReply *)&rep, 0, xFalse))
465     {
466       state.have_error = TRUE;
467       goto out;
468     }
469
470   if (rep.nChildren != 0)
471     {
472       nbytes = rep.nChildren << 2;
473       if (state.have_error)
474         {
475           _XEatData(dpy, (unsigned long) nbytes);
476           goto out;
477         }
478       *children = g_new (Window, rep.nChildren);
479       _XRead32 (dpy, (long *) *children, nbytes);
480     }
481
482   *nchildren = rep.nChildren;
483   *has_wm_state = state.has_wm_state;
484
485  out:
486   if (wm_state_atom)
487     DeqAsyncHandler(dpy, &async);
488   UnlockDisplay(dpy);
489   SyncHandle();
490   
491   return !state.have_error;
492 }
493
494 static void
495 handle_get_wa_reply (Display                   *dpy,
496                      ChildInfoState            *state,
497                      xGetWindowAttributesReply *repl)
498 {
499   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
500   child->is_mapped = repl->mapState != IsUnmapped;
501   child->window_class = repl->class;
502 }
503
504 static void
505 handle_get_geometry_reply (Display           *dpy,
506                            ChildInfoState    *state,
507                            xGetGeometryReply *repl)
508 {
509   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
510   
511   child->x = cvtINT16toInt (repl->x);
512   child->y = cvtINT16toInt (repl->y);
513   child->width = repl->width;
514   child->height = repl->height;
515 }
516
517 static void
518 handle_get_property_reply (Display           *dpy,
519                            ChildInfoState    *state,
520                            xGetPropertyReply *repl)
521 {
522   GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
523   child->has_wm_state = repl->propertyType != None;
524
525   /* Since we called GetProperty with longLength of 0, we don't
526    * have to worry about consuming the property data that would
527    * normally follow after the reply
528    */
529 }
530
531 static void
532 next_child (ChildInfoState *state)
533 {
534   if (state->current_request == CHILD_INFO_GET_GEOMETRY)
535     {
536       if (!state->have_error && !state->child_has_error)
537         {
538           state->child_info[state->n_children_found].window = state->children[state->current_child];
539           state->n_children_found++;
540         }
541       state->current_child++;
542       if (state->get_wm_state)
543         state->current_request = CHILD_INFO_GET_PROPERTY;
544       else
545         state->current_request = CHILD_INFO_GET_WA;
546       state->child_has_error = FALSE;
547       state->have_error = FALSE;
548     }
549   else
550     state->current_request++;
551 }
552
553 static Bool
554 get_child_info_handler (Display *dpy,
555                         xReply  *rep,
556                         char    *buf,
557                         int      len,
558                         XPointer data)
559 {
560   Bool result = True;
561   
562   ChildInfoState *state = (ChildInfoState *)data;
563
564   if (dpy->last_request_read != state->child_states[state->current_child].seq[state->current_request])
565     return False;
566   
567   if (rep->generic.type == X_Error)
568     {
569       state->child_has_error = TRUE;
570       if (rep->error.errorCode != BadDrawable ||
571           rep->error.errorCode != BadWindow)
572         {
573           state->have_error = TRUE;
574           result = False;
575         }
576     }
577   else
578     {
579       switch (state->current_request)
580         {
581         case CHILD_INFO_GET_PROPERTY:
582           {
583             xGetPropertyReply replbuf;
584             xGetPropertyReply *repl;
585             
586             repl = (xGetPropertyReply *)
587               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
588                               (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
589                               True);
590             
591             handle_get_property_reply (dpy, state, repl);
592           }
593           break;
594         case CHILD_INFO_GET_WA:
595           {
596             xGetWindowAttributesReply replbuf;
597             xGetWindowAttributesReply *repl;
598             
599             repl = (xGetWindowAttributesReply *)
600               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
601                               (sizeof(xGetWindowAttributesReply) - sizeof(xReply)) >> 2,
602                               True);
603             
604             handle_get_wa_reply (dpy, state, repl);
605           }
606           break;
607         case CHILD_INFO_GET_GEOMETRY:
608           {
609             xGetGeometryReply replbuf;
610             xGetGeometryReply *repl;
611             
612             repl = (xGetGeometryReply *)
613               _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
614                               (sizeof(xGetGeometryReply) - sizeof(xReply)) >> 2,
615                               True);
616             
617             handle_get_geometry_reply (dpy, state, repl);
618           }
619           break;
620         }
621     }
622
623   next_child (state);
624
625   return result;
626 }
627
628 gboolean
629 _gdk_x11_get_window_child_info (GdkDisplay       *display,
630                                 Window            window,
631                                 gboolean          get_wm_state,
632                                 gboolean         *win_has_wm_state,
633                                 GdkChildInfoX11 **children,
634                                 guint            *nchildren)
635 {
636   Display *dpy;
637   _XAsyncHandler async;
638   ChildInfoState state;
639   Atom wm_state_atom;
640   gboolean has_wm_state;
641   Bool result;
642   guint i;
643
644   *children = NULL;
645   *nchildren = 0;
646   
647   dpy = GDK_DISPLAY_XDISPLAY (display);
648   if (get_wm_state)
649     wm_state_atom = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
650   else
651     wm_state_atom = None;
652
653   state.children = NULL;
654   state.nchildren = 0;
655
656   gdk_x11_display_error_trap_push (display);
657   result = list_children_and_wm_state (dpy, window,
658                                        win_has_wm_state ? wm_state_atom : None,
659                                        &has_wm_state,
660                                        &state.children, &state.nchildren);
661   gdk_x11_display_error_trap_pop_ignored (display);
662   if (!result)
663     {
664       g_free (state.children);
665       return FALSE;
666     }
667
668   if (has_wm_state)
669     {
670       if (win_has_wm_state)
671         *win_has_wm_state = TRUE;
672       g_free (state.children);
673       return TRUE;
674     }
675   else
676     {
677       if (win_has_wm_state)
678         *win_has_wm_state = FALSE;
679     }
680
681   state.get_wm_state = get_wm_state;
682   state.child_info = g_new (GdkChildInfoX11, state.nchildren);
683   state.child_states = g_new (ChildInfoChildState, state.nchildren);
684   state.current_child = 0;
685   state.n_children_found = 0;
686   if (get_wm_state)
687     state.current_request = CHILD_INFO_GET_PROPERTY;
688   else
689     state.current_request = CHILD_INFO_GET_WA;
690   state.have_error = FALSE;
691   state.child_has_error = FALSE;
692
693   LockDisplay(dpy);
694
695   async.next = dpy->async_handlers;
696   async.handler = get_child_info_handler;
697   async.data = (XPointer) &state;
698   dpy->async_handlers = &async;
699   
700   for (i = 0; i < state.nchildren; i++)
701     {
702       xResourceReq *resource_req;
703       xGetPropertyReq *prop_req;
704       Window window = state.children[i];
705       
706       if (get_wm_state)
707         {
708           GetReq (GetProperty, prop_req);
709           prop_req->window = window;
710           prop_req->property = wm_state_atom;
711           prop_req->type = AnyPropertyType;
712           prop_req->delete = False;
713           prop_req->longOffset = 0;
714           prop_req->longLength = 0;
715
716           state.child_states[i].seq[CHILD_INFO_GET_PROPERTY] = dpy->request;
717         }
718       
719       GetResReq(GetWindowAttributes, window, resource_req);
720       state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
721       
722       GetResReq(GetGeometry, window, resource_req);
723       state.child_states[i].seq[CHILD_INFO_GET_GEOMETRY] = dpy->request;
724     }
725
726   if (i != 0)
727     {
728       /* Wait for the last reply
729        */
730       xGetGeometryReply rep;
731
732       /* On error, our async handler will get called
733        */
734       if (_XReply (dpy, (xReply *)&rep, 0, xTrue))
735         handle_get_geometry_reply (dpy, &state, &rep);
736
737       next_child (&state);
738     }
739
740   if (!state.have_error)
741     {
742       *children = state.child_info;
743       *nchildren = state.n_children_found;
744     }
745   else
746     {
747       g_free (state.child_info);
748     }
749
750   g_free (state.children);
751   g_free (state.child_states);
752   
753   DeqAsyncHandler(dpy, &async);
754   UnlockDisplay(dpy);
755   SyncHandle();
756
757   return !state.have_error;
758 }
759
760 static gboolean
761 roundtrip_callback_idle (gpointer data)
762 {
763   RoundtripState *state = (RoundtripState *)data;  
764   
765   state->callback (state->display, state->data, state->get_input_focus_req);
766
767   g_free (state);
768
769   return FALSE;
770 }
771
772 static Bool
773 roundtrip_handler (Display *dpy,
774                    xReply  *rep,
775                    char    *buf,
776                    int      len,
777                    XPointer data)
778 {
779   RoundtripState *state = (RoundtripState *)data;  
780   
781   if (dpy->last_request_read == state->get_input_focus_req)
782     {
783       xGetInputFocusReply replbuf;
784       xGetInputFocusReply *repl;
785       
786       if (rep->generic.type != X_Error)
787         {
788           /* Actually does nothing, since there are no additional bytes
789            * to read, but maintain good form.
790            */
791           repl = (xGetInputFocusReply *)
792             _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
793                             (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
794                             True);
795         }
796
797       
798       if (state->callback)
799         gdk_threads_add_idle (roundtrip_callback_idle, state);
800
801       DeqAsyncHandler(state->dpy, &state->async);
802
803       return (rep->generic.type != X_Error);
804     }
805
806   return False;
807 }
808
809 void
810 _gdk_x11_roundtrip_async (GdkDisplay           *display, 
811                           GdkRoundTripCallback callback,
812                           gpointer              data)
813 {
814   Display *dpy;
815   RoundtripState *state;
816   
817   dpy = GDK_DISPLAY_XDISPLAY (display);
818
819   state = g_new (RoundtripState, 1);
820
821   state->display = display;
822   state->dpy = dpy;
823   state->callback = callback;
824   state->data = data;
825   
826   LockDisplay(dpy);
827
828   state->async.next = dpy->async_handlers;
829   state->async.handler = roundtrip_handler;
830   state->async.data = (XPointer) state;
831   dpy->async_handlers = &state->async;
832
833   /*
834    * XSync (dpy, 0)
835    */
836   {
837     xReq *req;
838     
839     GetEmptyReq(GetInputFocus, req);
840     state->get_input_focus_req = dpy->request;
841   }
842   
843   UnlockDisplay(dpy);
844   SyncHandle();
845 }