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, see <http://www.gnu.org/licenses/>.Free
18 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
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/.
29 #include "gtksocketprivate.h"
33 #include "gtkmarshalers.h"
34 #include "gtksizerequest.h"
36 #include "gtkprivate.h"
41 #include "gtkwidgetprivate.h"
44 #include <gdk/gdkprivate.h>
47 #include <X11/extensions/Xfixes.h>
50 #include "gtkxembed.h"
55 * @Short_description: Container for widgets from other processes
57 * @include: gtk/gtkx.h
58 * @See_also: #GtkPlug, <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
60 * Together with #GtkPlug, #GtkSocket provides the ability to embed
61 * widgets from one process into another process in a fashion that
62 * is transparent to the user. One process creates a #GtkSocket widget
63 * and passes that widget's window ID to the other process, which then
64 * creates a #GtkPlug with that window ID. Any widgets contained in the
65 * #GtkPlug then will appear inside the first application's window.
67 * The socket's window ID is obtained by using gtk_socket_get_id().
68 * Before using this function, the socket must have been realized,
69 * and for hence, have been added to its parent.
72 * <title>Obtaining the window ID of a socket.</title>
74 * GtkWidget *socket = gtk_socket_new (<!-- -->);
75 * gtk_widget_show (socket);
76 * gtk_container_add (GTK_CONTAINER (parent), socket);
78 * /* The following call is only necessary if one of
79 * * the ancestors of the socket is not yet visible.
81 * gtk_widget_realize (socket);
82 * g_print ("The ID of the sockets window is %#x\n",
83 * gtk_socket_get_id (socket));
87 * Note that if you pass the window ID of the socket to another
88 * process that will create a plug in the socket, you must make
89 * sure that the socket widget is not destroyed until that plug
90 * is created. Violating this rule will cause unpredictable
91 * consequences, the most likely consequence being that the plug
92 * will appear as a separate toplevel window. You can check if
93 * the plug has been created by using gtk_socket_get_plug_window().
94 * If it returns a non-%NULL value, then the plug has been
95 * successfully created inside of the socket.
97 * When GTK+ is notified that the embedded window has been destroyed,
98 * then it will destroy the socket as well. You should always,
99 * therefore, be prepared for your sockets to be destroyed at any
100 * time when the main event loop is running. To prevent this from
101 * happening, you can connect to the #GtkSocket::plug-removed signal.
103 * The communication between a #GtkSocket and a #GtkPlug follows the
104 * <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
105 * protocol. This protocol has also been implemented in other toolkits,
106 * e.g. <application>Qt</application>, allowing the same level of
107 * integration when embedding a <application>Qt</application> widget
108 * in GTK or vice versa.
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 <filename>gtk/gtkx.h</filename>
119 /* Forward declararations */
121 static void gtk_socket_finalize (GObject *object);
122 static void gtk_socket_notify (GObject *object,
124 static void gtk_socket_realize (GtkWidget *widget);
125 static void gtk_socket_unrealize (GtkWidget *widget);
126 static void gtk_socket_get_preferred_width (GtkWidget *widget,
129 static void gtk_socket_get_preferred_height (GtkWidget *widget,
132 static void gtk_socket_size_allocate (GtkWidget *widget,
133 GtkAllocation *allocation);
134 static void gtk_socket_hierarchy_changed (GtkWidget *widget,
135 GtkWidget *old_toplevel);
136 static void gtk_socket_grab_notify (GtkWidget *widget,
137 gboolean was_grabbed);
138 static gboolean gtk_socket_key_event (GtkWidget *widget,
140 static gboolean gtk_socket_focus (GtkWidget *widget,
141 GtkDirectionType direction);
142 static void gtk_socket_remove (GtkContainer *container,
144 static void gtk_socket_forall (GtkContainer *container,
145 gboolean include_internals,
146 GtkCallback callback,
147 gpointer callback_data);
148 static void gtk_socket_add_window (GtkSocket *socket,
150 gboolean need_reparent);
151 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
155 static gboolean xembed_get_info (GdkWindow *gdk_window,
156 unsigned long *version,
157 unsigned long *flags);
160 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
168 GdkModifierType accel_mods;
177 static guint socket_signals[LAST_SIGNAL] = { 0 };
179 G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
182 gtk_socket_finalize (GObject *object)
184 GtkSocket *socket = GTK_SOCKET (object);
185 GtkSocketPrivate *priv = socket->priv;
187 g_object_unref (priv->accel_group);
189 G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
193 gtk_socket_class_init (GtkSocketClass *class)
195 GtkWidgetClass *widget_class;
196 GtkContainerClass *container_class;
197 GObjectClass *gobject_class;
199 gobject_class = (GObjectClass *) class;
200 widget_class = (GtkWidgetClass*) class;
201 container_class = (GtkContainerClass*) class;
203 gobject_class->finalize = gtk_socket_finalize;
204 gobject_class->notify = gtk_socket_notify;
206 widget_class->realize = gtk_socket_realize;
207 widget_class->unrealize = gtk_socket_unrealize;
208 widget_class->get_preferred_width = gtk_socket_get_preferred_width;
209 widget_class->get_preferred_height = gtk_socket_get_preferred_height;
210 widget_class->size_allocate = gtk_socket_size_allocate;
211 widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
212 widget_class->grab_notify = gtk_socket_grab_notify;
213 widget_class->key_press_event = gtk_socket_key_event;
214 widget_class->key_release_event = gtk_socket_key_event;
215 widget_class->focus = gtk_socket_focus;
217 /* We don't want to show_all the in-process plug, if any.
219 widget_class->show_all = gtk_widget_show;
221 container_class->remove = gtk_socket_remove;
222 container_class->forall = gtk_socket_forall;
225 * GtkSocket::plug-added:
226 * @socket_: the object which received the signal
228 * This signal is emitted when a client is successfully
229 * added to the socket.
231 socket_signals[PLUG_ADDED] =
232 g_signal_new (I_("plug-added"),
233 G_OBJECT_CLASS_TYPE (class),
235 G_STRUCT_OFFSET (GtkSocketClass, plug_added),
237 _gtk_marshal_VOID__VOID,
241 * GtkSocket::plug-removed:
242 * @socket_: the object which received the signal
244 * This signal is emitted when a client is removed from the socket.
245 * The default action is to destroy the #GtkSocket widget, so if you
246 * want to reuse it you must add a signal handler that returns %TRUE.
248 * Return value: %TRUE to stop other handlers from being invoked.
250 socket_signals[PLUG_REMOVED] =
251 g_signal_new (I_("plug-removed"),
252 G_OBJECT_CLASS_TYPE (class),
254 G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
255 _gtk_boolean_handled_accumulator, NULL,
256 _gtk_marshal_BOOLEAN__VOID,
259 g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate));
263 gtk_socket_init (GtkSocket *socket)
265 GtkSocketPrivate *priv;
267 priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
271 priv->request_width = 0;
272 priv->request_height = 0;
273 priv->current_width = 0;
274 priv->current_height = 0;
276 priv->plug_window = NULL;
277 priv->plug_widget = NULL;
278 priv->focus_in = FALSE;
279 priv->have_size = FALSE;
280 priv->need_map = FALSE;
281 priv->active = FALSE;
283 priv->accel_group = gtk_accel_group_new ();
284 g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
290 * Create a new empty #GtkSocket.
292 * Return value: the new #GtkSocket.
295 gtk_socket_new (void)
299 socket = g_object_new (GTK_TYPE_SOCKET, NULL);
301 return GTK_WIDGET (socket);
306 * @socket_: a #GtkSocket
307 * @window: the Window of a client participating in the XEMBED protocol.
309 * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The
310 * client may be in the same process or in a different process.
312 * To embed a #GtkPlug in a #GtkSocket, you can either create the
313 * #GtkPlug with <literal>gtk_plug_new (0)</literal>, call
314 * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
315 * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
316 * window ID for the socket, and call gtk_plug_new() passing in that
319 * The #GtkSocket must have already be added into a toplevel window
320 * before you can make this call.
323 gtk_socket_add_id (GtkSocket *socket,
326 g_return_if_fail (GTK_IS_SOCKET (socket));
327 g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
329 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
330 gtk_widget_realize (GTK_WIDGET (socket));
332 gtk_socket_add_window (socket, window, TRUE);
337 * @socket_: a #GtkSocket.
339 * Gets the window ID of a #GtkSocket widget, which can then
340 * be used to create a client embedded inside the socket, for
341 * instance with gtk_plug_new().
343 * The #GtkSocket must have already be added into a toplevel window
344 * before you can make this call.
346 * Return value: the window ID for the socket
349 gtk_socket_get_id (GtkSocket *socket)
351 g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
352 g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
354 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
355 gtk_widget_realize (GTK_WIDGET (socket));
357 return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
361 * gtk_socket_get_plug_window:
362 * @socket_: a #GtkSocket.
364 * Retrieves the window of the plug. Use this to check if the plug has
365 * been created inside of the socket.
367 * Return value: (transfer none): the window of the plug if available, or %NULL
372 gtk_socket_get_plug_window (GtkSocket *socket)
374 g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);
376 return socket->priv->plug_window;
380 gtk_socket_realize (GtkWidget *widget)
382 GtkAllocation allocation;
384 GdkWindowAttr attributes;
385 XWindowAttributes xattrs;
386 gint attributes_mask;
388 gtk_widget_set_realized (widget, TRUE);
390 gtk_widget_get_allocation (widget, &allocation);
392 attributes.window_type = GDK_WINDOW_CHILD;
393 attributes.x = allocation.x;
394 attributes.y = allocation.y;
395 attributes.width = allocation.width;
396 attributes.height = allocation.height;
397 attributes.wclass = GDK_INPUT_OUTPUT;
398 attributes.visual = gtk_widget_get_visual (widget);
399 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
401 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
403 window = gdk_window_new (gtk_widget_get_parent_window (widget),
404 &attributes, attributes_mask);
405 gtk_widget_set_window (widget, window);
406 gtk_widget_register_window (widget, window);
408 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
411 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
412 GDK_WINDOW_XID (window),
415 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
416 for input on the socket with a mask of 0x0fffff (for god knows why)
417 which includes ButtonPressMask causing a BadAccess if someone else
418 also selects for this. As per the client-side windows merge we always
419 normally selects for button press so we can emulate it on client
420 side children that selects for button press. However, we don't need
421 this for GtkSocket, so we unselect it here, fixing the crashes in
423 XSelectInput (GDK_WINDOW_XDISPLAY (window),
424 GDK_WINDOW_XID (window),
425 (xattrs.your_event_mask & ~ButtonPressMask) |
426 SubstructureNotifyMask | SubstructureRedirectMask);
428 gdk_window_add_filter (window,
429 gtk_socket_filter_func,
432 /* We sync here so that we make sure that if the XID for
433 * our window is passed to another application, SubstructureRedirectMask
434 * will be set by the time the other app creates its window.
436 gdk_display_sync (gtk_widget_get_display (widget));
440 * gtk_socket_end_embedding:
441 * @socket: a #GtkSocket
443 * Called to end the embedding of a plug in the socket.
446 gtk_socket_end_embedding (GtkSocket *socket)
448 GtkSocketPrivate *private = socket->priv;
450 g_object_unref (private->plug_window);
451 private->plug_window = NULL;
452 private->current_width = 0;
453 private->current_height = 0;
454 private->resize_count = 0;
456 gtk_accel_group_disconnect (private->accel_group, NULL);
460 gtk_socket_unrealize (GtkWidget *widget)
462 GtkSocket *socket = GTK_SOCKET (widget);
463 GtkSocketPrivate *private = socket->priv;
465 gtk_widget_set_realized (widget, FALSE);
467 if (private->plug_widget)
469 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
471 else if (private->plug_window)
473 gtk_socket_end_embedding (socket);
476 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
480 gtk_socket_size_request (GtkSocket *socket)
482 GtkSocketPrivate *private = socket->priv;
486 gdk_error_trap_push ();
488 private->request_width = 1;
489 private->request_height = 1;
491 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
492 GDK_WINDOW_XID (private->plug_window),
495 if (hints.flags & PMinSize)
497 private->request_width = MAX (hints.min_width, 1);
498 private->request_height = MAX (hints.min_height, 1);
500 else if (hints.flags & PBaseSize)
502 private->request_width = MAX (hints.base_width, 1);
503 private->request_height = MAX (hints.base_height, 1);
506 private->have_size = TRUE;
508 gdk_error_trap_pop_ignored ();
512 gtk_socket_get_preferred_width (GtkWidget *widget,
516 GtkSocket *socket = GTK_SOCKET (widget);
517 GtkSocketPrivate *private = socket->priv;
519 if (private->plug_widget)
521 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
525 if (private->is_mapped && !private->have_size && private->plug_window)
526 gtk_socket_size_request (socket);
528 if (private->is_mapped && private->have_size)
529 *minimum = *natural = MAX (private->request_width, 1);
531 *minimum = *natural = 1;
536 gtk_socket_get_preferred_height (GtkWidget *widget,
540 GtkSocket *socket = GTK_SOCKET (widget);
541 GtkSocketPrivate *private = socket->priv;
543 if (private->plug_widget)
545 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
549 if (private->is_mapped && !private->have_size && private->plug_window)
550 gtk_socket_size_request (socket);
552 if (private->is_mapped && private->have_size)
553 *minimum = *natural = MAX (private->request_height, 1);
555 *minimum = *natural = 1;
560 gtk_socket_send_configure_event (GtkSocket *socket)
562 GtkAllocation allocation;
563 XConfigureEvent xconfigure;
566 g_return_if_fail (socket->priv->plug_window != NULL);
568 memset (&xconfigure, 0, sizeof (xconfigure));
569 xconfigure.type = ConfigureNotify;
571 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
572 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
574 /* The ICCCM says that synthetic events should have root relative
575 * coordinates. We still aren't really ICCCM compliant, since
576 * we don't send events when the real toplevel is moved.
578 gdk_error_trap_push ();
579 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
580 gdk_error_trap_pop_ignored ();
582 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
585 xconfigure.width = allocation.width;
586 xconfigure.height = allocation.height;
588 xconfigure.border_width = 0;
589 xconfigure.above = None;
590 xconfigure.override_redirect = False;
592 gdk_error_trap_push ();
593 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
594 GDK_WINDOW_XID (socket->priv->plug_window),
595 False, NoEventMask, (XEvent *)&xconfigure);
596 gdk_error_trap_pop_ignored ();
600 gtk_socket_size_allocate (GtkWidget *widget,
601 GtkAllocation *allocation)
603 GtkSocket *socket = GTK_SOCKET (widget);
604 GtkSocketPrivate *private = socket->priv;
606 gtk_widget_set_allocation (widget, allocation);
607 if (gtk_widget_get_realized (widget))
609 gdk_window_move_resize (gtk_widget_get_window (widget),
610 allocation->x, allocation->y,
611 allocation->width, allocation->height);
613 if (private->plug_widget)
615 GtkAllocation child_allocation;
617 child_allocation.x = 0;
618 child_allocation.y = 0;
619 child_allocation.width = allocation->width;
620 child_allocation.height = allocation->height;
622 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
624 else if (private->plug_window)
626 gdk_error_trap_push ();
628 if (allocation->width != private->current_width ||
629 allocation->height != private->current_height)
631 gdk_window_move_resize (private->plug_window,
633 allocation->width, allocation->height);
634 if (private->resize_count)
635 private->resize_count--;
637 GTK_NOTE (PLUGSOCKET,
638 g_message ("GtkSocket - allocated: %d %d",
639 allocation->width, allocation->height));
640 private->current_width = allocation->width;
641 private->current_height = allocation->height;
644 if (private->need_map)
646 gdk_window_show (private->plug_window);
647 private->need_map = FALSE;
650 while (private->resize_count)
652 gtk_socket_send_configure_event (socket);
653 private->resize_count--;
654 GTK_NOTE (PLUGSOCKET,
655 g_message ("GtkSocket - sending synthetic configure: %d %d",
656 allocation->width, allocation->height));
659 gdk_error_trap_pop_ignored ();
665 gtk_socket_send_key_event (GtkSocket *socket,
667 gboolean mask_key_presses)
670 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
672 memset (&xkey, 0, sizeof (xkey));
673 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
674 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
675 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
676 xkey.subwindow = None;
677 xkey.time = gdk_event->key.time;
682 xkey.state = gdk_event->key.state;
683 xkey.keycode = gdk_event->key.hardware_keycode;
684 xkey.same_screen = True;/* FIXME ? */
686 gdk_error_trap_push ();
687 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
688 GDK_WINDOW_XID (socket->priv->plug_window),
690 (mask_key_presses ? KeyPressMask : NoEventMask),
692 gdk_error_trap_pop_ignored ();
696 activate_key (GtkAccelGroup *accel_group,
697 GObject *acceleratable,
699 GdkModifierType accel_mods,
700 GrabbedKey *grabbed_key)
702 GdkEvent *gdk_event = gtk_get_current_event ();
704 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
705 gboolean retval = FALSE;
707 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
709 gtk_socket_send_key_event (socket, gdk_event, FALSE);
714 gdk_event_free (gdk_event);
720 find_accel_key (GtkAccelKey *key,
724 GrabbedKey *grabbed_key = data;
726 return (key->accel_key == grabbed_key->accel_key &&
727 key->accel_mods == grabbed_key->accel_mods);
731 * gtk_socket_add_grabbed_key:
732 * @socket: a #GtkSocket
734 * @modifiers: modifiers for the key
736 * Called from the GtkSocket platform-specific backend when the
737 * corresponding plug has told the socket to grab a key.
740 gtk_socket_add_grabbed_key (GtkSocket *socket,
742 GdkModifierType modifiers)
745 GrabbedKey *grabbed_key;
747 grabbed_key = g_new (GrabbedKey, 1);
749 grabbed_key->accel_key = keyval;
750 grabbed_key->accel_mods = modifiers;
752 if (gtk_accel_group_find (socket->priv->accel_group,
756 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
758 g_free (grabbed_key);
762 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
764 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
769 * gtk_socket_remove_grabbed_key:
770 * @socket: a #GtkSocket
772 * @modifiers: modifiers for the key
774 * Called from the GtkSocket backend when the corresponding plug has
775 * told the socket to remove a key grab.
778 gtk_socket_remove_grabbed_key (GtkSocket *socket,
780 GdkModifierType modifiers)
782 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
783 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
788 socket_update_focus_in (GtkSocket *socket)
790 GtkSocketPrivate *private = socket->priv;
791 gboolean focus_in = FALSE;
793 if (private->plug_window)
795 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
797 if (gtk_widget_is_toplevel (toplevel) &&
798 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
799 gtk_widget_is_focus (GTK_WIDGET (socket)))
803 if (focus_in != private->focus_in)
805 private->focus_in = focus_in;
808 _gtk_xembed_send_focus_message (private->plug_window,
809 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
811 _gtk_xembed_send_message (private->plug_window,
812 XEMBED_FOCUS_OUT, 0, 0, 0);
817 socket_update_active (GtkSocket *socket)
819 GtkSocketPrivate *private = socket->priv;
820 gboolean active = FALSE;
822 if (private->plug_window)
824 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
826 if (gtk_widget_is_toplevel (toplevel) &&
827 gtk_window_is_active (GTK_WINDOW (toplevel)))
831 if (active != private->active)
833 private->active = active;
835 _gtk_xembed_send_message (private->plug_window,
836 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
842 gtk_socket_hierarchy_changed (GtkWidget *widget,
843 GtkWidget *old_toplevel)
845 GtkSocket *socket = GTK_SOCKET (widget);
846 GtkSocketPrivate *private = socket->priv;
847 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
849 if (toplevel && !GTK_IS_WINDOW (toplevel))
852 if (toplevel != private->toplevel)
854 if (private->toplevel)
856 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
857 g_signal_handlers_disconnect_by_func (private->toplevel,
858 socket_update_focus_in,
860 g_signal_handlers_disconnect_by_func (private->toplevel,
861 socket_update_active,
865 private->toplevel = toplevel;
869 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
870 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
871 G_CALLBACK (socket_update_focus_in), socket);
872 g_signal_connect_swapped (private->toplevel, "notify::is-active",
873 G_CALLBACK (socket_update_active), socket);
876 socket_update_focus_in (socket);
877 socket_update_active (socket);
882 gtk_socket_grab_notify (GtkWidget *widget,
883 gboolean was_grabbed)
885 GtkSocket *socket = GTK_SOCKET (widget);
887 if (!socket->priv->same_app)
888 _gtk_xembed_send_message (socket->priv->plug_window,
889 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
894 gtk_socket_key_event (GtkWidget *widget,
897 GtkSocket *socket = GTK_SOCKET (widget);
898 GtkSocketPrivate *private = socket->priv;
900 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
902 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
911 gtk_socket_notify (GObject *object,
914 if (strcmp (pspec->name, "is-focus") == 0)
915 socket_update_focus_in (GTK_SOCKET (object));
917 if (G_OBJECT_CLASS (gtk_socket_parent_class)->notify)
918 G_OBJECT_CLASS (gtk_socket_parent_class)->notify (object, pspec);
922 * gtk_socket_claim_focus:
923 * @socket: a #GtkSocket
926 * Claims focus for the socket. XXX send_event?
929 gtk_socket_claim_focus (GtkSocket *socket,
932 GtkWidget *widget = GTK_WIDGET (socket);
933 GtkSocketPrivate *private = socket->priv;
936 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
938 /* Oh, the trickery... */
940 gtk_widget_set_can_focus (widget, TRUE);
941 gtk_widget_grab_focus (widget);
942 gtk_widget_set_can_focus (widget, FALSE);
946 gtk_socket_focus (GtkWidget *widget,
947 GtkDirectionType direction)
949 GtkSocket *socket = GTK_SOCKET (widget);
950 GtkSocketPrivate *private = socket->priv;
952 if (private->plug_widget)
953 return gtk_widget_child_focus (private->plug_widget, direction);
955 if (!gtk_widget_is_focus (widget))
963 case GTK_DIR_TAB_BACKWARD:
964 detail = XEMBED_FOCUS_LAST;
968 case GTK_DIR_TAB_FORWARD:
969 detail = XEMBED_FOCUS_FIRST;
973 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
974 gtk_socket_claim_focus (socket, FALSE);
983 gtk_socket_remove (GtkContainer *container,
986 GtkSocket *socket = GTK_SOCKET (container);
987 GtkSocketPrivate *private = socket->priv;
989 g_return_if_fail (child == private->plug_widget);
991 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
995 gtk_socket_forall (GtkContainer *container,
996 gboolean include_internals,
997 GtkCallback callback,
998 gpointer callback_data)
1000 GtkSocket *socket = GTK_SOCKET (container);
1001 GtkSocketPrivate *private = socket->priv;
1003 if (private->plug_widget)
1004 (* callback) (private->plug_widget, callback_data);
1008 * gtk_socket_add_window:
1009 * @socket: a #GtkSocket
1010 * @xid: the native identifier for a window
1011 * @need_reparent: whether the socket's plug's window needs to be
1012 * reparented to the socket
1014 * Adds a window to a GtkSocket.
1017 gtk_socket_add_window (GtkSocket *socket,
1019 gboolean need_reparent)
1021 GtkWidget *widget = GTK_WIDGET (socket);
1022 GdkDisplay *display = gtk_widget_get_display (widget);
1023 gpointer user_data = NULL;
1024 GtkSocketPrivate *private = socket->priv;
1025 unsigned long version;
1026 unsigned long flags;
1028 if (GDK_IS_X11_DISPLAY (display))
1029 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1031 private->plug_window = NULL;
1033 if (private->plug_window)
1035 g_object_ref (private->plug_window);
1036 gdk_window_get_user_data (private->plug_window, &user_data);
1039 if (user_data) /* A widget's window in this process */
1041 GtkWidget *child_widget = user_data;
1043 if (!GTK_IS_PLUG (child_widget))
1045 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1046 private->plug_window = NULL;
1047 gdk_error_trap_pop_ignored ();
1052 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1054 else /* A foreign window */
1056 GdkDragProtocol protocol;
1058 gdk_error_trap_push ();
1060 if (!private->plug_window)
1062 if (GDK_IS_X11_DISPLAY (display))
1063 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1064 if (!private->plug_window) /* was deleted before we could get it */
1066 gdk_error_trap_pop_ignored ();
1071 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1072 GDK_WINDOW_XID (private->plug_window),
1073 StructureNotifyMask | PropertyChangeMask);
1075 if (gdk_error_trap_pop ())
1077 g_object_unref (private->plug_window);
1078 private->plug_window = NULL;
1082 /* OK, we now will reliably get destroy notification on socket->plug_window */
1084 gdk_error_trap_push ();
1088 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1089 gdk_window_reparent (private->plug_window,
1090 gtk_widget_get_window (widget),
1094 private->have_size = FALSE;
1096 private->xembed_version = -1;
1097 if (xembed_get_info (private->plug_window, &version, &flags))
1099 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1100 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1104 /* FIXME, we should probably actually check the state before we started */
1105 private->is_mapped = TRUE;
1108 private->need_map = private->is_mapped;
1110 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1112 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1115 gdk_error_trap_pop_ignored ();
1117 gdk_window_add_filter (private->plug_window,
1118 gtk_socket_filter_func,
1122 gdk_error_trap_push ();
1123 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1124 GDK_WINDOW_XID (private->plug_window),
1125 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1126 gdk_error_trap_pop_ignored ();
1128 _gtk_xembed_send_message (private->plug_window,
1129 XEMBED_EMBEDDED_NOTIFY, 0,
1130 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1131 private->xembed_version);
1133 socket_update_active (socket);
1134 socket_update_focus_in (socket);
1136 gtk_widget_queue_resize (GTK_WIDGET (socket));
1139 if (private->plug_window)
1140 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1144 * gtk_socket_handle_map_request:
1145 * @socket: a #GtkSocket
1147 * Called from the GtkSocket backend when the plug has been mapped.
1150 gtk_socket_handle_map_request (GtkSocket *socket)
1152 GtkSocketPrivate *private = socket->priv;
1153 if (!private->is_mapped)
1155 private->is_mapped = TRUE;
1156 private->need_map = TRUE;
1158 gtk_widget_queue_resize (GTK_WIDGET (socket));
1163 * gtk_socket_unmap_notify:
1164 * @socket: a #GtkSocket
1166 * Called from the GtkSocket backend when the plug has been unmapped ???
1169 gtk_socket_unmap_notify (GtkSocket *socket)
1171 GtkSocketPrivate *private = socket->priv;
1172 if (private->is_mapped)
1174 private->is_mapped = FALSE;
1175 gtk_widget_queue_resize (GTK_WIDGET (socket));
1180 * gtk_socket_advance_toplevel_focus:
1181 * @socket: a #GtkSocket
1182 * @direction: a direction
1184 * Called from the GtkSocket backend when the corresponding plug
1185 * has told the socket to move the focus.
1188 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1189 GtkDirectionType direction)
1193 GtkContainer *container;
1195 GtkWidget *focus_widget;
1196 GtkWidget *toplevel;
1197 GtkWidget *old_focus_child;
1200 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1204 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1206 gtk_widget_child_focus (toplevel,direction);
1210 container = GTK_CONTAINER (toplevel);
1211 window = GTK_WINDOW (toplevel);
1212 bin = GTK_BIN (toplevel);
1214 /* This is a copy of gtk_window_focus(), modified so that we
1215 * can detect wrap-around.
1217 old_focus_child = gtk_container_get_focus_child (container);
1219 if (old_focus_child)
1221 if (gtk_widget_child_focus (old_focus_child, direction))
1224 /* We are allowed exactly one wrap-around per sequence of focus
1227 if (_gtk_xembed_get_focus_wrapped ())
1230 _gtk_xembed_set_focus_wrapped ();
1233 focus_widget = gtk_window_get_focus (window);
1236 /* Wrapped off the end, clear the focus setting for the toplevel */
1237 parent = gtk_widget_get_parent (focus_widget);
1240 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1241 parent = gtk_widget_get_parent (parent);
1244 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1247 /* Now try to focus the first widget in the window */
1248 child = gtk_bin_get_child (bin);
1251 if (gtk_widget_child_focus (child, direction))
1257 xembed_get_info (GdkWindow *window,
1258 unsigned long *version,
1259 unsigned long *flags)
1261 GdkDisplay *display = gdk_window_get_display (window);
1262 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1265 unsigned long nitems, bytes_after;
1266 unsigned char *data;
1267 unsigned long *data_long;
1270 gdk_error_trap_push ();
1271 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1272 GDK_WINDOW_XID (window),
1275 xembed_info_atom, &type, &format,
1276 &nitems, &bytes_after, &data);
1277 gdk_error_trap_pop_ignored ();
1279 if (status != Success)
1280 return FALSE; /* Window vanished? */
1282 if (type == None) /* No info property */
1285 if (type != xembed_info_atom)
1287 g_warning ("_XEMBED_INFO property has wrong type\n");
1293 g_warning ("_XEMBED_INFO too short\n");
1298 data_long = (unsigned long *)data;
1300 *version = data_long[0];
1302 *flags = data_long[1] & XEMBED_MAPPED;
1309 handle_xembed_message (GtkSocket *socket,
1310 XEmbedMessageType message,
1316 GTK_NOTE (PLUGSOCKET,
1317 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1321 case XEMBED_EMBEDDED_NOTIFY:
1322 case XEMBED_WINDOW_ACTIVATE:
1323 case XEMBED_WINDOW_DEACTIVATE:
1324 case XEMBED_MODALITY_ON:
1325 case XEMBED_MODALITY_OFF:
1326 case XEMBED_FOCUS_IN:
1327 case XEMBED_FOCUS_OUT:
1328 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1331 case XEMBED_REQUEST_FOCUS:
1332 gtk_socket_claim_focus (socket, TRUE);
1335 case XEMBED_FOCUS_NEXT:
1336 case XEMBED_FOCUS_PREV:
1337 gtk_socket_advance_toplevel_focus (socket,
1338 (message == XEMBED_FOCUS_NEXT ?
1339 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1342 case XEMBED_GTK_GRAB_KEY:
1343 gtk_socket_add_grabbed_key (socket, data1, data2);
1345 case XEMBED_GTK_UNGRAB_KEY:
1346 gtk_socket_remove_grabbed_key (socket, data1, data2);
1349 case XEMBED_GRAB_KEY:
1350 case XEMBED_UNGRAB_KEY:
1354 GTK_NOTE (PLUGSOCKET,
1355 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1360 static GdkFilterReturn
1361 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1367 GdkDisplay *display;
1369 GtkSocketPrivate *private;
1371 GdkFilterReturn return_val;
1373 socket = GTK_SOCKET (data);
1374 private = socket->priv;
1376 return_val = GDK_FILTER_CONTINUE;
1378 if (private->plug_widget)
1381 widget = GTK_WIDGET (socket);
1382 xevent = (XEvent *)gdk_xevent;
1383 display = gtk_widget_get_display (widget);
1385 switch (xevent->type)
1388 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1390 _gtk_xembed_push_message (xevent);
1391 handle_xembed_message (socket,
1392 xevent->xclient.data.l[1],
1393 xevent->xclient.data.l[2],
1394 xevent->xclient.data.l[3],
1395 xevent->xclient.data.l[4],
1396 xevent->xclient.data.l[0]);
1397 _gtk_xembed_pop_message ();
1399 return_val = GDK_FILTER_REMOVE;
1405 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1407 if (!private->plug_window)
1409 gtk_socket_add_window (socket, xcwe->window, FALSE);
1411 if (private->plug_window)
1413 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1417 return_val = GDK_FILTER_REMOVE;
1422 case ConfigureRequest:
1424 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1426 if (!private->plug_window)
1427 gtk_socket_add_window (socket, xcre->window, FALSE);
1429 if (private->plug_window)
1431 if (xcre->value_mask & (CWWidth | CWHeight))
1433 GTK_NOTE (PLUGSOCKET,
1434 g_message ("GtkSocket - configure request: %d %d",
1435 private->request_width,
1436 private->request_height));
1438 private->resize_count++;
1439 gtk_widget_queue_resize (widget);
1441 else if (xcre->value_mask & (CWX | CWY))
1443 gtk_socket_send_configure_event (socket);
1445 /* Ignore stacking requests. */
1447 return_val = GDK_FILTER_REMOVE;
1454 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1456 /* Note that we get destroy notifies both from SubstructureNotify on
1457 * our window and StructureNotify on socket->plug_window
1459 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1463 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1465 gdk_window_destroy_notify (private->plug_window);
1466 gtk_socket_end_embedding (socket);
1468 g_object_ref (widget);
1469 g_signal_emit_by_name (widget, "plug-removed", &result);
1471 gtk_widget_destroy (widget);
1472 g_object_unref (widget);
1474 return_val = GDK_FILTER_REMOVE;
1480 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1482 gtk_socket_claim_focus (socket, TRUE);
1484 return_val = GDK_FILTER_REMOVE;
1487 return_val = GDK_FILTER_REMOVE;
1490 if (!private->plug_window)
1492 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1495 if (private->plug_window)
1497 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1499 gtk_socket_handle_map_request (socket);
1500 return_val = GDK_FILTER_REMOVE;
1503 case PropertyNotify:
1504 if (private->plug_window &&
1505 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1507 GdkDragProtocol protocol;
1509 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1511 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1512 private->have_size = FALSE;
1513 gtk_widget_queue_resize (widget);
1514 return_val = GDK_FILTER_REMOVE;
1516 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1517 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1519 gdk_error_trap_push ();
1520 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1522 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1523 private->plug_window,
1526 gdk_error_trap_pop_ignored ();
1527 return_val = GDK_FILTER_REMOVE;
1529 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1531 unsigned long flags;
1533 if (xembed_get_info (private->plug_window, NULL, &flags))
1535 gboolean was_mapped = private->is_mapped;
1536 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1538 if (was_mapped != is_mapped)
1541 gtk_socket_handle_map_request (socket);
1544 gdk_error_trap_push ();
1545 gdk_window_show (private->plug_window);
1546 gdk_error_trap_pop_ignored ();
1548 gtk_socket_unmap_notify (socket);
1552 return_val = GDK_FILTER_REMOVE;
1556 case ReparentNotify:
1559 XReparentEvent *xre = &xevent->xreparent;
1561 window = gtk_widget_get_window (widget);
1563 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1564 if (!private->plug_window &&
1565 xre->parent == GDK_WINDOW_XID (window))
1567 gtk_socket_add_window (socket, xre->window, FALSE);
1569 if (private->plug_window)
1571 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1574 return_val = GDK_FILTER_REMOVE;
1578 if (private->plug_window &&
1579 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1580 xre->parent != GDK_WINDOW_XID (window))
1584 gtk_socket_end_embedding (socket);
1586 g_object_ref (widget);
1587 g_signal_emit_by_name (widget, "plug-removed", &result);
1589 gtk_widget_destroy (widget);
1590 g_object_unref (widget);
1592 return_val = GDK_FILTER_REMOVE;
1599 if (private->plug_window &&
1600 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1602 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1604 gtk_socket_unmap_notify (socket);
1605 return_val = GDK_FILTER_REMOVE;