1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
19 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
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/.
30 #include "gtksocketprivate.h"
34 #include "gtkmainprivate.h"
35 #include "gtkmarshalers.h"
36 #include "gtksizerequest.h"
38 #include "gtkprivate.h"
42 #include "gtkwidgetprivate.h"
45 #include <gdk/gdkprivate.h>
48 #include <X11/extensions/Xfixes.h>
51 #include "gtkxembed.h"
56 * @Short_description: Container for widgets from other processes
58 * @include: gtk/gtkx.h
59 * @See_also: #GtkPlug, <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
61 * Together with #GtkPlug, #GtkSocket provides the ability to embed
62 * widgets from one process into another process in a fashion that
63 * is transparent to the user. One process creates a #GtkSocket widget
64 * and passes that widget's window ID to the other process, which then
65 * creates a #GtkPlug with that window ID. Any widgets contained in the
66 * #GtkPlug then will appear inside the first application's window.
68 * The socket's window ID is obtained by using gtk_socket_get_id().
69 * Before using this function, the socket must have been realized,
70 * and for hence, have been added to its parent.
73 * <title>Obtaining the window ID of a socket.</title>
75 * GtkWidget *socket = gtk_socket_new (<!-- -->);
76 * gtk_widget_show (socket);
77 * gtk_container_add (GTK_CONTAINER (parent), socket);
79 * /* The following call is only necessary if one of
80 * * the ancestors of the socket is not yet visible.
82 * gtk_widget_realize (socket);
83 * g_print ("The ID of the sockets window is %#x\n",
84 * gtk_socket_get_id (socket));
88 * Note that if you pass the window ID of the socket to another
89 * process that will create a plug in the socket, you must make
90 * sure that the socket widget is not destroyed until that plug
91 * is created. Violating this rule will cause unpredictable
92 * consequences, the most likely consequence being that the plug
93 * will appear as a separate toplevel window. You can check if
94 * the plug has been created by using gtk_socket_get_plug_window().
95 * If it returns a non-%NULL value, then the plug has been
96 * successfully created inside of the socket.
98 * When GTK+ is notified that the embedded window has been destroyed,
99 * then it will destroy the socket as well. You should always,
100 * therefore, be prepared for your sockets to be destroyed at any
101 * time when the main event loop is running. To prevent this from
102 * happening, you can connect to the #GtkSocket::plug-removed signal.
104 * The communication between a #GtkSocket and a #GtkPlug follows the
105 * <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
106 * protocol. This protocol has also been implemented in other toolkits,
107 * e.g. <application>Qt</application>, allowing the same level of
108 * integration when embedding a <application>Qt</application> widget
109 * in GTK or vice versa.
112 * The #GtkPlug and #GtkSocket widgets are only available when GTK+
113 * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined.
114 * They can only be used on a #GdkX11Display. To use #GtkPlug and
115 * #GtkSocket, you need to include the <filename>gtk/gtkx.h</filename>
120 /* Forward declararations */
122 static void gtk_socket_finalize (GObject *object);
123 static void gtk_socket_notify (GObject *object,
125 static void gtk_socket_realize (GtkWidget *widget);
126 static void gtk_socket_unrealize (GtkWidget *widget);
127 static void gtk_socket_get_preferred_width (GtkWidget *widget,
130 static void gtk_socket_get_preferred_height (GtkWidget *widget,
133 static void gtk_socket_size_allocate (GtkWidget *widget,
134 GtkAllocation *allocation);
135 static void gtk_socket_hierarchy_changed (GtkWidget *widget,
136 GtkWidget *old_toplevel);
137 static void gtk_socket_grab_notify (GtkWidget *widget,
138 gboolean was_grabbed);
139 static gboolean gtk_socket_key_event (GtkWidget *widget,
141 static gboolean gtk_socket_focus (GtkWidget *widget,
142 GtkDirectionType direction);
143 static void gtk_socket_remove (GtkContainer *container,
145 static void gtk_socket_forall (GtkContainer *container,
146 gboolean include_internals,
147 GtkCallback callback,
148 gpointer callback_data);
149 static void gtk_socket_add_window (GtkSocket *socket,
151 gboolean need_reparent);
152 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
156 static gboolean xembed_get_info (GdkWindow *gdk_window,
157 unsigned long *version,
158 unsigned long *flags);
161 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
169 GdkModifierType accel_mods;
178 static guint socket_signals[LAST_SIGNAL] = { 0 };
180 G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
183 gtk_socket_finalize (GObject *object)
185 GtkSocket *socket = GTK_SOCKET (object);
186 GtkSocketPrivate *priv = socket->priv;
188 g_object_unref (priv->accel_group);
190 G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
194 gtk_socket_class_init (GtkSocketClass *class)
196 GtkWidgetClass *widget_class;
197 GtkContainerClass *container_class;
198 GObjectClass *gobject_class;
200 gobject_class = (GObjectClass *) class;
201 widget_class = (GtkWidgetClass*) class;
202 container_class = (GtkContainerClass*) class;
204 gobject_class->finalize = gtk_socket_finalize;
205 gobject_class->notify = gtk_socket_notify;
207 widget_class->realize = gtk_socket_realize;
208 widget_class->unrealize = gtk_socket_unrealize;
209 widget_class->get_preferred_width = gtk_socket_get_preferred_width;
210 widget_class->get_preferred_height = gtk_socket_get_preferred_height;
211 widget_class->size_allocate = gtk_socket_size_allocate;
212 widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
213 widget_class->grab_notify = gtk_socket_grab_notify;
214 widget_class->key_press_event = gtk_socket_key_event;
215 widget_class->key_release_event = gtk_socket_key_event;
216 widget_class->focus = gtk_socket_focus;
218 /* We don't want to show_all the in-process plug, if any.
220 widget_class->show_all = gtk_widget_show;
222 container_class->remove = gtk_socket_remove;
223 container_class->forall = gtk_socket_forall;
226 * GtkSocket::plug-added:
227 * @socket_: the object which received the signal
229 * This signal is emitted when a client is successfully
230 * added to the socket.
232 socket_signals[PLUG_ADDED] =
233 g_signal_new (I_("plug-added"),
234 G_OBJECT_CLASS_TYPE (class),
236 G_STRUCT_OFFSET (GtkSocketClass, plug_added),
238 _gtk_marshal_VOID__VOID,
242 * GtkSocket::plug-removed:
243 * @socket_: the object which received the signal
245 * This signal is emitted when a client is removed from the socket.
246 * The default action is to destroy the #GtkSocket widget, so if you
247 * want to reuse it you must add a signal handler that returns %TRUE.
249 * Return value: %TRUE to stop other handlers from being invoked.
251 socket_signals[PLUG_REMOVED] =
252 g_signal_new (I_("plug-removed"),
253 G_OBJECT_CLASS_TYPE (class),
255 G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
256 _gtk_boolean_handled_accumulator, NULL,
257 _gtk_marshal_BOOLEAN__VOID,
260 g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate));
264 gtk_socket_init (GtkSocket *socket)
266 GtkSocketPrivate *priv;
268 priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
272 priv->request_width = 0;
273 priv->request_height = 0;
274 priv->current_width = 0;
275 priv->current_height = 0;
277 priv->plug_window = NULL;
278 priv->plug_widget = NULL;
279 priv->focus_in = FALSE;
280 priv->have_size = FALSE;
281 priv->need_map = FALSE;
282 priv->active = FALSE;
284 priv->accel_group = gtk_accel_group_new ();
285 g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
291 * Create a new empty #GtkSocket.
293 * Return value: the new #GtkSocket.
296 gtk_socket_new (void)
300 socket = g_object_new (GTK_TYPE_SOCKET, NULL);
302 return GTK_WIDGET (socket);
307 * @socket_: a #GtkSocket
308 * @window: the Window of a client participating in the XEMBED protocol.
310 * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The
311 * client may be in the same process or in a different process.
313 * To embed a #GtkPlug in a #GtkSocket, you can either create the
314 * #GtkPlug with <literal>gtk_plug_new (0)</literal>, call
315 * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
316 * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
317 * window ID for the socket, and call gtk_plug_new() passing in that
320 * The #GtkSocket must have already be added into a toplevel window
321 * before you can make this call.
324 gtk_socket_add_id (GtkSocket *socket,
327 g_return_if_fail (GTK_IS_SOCKET (socket));
328 g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
330 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
331 gtk_widget_realize (GTK_WIDGET (socket));
333 gtk_socket_add_window (socket, window, TRUE);
338 * @socket_: a #GtkSocket.
340 * Gets the window ID of a #GtkSocket widget, which can then
341 * be used to create a client embedded inside the socket, for
342 * instance with gtk_plug_new().
344 * The #GtkSocket must have already be added into a toplevel window
345 * before you can make this call.
347 * Return value: the window ID for the socket
350 gtk_socket_get_id (GtkSocket *socket)
352 g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
353 g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
355 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
356 gtk_widget_realize (GTK_WIDGET (socket));
358 return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
362 * gtk_socket_get_plug_window:
363 * @socket_: a #GtkSocket.
365 * Retrieves the window of the plug. Use this to check if the plug has
366 * been created inside of the socket.
368 * Return value: (transfer none): the window of the plug if available, or %NULL
373 gtk_socket_get_plug_window (GtkSocket *socket)
375 g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);
377 return socket->priv->plug_window;
381 gtk_socket_realize (GtkWidget *widget)
383 GtkAllocation allocation;
384 GtkSocket *socket = GTK_SOCKET (widget);
386 GdkWindowAttr attributes;
387 XWindowAttributes xattrs;
388 gint attributes_mask;
390 gtk_widget_set_realized (widget, TRUE);
392 gtk_widget_get_allocation (widget, &allocation);
394 attributes.window_type = GDK_WINDOW_CHILD;
395 attributes.x = allocation.x;
396 attributes.y = allocation.y;
397 attributes.width = allocation.width;
398 attributes.height = allocation.height;
399 attributes.wclass = GDK_INPUT_OUTPUT;
400 attributes.visual = gtk_widget_get_visual (widget);
401 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
403 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
405 window = gdk_window_new (gtk_widget_get_parent_window (widget),
406 &attributes, attributes_mask);
407 gtk_widget_set_window (widget, window);
408 gdk_window_set_user_data (window, socket);
410 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
413 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
414 GDK_WINDOW_XID (window),
417 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
418 for input on the socket with a mask of 0x0fffff (for god knows why)
419 which includes ButtonPressMask causing a BadAccess if someone else
420 also selects for this. As per the client-side windows merge we always
421 normally selects for button press so we can emulate it on client
422 side children that selects for button press. However, we don't need
423 this for GtkSocket, so we unselect it here, fixing the crashes in
425 XSelectInput (GDK_WINDOW_XDISPLAY (window),
426 GDK_WINDOW_XID (window),
427 (xattrs.your_event_mask & ~ButtonPressMask) |
428 SubstructureNotifyMask | SubstructureRedirectMask);
430 gdk_window_add_filter (window,
431 gtk_socket_filter_func,
434 /* We sync here so that we make sure that if the XID for
435 * our window is passed to another application, SubstructureRedirectMask
436 * will be set by the time the other app creates its window.
438 gdk_display_sync (gtk_widget_get_display (widget));
442 * gtk_socket_end_embedding:
444 * @socket: a #GtkSocket
446 * Called to end the embedding of a plug in the socket.
449 gtk_socket_end_embedding (GtkSocket *socket)
451 GtkSocketPrivate *private = socket->priv;
453 g_object_unref (private->plug_window);
454 private->plug_window = NULL;
455 private->current_width = 0;
456 private->current_height = 0;
457 private->resize_count = 0;
459 gtk_accel_group_disconnect (private->accel_group, NULL);
463 gtk_socket_unrealize (GtkWidget *widget)
465 GtkSocket *socket = GTK_SOCKET (widget);
466 GtkSocketPrivate *private = socket->priv;
468 gtk_widget_set_realized (widget, FALSE);
470 if (private->plug_widget)
472 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
474 else if (private->plug_window)
476 gtk_socket_end_embedding (socket);
479 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
483 gtk_socket_size_request (GtkSocket *socket)
485 GtkSocketPrivate *private = socket->priv;
489 gdk_error_trap_push ();
491 private->request_width = 1;
492 private->request_height = 1;
494 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
495 GDK_WINDOW_XID (private->plug_window),
498 if (hints.flags & PMinSize)
500 private->request_width = MAX (hints.min_width, 1);
501 private->request_height = MAX (hints.min_height, 1);
503 else if (hints.flags & PBaseSize)
505 private->request_width = MAX (hints.base_width, 1);
506 private->request_height = MAX (hints.base_height, 1);
509 private->have_size = TRUE;
511 gdk_error_trap_pop_ignored ();
515 gtk_socket_get_preferred_width (GtkWidget *widget,
519 GtkSocket *socket = GTK_SOCKET (widget);
520 GtkSocketPrivate *private = socket->priv;
522 if (private->plug_widget)
524 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
528 if (private->is_mapped && !private->have_size && private->plug_window)
529 gtk_socket_size_request (socket);
531 if (private->is_mapped && private->have_size)
532 *minimum = *natural = MAX (private->request_width, 1);
534 *minimum = *natural = 1;
539 gtk_socket_get_preferred_height (GtkWidget *widget,
543 GtkSocket *socket = GTK_SOCKET (widget);
544 GtkSocketPrivate *private = socket->priv;
546 if (private->plug_widget)
548 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
552 if (private->is_mapped && !private->have_size && private->plug_window)
553 gtk_socket_size_request (socket);
555 if (private->is_mapped && private->have_size)
556 *minimum = *natural = MAX (private->request_height, 1);
558 *minimum = *natural = 1;
563 gtk_socket_send_configure_event (GtkSocket *socket)
565 GtkAllocation allocation;
566 XConfigureEvent xconfigure;
569 g_return_if_fail (socket->priv->plug_window != NULL);
571 memset (&xconfigure, 0, sizeof (xconfigure));
572 xconfigure.type = ConfigureNotify;
574 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
575 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
577 /* The ICCCM says that synthetic events should have root relative
578 * coordinates. We still aren't really ICCCM compliant, since
579 * we don't send events when the real toplevel is moved.
581 gdk_error_trap_push ();
582 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
583 gdk_error_trap_pop_ignored ();
585 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
588 xconfigure.width = allocation.width;
589 xconfigure.height = allocation.height;
591 xconfigure.border_width = 0;
592 xconfigure.above = None;
593 xconfigure.override_redirect = False;
595 gdk_error_trap_push ();
596 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
597 GDK_WINDOW_XID (socket->priv->plug_window),
598 False, NoEventMask, (XEvent *)&xconfigure);
599 gdk_error_trap_pop_ignored ();
603 gtk_socket_size_allocate (GtkWidget *widget,
604 GtkAllocation *allocation)
606 GtkSocket *socket = GTK_SOCKET (widget);
607 GtkSocketPrivate *private = socket->priv;
609 gtk_widget_set_allocation (widget, allocation);
610 if (gtk_widget_get_realized (widget))
612 gdk_window_move_resize (gtk_widget_get_window (widget),
613 allocation->x, allocation->y,
614 allocation->width, allocation->height);
616 if (private->plug_widget)
618 GtkAllocation child_allocation;
620 child_allocation.x = 0;
621 child_allocation.y = 0;
622 child_allocation.width = allocation->width;
623 child_allocation.height = allocation->height;
625 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
627 else if (private->plug_window)
629 gdk_error_trap_push ();
631 if (allocation->width != private->current_width ||
632 allocation->height != private->current_height)
634 gdk_window_move_resize (private->plug_window,
636 allocation->width, allocation->height);
637 if (private->resize_count)
638 private->resize_count--;
640 GTK_NOTE (PLUGSOCKET,
641 g_message ("GtkSocket - allocated: %d %d",
642 allocation->width, allocation->height));
643 private->current_width = allocation->width;
644 private->current_height = allocation->height;
647 if (private->need_map)
649 gdk_window_show (private->plug_window);
650 private->need_map = FALSE;
653 while (private->resize_count)
655 gtk_socket_send_configure_event (socket);
656 private->resize_count--;
657 GTK_NOTE (PLUGSOCKET,
658 g_message ("GtkSocket - sending synthetic configure: %d %d",
659 allocation->width, allocation->height));
662 gdk_error_trap_pop_ignored ();
668 gtk_socket_send_key_event (GtkSocket *socket,
670 gboolean mask_key_presses)
673 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
675 memset (&xkey, 0, sizeof (xkey));
676 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
677 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
678 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
679 xkey.subwindow = None;
680 xkey.time = gdk_event->key.time;
685 xkey.state = gdk_event->key.state;
686 xkey.keycode = gdk_event->key.hardware_keycode;
687 xkey.same_screen = True;/* FIXME ? */
689 gdk_error_trap_push ();
690 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
691 GDK_WINDOW_XID (socket->priv->plug_window),
693 (mask_key_presses ? KeyPressMask : NoEventMask),
695 gdk_error_trap_pop_ignored ();
699 activate_key (GtkAccelGroup *accel_group,
700 GObject *acceleratable,
702 GdkModifierType accel_mods,
703 GrabbedKey *grabbed_key)
705 GdkEvent *gdk_event = gtk_get_current_event ();
707 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
708 gboolean retval = FALSE;
710 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
712 gtk_socket_send_key_event (socket, gdk_event, FALSE);
717 gdk_event_free (gdk_event);
723 find_accel_key (GtkAccelKey *key,
727 GrabbedKey *grabbed_key = data;
729 return (key->accel_key == grabbed_key->accel_key &&
730 key->accel_mods == grabbed_key->accel_mods);
734 * gtk_socket_add_grabbed_key:
736 * @socket: a #GtkSocket
738 * @modifiers: modifiers for the key
740 * Called from the GtkSocket platform-specific backend when the
741 * corresponding plug has told the socket to grab a key.
744 gtk_socket_add_grabbed_key (GtkSocket *socket,
746 GdkModifierType modifiers)
749 GrabbedKey *grabbed_key;
751 grabbed_key = g_new (GrabbedKey, 1);
753 grabbed_key->accel_key = keyval;
754 grabbed_key->accel_mods = modifiers;
756 if (gtk_accel_group_find (socket->priv->accel_group,
760 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
762 g_free (grabbed_key);
766 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
768 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
773 * gtk_socket_remove_grabbed_key:
775 * @socket: a #GtkSocket
777 * @modifiers: modifiers for the key
779 * Called from the GtkSocket backend when the corresponding plug has
780 * told the socket to remove a key grab.
783 gtk_socket_remove_grabbed_key (GtkSocket *socket,
785 GdkModifierType modifiers)
787 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
788 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
793 socket_update_focus_in (GtkSocket *socket)
795 GtkSocketPrivate *private = socket->priv;
796 gboolean focus_in = FALSE;
798 if (private->plug_window)
800 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
802 if (gtk_widget_is_toplevel (toplevel) &&
803 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
804 gtk_widget_is_focus (GTK_WIDGET (socket)))
808 if (focus_in != private->focus_in)
810 private->focus_in = focus_in;
813 _gtk_xembed_send_focus_message (private->plug_window,
814 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
816 _gtk_xembed_send_message (private->plug_window,
817 XEMBED_FOCUS_OUT, 0, 0, 0);
822 socket_update_active (GtkSocket *socket)
824 GtkSocketPrivate *private = socket->priv;
825 gboolean active = FALSE;
827 if (private->plug_window)
829 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
831 if (gtk_widget_is_toplevel (toplevel) &&
832 gtk_window_is_active (GTK_WINDOW (toplevel)))
836 if (active != private->active)
838 private->active = active;
840 _gtk_xembed_send_message (private->plug_window,
841 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
847 gtk_socket_hierarchy_changed (GtkWidget *widget,
848 GtkWidget *old_toplevel)
850 GtkSocket *socket = GTK_SOCKET (widget);
851 GtkSocketPrivate *private = socket->priv;
852 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
854 if (toplevel && !GTK_IS_WINDOW (toplevel))
857 if (toplevel != private->toplevel)
859 if (private->toplevel)
861 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
862 g_signal_handlers_disconnect_by_func (private->toplevel,
863 socket_update_focus_in,
865 g_signal_handlers_disconnect_by_func (private->toplevel,
866 socket_update_active,
870 private->toplevel = toplevel;
874 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
875 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
876 G_CALLBACK (socket_update_focus_in), socket);
877 g_signal_connect_swapped (private->toplevel, "notify::is-active",
878 G_CALLBACK (socket_update_active), socket);
881 socket_update_focus_in (socket);
882 socket_update_active (socket);
887 gtk_socket_grab_notify (GtkWidget *widget,
888 gboolean was_grabbed)
890 GtkSocket *socket = GTK_SOCKET (widget);
892 if (!socket->priv->same_app)
893 _gtk_xembed_send_message (socket->priv->plug_window,
894 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
899 gtk_socket_key_event (GtkWidget *widget,
902 GtkSocket *socket = GTK_SOCKET (widget);
903 GtkSocketPrivate *private = socket->priv;
905 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
907 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
916 gtk_socket_notify (GObject *object,
919 if (!strcmp (pspec->name, "is-focus"))
921 socket_update_focus_in (GTK_SOCKET (object));
925 * gtk_socket_claim_focus:
927 * @socket: a #GtkSocket
930 * Claims focus for the socket. XXX send_event?
933 gtk_socket_claim_focus (GtkSocket *socket,
936 GtkWidget *widget = GTK_WIDGET (socket);
937 GtkSocketPrivate *private = socket->priv;
940 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
942 /* Oh, the trickery... */
944 gtk_widget_set_can_focus (widget, TRUE);
945 gtk_widget_grab_focus (widget);
946 gtk_widget_set_can_focus (widget, FALSE);
950 gtk_socket_focus (GtkWidget *widget,
951 GtkDirectionType direction)
953 GtkSocket *socket = GTK_SOCKET (widget);
954 GtkSocketPrivate *private = socket->priv;
956 if (private->plug_widget)
957 return gtk_widget_child_focus (private->plug_widget, direction);
959 if (!gtk_widget_is_focus (widget))
967 case GTK_DIR_TAB_BACKWARD:
968 detail = XEMBED_FOCUS_LAST;
972 case GTK_DIR_TAB_FORWARD:
973 detail = XEMBED_FOCUS_FIRST;
977 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
978 gtk_socket_claim_focus (socket, FALSE);
987 gtk_socket_remove (GtkContainer *container,
990 GtkSocket *socket = GTK_SOCKET (container);
991 GtkSocketPrivate *private = socket->priv;
993 g_return_if_fail (child == private->plug_widget);
995 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
999 gtk_socket_forall (GtkContainer *container,
1000 gboolean include_internals,
1001 GtkCallback callback,
1002 gpointer callback_data)
1004 GtkSocket *socket = GTK_SOCKET (container);
1005 GtkSocketPrivate *private = socket->priv;
1007 if (private->plug_widget)
1008 (* callback) (private->plug_widget, callback_data);
1012 * gtk_socket_add_window:
1014 * @socket: a #GtkSocket
1015 * @xid: the native identifier for a window
1016 * @need_reparent: whether the socket's plug's window needs to be
1017 * reparented to the socket
1019 * Adds a window to a GtkSocket.
1022 gtk_socket_add_window (GtkSocket *socket,
1024 gboolean need_reparent)
1026 GtkWidget *widget = GTK_WIDGET (socket);
1027 GdkDisplay *display = gtk_widget_get_display (widget);
1028 gpointer user_data = NULL;
1029 GtkSocketPrivate *private = socket->priv;
1030 unsigned long version;
1031 unsigned long flags;
1033 if (GDK_IS_X11_DISPLAY (display))
1034 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1036 private->plug_window = NULL;
1038 if (private->plug_window)
1040 g_object_ref (private->plug_window);
1041 gdk_window_get_user_data (private->plug_window, &user_data);
1044 if (user_data) /* A widget's window in this process */
1046 GtkWidget *child_widget = user_data;
1048 if (!GTK_IS_PLUG (child_widget))
1050 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1051 private->plug_window = NULL;
1052 gdk_error_trap_pop_ignored ();
1057 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1059 else /* A foreign window */
1061 GdkDragProtocol protocol;
1063 gdk_error_trap_push ();
1065 if (!private->plug_window)
1067 if (GDK_IS_X11_DISPLAY (display))
1068 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1069 if (!private->plug_window) /* was deleted before we could get it */
1071 gdk_error_trap_pop_ignored ();
1076 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1077 GDK_WINDOW_XID (private->plug_window),
1078 StructureNotifyMask | PropertyChangeMask);
1080 if (gdk_error_trap_pop ())
1082 g_object_unref (private->plug_window);
1083 private->plug_window = NULL;
1087 /* OK, we now will reliably get destroy notification on socket->plug_window */
1089 gdk_error_trap_push ();
1093 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1094 gdk_window_reparent (private->plug_window,
1095 gtk_widget_get_window (widget),
1099 private->have_size = FALSE;
1101 private->xembed_version = -1;
1102 if (xembed_get_info (private->plug_window, &version, &flags))
1104 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1105 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1109 /* FIXME, we should probably actually check the state before we started */
1110 private->is_mapped = TRUE;
1113 private->need_map = private->is_mapped;
1115 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1117 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1120 gdk_error_trap_pop_ignored ();
1122 gdk_window_add_filter (private->plug_window,
1123 gtk_socket_filter_func,
1127 gdk_error_trap_push ();
1128 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1129 GDK_WINDOW_XID (private->plug_window),
1130 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1131 gdk_error_trap_pop_ignored ();
1133 _gtk_xembed_send_message (private->plug_window,
1134 XEMBED_EMBEDDED_NOTIFY, 0,
1135 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1136 private->xembed_version);
1138 socket_update_active (socket);
1139 socket_update_focus_in (socket);
1141 gtk_widget_queue_resize (GTK_WIDGET (socket));
1144 if (private->plug_window)
1145 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1149 * gtk_socket_handle_map_request:
1151 * @socket: a #GtkSocket
1153 * Called from the GtkSocket backend when the plug has been mapped.
1156 gtk_socket_handle_map_request (GtkSocket *socket)
1158 GtkSocketPrivate *private = socket->priv;
1159 if (!private->is_mapped)
1161 private->is_mapped = TRUE;
1162 private->need_map = TRUE;
1164 gtk_widget_queue_resize (GTK_WIDGET (socket));
1169 * gtk_socket_unmap_notify:
1171 * @socket: a #GtkSocket
1173 * Called from the GtkSocket backend when the plug has been unmapped ???
1176 gtk_socket_unmap_notify (GtkSocket *socket)
1178 GtkSocketPrivate *private = socket->priv;
1179 if (private->is_mapped)
1181 private->is_mapped = FALSE;
1182 gtk_widget_queue_resize (GTK_WIDGET (socket));
1187 * gtk_socket_advance_toplevel_focus:
1189 * @socket: a #GtkSocket
1190 * @direction: a direction
1192 * Called from the GtkSocket backend when the corresponding plug
1193 * has told the socket to move the focus.
1196 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1197 GtkDirectionType direction)
1201 GtkContainer *container;
1203 GtkWidget *focus_widget;
1204 GtkWidget *toplevel;
1205 GtkWidget *old_focus_child;
1208 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1212 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1214 gtk_widget_child_focus (toplevel,direction);
1218 container = GTK_CONTAINER (toplevel);
1219 window = GTK_WINDOW (toplevel);
1220 bin = GTK_BIN (toplevel);
1222 /* This is a copy of gtk_window_focus(), modified so that we
1223 * can detect wrap-around.
1225 old_focus_child = gtk_container_get_focus_child (container);
1227 if (old_focus_child)
1229 if (gtk_widget_child_focus (old_focus_child, direction))
1232 /* We are allowed exactly one wrap-around per sequence of focus
1235 if (_gtk_xembed_get_focus_wrapped ())
1238 _gtk_xembed_set_focus_wrapped ();
1241 focus_widget = gtk_window_get_focus (window);
1244 /* Wrapped off the end, clear the focus setting for the toplevel */
1245 parent = gtk_widget_get_parent (focus_widget);
1248 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1249 parent = gtk_widget_get_parent (parent);
1252 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1255 /* Now try to focus the first widget in the window */
1256 child = gtk_bin_get_child (bin);
1259 if (gtk_widget_child_focus (child, direction))
1265 xembed_get_info (GdkWindow *window,
1266 unsigned long *version,
1267 unsigned long *flags)
1269 GdkDisplay *display = gdk_window_get_display (window);
1270 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1273 unsigned long nitems, bytes_after;
1274 unsigned char *data;
1275 unsigned long *data_long;
1278 gdk_error_trap_push ();
1279 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1280 GDK_WINDOW_XID (window),
1283 xembed_info_atom, &type, &format,
1284 &nitems, &bytes_after, &data);
1285 gdk_error_trap_pop_ignored ();
1287 if (status != Success)
1288 return FALSE; /* Window vanished? */
1290 if (type == None) /* No info property */
1293 if (type != xembed_info_atom)
1295 g_warning ("_XEMBED_INFO property has wrong type\n");
1301 g_warning ("_XEMBED_INFO too short\n");
1306 data_long = (unsigned long *)data;
1308 *version = data_long[0];
1310 *flags = data_long[1] & XEMBED_MAPPED;
1317 handle_xembed_message (GtkSocket *socket,
1318 XEmbedMessageType message,
1324 GTK_NOTE (PLUGSOCKET,
1325 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1329 case XEMBED_EMBEDDED_NOTIFY:
1330 case XEMBED_WINDOW_ACTIVATE:
1331 case XEMBED_WINDOW_DEACTIVATE:
1332 case XEMBED_MODALITY_ON:
1333 case XEMBED_MODALITY_OFF:
1334 case XEMBED_FOCUS_IN:
1335 case XEMBED_FOCUS_OUT:
1336 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1339 case XEMBED_REQUEST_FOCUS:
1340 gtk_socket_claim_focus (socket, TRUE);
1343 case XEMBED_FOCUS_NEXT:
1344 case XEMBED_FOCUS_PREV:
1345 gtk_socket_advance_toplevel_focus (socket,
1346 (message == XEMBED_FOCUS_NEXT ?
1347 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1350 case XEMBED_GTK_GRAB_KEY:
1351 gtk_socket_add_grabbed_key (socket, data1, data2);
1353 case XEMBED_GTK_UNGRAB_KEY:
1354 gtk_socket_remove_grabbed_key (socket, data1, data2);
1357 case XEMBED_GRAB_KEY:
1358 case XEMBED_UNGRAB_KEY:
1362 GTK_NOTE (PLUGSOCKET,
1363 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1368 static GdkFilterReturn
1369 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1375 GdkDisplay *display;
1377 GtkSocketPrivate *private;
1379 GdkFilterReturn return_val;
1381 socket = GTK_SOCKET (data);
1382 private = socket->priv;
1384 return_val = GDK_FILTER_CONTINUE;
1386 if (private->plug_widget)
1389 widget = GTK_WIDGET (socket);
1390 xevent = (XEvent *)gdk_xevent;
1391 display = gtk_widget_get_display (widget);
1393 switch (xevent->type)
1396 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1398 _gtk_xembed_push_message (xevent);
1399 handle_xembed_message (socket,
1400 xevent->xclient.data.l[1],
1401 xevent->xclient.data.l[2],
1402 xevent->xclient.data.l[3],
1403 xevent->xclient.data.l[4],
1404 xevent->xclient.data.l[0]);
1405 _gtk_xembed_pop_message ();
1407 return_val = GDK_FILTER_REMOVE;
1413 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1415 if (!private->plug_window)
1417 gtk_socket_add_window (socket, xcwe->window, FALSE);
1419 if (private->plug_window)
1421 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1425 return_val = GDK_FILTER_REMOVE;
1430 case ConfigureRequest:
1432 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1434 if (!private->plug_window)
1435 gtk_socket_add_window (socket, xcre->window, FALSE);
1437 if (private->plug_window)
1439 if (xcre->value_mask & (CWWidth | CWHeight))
1441 GTK_NOTE (PLUGSOCKET,
1442 g_message ("GtkSocket - configure request: %d %d",
1443 private->request_width,
1444 private->request_height));
1446 private->resize_count++;
1447 gtk_widget_queue_resize (widget);
1449 else if (xcre->value_mask & (CWX | CWY))
1451 gtk_socket_send_configure_event (socket);
1453 /* Ignore stacking requests. */
1455 return_val = GDK_FILTER_REMOVE;
1462 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1464 /* Note that we get destroy notifies both from SubstructureNotify on
1465 * our window and StructureNotify on socket->plug_window
1467 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1471 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1473 gdk_window_destroy_notify (private->plug_window);
1474 gtk_socket_end_embedding (socket);
1476 g_object_ref (widget);
1477 g_signal_emit_by_name (widget, "plug-removed", &result);
1479 gtk_widget_destroy (widget);
1480 g_object_unref (widget);
1482 return_val = GDK_FILTER_REMOVE;
1488 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1490 gtk_socket_claim_focus (socket, TRUE);
1492 return_val = GDK_FILTER_REMOVE;
1495 return_val = GDK_FILTER_REMOVE;
1498 if (!private->plug_window)
1500 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1503 if (private->plug_window)
1505 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1507 gtk_socket_handle_map_request (socket);
1508 return_val = GDK_FILTER_REMOVE;
1511 case PropertyNotify:
1512 if (private->plug_window &&
1513 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1515 GdkDragProtocol protocol;
1517 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1519 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1520 private->have_size = FALSE;
1521 gtk_widget_queue_resize (widget);
1522 return_val = GDK_FILTER_REMOVE;
1524 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1525 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1527 gdk_error_trap_push ();
1528 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1530 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1531 private->plug_window,
1534 gdk_error_trap_pop_ignored ();
1535 return_val = GDK_FILTER_REMOVE;
1537 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1539 unsigned long flags;
1541 if (xembed_get_info (private->plug_window, NULL, &flags))
1543 gboolean was_mapped = private->is_mapped;
1544 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1546 if (was_mapped != is_mapped)
1549 gtk_socket_handle_map_request (socket);
1552 gdk_error_trap_push ();
1553 gdk_window_show (private->plug_window);
1554 gdk_error_trap_pop_ignored ();
1556 gtk_socket_unmap_notify (socket);
1560 return_val = GDK_FILTER_REMOVE;
1564 case ReparentNotify:
1567 XReparentEvent *xre = &xevent->xreparent;
1569 window = gtk_widget_get_window (widget);
1571 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1572 if (!private->plug_window &&
1573 xre->parent == GDK_WINDOW_XID (window))
1575 gtk_socket_add_window (socket, xre->window, FALSE);
1577 if (private->plug_window)
1579 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1582 return_val = GDK_FILTER_REMOVE;
1586 if (private->plug_window &&
1587 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1588 xre->parent != GDK_WINDOW_XID (window))
1592 gtk_socket_end_embedding (socket);
1594 g_object_ref (widget);
1595 g_signal_emit_by_name (widget, "plug-removed", &result);
1597 gtk_widget_destroy (widget);
1598 g_object_unref (widget);
1600 return_val = GDK_FILTER_REMOVE;
1607 if (private->plug_window &&
1608 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1610 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1612 gtk_socket_unmap_notify (socket);
1613 return_val = GDK_FILTER_REMOVE;