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