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, see <http://www.gnu.org/licenses/>.Free
16  */
17 
18 /* By Owen Taylor <otaylor@gtk.org>              98/4/4 */
19 
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 
27 #include "config.h"
28 
29 #include "gtksocketprivate.h"
30 
31 #include <string.h>
32 
33 #include "gtkmarshalers.h"
34 #include "gtksizerequest.h"
35 #include "gtkplug.h"
36 #include "gtkprivate.h"
37 #include "gtkrender.h"
38 #include "gtkdnd.h"
39 #include "gtkdragdest.h"
40 #include "gtkdebug.h"
41 #include "gtkintl.h"
42 #include "gtkmain.h"
43 #include "gtkwidgetprivate.h"
44 
45 #include <gdk/gdkx.h>
46 #include <gdk/gdkprivate.h>
47 
48 #ifdef HAVE_XFIXES
49 #include <X11/extensions/Xfixes.h>
50 #endif
51 
52 #include "gtkxembed.h"
53 
54 #include "a11y/gtksocketaccessible.h"
55 
56 
57 /**
58  * SECTION:gtksocket
59  * @Short_description: Container for widgets from other processes
60  * @Title: GtkSocket
61  * @include: gtk/gtkx.h
62  * @See_also: #GtkPlug, [XEmbed Protocol](http://www.freedesktop.org/Standards/xembed-spec)
63  *
64  * Together with #GtkPlug, #GtkSocket provides the ability to embed
65  * widgets from one process into another process in a fashion that
66  * is transparent to the user. One process creates a #GtkSocket widget
67  * and passes that widget’s window ID to the other process, which then
68  * creates a #GtkPlug with that window ID. Any widgets contained in the
69  * #GtkPlug then will appear inside the first application’s window.
70  *
71  * The socket’s window ID is obtained by using gtk_socket_get_id().
72  * Before using this function, the socket must have been realized,
73  * and for hence, have been added to its parent.
74  *
75  * ## Obtaining the window ID of a socket.
76  *
77  * |[<!-- language="C" -->
78  * GtkWidget *socket = gtk_socket_new ();
79  * gtk_widget_show (socket);
80  * gtk_container_add (GTK_CONTAINER (parent), socket);
81  *
82  * // The following call is only necessary if one of
83  * // the ancestors of the socket is not yet visible.
84  * gtk_widget_realize (socket);
85  * g_print ("The ID of the sockets window is %#x\n",
86  *          gtk_socket_get_id (socket));
87  * ]|
88  *
89  * Note that if you pass the window ID of the socket to another
90  * process that will create a plug in the socket, you must make
91  * sure that the socket widget is not destroyed until that plug
92  * is created. Violating this rule will cause unpredictable
93  * consequences, the most likely consequence being that the plug
94  * will appear as a separate toplevel window. You can check if
95  * the plug has been created by using gtk_socket_get_plug_window().
96  * If it returns a non-%NULL value, then the plug has been
97  * successfully created inside of the socket.
98  *
99  * When GTK+ is notified that the embedded window has been destroyed,
100  * then it will destroy the socket as well. You should always,
101  * therefore, be prepared for your sockets to be destroyed at any
102  * time when the main event loop is running. To prevent this from
103  * happening, you can connect to the #GtkSocket::plug-removed signal.
104  *
105  * The communication between a #GtkSocket and a #GtkPlug follows the
106  * [XEmbed Protocol](http://www.freedesktop.org/Standards/xembed-spec).
107  * This protocol has also been implemented in other toolkits, e.g. Qt,
108  * allowing the same level of integration when embedding a Qt widget
109  * in GTK or vice versa.
110  *
111  * The #GtkPlug and #GtkSocket widgets are only available when GTK+
112  * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined.
113  * They can only be used on a #GdkX11Display. To use #GtkPlug and
114  * #GtkSocket, you need to include the `gtk/gtkx.h` header.
115  */
116 
117 /* Forward declararations */
118 
119 static void     gtk_socket_finalize             (GObject          *object);
120 static void     gtk_socket_notify               (GObject          *object,
121 						 GParamSpec       *pspec);
122 static void     gtk_socket_realize              (GtkWidget        *widget);
123 static void     gtk_socket_unrealize            (GtkWidget        *widget);
124 static void     gtk_socket_get_preferred_width  (GtkWidget        *widget,
125                                                  gint             *minimum,
126                                                  gint             *natural);
127 static void     gtk_socket_get_preferred_height (GtkWidget        *widget,
128                                                  gint             *minimum,
129                                                  gint             *natural);
130 static void     gtk_socket_size_allocate        (GtkWidget        *widget,
131 						 GtkAllocation    *allocation);
132 static void     gtk_socket_hierarchy_changed    (GtkWidget        *widget,
133 						 GtkWidget        *old_toplevel);
134 static void     gtk_socket_grab_notify          (GtkWidget        *widget,
135 						 gboolean          was_grabbed);
136 static gboolean gtk_socket_key_event            (GtkWidget        *widget,
137 						 GdkEventKey      *event);
138 static gboolean gtk_socket_focus                (GtkWidget        *widget,
139 						 GtkDirectionType  direction);
140 static void     gtk_socket_remove               (GtkContainer     *container,
141 						 GtkWidget        *widget);
142 static void     gtk_socket_forall               (GtkContainer     *container,
143 						 gboolean          include_internals,
144 						 GtkCallback       callback,
145 						 gpointer          callback_data);
146 static void     gtk_socket_add_window           (GtkSocket        *socket,
147                                                  Window            xid,
148                                                  gboolean          need_reparent);
149 static GdkFilterReturn gtk_socket_filter_func   (GdkXEvent        *gdk_xevent,
150                                                  GdkEvent         *event,
151                                                  gpointer          data);
152 
153 static gboolean xembed_get_info                 (GdkWindow        *gdk_window,
154                                                  unsigned long    *version,
155                                                  unsigned long    *flags);
156 
157 static void     _gtk_socket_accessible_embed    (GtkWidget *socket,
158                                                  GdkWindow *window);
159 
160 /* From Tk */
161 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
162 
163 
164 /* Local data */
165 
166 typedef struct
167 {
168   guint			 accel_key;
169   GdkModifierType	 accel_mods;
170 } GrabbedKey;
171 
172 enum {
173   PLUG_ADDED,
174   PLUG_REMOVED,
175   LAST_SIGNAL
176 };
177 
178 static guint socket_signals[LAST_SIGNAL] = { 0 };
179 
G_DEFINE_TYPE_WITH_PRIVATE(GtkSocket,gtk_socket,GTK_TYPE_CONTAINER)180 G_DEFINE_TYPE_WITH_PRIVATE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
181 
182 static void
183 gtk_socket_finalize (GObject *object)
184 {
185   GtkSocket *socket = GTK_SOCKET (object);
186   GtkSocketPrivate *priv = socket->priv;
187 
188   g_object_unref (priv->accel_group);
189 
190   G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
191 }
192 
193 static gboolean
gtk_socket_draw(GtkWidget * widget,cairo_t * cr)194 gtk_socket_draw (GtkWidget *widget,
195                  cairo_t   *cr)
196 {
197   gtk_render_background (gtk_widget_get_style_context (widget), cr,
198                          0, 0,
199                          gtk_widget_get_allocated_width (widget),
200                          gtk_widget_get_allocated_height (widget));
201 
202   return GTK_WIDGET_CLASS (gtk_socket_parent_class)->draw (widget, cr);
203 }
204 
205 static void
gtk_socket_class_init(GtkSocketClass * class)206 gtk_socket_class_init (GtkSocketClass *class)
207 {
208   GtkWidgetClass *widget_class;
209   GtkContainerClass *container_class;
210   GObjectClass *gobject_class;
211 
212   gobject_class = (GObjectClass *) class;
213   widget_class = (GtkWidgetClass*) class;
214   container_class = (GtkContainerClass*) class;
215 
216   gobject_class->finalize = gtk_socket_finalize;
217   gobject_class->notify = gtk_socket_notify;
218 
219   widget_class->realize = gtk_socket_realize;
220   widget_class->unrealize = gtk_socket_unrealize;
221   widget_class->get_preferred_width = gtk_socket_get_preferred_width;
222   widget_class->get_preferred_height = gtk_socket_get_preferred_height;
223   widget_class->size_allocate = gtk_socket_size_allocate;
224   widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
225   widget_class->grab_notify = gtk_socket_grab_notify;
226   widget_class->key_press_event = gtk_socket_key_event;
227   widget_class->key_release_event = gtk_socket_key_event;
228   widget_class->focus = gtk_socket_focus;
229   widget_class->draw = gtk_socket_draw;
230 
231   /* We don't want to show_all the in-process plug, if any.
232    */
233   widget_class->show_all = gtk_widget_show;
234 
235   container_class->remove = gtk_socket_remove;
236   container_class->forall = gtk_socket_forall;
237 
238   /**
239    * GtkSocket::plug-added:
240    * @socket_: the object which received the signal
241    *
242    * This signal is emitted when a client is successfully
243    * added to the socket.
244    */
245   socket_signals[PLUG_ADDED] =
246     g_signal_new (I_("plug-added"),
247 		  G_OBJECT_CLASS_TYPE (class),
248 		  G_SIGNAL_RUN_LAST,
249 		  G_STRUCT_OFFSET (GtkSocketClass, plug_added),
250 		  NULL, NULL,
251 		  NULL,
252 		  G_TYPE_NONE, 0);
253 
254   /**
255    * GtkSocket::plug-removed:
256    * @socket_: the object which received the signal
257    *
258    * This signal is emitted when a client is removed from the socket.
259    * The default action is to destroy the #GtkSocket widget, so if you
260    * want to reuse it you must add a signal handler that returns %TRUE.
261    *
262    * Returns: %TRUE to stop other handlers from being invoked.
263    */
264   socket_signals[PLUG_REMOVED] =
265     g_signal_new (I_("plug-removed"),
266 		  G_OBJECT_CLASS_TYPE (class),
267 		  G_SIGNAL_RUN_LAST,
268 		  G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
269                   _gtk_boolean_handled_accumulator, NULL,
270 		  _gtk_marshal_BOOLEAN__VOID,
271 		  G_TYPE_BOOLEAN, 0);
272 
273 
274   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SOCKET_ACCESSIBLE);
275 }
276 
277 static void
gtk_socket_init(GtkSocket * socket)278 gtk_socket_init (GtkSocket *socket)
279 {
280   GtkSocketPrivate *priv;
281 
282   priv = gtk_socket_get_instance_private (socket);
283   socket->priv = priv;
284 
285   priv->request_width = 0;
286   priv->request_height = 0;
287   priv->current_width = 0;
288   priv->current_height = 0;
289 
290   priv->plug_window = NULL;
291   priv->plug_widget = NULL;
292   priv->focus_in = FALSE;
293   priv->have_size = FALSE;
294   priv->need_map = FALSE;
295   priv->active = FALSE;
296 
297   priv->accel_group = gtk_accel_group_new ();
298   g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
299 }
300 
301 /**
302  * gtk_socket_new:
303  *
304  * Create a new empty #GtkSocket.
305  *
306  * Returns:  the new #GtkSocket.
307  **/
308 GtkWidget*
gtk_socket_new(void)309 gtk_socket_new (void)
310 {
311   GtkSocket *socket;
312 
313   socket = g_object_new (GTK_TYPE_SOCKET, NULL);
314 
315   return GTK_WIDGET (socket);
316 }
317 
318 /**
319  * gtk_socket_add_id:
320  * @socket_: a #GtkSocket
321  * @window: the Window of a client participating in the XEMBED protocol.
322  *
323  * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket.  The
324  * client may be in the same process or in a different process.
325  *
326  * To embed a #GtkPlug in a #GtkSocket, you can either create the
327  * #GtkPlug with `gtk_plug_new (0)`, call
328  * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
329  * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
330  * window ID for the socket, and call gtk_plug_new() passing in that
331  * ID.
332  *
333  * The #GtkSocket must have already be added into a toplevel window
334  *  before you can make this call.
335  **/
336 void
gtk_socket_add_id(GtkSocket * socket,Window window)337 gtk_socket_add_id (GtkSocket      *socket,
338 		   Window          window)
339 {
340   g_return_if_fail (GTK_IS_SOCKET (socket));
341   g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
342 
343   if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
344     gtk_widget_realize (GTK_WIDGET (socket));
345 
346   gtk_socket_add_window (socket, window, TRUE);
347 }
348 
349 /**
350  * gtk_socket_get_id:
351  * @socket_: a #GtkSocket.
352  *
353  * Gets the window ID of a #GtkSocket widget, which can then
354  * be used to create a client embedded inside the socket, for
355  * instance with gtk_plug_new().
356  *
357  * The #GtkSocket must have already be added into a toplevel window
358  * before you can make this call.
359  *
360  * Returns: the window ID for the socket
361  **/
362 Window
gtk_socket_get_id(GtkSocket * socket)363 gtk_socket_get_id (GtkSocket *socket)
364 {
365   g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
366   g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
367 
368   if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
369     gtk_widget_realize (GTK_WIDGET (socket));
370 
371   return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
372 }
373 
374 /**
375  * gtk_socket_get_plug_window:
376  * @socket_: a #GtkSocket.
377  *
378  * Retrieves the window of the plug. Use this to check if the plug has
379  * been created inside of the socket.
380  *
381  * Returns: (nullable) (transfer none): the window of the plug if
382  * available, or %NULL
383  *
384  * Since:  2.14
385  **/
386 GdkWindow*
gtk_socket_get_plug_window(GtkSocket * socket)387 gtk_socket_get_plug_window (GtkSocket *socket)
388 {
389   g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);
390 
391   return socket->priv->plug_window;
392 }
393 
394 static void
gtk_socket_realize(GtkWidget * widget)395 gtk_socket_realize (GtkWidget *widget)
396 {
397   GtkAllocation allocation;
398   GdkWindow *window;
399   GdkWindowAttr attributes;
400   XWindowAttributes xattrs;
401   gint attributes_mask;
402   GdkScreen *screen;
403 
404   gtk_widget_set_realized (widget, TRUE);
405 
406   screen = gtk_widget_get_screen (widget);
407   if (!GDK_IS_X11_SCREEN (screen))
408     g_warning ("GtkSocket: only works under X11");
409 
410   gtk_widget_get_allocation (widget, &allocation);
411 
412   attributes.window_type = GDK_WINDOW_CHILD;
413   attributes.x = allocation.x;
414   attributes.y = allocation.y;
415   attributes.width = allocation.width;
416   attributes.height = allocation.height;
417   attributes.wclass = GDK_INPUT_OUTPUT;
418   attributes.visual = gtk_widget_get_visual (widget);
419   attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
420 
421   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
422 
423   window = gdk_window_new (gtk_widget_get_parent_window (widget),
424                            &attributes, attributes_mask);
425   gtk_widget_set_window (widget, window);
426   gtk_widget_register_window (widget, window);
427 
428   XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
429 			GDK_WINDOW_XID (window),
430 			&xattrs);
431 
432   /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
433      for input on the socket with a mask of 0x0fffff (for god knows why)
434      which includes ButtonPressMask causing a BadAccess if someone else
435      also selects for this. As per the client-side windows merge we always
436      normally selects for button press so we can emulate it on client
437      side children that selects for button press. However, we don't need
438      this for GtkSocket, so we unselect it here, fixing the crashes in
439      firefox. */
440   XSelectInput (GDK_WINDOW_XDISPLAY (window),
441 		GDK_WINDOW_XID (window),
442 		(xattrs.your_event_mask & ~ButtonPressMask) |
443 		SubstructureNotifyMask | SubstructureRedirectMask);
444 
445   gdk_window_add_filter (window,
446 			 gtk_socket_filter_func,
447 			 widget);
448 
449   /* We sync here so that we make sure that if the XID for
450    * our window is passed to another application, SubstructureRedirectMask
451    * will be set by the time the other app creates its window.
452    */
453   gdk_display_sync (gtk_widget_get_display (widget));
454 }
455 
456 /**
457  * gtk_socket_end_embedding:
458  * @socket: a #GtkSocket
459  *
460  * Called to end the embedding of a plug in the socket.
461  */
462 static void
gtk_socket_end_embedding(GtkSocket * socket)463 gtk_socket_end_embedding (GtkSocket *socket)
464 {
465   GtkSocketPrivate *private = socket->priv;
466 
467   g_object_unref (private->plug_window);
468   private->plug_window = NULL;
469   private->current_width = 0;
470   private->current_height = 0;
471   private->resize_count = 0;
472 
473   gtk_accel_group_disconnect (private->accel_group, NULL);
474 }
475 
476 static void
gtk_socket_unrealize(GtkWidget * widget)477 gtk_socket_unrealize (GtkWidget *widget)
478 {
479   GtkSocket *socket = GTK_SOCKET (widget);
480   GtkSocketPrivate *private = socket->priv;
481 
482   gtk_widget_set_realized (widget, FALSE);
483 
484   if (private->plug_widget)
485     {
486       _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
487     }
488   else if (private->plug_window)
489     {
490       gtk_socket_end_embedding (socket);
491     }
492 
493   GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
494 }
495 
496 static void
gtk_socket_size_request(GtkSocket * socket)497 gtk_socket_size_request (GtkSocket *socket)
498 {
499   GtkSocketPrivate *private = socket->priv;
500   GdkDisplay *display;
501   XSizeHints hints;
502   long supplied;
503   int scale;
504 
505   display = gtk_widget_get_display (GTK_WIDGET (socket));
506   gdk_x11_display_error_trap_push (display);
507 
508   private->request_width = 1;
509   private->request_height = 1;
510   scale = gtk_widget_get_scale_factor (GTK_WIDGET(socket));
511 
512   if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
513 			 GDK_WINDOW_XID (private->plug_window),
514 			 &hints, &supplied))
515     {
516       if (hints.flags & PMinSize)
517 	{
518 	  private->request_width = MAX (hints.min_width / scale, 1);
519 	  private->request_height = MAX (hints.min_height / scale, 1);
520 	}
521       else if (hints.flags & PBaseSize)
522 	{
523 	  private->request_width = MAX (hints.base_width / scale, 1);
524 	  private->request_height = MAX (hints.base_height / scale, 1);
525 	}
526     }
527   private->have_size = TRUE;
528 
529   gdk_x11_display_error_trap_pop_ignored (display);
530 }
531 
532 static void
gtk_socket_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)533 gtk_socket_get_preferred_width (GtkWidget *widget,
534                                 gint      *minimum,
535                                 gint      *natural)
536 {
537   GtkSocket *socket = GTK_SOCKET (widget);
538   GtkSocketPrivate *private = socket->priv;
539 
540   if (private->plug_widget)
541     {
542       gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
543     }
544   else
545     {
546       if (private->is_mapped && !private->have_size && private->plug_window)
547         gtk_socket_size_request (socket);
548 
549       if (private->is_mapped && private->have_size)
550         *minimum = *natural = MAX (private->request_width, 1);
551       else
552         *minimum = *natural = 1;
553     }
554 }
555 
556 static void
gtk_socket_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)557 gtk_socket_get_preferred_height (GtkWidget *widget,
558                                  gint      *minimum,
559                                  gint      *natural)
560 {
561   GtkSocket *socket = GTK_SOCKET (widget);
562   GtkSocketPrivate *private = socket->priv;
563 
564   if (private->plug_widget)
565     {
566       gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
567     }
568   else
569     {
570       if (private->is_mapped && !private->have_size && private->plug_window)
571         gtk_socket_size_request (socket);
572 
573       if (private->is_mapped && private->have_size)
574         *minimum = *natural = MAX (private->request_height, 1);
575       else
576         *minimum = *natural = 1;
577     }
578 }
579 
580 static void
gtk_socket_send_configure_event(GtkSocket * socket)581 gtk_socket_send_configure_event (GtkSocket *socket)
582 {
583   GtkAllocation allocation;
584   XConfigureEvent xconfigure;
585   GdkDisplay *display;
586   int x, y, scale;
587 
588   g_return_if_fail (socket->priv->plug_window != NULL);
589 
590   memset (&xconfigure, 0, sizeof (xconfigure));
591   xconfigure.type = ConfigureNotify;
592 
593   xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
594   xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
595 
596   /* The ICCCM says that synthetic events should have root relative
597    * coordinates. We still aren't really ICCCM compliant, since
598    * we don't send events when the real toplevel is moved.
599    */
600   display = gdk_window_get_display (socket->priv->plug_window);
601   gdk_x11_display_error_trap_push (display);
602   gdk_window_get_origin (socket->priv->plug_window, &x, &y);
603   gdk_x11_display_error_trap_pop_ignored (display);
604 
605   gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
606   scale = gtk_widget_get_scale_factor (GTK_WIDGET(socket));
607   xconfigure.x = x * scale;
608   xconfigure.y = y * scale;
609   xconfigure.width = allocation.width * scale;
610   xconfigure.height = allocation.height * scale;
611 
612   xconfigure.border_width = 0;
613   xconfigure.above = None;
614   xconfigure.override_redirect = False;
615 
616   gdk_x11_display_error_trap_push (display);
617   XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
618 	      GDK_WINDOW_XID (socket->priv->plug_window),
619 	      False, NoEventMask, (XEvent *)&xconfigure);
620   gdk_x11_display_error_trap_pop_ignored (display);
621 }
622 
623 static void
gtk_socket_size_allocate(GtkWidget * widget,GtkAllocation * allocation)624 gtk_socket_size_allocate (GtkWidget     *widget,
625 			  GtkAllocation *allocation)
626 {
627   GtkSocket *socket = GTK_SOCKET (widget);
628   GtkSocketPrivate *private = socket->priv;
629 
630   gtk_widget_set_allocation (widget, allocation);
631   if (gtk_widget_get_realized (widget))
632     {
633       gdk_window_move_resize (gtk_widget_get_window (widget),
634 			      allocation->x, allocation->y,
635 			      allocation->width, allocation->height);
636 
637       if (private->plug_widget)
638 	{
639 	  GtkAllocation child_allocation;
640 
641 	  child_allocation.x = 0;
642 	  child_allocation.y = 0;
643 	  child_allocation.width = allocation->width;
644 	  child_allocation.height = allocation->height;
645 
646 	  gtk_widget_size_allocate (private->plug_widget, &child_allocation);
647 	}
648       else if (private->plug_window)
649 	{
650           GdkDisplay *display = gdk_window_get_display (private->plug_window);
651 
652 	  gdk_x11_display_error_trap_push (display);
653 
654 	  if (allocation->width != private->current_width ||
655 	      allocation->height != private->current_height)
656 	    {
657 	      gdk_window_move_resize (private->plug_window,
658 				      0, 0,
659 				      allocation->width, allocation->height);
660 	      if (private->resize_count)
661 		private->resize_count--;
662 
663 	      GTK_NOTE (PLUGSOCKET,
664 			g_message ("GtkSocket - allocated: %d %d",
665 				   allocation->width, allocation->height));
666 	      private->current_width = allocation->width;
667 	      private->current_height = allocation->height;
668 	    }
669 
670 	  if (private->need_map)
671 	    {
672 	      gdk_window_show (private->plug_window);
673 	      private->need_map = FALSE;
674 	    }
675 
676 	  while (private->resize_count)
677  	    {
678  	      gtk_socket_send_configure_event (socket);
679  	      private->resize_count--;
680  	      GTK_NOTE (PLUGSOCKET,
681 			g_message ("GtkSocket - sending synthetic configure: %d %d",
682 				   allocation->width, allocation->height));
683  	    }
684 
685 	  gdk_x11_display_error_trap_pop_ignored (display);
686 	}
687     }
688 }
689 
690 static void
gtk_socket_send_key_event(GtkSocket * socket,GdkEvent * gdk_event,gboolean mask_key_presses)691 gtk_socket_send_key_event (GtkSocket *socket,
692 			   GdkEvent  *gdk_event,
693 			   gboolean   mask_key_presses)
694 {
695   XKeyEvent xkey;
696   GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
697 
698   memset (&xkey, 0, sizeof (xkey));
699   xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
700   xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
701   xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
702   xkey.subwindow = None;
703   xkey.time = gdk_event->key.time;
704   xkey.x = 0;
705   xkey.y = 0;
706   xkey.x_root = 0;
707   xkey.y_root = 0;
708   xkey.state = gdk_event->key.state;
709   xkey.keycode = gdk_event->key.hardware_keycode;
710   xkey.same_screen = True;/* FIXME ? */
711 
712   gdk_x11_display_error_trap_push (gdk_window_get_display (socket->priv->plug_window));
713   XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
714 	      GDK_WINDOW_XID (socket->priv->plug_window),
715 	      False,
716 	      (mask_key_presses ? KeyPressMask : NoEventMask),
717 	      (XEvent *)&xkey);
718   gdk_x11_display_error_trap_pop_ignored (gdk_window_get_display (socket->priv->plug_window));
719 }
720 
721 static gboolean
activate_key(GtkAccelGroup * accel_group,GObject * acceleratable,guint accel_key,GdkModifierType accel_mods,GrabbedKey * grabbed_key)722 activate_key (GtkAccelGroup  *accel_group,
723 	      GObject        *acceleratable,
724 	      guint           accel_key,
725 	      GdkModifierType accel_mods,
726 	      GrabbedKey     *grabbed_key)
727 {
728   GdkEvent *gdk_event = gtk_get_current_event ();
729 
730   GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
731   gboolean retval = FALSE;
732 
733   if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
734     {
735       gtk_socket_send_key_event (socket, gdk_event, FALSE);
736       retval = TRUE;
737     }
738 
739   if (gdk_event)
740     gdk_event_free (gdk_event);
741 
742   return retval;
743 }
744 
745 static gboolean
find_accel_key(GtkAccelKey * key,GClosure * closure,gpointer data)746 find_accel_key (GtkAccelKey *key,
747 		GClosure    *closure,
748 		gpointer     data)
749 {
750   GrabbedKey *grabbed_key = data;
751 
752   return (key->accel_key == grabbed_key->accel_key &&
753 	  key->accel_mods == grabbed_key->accel_mods);
754 }
755 
756 /**
757  * gtk_socket_add_grabbed_key:
758  * @socket: a #GtkSocket
759  * @keyval: a key
760  * @modifiers: modifiers for the key
761  *
762  * Called from the GtkSocket platform-specific backend when the
763  * corresponding plug has told the socket to grab a key.
764  */
765 static void
gtk_socket_add_grabbed_key(GtkSocket * socket,guint keyval,GdkModifierType modifiers)766 gtk_socket_add_grabbed_key (GtkSocket       *socket,
767 			    guint            keyval,
768 			    GdkModifierType  modifiers)
769 {
770   GClosure *closure;
771   GrabbedKey *grabbed_key;
772 
773   grabbed_key = g_new (GrabbedKey, 1);
774 
775   grabbed_key->accel_key = keyval;
776   grabbed_key->accel_mods = modifiers;
777 
778   if (gtk_accel_group_find (socket->priv->accel_group,
779 			    find_accel_key,
780 			    &grabbed_key))
781     {
782       g_warning ("GtkSocket: request to add already present grabbed key %u,%#x",
783 		 keyval, modifiers);
784       g_free (grabbed_key);
785       return;
786     }
787 
788   closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
789 
790   gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
791 			   closure);
792 }
793 
794 /**
795  * gtk_socket_remove_grabbed_key:
796  * @socket: a #GtkSocket
797  * @keyval: a key
798  * @modifiers: modifiers for the key
799  *
800  * Called from the GtkSocket backend when the corresponding plug has
801  * told the socket to remove a key grab.
802  */
803 static void
gtk_socket_remove_grabbed_key(GtkSocket * socket,guint keyval,GdkModifierType modifiers)804 gtk_socket_remove_grabbed_key (GtkSocket      *socket,
805 			       guint           keyval,
806 			       GdkModifierType modifiers)
807 {
808   if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
809     g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x",
810 	       keyval, modifiers);
811 }
812 
813 static void
socket_update_focus_in(GtkSocket * socket)814 socket_update_focus_in (GtkSocket *socket)
815 {
816   GtkSocketPrivate *private = socket->priv;
817   gboolean focus_in = FALSE;
818 
819   if (private->plug_window)
820     {
821       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
822 
823       if (gtk_widget_is_toplevel (toplevel) &&
824 	  gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
825 	  gtk_widget_is_focus (GTK_WIDGET (socket)))
826 	focus_in = TRUE;
827     }
828 
829   if (focus_in != private->focus_in)
830     {
831       private->focus_in = focus_in;
832 
833       if (focus_in)
834         _gtk_xembed_send_focus_message (private->plug_window,
835                                         XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
836       else
837         _gtk_xembed_send_message (private->plug_window,
838                                   XEMBED_FOCUS_OUT, 0, 0, 0);
839     }
840 }
841 
842 static void
socket_update_active(GtkSocket * socket)843 socket_update_active (GtkSocket *socket)
844 {
845   GtkSocketPrivate *private = socket->priv;
846   gboolean active = FALSE;
847 
848   if (private->plug_window)
849     {
850       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
851 
852       if (gtk_widget_is_toplevel (toplevel) &&
853 	  gtk_window_is_active  (GTK_WINDOW (toplevel)))
854 	active = TRUE;
855     }
856 
857   if (active != private->active)
858     {
859       private->active = active;
860 
861       _gtk_xembed_send_message (private->plug_window,
862                                 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
863                                 0, 0, 0);
864     }
865 }
866 
867 static void
gtk_socket_hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel)868 gtk_socket_hierarchy_changed (GtkWidget *widget,
869 			      GtkWidget *old_toplevel)
870 {
871   GtkSocket *socket = GTK_SOCKET (widget);
872   GtkSocketPrivate *private = socket->priv;
873   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
874 
875   if (toplevel && !GTK_IS_WINDOW (toplevel))
876     toplevel = NULL;
877 
878   if (toplevel != private->toplevel)
879     {
880       if (private->toplevel)
881 	{
882 	  gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
883 	  g_signal_handlers_disconnect_by_func (private->toplevel,
884 						socket_update_focus_in,
885 						socket);
886 	  g_signal_handlers_disconnect_by_func (private->toplevel,
887 						socket_update_active,
888 						socket);
889 	}
890 
891       private->toplevel = toplevel;
892 
893       if (toplevel)
894 	{
895 	  gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
896 	  g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
897 				    G_CALLBACK (socket_update_focus_in), socket);
898 	  g_signal_connect_swapped (private->toplevel, "notify::is-active",
899 				    G_CALLBACK (socket_update_active), socket);
900 	}
901 
902       socket_update_focus_in (socket);
903       socket_update_active (socket);
904     }
905 }
906 
907 static void
gtk_socket_grab_notify(GtkWidget * widget,gboolean was_grabbed)908 gtk_socket_grab_notify (GtkWidget *widget,
909 			gboolean   was_grabbed)
910 {
911   GtkSocket *socket = GTK_SOCKET (widget);
912 
913   if (!socket->priv->same_app)
914     _gtk_xembed_send_message (socket->priv->plug_window,
915                               was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
916                               0, 0, 0);
917 }
918 
919 static gboolean
gtk_socket_key_event(GtkWidget * widget,GdkEventKey * event)920 gtk_socket_key_event (GtkWidget   *widget,
921                       GdkEventKey *event)
922 {
923   GtkSocket *socket = GTK_SOCKET (widget);
924   GtkSocketPrivate *private = socket->priv;
925 
926   if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
927     {
928       gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
929 
930       return TRUE;
931     }
932   else
933     return FALSE;
934 }
935 
936 static void
gtk_socket_notify(GObject * object,GParamSpec * pspec)937 gtk_socket_notify (GObject    *object,
938 		   GParamSpec *pspec)
939 {
940   if (strcmp (pspec->name, "is-focus") == 0)
941     socket_update_focus_in (GTK_SOCKET (object));
942 
943   if (G_OBJECT_CLASS (gtk_socket_parent_class)->notify)
944     G_OBJECT_CLASS (gtk_socket_parent_class)->notify (object, pspec);
945 }
946 
947 /**
948  * gtk_socket_claim_focus:
949  * @socket: a #GtkSocket
950  * @send_event: huh?
951  *
952  * Claims focus for the socket. XXX send_event?
953  */
954 static void
gtk_socket_claim_focus(GtkSocket * socket,gboolean send_event)955 gtk_socket_claim_focus (GtkSocket *socket,
956 			gboolean   send_event)
957 {
958   GtkWidget *widget = GTK_WIDGET (socket);
959   GtkSocketPrivate *private = socket->priv;
960 
961   if (!send_event)
962     private->focus_in = TRUE;	/* Otherwise, our notify handler will send FOCUS_IN  */
963 
964   /* Oh, the trickery... */
965 
966   gtk_widget_set_can_focus (widget, TRUE);
967   gtk_widget_grab_focus (widget);
968   gtk_widget_set_can_focus (widget, FALSE);
969 }
970 
971 static gboolean
gtk_socket_focus(GtkWidget * widget,GtkDirectionType direction)972 gtk_socket_focus (GtkWidget       *widget,
973 		  GtkDirectionType direction)
974 {
975   GtkSocket *socket = GTK_SOCKET (widget);
976   GtkSocketPrivate *private = socket->priv;
977 
978   if (private->plug_widget)
979     return gtk_widget_child_focus (private->plug_widget, direction);
980 
981   if (!gtk_widget_is_focus (widget))
982     {
983       gint detail = -1;
984 
985       switch (direction)
986         {
987         case GTK_DIR_UP:
988         case GTK_DIR_LEFT:
989         case GTK_DIR_TAB_BACKWARD:
990           detail = XEMBED_FOCUS_LAST;
991           break;
992         case GTK_DIR_DOWN:
993         case GTK_DIR_RIGHT:
994         case GTK_DIR_TAB_FORWARD:
995           detail = XEMBED_FOCUS_FIRST;
996           break;
997         }
998 
999       _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
1000       gtk_socket_claim_focus (socket, FALSE);
1001 
1002       return TRUE;
1003     }
1004   else
1005     return FALSE;
1006 }
1007 
1008 static void
gtk_socket_remove(GtkContainer * container,GtkWidget * child)1009 gtk_socket_remove (GtkContainer *container,
1010 		   GtkWidget    *child)
1011 {
1012   GtkSocket *socket = GTK_SOCKET (container);
1013   GtkSocketPrivate *private = socket->priv;
1014 
1015   g_return_if_fail (child == private->plug_widget);
1016 
1017   _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
1018 }
1019 
1020 static void
gtk_socket_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)1021 gtk_socket_forall (GtkContainer *container,
1022 		   gboolean      include_internals,
1023 		   GtkCallback   callback,
1024 		   gpointer      callback_data)
1025 {
1026   GtkSocket *socket = GTK_SOCKET (container);
1027   GtkSocketPrivate *private = socket->priv;
1028 
1029   if (private->plug_widget)
1030     (* callback) (private->plug_widget, callback_data);
1031 }
1032 
1033 /**
1034  * gtk_socket_add_window:
1035  * @socket: a #GtkSocket
1036  * @xid: the native identifier for a window
1037  * @need_reparent: whether the socket’s plug’s window needs to be
1038  *                 reparented to the socket
1039  *
1040  * Adds a window to a GtkSocket.
1041  */
1042 static void
gtk_socket_add_window(GtkSocket * socket,Window xid,gboolean need_reparent)1043 gtk_socket_add_window (GtkSocket       *socket,
1044 		       Window           xid,
1045 		       gboolean         need_reparent)
1046 {
1047   GtkWidget *widget = GTK_WIDGET (socket);
1048   GdkDisplay *display = gtk_widget_get_display (widget);
1049   gpointer user_data = NULL;
1050   GtkSocketPrivate *private = socket->priv;
1051   unsigned long version;
1052   unsigned long flags;
1053 
1054   if (GDK_IS_X11_DISPLAY (display))
1055     private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1056   else
1057     private->plug_window = NULL;
1058 
1059   if (private->plug_window)
1060     {
1061       g_object_ref (private->plug_window);
1062       gdk_window_get_user_data (private->plug_window, &user_data);
1063     }
1064 
1065   if (user_data) /* A widget's window in this process */
1066     {
1067       GtkWidget *child_widget = user_data;
1068 
1069       if (!GTK_IS_PLUG (child_widget))
1070         {
1071           g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1072           private->plug_window = NULL;
1073           gdk_x11_display_error_trap_pop_ignored (display);
1074 
1075           return;
1076         }
1077 
1078       _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1079     }
1080   else  /* A foreign window */
1081     {
1082       GdkDragProtocol protocol;
1083 
1084       gdk_x11_display_error_trap_push (display);
1085 
1086       if (!private->plug_window)
1087         {
1088           if (GDK_IS_X11_DISPLAY (display))
1089             private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1090           if (!private->plug_window) /* was deleted before we could get it */
1091             {
1092               gdk_x11_display_error_trap_pop_ignored (display);
1093               return;
1094             }
1095         }
1096 
1097       XSelectInput (GDK_DISPLAY_XDISPLAY (display),
1098                     GDK_WINDOW_XID (private->plug_window),
1099                     StructureNotifyMask | PropertyChangeMask);
1100 
1101       if (gdk_x11_display_error_trap_pop (display))
1102 	{
1103 	  g_object_unref (private->plug_window);
1104 	  private->plug_window = NULL;
1105 	  return;
1106 	}
1107 
1108       /* OK, we now will reliably get destroy notification on socket->plug_window */
1109 
1110       gdk_x11_display_error_trap_push (display);
1111 
1112       if (need_reparent)
1113 	{
1114 	  gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1115 	  gdk_window_reparent (private->plug_window,
1116                                gtk_widget_get_window (widget),
1117                                0, 0);
1118 	}
1119 
1120       private->have_size = FALSE;
1121 
1122       private->xembed_version = -1;
1123       if (xembed_get_info (private->plug_window, &version, &flags))
1124         {
1125           private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1126           private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1127         }
1128       else
1129         {
1130           /* FIXME, we should probably actually check the state before we started */
1131           private->is_mapped = TRUE;
1132         }
1133 
1134       private->need_map = private->is_mapped;
1135 
1136 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1137       protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1138       if (protocol)
1139 	gtk_drag_dest_set_proxy (widget, private->plug_window, protocol, TRUE);
1140 G_GNUC_END_IGNORE_DEPRECATIONS
1141 
1142       gdk_x11_display_error_trap_pop_ignored (display);
1143 
1144       gdk_window_add_filter (private->plug_window,
1145 			     gtk_socket_filter_func,
1146 			     socket);
1147 
1148 #ifdef HAVE_XFIXES
1149       gdk_x11_display_error_trap_push (display);
1150       XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display),
1151                            GDK_WINDOW_XID (private->plug_window),
1152                            SetModeInsert, SaveSetRoot, SaveSetUnmap);
1153       gdk_x11_display_error_trap_pop_ignored (display);
1154 #endif
1155       _gtk_xembed_send_message (private->plug_window,
1156                                 XEMBED_EMBEDDED_NOTIFY, 0,
1157                                 GDK_WINDOW_XID (gtk_widget_get_window (widget)),
1158                                 private->xembed_version);
1159 
1160       socket_update_active (socket);
1161       socket_update_focus_in (socket);
1162 
1163       gtk_widget_queue_resize (GTK_WIDGET (socket));
1164 
1165       _gtk_socket_accessible_embed (GTK_WIDGET (socket), private->plug_window);
1166     }
1167 
1168   if (private->plug_window)
1169     g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1170 }
1171 
1172 /**
1173  * gtk_socket_handle_map_request:
1174  * @socket: a #GtkSocket
1175  *
1176  * Called from the GtkSocket backend when the plug has been mapped.
1177  */
1178 static void
gtk_socket_handle_map_request(GtkSocket * socket)1179 gtk_socket_handle_map_request (GtkSocket *socket)
1180 {
1181   GtkSocketPrivate *private = socket->priv;
1182   if (!private->is_mapped)
1183     {
1184       private->is_mapped = TRUE;
1185       private->need_map = TRUE;
1186 
1187       gtk_widget_queue_resize (GTK_WIDGET (socket));
1188     }
1189 }
1190 
1191 /**
1192  * gtk_socket_unmap_notify:
1193  * @socket: a #GtkSocket
1194  *
1195  * Called from the GtkSocket backend when the plug has been unmapped ???
1196  */
1197 static void
gtk_socket_unmap_notify(GtkSocket * socket)1198 gtk_socket_unmap_notify (GtkSocket *socket)
1199 {
1200   GtkSocketPrivate *private = socket->priv;
1201   if (private->is_mapped)
1202     {
1203       private->is_mapped = FALSE;
1204       gtk_widget_queue_resize (GTK_WIDGET (socket));
1205     }
1206 }
1207 
1208 /**
1209  * gtk_socket_advance_toplevel_focus:
1210  * @socket: a #GtkSocket
1211  * @direction: a direction
1212  *
1213  * Called from the GtkSocket backend when the corresponding plug
1214  * has told the socket to move the focus.
1215  */
1216 static void
gtk_socket_advance_toplevel_focus(GtkSocket * socket,GtkDirectionType direction)1217 gtk_socket_advance_toplevel_focus (GtkSocket        *socket,
1218 				   GtkDirectionType  direction)
1219 {
1220   GtkBin *bin;
1221   GtkWindow *window;
1222   GtkContainer *container;
1223   GtkWidget *child;
1224   GtkWidget *focus_widget;
1225   GtkWidget *toplevel;
1226   GtkWidget *old_focus_child;
1227   GtkWidget *parent;
1228 
1229   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1230   if (!toplevel)
1231     return;
1232 
1233   if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1234     {
1235       gtk_widget_child_focus (toplevel,direction);
1236       return;
1237     }
1238 
1239   container = GTK_CONTAINER (toplevel);
1240   window = GTK_WINDOW (toplevel);
1241   bin = GTK_BIN (toplevel);
1242 
1243   /* This is a copy of gtk_window_focus(), modified so that we
1244    * can detect wrap-around.
1245    */
1246   old_focus_child = gtk_container_get_focus_child (container);
1247 
1248   if (old_focus_child)
1249     {
1250       if (gtk_widget_child_focus (old_focus_child, direction))
1251 	return;
1252 
1253       /* We are allowed exactly one wrap-around per sequence of focus
1254        * events
1255        */
1256       if (_gtk_xembed_get_focus_wrapped ())
1257 	return;
1258       else
1259 	_gtk_xembed_set_focus_wrapped ();
1260     }
1261 
1262   focus_widget = gtk_window_get_focus (window);
1263   if (window)
1264     {
1265       /* Wrapped off the end, clear the focus setting for the toplevel */
1266       parent = gtk_widget_get_parent (focus_widget);
1267       while (parent)
1268 	{
1269 	  gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1270           parent = gtk_widget_get_parent (parent);
1271 	}
1272 
1273       gtk_window_set_focus (GTK_WINDOW (container), NULL);
1274     }
1275 
1276   /* Now try to focus the first widget in the window */
1277   child = gtk_bin_get_child (bin);
1278   if (child)
1279     {
1280       if (gtk_widget_child_focus (child, direction))
1281         return;
1282     }
1283 }
1284 
1285 static gboolean
xembed_get_info(GdkWindow * window,unsigned long * version,unsigned long * flags)1286 xembed_get_info (GdkWindow     *window,
1287 		 unsigned long *version,
1288 		 unsigned long *flags)
1289 {
1290   GdkDisplay *display = gdk_window_get_display (window);
1291   Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1292   Atom type;
1293   int format;
1294   unsigned long nitems, bytes_after;
1295   unsigned char *data;
1296   unsigned long *data_long;
1297   int status;
1298 
1299   gdk_x11_display_error_trap_push (display);
1300   status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1301 			       GDK_WINDOW_XID (window),
1302 			       xembed_info_atom,
1303 			       0, 2, False,
1304 			       xembed_info_atom, &type, &format,
1305 			       &nitems, &bytes_after, &data);
1306   gdk_x11_display_error_trap_pop_ignored (display);
1307 
1308   if (status != Success)
1309     return FALSE;		/* Window vanished? */
1310 
1311   if (type == None)		/* No info property */
1312     return FALSE;
1313 
1314   if (type != xembed_info_atom)
1315     {
1316       g_warning ("_XEMBED_INFO property has wrong type");
1317       return FALSE;
1318     }
1319 
1320   if (nitems < 2)
1321     {
1322       g_warning ("_XEMBED_INFO too short");
1323       XFree (data);
1324       return FALSE;
1325     }
1326 
1327   data_long = (unsigned long *)data;
1328   if (version)
1329     *version = data_long[0];
1330   if (flags)
1331     *flags = data_long[1] & XEMBED_MAPPED;
1332 
1333   XFree (data);
1334   return TRUE;
1335 }
1336 
1337 static void
handle_xembed_message(GtkSocket * socket,XEmbedMessageType message,glong detail,glong data1,glong data2,guint32 time)1338 handle_xembed_message (GtkSocket        *socket,
1339 		       XEmbedMessageType message,
1340 		       glong             detail,
1341 		       glong             data1,
1342 		       glong             data2,
1343 		       guint32           time)
1344 {
1345   GTK_NOTE (PLUGSOCKET,
1346 	    g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1347 
1348   switch (message)
1349     {
1350     case XEMBED_EMBEDDED_NOTIFY:
1351     case XEMBED_WINDOW_ACTIVATE:
1352     case XEMBED_WINDOW_DEACTIVATE:
1353     case XEMBED_MODALITY_ON:
1354     case XEMBED_MODALITY_OFF:
1355     case XEMBED_FOCUS_IN:
1356     case XEMBED_FOCUS_OUT:
1357       g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1358       break;
1359 
1360     case XEMBED_REQUEST_FOCUS:
1361       gtk_socket_claim_focus (socket, TRUE);
1362       break;
1363 
1364     case XEMBED_FOCUS_NEXT:
1365     case XEMBED_FOCUS_PREV:
1366       gtk_socket_advance_toplevel_focus (socket,
1367 					 (message == XEMBED_FOCUS_NEXT ?
1368 					  GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1369       break;
1370 
1371     case XEMBED_GTK_GRAB_KEY:
1372       gtk_socket_add_grabbed_key (socket, data1, data2);
1373       break;
1374     case XEMBED_GTK_UNGRAB_KEY:
1375       gtk_socket_remove_grabbed_key (socket, data1, data2);
1376       break;
1377 
1378     case XEMBED_GRAB_KEY:
1379     case XEMBED_UNGRAB_KEY:
1380       break;
1381 
1382     default:
1383       GTK_NOTE (PLUGSOCKET,
1384 		g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1385       break;
1386     }
1387 }
1388 
1389 static void
_gtk_socket_accessible_embed(GtkWidget * socket,GdkWindow * window)1390 _gtk_socket_accessible_embed (GtkWidget *socket, GdkWindow *window)
1391 {
1392   GdkDisplay *display = gdk_window_get_display (window);
1393   Atom net_at_spi_path_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_AT_SPI_PATH");
1394   Atom type;
1395   int format;
1396   unsigned long nitems, bytes_after;
1397   unsigned char *data;
1398   int status;
1399 
1400   gdk_x11_display_error_trap_push (display);
1401   status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1402 			       GDK_WINDOW_XID (window),
1403 			       net_at_spi_path_atom,
1404 			       0, INT_MAX / 4, False,
1405 			       net_at_spi_path_atom, &type, &format,
1406 			       &nitems, &bytes_after, &data);
1407   gdk_x11_display_error_trap_pop_ignored (display);
1408 
1409   if (status != Success)
1410     return;			/* Window vanished? */
1411 
1412   if (type == None)		/* No info property */
1413     return;
1414 
1415   if (type != net_at_spi_path_atom)
1416     {
1417       g_warning ("_XEMBED_AT_SPI_PATH property has wrong type");
1418       return;
1419     }
1420 
1421   if (nitems == 0)
1422     {
1423       g_warning ("_XEMBED_AT_SPI_PATH too short");
1424       XFree (data);
1425       return;
1426     }
1427 
1428   if (nitems > INT_MAX)
1429     {
1430       g_warning ("_XEMBED_AT_SPI_PATH too long");
1431       XFree (data);
1432       return;
1433     }
1434 
1435   gtk_socket_accessible_embed (GTK_SOCKET_ACCESSIBLE (gtk_widget_get_accessible (socket)), (gchar*) data);
1436   XFree (data);
1437 
1438   return;
1439 }
1440 
1441 static GdkFilterReturn
gtk_socket_filter_func(GdkXEvent * gdk_xevent,GdkEvent * event,gpointer data)1442 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1443 			GdkEvent  *event,
1444 			gpointer   data)
1445 {
1446   GtkSocket *socket;
1447   GtkWidget *widget;
1448   GdkDisplay *display;
1449   XEvent *xevent;
1450   GtkSocketPrivate *private;
1451 
1452   GdkFilterReturn return_val;
1453 
1454   socket = GTK_SOCKET (data);
1455   private = socket->priv;
1456 
1457   return_val = GDK_FILTER_CONTINUE;
1458 
1459   if (private->plug_widget)
1460     return return_val;
1461 
1462   widget = GTK_WIDGET (socket);
1463   xevent = (XEvent *)gdk_xevent;
1464   display = gtk_widget_get_display (widget);
1465 
1466   switch (xevent->type)
1467     {
1468     case ClientMessage:
1469       if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1470 	{
1471 	  _gtk_xembed_push_message (xevent);
1472 	  handle_xembed_message (socket,
1473 				 xevent->xclient.data.l[1],
1474 				 xevent->xclient.data.l[2],
1475 				 xevent->xclient.data.l[3],
1476 				 xevent->xclient.data.l[4],
1477 				 xevent->xclient.data.l[0]);
1478 	  _gtk_xembed_pop_message ();
1479 
1480 	  return_val = GDK_FILTER_REMOVE;
1481 	}
1482       break;
1483 
1484     case CreateNotify:
1485       {
1486 	XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1487 
1488 	if (!private->plug_window)
1489 	  {
1490 	    gtk_socket_add_window (socket, xcwe->window, FALSE);
1491 
1492 	    if (private->plug_window)
1493 	      {
1494 		GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1495 	      }
1496 	  }
1497 
1498 	return_val = GDK_FILTER_REMOVE;
1499 
1500 	break;
1501       }
1502 
1503     case ConfigureRequest:
1504       {
1505 	XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1506 
1507 	if (!private->plug_window)
1508 	  gtk_socket_add_window (socket, xcre->window, FALSE);
1509 
1510 	if (private->plug_window)
1511 	  {
1512 	    if (xcre->value_mask & (CWWidth | CWHeight))
1513 	      {
1514 		GTK_NOTE (PLUGSOCKET,
1515 			  g_message ("GtkSocket - configure request: %d %d",
1516 				     private->request_width,
1517 				     private->request_height));
1518 
1519 		private->resize_count++;
1520 		gtk_widget_queue_resize (widget);
1521 	      }
1522 	    else if (xcre->value_mask & (CWX | CWY))
1523 	      {
1524 		gtk_socket_send_configure_event (socket);
1525 	      }
1526 	    /* Ignore stacking requests. */
1527 
1528 	    return_val = GDK_FILTER_REMOVE;
1529 	  }
1530 	break;
1531       }
1532 
1533     case DestroyNotify:
1534       {
1535 	XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1536 
1537 	/* Note that we get destroy notifies both from SubstructureNotify on
1538 	 * our window and StructureNotify on socket->plug_window
1539 	 */
1540 	if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1541 	  {
1542 	    gboolean result;
1543 
1544 	    GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1545 
1546 	    gdk_window_destroy_notify (private->plug_window);
1547 	    gtk_socket_end_embedding (socket);
1548 
1549 	    g_object_ref (widget);
1550 	    g_signal_emit_by_name (widget, "plug-removed", &result);
1551 	    if (!result)
1552 	      gtk_widget_destroy (widget);
1553 	    g_object_unref (widget);
1554 
1555 	    return_val = GDK_FILTER_REMOVE;
1556 	  }
1557 	break;
1558       }
1559 
1560     case FocusIn:
1561       if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1562 	{
1563 	  gtk_socket_claim_focus (socket, TRUE);
1564 	}
1565       return_val = GDK_FILTER_REMOVE;
1566       break;
1567     case FocusOut:
1568       return_val = GDK_FILTER_REMOVE;
1569       break;
1570     case MapRequest:
1571       if (!private->plug_window)
1572 	{
1573 	  gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1574 	}
1575 
1576       if (private->plug_window)
1577 	{
1578 	  GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1579 
1580 	  gtk_socket_handle_map_request (socket);
1581 	  return_val = GDK_FILTER_REMOVE;
1582 	}
1583       break;
1584     case PropertyNotify:
1585       if (private->plug_window &&
1586 	  xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1587 	{
1588 	  GdkDragProtocol protocol;
1589 
1590 	  if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1591 	    {
1592 	      GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1593 	      private->have_size = FALSE;
1594 	      gtk_widget_queue_resize (widget);
1595 	      return_val = GDK_FILTER_REMOVE;
1596 	    }
1597 	  else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1598 	      (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1599 	    {
1600 	      gdk_x11_display_error_trap_push (display);
1601 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1602               protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1603               if (protocol)
1604 		gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1605 					 private->plug_window,
1606 					 protocol, TRUE);
1607 G_GNUC_END_IGNORE_DEPRECATIONS
1608 
1609 	      gdk_x11_display_error_trap_pop_ignored (display);
1610 	      return_val = GDK_FILTER_REMOVE;
1611 	    }
1612 	  else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1613 	    {
1614 	      unsigned long flags;
1615 
1616 	      if (xembed_get_info (private->plug_window, NULL, &flags))
1617 		{
1618 		  gboolean was_mapped = private->is_mapped;
1619 		  gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1620 
1621 		  if (was_mapped != is_mapped)
1622 		    {
1623 		      if (is_mapped)
1624 			gtk_socket_handle_map_request (socket);
1625 		      else
1626 			{
1627 			  gdk_x11_display_error_trap_push (display);
1628 			  gdk_window_show (private->plug_window);
1629 			  gdk_x11_display_error_trap_pop_ignored (display);
1630 
1631 			  gtk_socket_unmap_notify (socket);
1632 			}
1633 		    }
1634 		}
1635 	      return_val = GDK_FILTER_REMOVE;
1636 	    }
1637 	}
1638       break;
1639     case ReparentNotify:
1640       {
1641         GdkWindow *window;
1642 	XReparentEvent *xre = &xevent->xreparent;
1643 
1644         window = gtk_widget_get_window (widget);
1645 
1646 	GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1647 	if (!private->plug_window &&
1648             xre->parent == GDK_WINDOW_XID (window))
1649 	  {
1650 	    gtk_socket_add_window (socket, xre->window, FALSE);
1651 
1652 	    if (private->plug_window)
1653 	      {
1654 		GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1655 	      }
1656 
1657 	    return_val = GDK_FILTER_REMOVE;
1658 	  }
1659         else
1660           {
1661             if (private->plug_window &&
1662                 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1663                 xre->parent != GDK_WINDOW_XID (window))
1664               {
1665                 gboolean result;
1666 
1667                 gtk_socket_end_embedding (socket);
1668 
1669                 g_object_ref (widget);
1670                 g_signal_emit_by_name (widget, "plug-removed", &result);
1671                 if (!result)
1672                   gtk_widget_destroy (widget);
1673                 g_object_unref (widget);
1674 
1675                 return_val = GDK_FILTER_REMOVE;
1676               }
1677           }
1678 
1679 	break;
1680       }
1681     case UnmapNotify:
1682       if (private->plug_window &&
1683 	  xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1684 	{
1685 	  GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1686 
1687 	  gtk_socket_unmap_notify (socket);
1688 	  return_val = GDK_FILTER_REMOVE;
1689 	}
1690       break;
1691 
1692     }
1693 
1694   return return_val;
1695 }
1696