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 |
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 (mem: state); |
133 | |
134 | return FALSE; |
135 | } |
136 | |
137 | static Bool |
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 (function: callback_idle, data: state); |
175 | gdk_source_set_static_name_by_id (tag: id, name: "[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 |
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 (ev: event_send, event: &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 (dest: (char *) &req->event, src: (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 |
292 | list_children_handler (Display *dpy, |
293 | xReply *rep, |
294 | char *buf, |
295 | int len, |
296 | XPointer data) |
297 | { |
298 | ListChildrenState *state = (ListChildrenState *)data; |
299 | |
300 | if (dpy->last_request_read != state->get_property_req) |
301 | return False; |
302 | |
303 | if (rep->generic.type == X_Error) |
304 | { |
305 | state->have_error = TRUE; |
306 | return False; |
307 | } |
308 | else |
309 | { |
310 | xGetPropertyReply replbuf; |
311 | xGetPropertyReply *repl; |
312 | |
313 | repl = (xGetPropertyReply *) |
314 | _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, |
315 | (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2, |
316 | True); |
317 | |
318 | state->has_wm_state = repl->propertyType != None; |
319 | /* Since we called GetProperty with longLength of 0, we don't |
320 | * have to worry about consuming the property data that would |
321 | * normally follow after the reply |
322 | */ |
323 | |
324 | return True; |
325 | } |
326 | } |
327 | |
328 | static gboolean |
329 | list_children_and_wm_state (Display *dpy, |
330 | Window w, |
331 | Atom wm_state_atom, |
332 | gboolean *has_wm_state, |
333 | Window **children, |
334 | unsigned int *nchildren) |
335 | { |
336 | ListChildrenState state; |
337 | _XAsyncHandler async; |
338 | long nbytes; |
339 | xQueryTreeReply rep; |
340 | register xResourceReq *req; |
341 | xGetPropertyReq *prop_req; |
342 | |
343 | LockDisplay(dpy); |
344 | |
345 | *children = NULL; |
346 | *nchildren = 0; |
347 | *has_wm_state = FALSE; |
348 | |
349 | state.have_error = FALSE; |
350 | state.has_wm_state = FALSE; |
351 | |
352 | if (wm_state_atom) |
353 | { |
354 | async.next = dpy->async_handlers; |
355 | async.handler = list_children_handler; |
356 | async.data = (XPointer) &state; |
357 | dpy->async_handlers = &async; |
358 | |
359 | GetReq (GetProperty, prop_req); |
360 | prop_req->window = w; |
361 | prop_req->property = wm_state_atom; |
362 | prop_req->type = AnyPropertyType; |
363 | prop_req->delete = False; |
364 | prop_req->longOffset = 0; |
365 | prop_req->longLength = 0; |
366 | |
367 | state.get_property_req = dpy->request; |
368 | } |
369 | |
370 | GetResReq(QueryTree, w, req); |
371 | if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) |
372 | { |
373 | state.have_error = TRUE; |
374 | goto out; |
375 | } |
376 | |
377 | if (rep.nChildren != 0) |
378 | { |
379 | nbytes = rep.nChildren << 2; |
380 | if (state.have_error) |
381 | { |
382 | _XEatData(dpy, (unsigned long) nbytes); |
383 | goto out; |
384 | } |
385 | *children = g_new (Window, rep.nChildren); |
386 | _XRead32 (dpy, data: (long *) *children, len: nbytes); |
387 | } |
388 | |
389 | *nchildren = rep.nChildren; |
390 | *has_wm_state = state.has_wm_state; |
391 | |
392 | out: |
393 | if (wm_state_atom) |
394 | DeqAsyncHandler(dpy, &async); |
395 | UnlockDisplay(dpy); |
396 | SyncHandle(); |
397 | |
398 | return !state.have_error; |
399 | } |
400 | |
401 | static void |
402 | handle_get_wa_reply (Display *dpy, |
403 | ChildInfoState *state, |
404 | xGetWindowAttributesReply *repl) |
405 | { |
406 | GdkChildInfoX11 *child = &state->child_info[state->n_children_found]; |
407 | child->is_mapped = repl->mapState != IsUnmapped; |
408 | child->window_class = repl->class; |
409 | } |
410 | |
411 | static void |
412 | handle_get_geometry_reply (Display *dpy, |
413 | ChildInfoState *state, |
414 | xGetGeometryReply *repl) |
415 | { |
416 | GdkChildInfoX11 *child = &state->child_info[state->n_children_found]; |
417 | |
418 | child->x = cvtINT16toInt (repl->x); |
419 | child->y = cvtINT16toInt (repl->y); |
420 | child->width = repl->width; |
421 | child->height = repl->height; |
422 | } |
423 | |
424 | static void |
425 | handle_get_property_reply (Display *dpy, |
426 | ChildInfoState *state, |
427 | xGetPropertyReply *repl) |
428 | { |
429 | GdkChildInfoX11 *child = &state->child_info[state->n_children_found]; |
430 | child->has_wm_state = repl->propertyType != None; |
431 | |
432 | /* Since we called GetProperty with longLength of 0, we don't |
433 | * have to worry about consuming the property data that would |
434 | * normally follow after the reply |
435 | */ |
436 | } |
437 | |
438 | static void |
439 | next_child (ChildInfoState *state) |
440 | { |
441 | if (state->current_request == CHILD_INFO_GET_GEOMETRY) |
442 | { |
443 | if (!state->have_error && !state->child_has_error) |
444 | { |
445 | state->child_info[state->n_children_found].window = state->children[state->current_child]; |
446 | state->n_children_found++; |
447 | } |
448 | state->current_child++; |
449 | if (state->get_wm_state) |
450 | state->current_request = CHILD_INFO_GET_PROPERTY; |
451 | else |
452 | state->current_request = CHILD_INFO_GET_WA; |
453 | state->child_has_error = FALSE; |
454 | state->have_error = FALSE; |
455 | } |
456 | else |
457 | state->current_request++; |
458 | } |
459 | |
460 | static Bool |
461 | get_child_info_handler (Display *dpy, |
462 | xReply *rep, |
463 | char *buf, |
464 | int len, |
465 | XPointer data) |
466 | { |
467 | Bool result = True; |
468 | |
469 | ChildInfoState *state = (ChildInfoState *)data; |
470 | |
471 | if (dpy->last_request_read != state->child_states[state->current_child].seq[state->current_request]) |
472 | return False; |
473 | |
474 | if (rep->generic.type == X_Error) |
475 | { |
476 | state->child_has_error = TRUE; |
477 | if (rep->error.errorCode != BadDrawable && |
478 | rep->error.errorCode != BadWindow) |
479 | { |
480 | state->have_error = TRUE; |
481 | result = False; |
482 | } |
483 | } |
484 | else |
485 | { |
486 | switch (state->current_request) |
487 | { |
488 | case CHILD_INFO_GET_PROPERTY: |
489 | { |
490 | xGetPropertyReply replbuf; |
491 | xGetPropertyReply *repl; |
492 | |
493 | repl = (xGetPropertyReply *) |
494 | _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, |
495 | (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2, |
496 | True); |
497 | |
498 | handle_get_property_reply (dpy, state, repl); |
499 | } |
500 | break; |
501 | case CHILD_INFO_GET_WA: |
502 | { |
503 | xGetWindowAttributesReply replbuf; |
504 | xGetWindowAttributesReply *repl; |
505 | |
506 | repl = (xGetWindowAttributesReply *) |
507 | _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, |
508 | (sizeof(xGetWindowAttributesReply) - sizeof(xReply)) >> 2, |
509 | True); |
510 | |
511 | handle_get_wa_reply (dpy, state, repl); |
512 | } |
513 | break; |
514 | case CHILD_INFO_GET_GEOMETRY: |
515 | { |
516 | xGetGeometryReply replbuf; |
517 | xGetGeometryReply *repl; |
518 | |
519 | repl = (xGetGeometryReply *) |
520 | _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, |
521 | (sizeof(xGetGeometryReply) - sizeof(xReply)) >> 2, |
522 | True); |
523 | |
524 | handle_get_geometry_reply (dpy, state, repl); |
525 | } |
526 | break; |
527 | default: |
528 | break; |
529 | } |
530 | } |
531 | |
532 | next_child (state); |
533 | |
534 | return result; |
535 | } |
536 | |
537 | gboolean |
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, atom_name: "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, w: window, |
567 | wm_state_atom: win_has_wm_state ? wm_state_atom : None, |
568 | has_wm_state: &has_wm_state, |
569 | children: &state.children, nchildren: &state.nchildren); |
570 | gdk_x11_display_error_trap_pop_ignored (display); |
571 | if (!result) |
572 | { |
573 | g_free (mem: 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 (mem: 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: &state, repl: &rep); |
645 | |
646 | next_child (state: &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 (mem: state.child_info); |
657 | } |
658 | |
659 | g_free (mem: state.children); |
660 | g_free (mem: state.child_states); |
661 | |
662 | DeqAsyncHandler(dpy, &async); |
663 | UnlockDisplay(dpy); |
664 | SyncHandle(); |
665 | |
666 | return !state.have_error; |
667 | } |
668 | |
669 | static gboolean |
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 (mem: state); |
677 | |
678 | return FALSE; |
679 | } |
680 | |
681 | static Bool |
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 (function: roundtrip_callback_idle, data: state); |
711 | gdk_source_set_static_name_by_id (tag: id, name: "[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 |
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 | |