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/*
21Copyright 1986, 1998 The Open Group
22
23Permission to use, copy, modify, distribute, and sell this software and its
24documentation for any purpose is hereby granted without fee, provided that
25the above copyright notice appear in all copies and that both that
26copyright notice and this permission notice appear in supporting
27documentation.
28
29The above copyright notice and this permission notice shall be included in
30all copies or substantial portions of the Software.
31
32THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
36AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
37CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38
39Except as contained in this notice, the name of The Open Group shall not be
40used in advertising or otherwise to promote the sale, use or other dealings
41in 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
54typedef struct _ChildInfoChildState ChildInfoChildState;
55typedef struct _ChildInfoState ChildInfoState;
56typedef struct _ListChildrenState ListChildrenState;
57typedef struct _SendEventState SendEventState;
58typedef struct _SetInputFocusState SetInputFocusState;
59typedef struct _RoundtripState RoundtripState;
60
61typedef enum {
62 CHILD_INFO_GET_PROPERTY,
63 CHILD_INFO_GET_WA,
64 CHILD_INFO_GET_GEOMETRY
65} ChildInfoReq;
66
67struct _ChildInfoChildState
68{
69 gulong seq[3];
70};
71
72struct _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
87struct _ListChildrenState
88{
89 Display *dpy;
90 gulong get_property_req;
91 gboolean have_error;
92 gboolean has_wm_state;
93};
94
95struct _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
107struct _SetInputFocusState
108{
109 Display *dpy;
110 _XAsyncHandler async;
111 gulong set_input_focus_req;
112 gulong get_input_focus_req;
113};
114
115struct _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
125static gboolean
126callback_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
137static Bool
138send_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
186static void
187client_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
228void
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
291static Bool
292list_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
328static gboolean
329list_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
401static void
402handle_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
411static void
412handle_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
424static void
425handle_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
438static void
439next_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
460static Bool
461get_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
537gboolean
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
669static gboolean
670roundtrip_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
681static Bool
682roundtrip_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
722void
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

source code of gtk/gdk/x11/gdkasync.c