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, see <http://www.gnu.org/licenses/>.
17  */
18 /* Portions of code in this file are based on code from Xlib
19  */
20 /*
21 Copyright 1986, 1998  The Open Group
22 
23 Permission to use, copy, modify, distribute, and sell this software and its
24 documentation for any purpose is hereby granted without fee, provided that
25 the above copyright notice appear in all copies and that both that
26 copyright notice and this permission notice appear in supporting
27 documentation.
28 
29 The above copyright notice and this permission notice shall be included in
30 all copies or substantial portions of the Software.
31 
32 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
35 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
36 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
37 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 
39 Except as contained in this notice, the name of The Open Group shall not be
40 used in advertising or otherwise to promote the sale, use or other dealings
41 in this Software without prior written authorization from The Open Group.
42 
43 */
44 #include "config.h"
45 
46 #include "gdkasync.h"
47 #include "gdkprivate-x11.h"
48 #include "gdkdisplay-x11.h"
49 #include "gdk-private.h"
50 
51 #include <X11/Xlibint.h>
52 
53 
54 typedef struct _ChildInfoChildState ChildInfoChildState;
55 typedef struct _ChildInfoState ChildInfoState;
56 typedef struct _ListChildrenState ListChildrenState;
57 typedef struct _SendEventState SendEventState;
58 typedef struct _SetInputFocusState SetInputFocusState;
59 typedef struct _RoundtripState RoundtripState;
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   int 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 struct _RoundtripState
116 {
117   Display *dpy;
118   _XAsyncHandler async;
119   gulong get_input_focus_req;
120   GdkDisplay *display;
121   GdkRoundTripCallback callback;
122   gpointer data;
123 };
124 
125 static gboolean
callback_idle(gpointer data)126 callback_idle (gpointer data)
127 {
128   SendEventState *state = (SendEventState *)data;
129 
130   state->callback (state->window, !state->have_error, state->data);
131 
132   g_free (state);
133 
134   return FALSE;
135 }
136 
137 static Bool
send_event_handler(Display * dpy,xReply * rep,char * buf,int len,XPointer data)138 send_event_handler (Display *dpy,
139 		    xReply  *rep,
140 		    char    *buf,
141 		    int      len,
142 		    XPointer data)
143 {
144   SendEventState *state = (SendEventState *)data;
145 
146   if (dpy->last_request_read == state->send_event_req)
147     {
148       if (rep->generic.type == X_Error &&
149 	  rep->error.errorCode == BadWindow)
150 	{
151 	  state->have_error = TRUE;
152 	  return True;
153 	}
154     }
155   else if (dpy->last_request_read == state->get_input_focus_req)
156     {
157       xGetInputFocusReply replbuf;
158       xGetInputFocusReply *repl G_GNUC_UNUSED;
159 
160       if (rep->generic.type != X_Error)
161 	{
162 	  /* Actually does nothing, since there are no additional bytes
163 	   * to read, but maintain good form.
164 	   */
165 	  repl = (xGetInputFocusReply *)
166 	    _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
167 			    (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
168 			    True);
169 	}
170 
171       if (state->callback)
172         {
173           guint id;
174           id = g_idle_add (callback_idle, state);
175           gdk_source_set_static_name_by_id (id, "[gtk] callback_idle");
176         }
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
client_message_to_wire(XClientMessageEvent * ev,xEvent * event)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
_gdk_x11_send_client_message_async(GdkDisplay * display,Window window,gboolean propagate,glong event_mask,XClientMessageEvent * event_send,GdkSendXEventCallback callback,gpointer data)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     G_GNUC_UNUSED 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
list_children_handler(Display * dpy,xReply * rep,char * buf,int len,XPointer data)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
list_children_and_wm_state(Display * dpy,Window w,Atom wm_state_atom,gboolean * has_wm_state,Window ** children,unsigned int * nchildren)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
handle_get_wa_reply(Display * dpy,ChildInfoState * state,xGetWindowAttributesReply * repl)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
handle_get_geometry_reply(Display * dpy,ChildInfoState * state,xGetGeometryReply * repl)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
handle_get_property_reply(Display * dpy,ChildInfoState * state,xGetPropertyReply * repl)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
next_child(ChildInfoState * state)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
get_child_info_handler(Display * dpy,xReply * rep,char * buf,int len,XPointer data)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         default:
528           break;
529 	}
530     }
531 
532   next_child (state);
533 
534   return result;
535 }
536 
537 gboolean
_gdk_x11_get_window_child_info(GdkDisplay * display,Window window,gboolean get_wm_state,gboolean * win_has_wm_state,GdkChildInfoX11 ** children,guint * nchildren)538 _gdk_x11_get_window_child_info (GdkDisplay       *display,
539 				Window            window,
540 				gboolean          get_wm_state,
541 				gboolean         *win_has_wm_state,
542 				GdkChildInfoX11 **children,
543 				guint            *nchildren)
544 {
545   Display *dpy;
546   _XAsyncHandler async;
547   ChildInfoState state;
548   Atom wm_state_atom;
549   gboolean has_wm_state;
550   Bool result;
551   guint i;
552 
553   *children = NULL;
554   *nchildren = 0;
555 
556   dpy = GDK_DISPLAY_XDISPLAY (display);
557   if (get_wm_state)
558     wm_state_atom = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
559   else
560     wm_state_atom = None;
561 
562   state.children = NULL;
563   state.nchildren = 0;
564 
565   gdk_x11_display_error_trap_push (display);
566   result = list_children_and_wm_state (dpy, window,
567 				       win_has_wm_state ? wm_state_atom : None,
568 				       &has_wm_state,
569 				       &state.children, &state.nchildren);
570   gdk_x11_display_error_trap_pop_ignored (display);
571   if (!result)
572     {
573       g_free (state.children);
574       return FALSE;
575     }
576 
577   if (has_wm_state)
578     {
579       if (win_has_wm_state)
580 	*win_has_wm_state = TRUE;
581       g_free (state.children);
582       return TRUE;
583     }
584   else
585     {
586       if (win_has_wm_state)
587 	*win_has_wm_state = FALSE;
588     }
589 
590   state.get_wm_state = get_wm_state;
591   state.child_info = g_new (GdkChildInfoX11, state.nchildren);
592   state.child_states = g_new (ChildInfoChildState, state.nchildren);
593   state.current_child = 0;
594   state.n_children_found = 0;
595   if (get_wm_state)
596     state.current_request = CHILD_INFO_GET_PROPERTY;
597   else
598     state.current_request = CHILD_INFO_GET_WA;
599   state.have_error = FALSE;
600   state.child_has_error = FALSE;
601 
602   LockDisplay(dpy);
603 
604   async.next = dpy->async_handlers;
605   async.handler = get_child_info_handler;
606   async.data = (XPointer) &state;
607   dpy->async_handlers = &async;
608 
609   for (i = 0; i < state.nchildren; i++)
610     {
611       xResourceReq *resource_req;
612       xGetPropertyReq *prop_req;
613       Window win = state.children[i];
614 
615       if (get_wm_state)
616 	{
617 	  GetReq (GetProperty, prop_req);
618 	  prop_req->window = win;
619 	  prop_req->property = wm_state_atom;
620 	  prop_req->type = AnyPropertyType;
621 	  prop_req->delete = False;
622 	  prop_req->longOffset = 0;
623 	  prop_req->longLength = 0;
624 
625 	  state.child_states[i].seq[CHILD_INFO_GET_PROPERTY] = dpy->request;
626 	}
627 
628       GetResReq(GetWindowAttributes, win, resource_req);
629       state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
630 
631       GetResReq(GetGeometry, win, resource_req);
632       state.child_states[i].seq[CHILD_INFO_GET_GEOMETRY] = dpy->request;
633     }
634 
635   if (i != 0)
636     {
637       /* Wait for the last reply
638        */
639       xGetGeometryReply rep;
640 
641       /* On error, our async handler will get called
642        */
643       if (_XReply (dpy, (xReply *)&rep, 0, xTrue))
644 	handle_get_geometry_reply (dpy, &state, &rep);
645 
646       next_child (&state);
647     }
648 
649   if (!state.have_error)
650     {
651       *children = state.child_info;
652       *nchildren = state.n_children_found;
653     }
654   else
655     {
656       g_free (state.child_info);
657     }
658 
659   g_free (state.children);
660   g_free (state.child_states);
661 
662   DeqAsyncHandler(dpy, &async);
663   UnlockDisplay(dpy);
664   SyncHandle();
665 
666   return !state.have_error;
667 }
668 
669 static gboolean
roundtrip_callback_idle(gpointer data)670 roundtrip_callback_idle (gpointer data)
671 {
672   RoundtripState *state = (RoundtripState *)data;
673 
674   state->callback (state->display, state->data, state->get_input_focus_req);
675 
676   g_free (state);
677 
678   return FALSE;
679 }
680 
681 static Bool
roundtrip_handler(Display * dpy,xReply * rep,char * buf,int len,XPointer data)682 roundtrip_handler (Display *dpy,
683 		   xReply  *rep,
684 		   char    *buf,
685 		   int      len,
686 		   XPointer data)
687 {
688   RoundtripState *state = (RoundtripState *)data;
689 
690   if (dpy->last_request_read == state->get_input_focus_req)
691     {
692       xGetInputFocusReply replbuf;
693       xGetInputFocusReply *repl G_GNUC_UNUSED;
694 
695       if (rep->generic.type != X_Error)
696 	{
697 	  /* Actually does nothing, since there are no additional bytes
698 	   * to read, but maintain good form.
699 	   */
700 	  repl = (xGetInputFocusReply *)
701 	    _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
702 			    (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
703 			    True);
704 	}
705 
706 
707       if (state->callback)
708         {
709           guint id;
710           id = g_idle_add (roundtrip_callback_idle, state);
711           gdk_source_set_static_name_by_id (id, "[gtk] roundtrip_callback_idle");
712         }
713 
714       DeqAsyncHandler(state->dpy, &state->async);
715 
716       return (rep->generic.type != X_Error);
717     }
718 
719   return False;
720 }
721 
722 void
_gdk_x11_roundtrip_async(GdkDisplay * display,GdkRoundTripCallback callback,gpointer data)723 _gdk_x11_roundtrip_async (GdkDisplay           *display,
724 			  GdkRoundTripCallback callback,
725 			  gpointer              data)
726 {
727   Display *dpy;
728   RoundtripState *state;
729 
730   dpy = GDK_DISPLAY_XDISPLAY (display);
731 
732   state = g_new (RoundtripState, 1);
733 
734   state->display = display;
735   state->dpy = dpy;
736   state->callback = callback;
737   state->data = data;
738 
739   LockDisplay(dpy);
740 
741   state->async.next = dpy->async_handlers;
742   state->async.handler = roundtrip_handler;
743   state->async.data = (XPointer) state;
744   dpy->async_handlers = &state->async;
745 
746   /*
747    * XSync (dpy, 0)
748    */
749   {
750     G_GNUC_UNUSED xReq *req;
751 
752     GetEmptyReq(GetInputFocus, req);
753     state->get_input_focus_req = dpy->request;
754   }
755 
756   UnlockDisplay(dpy);
757   SyncHandle();
758 }
759