]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkasync.c
Change GdkFrameClock from an interface to a class
[~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, 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 #ifdef NEED_XIPROTO_H_FOR_XREPLY
50 #include <X11/extensions/XIproto.h>
51 #endif
52
53 #include <X11/Xlibint.h>
54
55
56 typedef struct _ChildInfoChildState ChildInfoChildState;
57 typedef struct _ChildInfoState ChildInfoState;
58 typedef struct _ListChildrenState ListChildrenState;
59 typedef struct _SendEventState SendEventState;
60 typedef struct _SetInputFocusState SetInputFocusState;
61 typedef struct _RoundtripState RoundtripState;
62
63 typedef enum {
64   CHILD_INFO_GET_PROPERTY,
65   CHILD_INFO_GET_WA,
66   CHILD_INFO_GET_GEOMETRY
67 } ChildInfoReq;
68
69 struct _ChildInfoChildState
70 {
71   gulong seq[3];
72 };
73
74 struct _ChildInfoState
75 {
76   gboolean get_wm_state;
77   Window *children;
78   guint nchildren;
79   GdkChildInfoX11 *child_info;
80   ChildInfoChildState *child_states;
81
82   guint current_child;
83   guint n_children_found;
84   gint current_request;
85   gboolean have_error;
86   gboolean child_has_error;
87 };
88
89 struct _ListChildrenState
90 {
91   Display *dpy;
92   gulong get_property_req;
93   gboolean have_error;
94   gboolean has_wm_state;
95 };
96
97 struct _SendEventState
98 {
99   Display *dpy;
100   Window window;
101   _XAsyncHandler async;
102   gulong send_event_req;
103   gulong get_input_focus_req;
104   gboolean have_error;
105   GdkSendXEventCallback callback;
106   gpointer data;
107 };
108
109 struct _SetInputFocusState
110 {
111   Display *dpy;
112   _XAsyncHandler async;
113   gulong set_input_focus_req;
114   gulong get_input_focus_req;
115 };
116
117 struct _RoundtripState
118 {
119   Display *dpy;
120   _XAsyncHandler async;
121   gulong get_input_focus_req;
122   GdkDisplay *display;
123   GdkRoundTripCallback callback;
124   gpointer data;
125 };
126
127 static gboolean
128 callback_idle (gpointer data)
129 {
130   SendEventState *state = (SendEventState *)data;  
131   
132   state->callback (state->window, !state->have_error, state->data);
133
134   g_free (state);
135
136   return FALSE;
137 }
138
139 static Bool
140 send_event_handler (Display *dpy,
141                     xReply  *rep,
142                     char    *buf,
143                     int      len,
144                     XPointer data)
145 {
146   SendEventState *state = (SendEventState *)data;  
147
148   if (dpy->last_request_read == state->send_event_req)
149     {
150       if (rep->generic.type == X_Error &&
151           rep->error.errorCode == BadWindow)
152         {
153           state->have_error = TRUE;
154           return True;
155         }
156     }
157   else if (dpy->last_request_read == state->get_input_focus_req)
158     {
159       xGetInputFocusReply replbuf;
160       xGetInputFocusReply *repl G_GNUC_UNUSED;
161       
162       if (rep->generic.type != X_Error)
163         {
164           /* Actually does nothing, since there are no additional bytes
165            * to read, but maintain good form.
166            */
167           repl = (xGetInputFocusReply *)
168             _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
169                             (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
170                             True);
171         }
172
173       if (state->callback)
174         gdk_threads_add_idle (callback_idle, state);
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
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
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
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
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
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
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
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
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
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
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 window = state.children[i];
610       
611       if (get_wm_state)
612         {
613           GetReq (GetProperty, prop_req);
614           prop_req->window = window;
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, window, resource_req);
625       state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
626       
627       GetResReq(GetGeometry, window, 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
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
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         gdk_threads_add_idle (roundtrip_callback_idle, state);
705
706       DeqAsyncHandler(state->dpy, &state->async);
707
708       return (rep->generic.type != X_Error);
709     }
710
711   return False;
712 }
713
714 void
715 _gdk_x11_roundtrip_async (GdkDisplay           *display, 
716                           GdkRoundTripCallback callback,
717                           gpointer              data)
718 {
719   Display *dpy;
720   RoundtripState *state;
721   
722   dpy = GDK_DISPLAY_XDISPLAY (display);
723
724   state = g_new (RoundtripState, 1);
725
726   state->display = display;
727   state->dpy = dpy;
728   state->callback = callback;
729   state->data = data;
730   
731   LockDisplay(dpy);
732
733   state->async.next = dpy->async_handlers;
734   state->async.handler = roundtrip_handler;
735   state->async.data = (XPointer) state;
736   dpy->async_handlers = &state->async;
737
738   /*
739    * XSync (dpy, 0)
740    */
741   {
742     G_GNUC_UNUSED xReq *req;
743     
744     GetEmptyReq(GetInputFocus, req);
745     state->get_input_focus_req = dpy->request;
746   }
747   
748   UnlockDisplay(dpy);
749   SyncHandle();
750 }