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 "gtkmarshalers.h"
35 #include "gtksizerequest.h"
37 #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") == 0)
920 socket_update_focus_in (GTK_SOCKET (object));
924 * gtk_socket_claim_focus:
926 * @socket: a #GtkSocket
929 * Claims focus for the socket. XXX send_event?
932 gtk_socket_claim_focus (GtkSocket *socket,
935 GtkWidget *widget = GTK_WIDGET (socket);
936 GtkSocketPrivate *private = socket->priv;
939 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
941 /* Oh, the trickery... */
943 gtk_widget_set_can_focus (widget, TRUE);
944 gtk_widget_grab_focus (widget);
945 gtk_widget_set_can_focus (widget, FALSE);
949 gtk_socket_focus (GtkWidget *widget,
950 GtkDirectionType direction)
952 GtkSocket *socket = GTK_SOCKET (widget);
953 GtkSocketPrivate *private = socket->priv;
955 if (private->plug_widget)
956 return gtk_widget_child_focus (private->plug_widget, direction);
958 if (!gtk_widget_is_focus (widget))
966 case GTK_DIR_TAB_BACKWARD:
967 detail = XEMBED_FOCUS_LAST;
971 case GTK_DIR_TAB_FORWARD:
972 detail = XEMBED_FOCUS_FIRST;
976 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
977 gtk_socket_claim_focus (socket, FALSE);
986 gtk_socket_remove (GtkContainer *container,
989 GtkSocket *socket = GTK_SOCKET (container);
990 GtkSocketPrivate *private = socket->priv;
992 g_return_if_fail (child == private->plug_widget);
994 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
998 gtk_socket_forall (GtkContainer *container,
999 gboolean include_internals,
1000 GtkCallback callback,
1001 gpointer callback_data)
1003 GtkSocket *socket = GTK_SOCKET (container);
1004 GtkSocketPrivate *private = socket->priv;
1006 if (private->plug_widget)
1007 (* callback) (private->plug_widget, callback_data);
1011 * gtk_socket_add_window:
1013 * @socket: a #GtkSocket
1014 * @xid: the native identifier for a window
1015 * @need_reparent: whether the socket's plug's window needs to be
1016 * reparented to the socket
1018 * Adds a window to a GtkSocket.
1021 gtk_socket_add_window (GtkSocket *socket,
1023 gboolean need_reparent)
1025 GtkWidget *widget = GTK_WIDGET (socket);
1026 GdkDisplay *display = gtk_widget_get_display (widget);
1027 gpointer user_data = NULL;
1028 GtkSocketPrivate *private = socket->priv;
1029 unsigned long version;
1030 unsigned long flags;
1032 if (GDK_IS_X11_DISPLAY (display))
1033 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1035 private->plug_window = NULL;
1037 if (private->plug_window)
1039 g_object_ref (private->plug_window);
1040 gdk_window_get_user_data (private->plug_window, &user_data);
1043 if (user_data) /* A widget's window in this process */
1045 GtkWidget *child_widget = user_data;
1047 if (!GTK_IS_PLUG (child_widget))
1049 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1050 private->plug_window = NULL;
1051 gdk_error_trap_pop_ignored ();
1056 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1058 else /* A foreign window */
1060 GdkDragProtocol protocol;
1062 gdk_error_trap_push ();
1064 if (!private->plug_window)
1066 if (GDK_IS_X11_DISPLAY (display))
1067 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1068 if (!private->plug_window) /* was deleted before we could get it */
1070 gdk_error_trap_pop_ignored ();
1075 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1076 GDK_WINDOW_XID (private->plug_window),
1077 StructureNotifyMask | PropertyChangeMask);
1079 if (gdk_error_trap_pop ())
1081 g_object_unref (private->plug_window);
1082 private->plug_window = NULL;
1086 /* OK, we now will reliably get destroy notification on socket->plug_window */
1088 gdk_error_trap_push ();
1092 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1093 gdk_window_reparent (private->plug_window,
1094 gtk_widget_get_window (widget),
1098 private->have_size = FALSE;
1100 private->xembed_version = -1;
1101 if (xembed_get_info (private->plug_window, &version, &flags))
1103 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1104 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1108 /* FIXME, we should probably actually check the state before we started */
1109 private->is_mapped = TRUE;
1112 private->need_map = private->is_mapped;
1114 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1116 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1119 gdk_error_trap_pop_ignored ();
1121 gdk_window_add_filter (private->plug_window,
1122 gtk_socket_filter_func,
1126 gdk_error_trap_push ();
1127 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1128 GDK_WINDOW_XID (private->plug_window),
1129 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1130 gdk_error_trap_pop_ignored ();
1132 _gtk_xembed_send_message (private->plug_window,
1133 XEMBED_EMBEDDED_NOTIFY, 0,
1134 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1135 private->xembed_version);
1137 socket_update_active (socket);
1138 socket_update_focus_in (socket);
1140 gtk_widget_queue_resize (GTK_WIDGET (socket));
1143 if (private->plug_window)
1144 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1148 * gtk_socket_handle_map_request:
1150 * @socket: a #GtkSocket
1152 * Called from the GtkSocket backend when the plug has been mapped.
1155 gtk_socket_handle_map_request (GtkSocket *socket)
1157 GtkSocketPrivate *private = socket->priv;
1158 if (!private->is_mapped)
1160 private->is_mapped = TRUE;
1161 private->need_map = TRUE;
1163 gtk_widget_queue_resize (GTK_WIDGET (socket));
1168 * gtk_socket_unmap_notify:
1170 * @socket: a #GtkSocket
1172 * Called from the GtkSocket backend when the plug has been unmapped ???
1175 gtk_socket_unmap_notify (GtkSocket *socket)
1177 GtkSocketPrivate *private = socket->priv;
1178 if (private->is_mapped)
1180 private->is_mapped = FALSE;
1181 gtk_widget_queue_resize (GTK_WIDGET (socket));
1186 * gtk_socket_advance_toplevel_focus:
1188 * @socket: a #GtkSocket
1189 * @direction: a direction
1191 * Called from the GtkSocket backend when the corresponding plug
1192 * has told the socket to move the focus.
1195 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1196 GtkDirectionType direction)
1200 GtkContainer *container;
1202 GtkWidget *focus_widget;
1203 GtkWidget *toplevel;
1204 GtkWidget *old_focus_child;
1207 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1211 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1213 gtk_widget_child_focus (toplevel,direction);
1217 container = GTK_CONTAINER (toplevel);
1218 window = GTK_WINDOW (toplevel);
1219 bin = GTK_BIN (toplevel);
1221 /* This is a copy of gtk_window_focus(), modified so that we
1222 * can detect wrap-around.
1224 old_focus_child = gtk_container_get_focus_child (container);
1226 if (old_focus_child)
1228 if (gtk_widget_child_focus (old_focus_child, direction))
1231 /* We are allowed exactly one wrap-around per sequence of focus
1234 if (_gtk_xembed_get_focus_wrapped ())
1237 _gtk_xembed_set_focus_wrapped ();
1240 focus_widget = gtk_window_get_focus (window);
1243 /* Wrapped off the end, clear the focus setting for the toplevel */
1244 parent = gtk_widget_get_parent (focus_widget);
1247 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1248 parent = gtk_widget_get_parent (parent);
1251 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1254 /* Now try to focus the first widget in the window */
1255 child = gtk_bin_get_child (bin);
1258 if (gtk_widget_child_focus (child, direction))
1264 xembed_get_info (GdkWindow *window,
1265 unsigned long *version,
1266 unsigned long *flags)
1268 GdkDisplay *display = gdk_window_get_display (window);
1269 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1272 unsigned long nitems, bytes_after;
1273 unsigned char *data;
1274 unsigned long *data_long;
1277 gdk_error_trap_push ();
1278 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1279 GDK_WINDOW_XID (window),
1282 xembed_info_atom, &type, &format,
1283 &nitems, &bytes_after, &data);
1284 gdk_error_trap_pop_ignored ();
1286 if (status != Success)
1287 return FALSE; /* Window vanished? */
1289 if (type == None) /* No info property */
1292 if (type != xembed_info_atom)
1294 g_warning ("_XEMBED_INFO property has wrong type\n");
1300 g_warning ("_XEMBED_INFO too short\n");
1305 data_long = (unsigned long *)data;
1307 *version = data_long[0];
1309 *flags = data_long[1] & XEMBED_MAPPED;
1316 handle_xembed_message (GtkSocket *socket,
1317 XEmbedMessageType message,
1323 GTK_NOTE (PLUGSOCKET,
1324 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1328 case XEMBED_EMBEDDED_NOTIFY:
1329 case XEMBED_WINDOW_ACTIVATE:
1330 case XEMBED_WINDOW_DEACTIVATE:
1331 case XEMBED_MODALITY_ON:
1332 case XEMBED_MODALITY_OFF:
1333 case XEMBED_FOCUS_IN:
1334 case XEMBED_FOCUS_OUT:
1335 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1338 case XEMBED_REQUEST_FOCUS:
1339 gtk_socket_claim_focus (socket, TRUE);
1342 case XEMBED_FOCUS_NEXT:
1343 case XEMBED_FOCUS_PREV:
1344 gtk_socket_advance_toplevel_focus (socket,
1345 (message == XEMBED_FOCUS_NEXT ?
1346 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1349 case XEMBED_GTK_GRAB_KEY:
1350 gtk_socket_add_grabbed_key (socket, data1, data2);
1352 case XEMBED_GTK_UNGRAB_KEY:
1353 gtk_socket_remove_grabbed_key (socket, data1, data2);
1356 case XEMBED_GRAB_KEY:
1357 case XEMBED_UNGRAB_KEY:
1361 GTK_NOTE (PLUGSOCKET,
1362 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1367 static GdkFilterReturn
1368 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1374 GdkDisplay *display;
1376 GtkSocketPrivate *private;
1378 GdkFilterReturn return_val;
1380 socket = GTK_SOCKET (data);
1381 private = socket->priv;
1383 return_val = GDK_FILTER_CONTINUE;
1385 if (private->plug_widget)
1388 widget = GTK_WIDGET (socket);
1389 xevent = (XEvent *)gdk_xevent;
1390 display = gtk_widget_get_display (widget);
1392 switch (xevent->type)
1395 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1397 _gtk_xembed_push_message (xevent);
1398 handle_xembed_message (socket,
1399 xevent->xclient.data.l[1],
1400 xevent->xclient.data.l[2],
1401 xevent->xclient.data.l[3],
1402 xevent->xclient.data.l[4],
1403 xevent->xclient.data.l[0]);
1404 _gtk_xembed_pop_message ();
1406 return_val = GDK_FILTER_REMOVE;
1412 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1414 if (!private->plug_window)
1416 gtk_socket_add_window (socket, xcwe->window, FALSE);
1418 if (private->plug_window)
1420 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1424 return_val = GDK_FILTER_REMOVE;
1429 case ConfigureRequest:
1431 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1433 if (!private->plug_window)
1434 gtk_socket_add_window (socket, xcre->window, FALSE);
1436 if (private->plug_window)
1438 if (xcre->value_mask & (CWWidth | CWHeight))
1440 GTK_NOTE (PLUGSOCKET,
1441 g_message ("GtkSocket - configure request: %d %d",
1442 private->request_width,
1443 private->request_height));
1445 private->resize_count++;
1446 gtk_widget_queue_resize (widget);
1448 else if (xcre->value_mask & (CWX | CWY))
1450 gtk_socket_send_configure_event (socket);
1452 /* Ignore stacking requests. */
1454 return_val = GDK_FILTER_REMOVE;
1461 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1463 /* Note that we get destroy notifies both from SubstructureNotify on
1464 * our window and StructureNotify on socket->plug_window
1466 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1470 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1472 gdk_window_destroy_notify (private->plug_window);
1473 gtk_socket_end_embedding (socket);
1475 g_object_ref (widget);
1476 g_signal_emit_by_name (widget, "plug-removed", &result);
1478 gtk_widget_destroy (widget);
1479 g_object_unref (widget);
1481 return_val = GDK_FILTER_REMOVE;
1487 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1489 gtk_socket_claim_focus (socket, TRUE);
1491 return_val = GDK_FILTER_REMOVE;
1494 return_val = GDK_FILTER_REMOVE;
1497 if (!private->plug_window)
1499 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1502 if (private->plug_window)
1504 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1506 gtk_socket_handle_map_request (socket);
1507 return_val = GDK_FILTER_REMOVE;
1510 case PropertyNotify:
1511 if (private->plug_window &&
1512 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1514 GdkDragProtocol protocol;
1516 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1518 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1519 private->have_size = FALSE;
1520 gtk_widget_queue_resize (widget);
1521 return_val = GDK_FILTER_REMOVE;
1523 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1524 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1526 gdk_error_trap_push ();
1527 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1529 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1530 private->plug_window,
1533 gdk_error_trap_pop_ignored ();
1534 return_val = GDK_FILTER_REMOVE;
1536 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1538 unsigned long flags;
1540 if (xembed_get_info (private->plug_window, NULL, &flags))
1542 gboolean was_mapped = private->is_mapped;
1543 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1545 if (was_mapped != is_mapped)
1548 gtk_socket_handle_map_request (socket);
1551 gdk_error_trap_push ();
1552 gdk_window_show (private->plug_window);
1553 gdk_error_trap_pop_ignored ();
1555 gtk_socket_unmap_notify (socket);
1559 return_val = GDK_FILTER_REMOVE;
1563 case ReparentNotify:
1566 XReparentEvent *xre = &xevent->xreparent;
1568 window = gtk_widget_get_window (widget);
1570 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1571 if (!private->plug_window &&
1572 xre->parent == GDK_WINDOW_XID (window))
1574 gtk_socket_add_window (socket, xre->window, FALSE);
1576 if (private->plug_window)
1578 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1581 return_val = GDK_FILTER_REMOVE;
1585 if (private->plug_window &&
1586 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1587 xre->parent != GDK_WINDOW_XID (window))
1591 gtk_socket_end_embedding (socket);
1593 g_object_ref (widget);
1594 g_signal_emit_by_name (widget, "plug-removed", &result);
1596 gtk_widget_destroy (widget);
1597 g_object_unref (widget);
1599 return_val = GDK_FILTER_REMOVE;
1606 if (private->plug_window &&
1607 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1609 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1611 gtk_socket_unmap_notify (socket);
1612 return_val = GDK_FILTER_REMOVE;