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