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;
383 GtkSocket *socket = GTK_SOCKET (widget);
385 GdkWindowAttr attributes;
386 XWindowAttributes xattrs;
387 gint attributes_mask;
389 gtk_widget_set_realized (widget, TRUE);
391 gtk_widget_get_allocation (widget, &allocation);
393 attributes.window_type = GDK_WINDOW_CHILD;
394 attributes.x = allocation.x;
395 attributes.y = allocation.y;
396 attributes.width = allocation.width;
397 attributes.height = allocation.height;
398 attributes.wclass = GDK_INPUT_OUTPUT;
399 attributes.visual = gtk_widget_get_visual (widget);
400 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
402 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
404 window = gdk_window_new (gtk_widget_get_parent_window (widget),
405 &attributes, attributes_mask);
406 gtk_widget_set_window (widget, window);
407 gdk_window_set_user_data (window, socket);
409 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
412 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
413 GDK_WINDOW_XID (window),
416 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
417 for input on the socket with a mask of 0x0fffff (for god knows why)
418 which includes ButtonPressMask causing a BadAccess if someone else
419 also selects for this. As per the client-side windows merge we always
420 normally selects for button press so we can emulate it on client
421 side children that selects for button press. However, we don't need
422 this for GtkSocket, so we unselect it here, fixing the crashes in
424 XSelectInput (GDK_WINDOW_XDISPLAY (window),
425 GDK_WINDOW_XID (window),
426 (xattrs.your_event_mask & ~ButtonPressMask) |
427 SubstructureNotifyMask | SubstructureRedirectMask);
429 gdk_window_add_filter (window,
430 gtk_socket_filter_func,
433 /* We sync here so that we make sure that if the XID for
434 * our window is passed to another application, SubstructureRedirectMask
435 * will be set by the time the other app creates its window.
437 gdk_display_sync (gtk_widget_get_display (widget));
441 * gtk_socket_end_embedding:
443 * @socket: a #GtkSocket
445 * Called to end the embedding of a plug in the socket.
448 gtk_socket_end_embedding (GtkSocket *socket)
450 GtkSocketPrivate *private = socket->priv;
452 g_object_unref (private->plug_window);
453 private->plug_window = NULL;
454 private->current_width = 0;
455 private->current_height = 0;
456 private->resize_count = 0;
458 gtk_accel_group_disconnect (private->accel_group, NULL);
462 gtk_socket_unrealize (GtkWidget *widget)
464 GtkSocket *socket = GTK_SOCKET (widget);
465 GtkSocketPrivate *private = socket->priv;
467 gtk_widget_set_realized (widget, FALSE);
469 if (private->plug_widget)
471 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
473 else if (private->plug_window)
475 gtk_socket_end_embedding (socket);
478 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
482 gtk_socket_size_request (GtkSocket *socket)
484 GtkSocketPrivate *private = socket->priv;
488 gdk_error_trap_push ();
490 private->request_width = 1;
491 private->request_height = 1;
493 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
494 GDK_WINDOW_XID (private->plug_window),
497 if (hints.flags & PMinSize)
499 private->request_width = MAX (hints.min_width, 1);
500 private->request_height = MAX (hints.min_height, 1);
502 else if (hints.flags & PBaseSize)
504 private->request_width = MAX (hints.base_width, 1);
505 private->request_height = MAX (hints.base_height, 1);
508 private->have_size = TRUE;
510 gdk_error_trap_pop_ignored ();
514 gtk_socket_get_preferred_width (GtkWidget *widget,
518 GtkSocket *socket = GTK_SOCKET (widget);
519 GtkSocketPrivate *private = socket->priv;
521 if (private->plug_widget)
523 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
527 if (private->is_mapped && !private->have_size && private->plug_window)
528 gtk_socket_size_request (socket);
530 if (private->is_mapped && private->have_size)
531 *minimum = *natural = MAX (private->request_width, 1);
533 *minimum = *natural = 1;
538 gtk_socket_get_preferred_height (GtkWidget *widget,
542 GtkSocket *socket = GTK_SOCKET (widget);
543 GtkSocketPrivate *private = socket->priv;
545 if (private->plug_widget)
547 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
551 if (private->is_mapped && !private->have_size && private->plug_window)
552 gtk_socket_size_request (socket);
554 if (private->is_mapped && private->have_size)
555 *minimum = *natural = MAX (private->request_height, 1);
557 *minimum = *natural = 1;
562 gtk_socket_send_configure_event (GtkSocket *socket)
564 GtkAllocation allocation;
565 XConfigureEvent xconfigure;
568 g_return_if_fail (socket->priv->plug_window != NULL);
570 memset (&xconfigure, 0, sizeof (xconfigure));
571 xconfigure.type = ConfigureNotify;
573 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
574 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
576 /* The ICCCM says that synthetic events should have root relative
577 * coordinates. We still aren't really ICCCM compliant, since
578 * we don't send events when the real toplevel is moved.
580 gdk_error_trap_push ();
581 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
582 gdk_error_trap_pop_ignored ();
584 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
587 xconfigure.width = allocation.width;
588 xconfigure.height = allocation.height;
590 xconfigure.border_width = 0;
591 xconfigure.above = None;
592 xconfigure.override_redirect = False;
594 gdk_error_trap_push ();
595 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
596 GDK_WINDOW_XID (socket->priv->plug_window),
597 False, NoEventMask, (XEvent *)&xconfigure);
598 gdk_error_trap_pop_ignored ();
602 gtk_socket_size_allocate (GtkWidget *widget,
603 GtkAllocation *allocation)
605 GtkSocket *socket = GTK_SOCKET (widget);
606 GtkSocketPrivate *private = socket->priv;
608 gtk_widget_set_allocation (widget, allocation);
609 if (gtk_widget_get_realized (widget))
611 gdk_window_move_resize (gtk_widget_get_window (widget),
612 allocation->x, allocation->y,
613 allocation->width, allocation->height);
615 if (private->plug_widget)
617 GtkAllocation child_allocation;
619 child_allocation.x = 0;
620 child_allocation.y = 0;
621 child_allocation.width = allocation->width;
622 child_allocation.height = allocation->height;
624 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
626 else if (private->plug_window)
628 gdk_error_trap_push ();
630 if (allocation->width != private->current_width ||
631 allocation->height != private->current_height)
633 gdk_window_move_resize (private->plug_window,
635 allocation->width, allocation->height);
636 if (private->resize_count)
637 private->resize_count--;
639 GTK_NOTE (PLUGSOCKET,
640 g_message ("GtkSocket - allocated: %d %d",
641 allocation->width, allocation->height));
642 private->current_width = allocation->width;
643 private->current_height = allocation->height;
646 if (private->need_map)
648 gdk_window_show (private->plug_window);
649 private->need_map = FALSE;
652 while (private->resize_count)
654 gtk_socket_send_configure_event (socket);
655 private->resize_count--;
656 GTK_NOTE (PLUGSOCKET,
657 g_message ("GtkSocket - sending synthetic configure: %d %d",
658 allocation->width, allocation->height));
661 gdk_error_trap_pop_ignored ();
667 gtk_socket_send_key_event (GtkSocket *socket,
669 gboolean mask_key_presses)
672 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
674 memset (&xkey, 0, sizeof (xkey));
675 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
676 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
677 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
678 xkey.subwindow = None;
679 xkey.time = gdk_event->key.time;
684 xkey.state = gdk_event->key.state;
685 xkey.keycode = gdk_event->key.hardware_keycode;
686 xkey.same_screen = True;/* FIXME ? */
688 gdk_error_trap_push ();
689 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
690 GDK_WINDOW_XID (socket->priv->plug_window),
692 (mask_key_presses ? KeyPressMask : NoEventMask),
694 gdk_error_trap_pop_ignored ();
698 activate_key (GtkAccelGroup *accel_group,
699 GObject *acceleratable,
701 GdkModifierType accel_mods,
702 GrabbedKey *grabbed_key)
704 GdkEvent *gdk_event = gtk_get_current_event ();
706 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
707 gboolean retval = FALSE;
709 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
711 gtk_socket_send_key_event (socket, gdk_event, FALSE);
716 gdk_event_free (gdk_event);
722 find_accel_key (GtkAccelKey *key,
726 GrabbedKey *grabbed_key = data;
728 return (key->accel_key == grabbed_key->accel_key &&
729 key->accel_mods == grabbed_key->accel_mods);
733 * gtk_socket_add_grabbed_key:
735 * @socket: a #GtkSocket
737 * @modifiers: modifiers for the key
739 * Called from the GtkSocket platform-specific backend when the
740 * corresponding plug has told the socket to grab a key.
743 gtk_socket_add_grabbed_key (GtkSocket *socket,
745 GdkModifierType modifiers)
748 GrabbedKey *grabbed_key;
750 grabbed_key = g_new (GrabbedKey, 1);
752 grabbed_key->accel_key = keyval;
753 grabbed_key->accel_mods = modifiers;
755 if (gtk_accel_group_find (socket->priv->accel_group,
759 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
761 g_free (grabbed_key);
765 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
767 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
772 * gtk_socket_remove_grabbed_key:
774 * @socket: a #GtkSocket
776 * @modifiers: modifiers for the key
778 * Called from the GtkSocket backend when the corresponding plug has
779 * told the socket to remove a key grab.
782 gtk_socket_remove_grabbed_key (GtkSocket *socket,
784 GdkModifierType modifiers)
786 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
787 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
792 socket_update_focus_in (GtkSocket *socket)
794 GtkSocketPrivate *private = socket->priv;
795 gboolean focus_in = FALSE;
797 if (private->plug_window)
799 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
801 if (gtk_widget_is_toplevel (toplevel) &&
802 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
803 gtk_widget_is_focus (GTK_WIDGET (socket)))
807 if (focus_in != private->focus_in)
809 private->focus_in = focus_in;
812 _gtk_xembed_send_focus_message (private->plug_window,
813 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
815 _gtk_xembed_send_message (private->plug_window,
816 XEMBED_FOCUS_OUT, 0, 0, 0);
821 socket_update_active (GtkSocket *socket)
823 GtkSocketPrivate *private = socket->priv;
824 gboolean active = FALSE;
826 if (private->plug_window)
828 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
830 if (gtk_widget_is_toplevel (toplevel) &&
831 gtk_window_is_active (GTK_WINDOW (toplevel)))
835 if (active != private->active)
837 private->active = active;
839 _gtk_xembed_send_message (private->plug_window,
840 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
846 gtk_socket_hierarchy_changed (GtkWidget *widget,
847 GtkWidget *old_toplevel)
849 GtkSocket *socket = GTK_SOCKET (widget);
850 GtkSocketPrivate *private = socket->priv;
851 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
853 if (toplevel && !GTK_IS_WINDOW (toplevel))
856 if (toplevel != private->toplevel)
858 if (private->toplevel)
860 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
861 g_signal_handlers_disconnect_by_func (private->toplevel,
862 socket_update_focus_in,
864 g_signal_handlers_disconnect_by_func (private->toplevel,
865 socket_update_active,
869 private->toplevel = toplevel;
873 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
874 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
875 G_CALLBACK (socket_update_focus_in), socket);
876 g_signal_connect_swapped (private->toplevel, "notify::is-active",
877 G_CALLBACK (socket_update_active), socket);
880 socket_update_focus_in (socket);
881 socket_update_active (socket);
886 gtk_socket_grab_notify (GtkWidget *widget,
887 gboolean was_grabbed)
889 GtkSocket *socket = GTK_SOCKET (widget);
891 if (!socket->priv->same_app)
892 _gtk_xembed_send_message (socket->priv->plug_window,
893 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
898 gtk_socket_key_event (GtkWidget *widget,
901 GtkSocket *socket = GTK_SOCKET (widget);
902 GtkSocketPrivate *private = socket->priv;
904 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
906 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
915 gtk_socket_notify (GObject *object,
918 if (strcmp (pspec->name, "is-focus") == 0)
919 socket_update_focus_in (GTK_SOCKET (object));
923 * gtk_socket_claim_focus:
925 * @socket: a #GtkSocket
928 * Claims focus for the socket. XXX send_event?
931 gtk_socket_claim_focus (GtkSocket *socket,
934 GtkWidget *widget = GTK_WIDGET (socket);
935 GtkSocketPrivate *private = socket->priv;
938 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
940 /* Oh, the trickery... */
942 gtk_widget_set_can_focus (widget, TRUE);
943 gtk_widget_grab_focus (widget);
944 gtk_widget_set_can_focus (widget, FALSE);
948 gtk_socket_focus (GtkWidget *widget,
949 GtkDirectionType direction)
951 GtkSocket *socket = GTK_SOCKET (widget);
952 GtkSocketPrivate *private = socket->priv;
954 if (private->plug_widget)
955 return gtk_widget_child_focus (private->plug_widget, direction);
957 if (!gtk_widget_is_focus (widget))
965 case GTK_DIR_TAB_BACKWARD:
966 detail = XEMBED_FOCUS_LAST;
970 case GTK_DIR_TAB_FORWARD:
971 detail = XEMBED_FOCUS_FIRST;
975 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
976 gtk_socket_claim_focus (socket, FALSE);
985 gtk_socket_remove (GtkContainer *container,
988 GtkSocket *socket = GTK_SOCKET (container);
989 GtkSocketPrivate *private = socket->priv;
991 g_return_if_fail (child == private->plug_widget);
993 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
997 gtk_socket_forall (GtkContainer *container,
998 gboolean include_internals,
999 GtkCallback callback,
1000 gpointer callback_data)
1002 GtkSocket *socket = GTK_SOCKET (container);
1003 GtkSocketPrivate *private = socket->priv;
1005 if (private->plug_widget)
1006 (* callback) (private->plug_widget, callback_data);
1010 * gtk_socket_add_window:
1012 * @socket: a #GtkSocket
1013 * @xid: the native identifier for a window
1014 * @need_reparent: whether the socket's plug's window needs to be
1015 * reparented to the socket
1017 * Adds a window to a GtkSocket.
1020 gtk_socket_add_window (GtkSocket *socket,
1022 gboolean need_reparent)
1024 GtkWidget *widget = GTK_WIDGET (socket);
1025 GdkDisplay *display = gtk_widget_get_display (widget);
1026 gpointer user_data = NULL;
1027 GtkSocketPrivate *private = socket->priv;
1028 unsigned long version;
1029 unsigned long flags;
1031 if (GDK_IS_X11_DISPLAY (display))
1032 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1034 private->plug_window = NULL;
1036 if (private->plug_window)
1038 g_object_ref (private->plug_window);
1039 gdk_window_get_user_data (private->plug_window, &user_data);
1042 if (user_data) /* A widget's window in this process */
1044 GtkWidget *child_widget = user_data;
1046 if (!GTK_IS_PLUG (child_widget))
1048 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1049 private->plug_window = NULL;
1050 gdk_error_trap_pop_ignored ();
1055 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1057 else /* A foreign window */
1059 GdkDragProtocol protocol;
1061 gdk_error_trap_push ();
1063 if (!private->plug_window)
1065 if (GDK_IS_X11_DISPLAY (display))
1066 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1067 if (!private->plug_window) /* was deleted before we could get it */
1069 gdk_error_trap_pop_ignored ();
1074 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1075 GDK_WINDOW_XID (private->plug_window),
1076 StructureNotifyMask | PropertyChangeMask);
1078 if (gdk_error_trap_pop ())
1080 g_object_unref (private->plug_window);
1081 private->plug_window = NULL;
1085 /* OK, we now will reliably get destroy notification on socket->plug_window */
1087 gdk_error_trap_push ();
1091 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1092 gdk_window_reparent (private->plug_window,
1093 gtk_widget_get_window (widget),
1097 private->have_size = FALSE;
1099 private->xembed_version = -1;
1100 if (xembed_get_info (private->plug_window, &version, &flags))
1102 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1103 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1107 /* FIXME, we should probably actually check the state before we started */
1108 private->is_mapped = TRUE;
1111 private->need_map = private->is_mapped;
1113 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1115 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1118 gdk_error_trap_pop_ignored ();
1120 gdk_window_add_filter (private->plug_window,
1121 gtk_socket_filter_func,
1125 gdk_error_trap_push ();
1126 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1127 GDK_WINDOW_XID (private->plug_window),
1128 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1129 gdk_error_trap_pop_ignored ();
1131 _gtk_xembed_send_message (private->plug_window,
1132 XEMBED_EMBEDDED_NOTIFY, 0,
1133 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1134 private->xembed_version);
1136 socket_update_active (socket);
1137 socket_update_focus_in (socket);
1139 gtk_widget_queue_resize (GTK_WIDGET (socket));
1142 if (private->plug_window)
1143 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1147 * gtk_socket_handle_map_request:
1149 * @socket: a #GtkSocket
1151 * Called from the GtkSocket backend when the plug has been mapped.
1154 gtk_socket_handle_map_request (GtkSocket *socket)
1156 GtkSocketPrivate *private = socket->priv;
1157 if (!private->is_mapped)
1159 private->is_mapped = TRUE;
1160 private->need_map = TRUE;
1162 gtk_widget_queue_resize (GTK_WIDGET (socket));
1167 * gtk_socket_unmap_notify:
1169 * @socket: a #GtkSocket
1171 * Called from the GtkSocket backend when the plug has been unmapped ???
1174 gtk_socket_unmap_notify (GtkSocket *socket)
1176 GtkSocketPrivate *private = socket->priv;
1177 if (private->is_mapped)
1179 private->is_mapped = FALSE;
1180 gtk_widget_queue_resize (GTK_WIDGET (socket));
1185 * gtk_socket_advance_toplevel_focus:
1187 * @socket: a #GtkSocket
1188 * @direction: a direction
1190 * Called from the GtkSocket backend when the corresponding plug
1191 * has told the socket to move the focus.
1194 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1195 GtkDirectionType direction)
1199 GtkContainer *container;
1201 GtkWidget *focus_widget;
1202 GtkWidget *toplevel;
1203 GtkWidget *old_focus_child;
1206 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1210 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1212 gtk_widget_child_focus (toplevel,direction);
1216 container = GTK_CONTAINER (toplevel);
1217 window = GTK_WINDOW (toplevel);
1218 bin = GTK_BIN (toplevel);
1220 /* This is a copy of gtk_window_focus(), modified so that we
1221 * can detect wrap-around.
1223 old_focus_child = gtk_container_get_focus_child (container);
1225 if (old_focus_child)
1227 if (gtk_widget_child_focus (old_focus_child, direction))
1230 /* We are allowed exactly one wrap-around per sequence of focus
1233 if (_gtk_xembed_get_focus_wrapped ())
1236 _gtk_xembed_set_focus_wrapped ();
1239 focus_widget = gtk_window_get_focus (window);
1242 /* Wrapped off the end, clear the focus setting for the toplevel */
1243 parent = gtk_widget_get_parent (focus_widget);
1246 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1247 parent = gtk_widget_get_parent (parent);
1250 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1253 /* Now try to focus the first widget in the window */
1254 child = gtk_bin_get_child (bin);
1257 if (gtk_widget_child_focus (child, direction))
1263 xembed_get_info (GdkWindow *window,
1264 unsigned long *version,
1265 unsigned long *flags)
1267 GdkDisplay *display = gdk_window_get_display (window);
1268 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1271 unsigned long nitems, bytes_after;
1272 unsigned char *data;
1273 unsigned long *data_long;
1276 gdk_error_trap_push ();
1277 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1278 GDK_WINDOW_XID (window),
1281 xembed_info_atom, &type, &format,
1282 &nitems, &bytes_after, &data);
1283 gdk_error_trap_pop_ignored ();
1285 if (status != Success)
1286 return FALSE; /* Window vanished? */
1288 if (type == None) /* No info property */
1291 if (type != xembed_info_atom)
1293 g_warning ("_XEMBED_INFO property has wrong type\n");
1299 g_warning ("_XEMBED_INFO too short\n");
1304 data_long = (unsigned long *)data;
1306 *version = data_long[0];
1308 *flags = data_long[1] & XEMBED_MAPPED;
1315 handle_xembed_message (GtkSocket *socket,
1316 XEmbedMessageType message,
1322 GTK_NOTE (PLUGSOCKET,
1323 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1327 case XEMBED_EMBEDDED_NOTIFY:
1328 case XEMBED_WINDOW_ACTIVATE:
1329 case XEMBED_WINDOW_DEACTIVATE:
1330 case XEMBED_MODALITY_ON:
1331 case XEMBED_MODALITY_OFF:
1332 case XEMBED_FOCUS_IN:
1333 case XEMBED_FOCUS_OUT:
1334 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1337 case XEMBED_REQUEST_FOCUS:
1338 gtk_socket_claim_focus (socket, TRUE);
1341 case XEMBED_FOCUS_NEXT:
1342 case XEMBED_FOCUS_PREV:
1343 gtk_socket_advance_toplevel_focus (socket,
1344 (message == XEMBED_FOCUS_NEXT ?
1345 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1348 case XEMBED_GTK_GRAB_KEY:
1349 gtk_socket_add_grabbed_key (socket, data1, data2);
1351 case XEMBED_GTK_UNGRAB_KEY:
1352 gtk_socket_remove_grabbed_key (socket, data1, data2);
1355 case XEMBED_GRAB_KEY:
1356 case XEMBED_UNGRAB_KEY:
1360 GTK_NOTE (PLUGSOCKET,
1361 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1366 static GdkFilterReturn
1367 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1373 GdkDisplay *display;
1375 GtkSocketPrivate *private;
1377 GdkFilterReturn return_val;
1379 socket = GTK_SOCKET (data);
1380 private = socket->priv;
1382 return_val = GDK_FILTER_CONTINUE;
1384 if (private->plug_widget)
1387 widget = GTK_WIDGET (socket);
1388 xevent = (XEvent *)gdk_xevent;
1389 display = gtk_widget_get_display (widget);
1391 switch (xevent->type)
1394 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1396 _gtk_xembed_push_message (xevent);
1397 handle_xembed_message (socket,
1398 xevent->xclient.data.l[1],
1399 xevent->xclient.data.l[2],
1400 xevent->xclient.data.l[3],
1401 xevent->xclient.data.l[4],
1402 xevent->xclient.data.l[0]);
1403 _gtk_xembed_pop_message ();
1405 return_val = GDK_FILTER_REMOVE;
1411 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1413 if (!private->plug_window)
1415 gtk_socket_add_window (socket, xcwe->window, FALSE);
1417 if (private->plug_window)
1419 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1423 return_val = GDK_FILTER_REMOVE;
1428 case ConfigureRequest:
1430 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1432 if (!private->plug_window)
1433 gtk_socket_add_window (socket, xcre->window, FALSE);
1435 if (private->plug_window)
1437 if (xcre->value_mask & (CWWidth | CWHeight))
1439 GTK_NOTE (PLUGSOCKET,
1440 g_message ("GtkSocket - configure request: %d %d",
1441 private->request_width,
1442 private->request_height));
1444 private->resize_count++;
1445 gtk_widget_queue_resize (widget);
1447 else if (xcre->value_mask & (CWX | CWY))
1449 gtk_socket_send_configure_event (socket);
1451 /* Ignore stacking requests. */
1453 return_val = GDK_FILTER_REMOVE;
1460 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1462 /* Note that we get destroy notifies both from SubstructureNotify on
1463 * our window and StructureNotify on socket->plug_window
1465 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1469 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1471 gdk_window_destroy_notify (private->plug_window);
1472 gtk_socket_end_embedding (socket);
1474 g_object_ref (widget);
1475 g_signal_emit_by_name (widget, "plug-removed", &result);
1477 gtk_widget_destroy (widget);
1478 g_object_unref (widget);
1480 return_val = GDK_FILTER_REMOVE;
1486 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1488 gtk_socket_claim_focus (socket, TRUE);
1490 return_val = GDK_FILTER_REMOVE;
1493 return_val = GDK_FILTER_REMOVE;
1496 if (!private->plug_window)
1498 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1501 if (private->plug_window)
1503 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1505 gtk_socket_handle_map_request (socket);
1506 return_val = GDK_FILTER_REMOVE;
1509 case PropertyNotify:
1510 if (private->plug_window &&
1511 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1513 GdkDragProtocol protocol;
1515 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1517 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1518 private->have_size = FALSE;
1519 gtk_widget_queue_resize (widget);
1520 return_val = GDK_FILTER_REMOVE;
1522 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1523 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1525 gdk_error_trap_push ();
1526 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1528 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1529 private->plug_window,
1532 gdk_error_trap_pop_ignored ();
1533 return_val = GDK_FILTER_REMOVE;
1535 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1537 unsigned long flags;
1539 if (xembed_get_info (private->plug_window, NULL, &flags))
1541 gboolean was_mapped = private->is_mapped;
1542 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1544 if (was_mapped != is_mapped)
1547 gtk_socket_handle_map_request (socket);
1550 gdk_error_trap_push ();
1551 gdk_window_show (private->plug_window);
1552 gdk_error_trap_pop_ignored ();
1554 gtk_socket_unmap_notify (socket);
1558 return_val = GDK_FILTER_REMOVE;
1562 case ReparentNotify:
1565 XReparentEvent *xre = &xevent->xreparent;
1567 window = gtk_widget_get_window (widget);
1569 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1570 if (!private->plug_window &&
1571 xre->parent == GDK_WINDOW_XID (window))
1573 gtk_socket_add_window (socket, xre->window, FALSE);
1575 if (private->plug_window)
1577 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1580 return_val = GDK_FILTER_REMOVE;
1584 if (private->plug_window &&
1585 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1586 xre->parent != GDK_WINDOW_XID (window))
1590 gtk_socket_end_embedding (socket);
1592 g_object_ref (widget);
1593 g_signal_emit_by_name (widget, "plug-removed", &result);
1595 gtk_widget_destroy (widget);
1596 g_object_unref (widget);
1598 return_val = GDK_FILTER_REMOVE;
1605 if (private->plug_window &&
1606 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1608 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1610 gtk_socket_unmap_notify (socket);
1611 return_val = GDK_FILTER_REMOVE;