]> Pileus Git - ~andy/gtk/blob - gtk/gtksocket-x11.c
Merge branch 'master' into broadway
[~andy/gtk] / gtk / gtksocket-x11.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* By Owen Taylor <otaylor@gtk.org>              98/4/4 */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29 #include <string.h>
30
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkmain.h"
33 #include "gtkmarshalers.h"
34 #include "gtkwindow.h"
35 #include "gtkplug.h"
36 #include "gtkprivate.h"
37 #include "gtksocket.h"
38 #include "gtksocketprivate.h"
39 #include "gtkdnd.h"
40 #include "gtkdebug.h"
41
42 #include "gdk/x11/gdkx.h"
43 #include "gdk/gdkprivate.h"
44
45 #ifdef HAVE_XFIXES
46 #include <X11/extensions/Xfixes.h>
47 #endif
48
49 #include "gtkxembed.h"
50
51 static gboolean xembed_get_info     (GdkWindow     *gdk_window,
52                                      unsigned long *version,
53                                      unsigned long *flags);
54
55 /* From Tk */
56 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
57
58 GdkNativeWindow
59 _gtk_socket_windowing_get_id (GtkSocket *socket)
60 {
61   return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
62 }
63
64 void
65 _gtk_socket_windowing_realize_window (GtkSocket *socket)
66 {
67   GdkWindow *window;
68   XWindowAttributes xattrs;
69
70   window = gtk_widget_get_window (GTK_WIDGET (socket));
71
72   XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
73                         GDK_WINDOW_XID (window),
74                         &xattrs);
75
76   /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
77      for input on the socket with a mask of 0x0fffff (for god knows why)
78      which includes ButtonPressMask causing a BadAccess if someone else
79      also selects for this. As per the client-side windows merge we always
80      normally selects for button press so we can emulate it on client
81      side children that selects for button press. However, we don't need
82      this for GtkSocket, so we unselect it here, fixing the crashes in
83      firefox. */
84   XSelectInput (GDK_WINDOW_XDISPLAY (window),
85                 GDK_WINDOW_XID (window), 
86                 (xattrs.your_event_mask & ~ButtonPressMask) |
87                 SubstructureNotifyMask | SubstructureRedirectMask);
88 }
89
90 void
91 _gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket)
92 {
93   gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
94                                   GDK_WINDOW_XID (socket->plug_window));
95 }
96
97 void
98 _gtk_socket_windowing_size_request (GtkSocket *socket)
99 {
100   XSizeHints hints;
101   long supplied;
102           
103   gdk_error_trap_push ();
104
105   socket->request_width = 1;
106   socket->request_height = 1;
107           
108   if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window),
109                          GDK_WINDOW_XID (socket->plug_window),
110                          &hints, &supplied))
111     {
112       if (hints.flags & PMinSize)
113         {
114           socket->request_width = MAX (hints.min_width, 1);
115           socket->request_height = MAX (hints.min_height, 1);
116         }
117       else if (hints.flags & PBaseSize)
118         {
119           socket->request_width = MAX (hints.base_width, 1);
120           socket->request_height = MAX (hints.base_height, 1);
121         }
122     }
123   socket->have_size = TRUE;
124   
125   gdk_error_trap_pop_ignored ();
126 }
127
128 void
129 _gtk_socket_windowing_send_key_event (GtkSocket *socket,
130                                       GdkEvent  *gdk_event,
131                                       gboolean   mask_key_presses)
132 {
133   XKeyEvent xkey;
134   GdkScreen *screen = gdk_window_get_screen (socket->plug_window);
135
136   memset (&xkey, 0, sizeof (xkey));
137   xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
138   xkey.window = GDK_WINDOW_XID (socket->plug_window);
139   xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
140   xkey.subwindow = None;
141   xkey.time = gdk_event->key.time;
142   xkey.x = 0;
143   xkey.y = 0;
144   xkey.x_root = 0;
145   xkey.y_root = 0;
146   xkey.state = gdk_event->key.state;
147   xkey.keycode = gdk_event->key.hardware_keycode;
148   xkey.same_screen = True;/* FIXME ? */
149
150   gdk_error_trap_push ();
151   XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
152               GDK_WINDOW_XID (socket->plug_window),
153               False,
154               (mask_key_presses ? KeyPressMask : NoEventMask),
155               (XEvent *)&xkey);
156   gdk_error_trap_pop_ignored ();
157 }
158
159 void
160 _gtk_socket_windowing_focus_change (GtkSocket *socket,
161                                     gboolean   focus_in)
162 {
163   if (focus_in)
164     _gtk_xembed_send_focus_message (socket->plug_window,
165                                     XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
166   else
167     _gtk_xembed_send_message (socket->plug_window,
168                               XEMBED_FOCUS_OUT, 0, 0, 0);
169 }
170
171 void
172 _gtk_socket_windowing_update_active (GtkSocket *socket,
173                                      gboolean   active)
174 {
175   _gtk_xembed_send_message (socket->plug_window,
176                             active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
177                             0, 0, 0);
178 }
179
180 void
181 _gtk_socket_windowing_update_modality (GtkSocket *socket,
182                                        gboolean   modality)
183 {
184   _gtk_xembed_send_message (socket->plug_window,
185                             modality ? XEMBED_MODALITY_ON : XEMBED_MODALITY_OFF,
186                             0, 0, 0);
187 }
188
189 void
190 _gtk_socket_windowing_focus (GtkSocket       *socket,
191                              GtkDirectionType direction)
192 {
193   gint detail = -1;
194
195   switch (direction)
196     {
197     case GTK_DIR_UP:
198     case GTK_DIR_LEFT:
199     case GTK_DIR_TAB_BACKWARD:
200       detail = XEMBED_FOCUS_LAST;
201       break;
202     case GTK_DIR_DOWN:
203     case GTK_DIR_RIGHT:
204     case GTK_DIR_TAB_FORWARD:
205       detail = XEMBED_FOCUS_FIRST;
206       break;
207     }
208   
209   _gtk_xembed_send_focus_message (socket->plug_window, XEMBED_FOCUS_IN, detail);
210 }
211
212 void
213 _gtk_socket_windowing_send_configure_event (GtkSocket *socket)
214 {
215   GtkAllocation allocation;
216   XConfigureEvent xconfigure;
217   gint x, y;
218
219   g_return_if_fail (socket->plug_window != NULL);
220
221   memset (&xconfigure, 0, sizeof (xconfigure));
222   xconfigure.type = ConfigureNotify;
223
224   xconfigure.event = GDK_WINDOW_XID (socket->plug_window);
225   xconfigure.window = GDK_WINDOW_XID (socket->plug_window);
226
227   /* The ICCCM says that synthetic events should have root relative
228    * coordinates. We still aren't really ICCCM compliant, since
229    * we don't send events when the real toplevel is moved.
230    */
231   gdk_error_trap_push ();
232   gdk_window_get_origin (socket->plug_window, &x, &y);
233   gdk_error_trap_pop_ignored ();
234
235   gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
236   xconfigure.x = x;
237   xconfigure.y = y;
238   xconfigure.width = allocation.width;
239   xconfigure.height = allocation.height;
240
241   xconfigure.border_width = 0;
242   xconfigure.above = None;
243   xconfigure.override_redirect = False;
244
245   gdk_error_trap_push ();
246   XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
247               GDK_WINDOW_XID (socket->plug_window),
248               False, NoEventMask, (XEvent *)&xconfigure);
249   gdk_error_trap_pop_ignored ();
250 }
251
252 void
253 _gtk_socket_windowing_select_plug_window_input (GtkSocket *socket)
254 {
255   XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
256                 GDK_WINDOW_XID (socket->plug_window),
257                 StructureNotifyMask | PropertyChangeMask);
258 }
259
260 void
261 _gtk_socket_windowing_embed_get_info (GtkSocket *socket)
262 {
263   unsigned long version;
264   unsigned long flags;
265
266   socket->xembed_version = -1;
267   if (xembed_get_info (socket->plug_window, &version, &flags))
268     {
269       socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
270       socket->is_mapped = (flags & XEMBED_MAPPED) != 0;
271     }
272   else
273     {
274       /* FIXME, we should probably actually check the state before we started */
275       socket->is_mapped = TRUE;
276     }
277 }
278
279 void
280 _gtk_socket_windowing_embed_notify (GtkSocket *socket)
281 {
282 #ifdef HAVE_XFIXES
283   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket));
284
285   gdk_error_trap_push ();
286   XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display),
287                        GDK_WINDOW_XID (socket->plug_window),
288                        SetModeInsert, SaveSetRoot, SaveSetUnmap);
289   gdk_error_trap_pop_ignored ();
290 #endif
291   _gtk_xembed_send_message (socket->plug_window,
292                             XEMBED_EMBEDDED_NOTIFY, 0,
293                             GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
294                             socket->xembed_version);
295 }
296
297 static gboolean
298 xembed_get_info (GdkWindow     *window,
299                  unsigned long *version,
300                  unsigned long *flags)
301 {
302   GdkDisplay *display = gdk_window_get_display (window);
303   Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
304   Atom type;
305   int format;
306   unsigned long nitems, bytes_after;
307   unsigned char *data;
308   unsigned long *data_long;
309   int status;
310   
311   gdk_error_trap_push ();
312   status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
313                                GDK_WINDOW_XID (window),
314                                xembed_info_atom,
315                                0, 2, False,
316                                xembed_info_atom, &type, &format,
317                                &nitems, &bytes_after, &data);
318   gdk_error_trap_pop_ignored ();
319
320   if (status != Success)
321     return FALSE;               /* Window vanished? */
322
323   if (type == None)             /* No info property */
324     return FALSE;
325
326   if (type != xembed_info_atom)
327     {
328       g_warning ("_XEMBED_INFO property has wrong type\n");
329       return FALSE;
330     }
331   
332   if (nitems < 2)
333     {
334       g_warning ("_XEMBED_INFO too short\n");
335       XFree (data);
336       return FALSE;
337     }
338   
339   data_long = (unsigned long *)data;
340   if (version)
341     *version = data_long[0];
342   if (flags)
343     *flags = data_long[1] & XEMBED_MAPPED;
344   
345   XFree (data);
346   return TRUE;
347 }
348
349 gboolean
350 _gtk_socket_windowing_embed_get_focus_wrapped (void)
351 {
352   return _gtk_xembed_get_focus_wrapped ();
353 }
354
355 void
356 _gtk_socket_windowing_embed_set_focus_wrapped (void)
357 {
358   _gtk_xembed_set_focus_wrapped ();
359 }
360
361 static void
362 handle_xembed_message (GtkSocket        *socket,
363                        XEmbedMessageType message,
364                        glong             detail,
365                        glong             data1,
366                        glong             data2,
367                        guint32           time)
368 {
369   GTK_NOTE (PLUGSOCKET,
370             g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
371   
372   switch (message)
373     {
374     case XEMBED_EMBEDDED_NOTIFY:
375     case XEMBED_WINDOW_ACTIVATE:
376     case XEMBED_WINDOW_DEACTIVATE:
377     case XEMBED_MODALITY_ON:
378     case XEMBED_MODALITY_OFF:
379     case XEMBED_FOCUS_IN:
380     case XEMBED_FOCUS_OUT:
381       g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
382       break;
383       
384     case XEMBED_REQUEST_FOCUS:
385       _gtk_socket_claim_focus (socket, TRUE);
386       break;
387
388     case XEMBED_FOCUS_NEXT:
389     case XEMBED_FOCUS_PREV:
390       _gtk_socket_advance_toplevel_focus (socket,
391                                           (message == XEMBED_FOCUS_NEXT ?
392                                            GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
393       break;
394       
395     case XEMBED_GTK_GRAB_KEY:
396       _gtk_socket_add_grabbed_key (socket, data1, data2);
397       break; 
398     case XEMBED_GTK_UNGRAB_KEY:
399       _gtk_socket_remove_grabbed_key (socket, data1, data2);
400       break;
401
402     case XEMBED_GRAB_KEY:
403     case XEMBED_UNGRAB_KEY:
404       break;
405       
406     default:
407       GTK_NOTE (PLUGSOCKET,
408                 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
409       break;
410     }
411 }
412
413 GdkFilterReturn
414 _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
415                                    GdkEvent  *event,
416                                    gpointer   data)
417 {
418   GtkSocket *socket;
419   GtkWidget *widget;
420   GdkDisplay *display;
421   XEvent *xevent;
422
423   GdkFilterReturn return_val;
424   
425   socket = GTK_SOCKET (data);
426
427   return_val = GDK_FILTER_CONTINUE;
428
429   if (socket->plug_widget)
430     return return_val;
431
432   widget = GTK_WIDGET (socket);
433   xevent = (XEvent *)gdk_xevent;
434   display = gtk_widget_get_display (widget);
435
436   switch (xevent->type)
437     {
438     case ClientMessage:
439       if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
440         {
441           _gtk_xembed_push_message (xevent);
442           handle_xembed_message (socket,
443                                  xevent->xclient.data.l[1],
444                                  xevent->xclient.data.l[2],
445                                  xevent->xclient.data.l[3],
446                                  xevent->xclient.data.l[4],
447                                  xevent->xclient.data.l[0]);
448           _gtk_xembed_pop_message ();
449           
450           return_val = GDK_FILTER_REMOVE;
451         }
452       break;
453
454     case CreateNotify:
455       {
456         XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
457
458         if (!socket->plug_window)
459           {
460             _gtk_socket_add_window (socket, xcwe->window, FALSE);
461
462             if (socket->plug_window)
463               {
464                 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
465               }
466           }
467         
468         return_val = GDK_FILTER_REMOVE;
469         
470         break;
471       }
472
473     case ConfigureRequest:
474       {
475         XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
476         
477         if (!socket->plug_window)
478           _gtk_socket_add_window (socket, xcre->window, FALSE);
479         
480         if (socket->plug_window)
481           {
482             GtkSocketPrivate *private = _gtk_socket_get_private (socket);
483             
484             if (xcre->value_mask & (CWWidth | CWHeight))
485               {
486                 GTK_NOTE (PLUGSOCKET,
487                           g_message ("GtkSocket - configure request: %d %d",
488                                      socket->request_width,
489                                      socket->request_height));
490
491                 private->resize_count++;
492                 gtk_widget_queue_resize (widget);
493               }
494             else if (xcre->value_mask & (CWX | CWY))
495               {
496                 _gtk_socket_windowing_send_configure_event (socket);
497               }
498             /* Ignore stacking requests. */
499             
500             return_val = GDK_FILTER_REMOVE;
501           }
502         break;
503       }
504
505     case DestroyNotify:
506       {
507         XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
508
509         /* Note that we get destroy notifies both from SubstructureNotify on
510          * our window and StructureNotify on socket->plug_window
511          */
512         if (socket->plug_window && (xdwe->window == GDK_WINDOW_XID (socket->plug_window)))
513           {
514             gboolean result;
515             
516             GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
517             
518             gdk_window_destroy_notify (socket->plug_window);
519             _gtk_socket_end_embedding (socket);
520
521             g_object_ref (widget);
522             g_signal_emit_by_name (widget, "plug-removed", &result);
523             if (!result)
524               gtk_widget_destroy (widget);
525             g_object_unref (widget);
526             
527             return_val = GDK_FILTER_REMOVE;
528           }
529         break;
530       }
531
532     case FocusIn:
533       if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
534         {
535           _gtk_socket_claim_focus (socket, TRUE);
536         }
537       return_val = GDK_FILTER_REMOVE;
538       break;
539     case FocusOut:
540       return_val = GDK_FILTER_REMOVE;
541       break;
542     case MapRequest:
543       if (!socket->plug_window)
544         {
545           _gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
546         }
547         
548       if (socket->plug_window)
549         {
550           GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
551
552           _gtk_socket_handle_map_request (socket);
553           return_val = GDK_FILTER_REMOVE;
554         }
555       break;
556     case PropertyNotify:
557       if (socket->plug_window &&
558           xevent->xproperty.window == GDK_WINDOW_XID (socket->plug_window))
559         {
560           GdkDragProtocol protocol;
561
562           if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
563             {
564               GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
565               socket->have_size = FALSE;
566               gtk_widget_queue_resize (widget);
567               return_val = GDK_FILTER_REMOVE;
568             }
569           else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
570               (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
571             {
572               gdk_error_trap_push ();
573               if (gdk_drag_get_protocol_for_display (display,
574                                                      xevent->xproperty.window,
575                                                      &protocol))
576                 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
577                                          socket->plug_window,
578                                          protocol, TRUE);
579
580               gdk_error_trap_pop_ignored ();
581               return_val = GDK_FILTER_REMOVE;
582             }
583           else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
584             {
585               unsigned long flags;
586               
587               if (xembed_get_info (socket->plug_window, NULL, &flags))
588                 {
589                   gboolean was_mapped = socket->is_mapped;
590                   gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
591
592                   if (was_mapped != is_mapped)
593                     {
594                       if (is_mapped)
595                         _gtk_socket_handle_map_request (socket);
596                       else
597                         {
598                           gdk_error_trap_push ();
599                           gdk_window_show (socket->plug_window);
600                           gdk_error_trap_pop_ignored ();
601                           
602                           _gtk_socket_unmap_notify (socket);
603                         }
604                     }
605                 }
606               return_val = GDK_FILTER_REMOVE;
607             }
608         }
609       break;
610     case ReparentNotify:
611       {
612         GdkWindow *window;
613         XReparentEvent *xre = &xevent->xreparent;
614
615         window = gtk_widget_get_window (widget);
616
617         GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
618         if (!socket->plug_window &&
619             xre->parent == GDK_WINDOW_XID (window))
620           {
621             _gtk_socket_add_window (socket, xre->window, FALSE);
622             
623             if (socket->plug_window)
624               {
625                 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
626               }
627             
628             return_val = GDK_FILTER_REMOVE;
629           }
630         else
631           {
632             if (socket->plug_window &&
633                 xre->window == GDK_WINDOW_XID (socket->plug_window) &&
634                 xre->parent != GDK_WINDOW_XID (window))
635               {
636                 gboolean result;
637
638                 _gtk_socket_end_embedding (socket);
639
640                 g_object_ref (widget);
641                 g_signal_emit_by_name (widget, "plug-removed", &result);
642                 if (!result)
643                   gtk_widget_destroy (widget);
644                 g_object_unref (widget);
645
646                 return_val = GDK_FILTER_REMOVE;
647               }
648           }
649
650         break;
651       }
652     case UnmapNotify:
653       if (socket->plug_window &&
654           xevent->xunmap.window == GDK_WINDOW_XID (socket->plug_window))
655         {
656           GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
657
658           _gtk_socket_unmap_notify (socket);
659           return_val = GDK_FILTER_REMOVE;
660         }
661       break;
662       
663     }
664   
665   return return_val;
666 }