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:
442 * @socket: a #GtkSocket
444 * Called to end the embedding of a plug in the socket.
447 gtk_socket_end_embedding (GtkSocket *socket)
449 GtkSocketPrivate *private = socket->priv;
451 g_object_unref (private->plug_window);
452 private->plug_window = NULL;
453 private->current_width = 0;
454 private->current_height = 0;
455 private->resize_count = 0;
457 gtk_accel_group_disconnect (private->accel_group, NULL);
461 gtk_socket_unrealize (GtkWidget *widget)
463 GtkSocket *socket = GTK_SOCKET (widget);
464 GtkSocketPrivate *private = socket->priv;
466 gtk_widget_set_realized (widget, FALSE);
468 if (private->plug_widget)
470 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
472 else if (private->plug_window)
474 gtk_socket_end_embedding (socket);
477 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
481 gtk_socket_size_request (GtkSocket *socket)
483 GtkSocketPrivate *private = socket->priv;
487 gdk_error_trap_push ();
489 private->request_width = 1;
490 private->request_height = 1;
492 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
493 GDK_WINDOW_XID (private->plug_window),
496 if (hints.flags & PMinSize)
498 private->request_width = MAX (hints.min_width, 1);
499 private->request_height = MAX (hints.min_height, 1);
501 else if (hints.flags & PBaseSize)
503 private->request_width = MAX (hints.base_width, 1);
504 private->request_height = MAX (hints.base_height, 1);
507 private->have_size = TRUE;
509 gdk_error_trap_pop_ignored ();
513 gtk_socket_get_preferred_width (GtkWidget *widget,
517 GtkSocket *socket = GTK_SOCKET (widget);
518 GtkSocketPrivate *private = socket->priv;
520 if (private->plug_widget)
522 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
526 if (private->is_mapped && !private->have_size && private->plug_window)
527 gtk_socket_size_request (socket);
529 if (private->is_mapped && private->have_size)
530 *minimum = *natural = MAX (private->request_width, 1);
532 *minimum = *natural = 1;
537 gtk_socket_get_preferred_height (GtkWidget *widget,
541 GtkSocket *socket = GTK_SOCKET (widget);
542 GtkSocketPrivate *private = socket->priv;
544 if (private->plug_widget)
546 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
550 if (private->is_mapped && !private->have_size && private->plug_window)
551 gtk_socket_size_request (socket);
553 if (private->is_mapped && private->have_size)
554 *minimum = *natural = MAX (private->request_height, 1);
556 *minimum = *natural = 1;
561 gtk_socket_send_configure_event (GtkSocket *socket)
563 GtkAllocation allocation;
564 XConfigureEvent xconfigure;
567 g_return_if_fail (socket->priv->plug_window != NULL);
569 memset (&xconfigure, 0, sizeof (xconfigure));
570 xconfigure.type = ConfigureNotify;
572 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
573 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
575 /* The ICCCM says that synthetic events should have root relative
576 * coordinates. We still aren't really ICCCM compliant, since
577 * we don't send events when the real toplevel is moved.
579 gdk_error_trap_push ();
580 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
581 gdk_error_trap_pop_ignored ();
583 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
586 xconfigure.width = allocation.width;
587 xconfigure.height = allocation.height;
589 xconfigure.border_width = 0;
590 xconfigure.above = None;
591 xconfigure.override_redirect = False;
593 gdk_error_trap_push ();
594 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
595 GDK_WINDOW_XID (socket->priv->plug_window),
596 False, NoEventMask, (XEvent *)&xconfigure);
597 gdk_error_trap_pop_ignored ();
601 gtk_socket_size_allocate (GtkWidget *widget,
602 GtkAllocation *allocation)
604 GtkSocket *socket = GTK_SOCKET (widget);
605 GtkSocketPrivate *private = socket->priv;
607 gtk_widget_set_allocation (widget, allocation);
608 if (gtk_widget_get_realized (widget))
610 gdk_window_move_resize (gtk_widget_get_window (widget),
611 allocation->x, allocation->y,
612 allocation->width, allocation->height);
614 if (private->plug_widget)
616 GtkAllocation child_allocation;
618 child_allocation.x = 0;
619 child_allocation.y = 0;
620 child_allocation.width = allocation->width;
621 child_allocation.height = allocation->height;
623 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
625 else if (private->plug_window)
627 gdk_error_trap_push ();
629 if (allocation->width != private->current_width ||
630 allocation->height != private->current_height)
632 gdk_window_move_resize (private->plug_window,
634 allocation->width, allocation->height);
635 if (private->resize_count)
636 private->resize_count--;
638 GTK_NOTE (PLUGSOCKET,
639 g_message ("GtkSocket - allocated: %d %d",
640 allocation->width, allocation->height));
641 private->current_width = allocation->width;
642 private->current_height = allocation->height;
645 if (private->need_map)
647 gdk_window_show (private->plug_window);
648 private->need_map = FALSE;
651 while (private->resize_count)
653 gtk_socket_send_configure_event (socket);
654 private->resize_count--;
655 GTK_NOTE (PLUGSOCKET,
656 g_message ("GtkSocket - sending synthetic configure: %d %d",
657 allocation->width, allocation->height));
660 gdk_error_trap_pop_ignored ();
666 gtk_socket_send_key_event (GtkSocket *socket,
668 gboolean mask_key_presses)
671 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
673 memset (&xkey, 0, sizeof (xkey));
674 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
675 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
676 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
677 xkey.subwindow = None;
678 xkey.time = gdk_event->key.time;
683 xkey.state = gdk_event->key.state;
684 xkey.keycode = gdk_event->key.hardware_keycode;
685 xkey.same_screen = True;/* FIXME ? */
687 gdk_error_trap_push ();
688 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
689 GDK_WINDOW_XID (socket->priv->plug_window),
691 (mask_key_presses ? KeyPressMask : NoEventMask),
693 gdk_error_trap_pop_ignored ();
697 activate_key (GtkAccelGroup *accel_group,
698 GObject *acceleratable,
700 GdkModifierType accel_mods,
701 GrabbedKey *grabbed_key)
703 GdkEvent *gdk_event = gtk_get_current_event ();
705 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
706 gboolean retval = FALSE;
708 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
710 gtk_socket_send_key_event (socket, gdk_event, FALSE);
715 gdk_event_free (gdk_event);
721 find_accel_key (GtkAccelKey *key,
725 GrabbedKey *grabbed_key = data;
727 return (key->accel_key == grabbed_key->accel_key &&
728 key->accel_mods == grabbed_key->accel_mods);
732 * gtk_socket_add_grabbed_key:
733 * @socket: a #GtkSocket
735 * @modifiers: modifiers for the key
737 * Called from the GtkSocket platform-specific backend when the
738 * corresponding plug has told the socket to grab a key.
741 gtk_socket_add_grabbed_key (GtkSocket *socket,
743 GdkModifierType modifiers)
746 GrabbedKey *grabbed_key;
748 grabbed_key = g_new (GrabbedKey, 1);
750 grabbed_key->accel_key = keyval;
751 grabbed_key->accel_mods = modifiers;
753 if (gtk_accel_group_find (socket->priv->accel_group,
757 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
759 g_free (grabbed_key);
763 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
765 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
770 * gtk_socket_remove_grabbed_key:
771 * @socket: a #GtkSocket
773 * @modifiers: modifiers for the key
775 * Called from the GtkSocket backend when the corresponding plug has
776 * told the socket to remove a key grab.
779 gtk_socket_remove_grabbed_key (GtkSocket *socket,
781 GdkModifierType modifiers)
783 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
784 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
789 socket_update_focus_in (GtkSocket *socket)
791 GtkSocketPrivate *private = socket->priv;
792 gboolean focus_in = FALSE;
794 if (private->plug_window)
796 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
798 if (gtk_widget_is_toplevel (toplevel) &&
799 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
800 gtk_widget_is_focus (GTK_WIDGET (socket)))
804 if (focus_in != private->focus_in)
806 private->focus_in = focus_in;
809 _gtk_xembed_send_focus_message (private->plug_window,
810 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
812 _gtk_xembed_send_message (private->plug_window,
813 XEMBED_FOCUS_OUT, 0, 0, 0);
818 socket_update_active (GtkSocket *socket)
820 GtkSocketPrivate *private = socket->priv;
821 gboolean active = FALSE;
823 if (private->plug_window)
825 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
827 if (gtk_widget_is_toplevel (toplevel) &&
828 gtk_window_is_active (GTK_WINDOW (toplevel)))
832 if (active != private->active)
834 private->active = active;
836 _gtk_xembed_send_message (private->plug_window,
837 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
843 gtk_socket_hierarchy_changed (GtkWidget *widget,
844 GtkWidget *old_toplevel)
846 GtkSocket *socket = GTK_SOCKET (widget);
847 GtkSocketPrivate *private = socket->priv;
848 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
850 if (toplevel && !GTK_IS_WINDOW (toplevel))
853 if (toplevel != private->toplevel)
855 if (private->toplevel)
857 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
858 g_signal_handlers_disconnect_by_func (private->toplevel,
859 socket_update_focus_in,
861 g_signal_handlers_disconnect_by_func (private->toplevel,
862 socket_update_active,
866 private->toplevel = toplevel;
870 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
871 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
872 G_CALLBACK (socket_update_focus_in), socket);
873 g_signal_connect_swapped (private->toplevel, "notify::is-active",
874 G_CALLBACK (socket_update_active), socket);
877 socket_update_focus_in (socket);
878 socket_update_active (socket);
883 gtk_socket_grab_notify (GtkWidget *widget,
884 gboolean was_grabbed)
886 GtkSocket *socket = GTK_SOCKET (widget);
888 if (!socket->priv->same_app)
889 _gtk_xembed_send_message (socket->priv->plug_window,
890 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
895 gtk_socket_key_event (GtkWidget *widget,
898 GtkSocket *socket = GTK_SOCKET (widget);
899 GtkSocketPrivate *private = socket->priv;
901 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
903 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
912 gtk_socket_notify (GObject *object,
915 if (strcmp (pspec->name, "is-focus") == 0)
916 socket_update_focus_in (GTK_SOCKET (object));
918 if (G_OBJECT_CLASS (gtk_socket_parent_class)->notify)
919 G_OBJECT_CLASS (gtk_socket_parent_class)->notify (object, pspec);
923 * gtk_socket_claim_focus:
924 * @socket: a #GtkSocket
927 * Claims focus for the socket. XXX send_event?
930 gtk_socket_claim_focus (GtkSocket *socket,
933 GtkWidget *widget = GTK_WIDGET (socket);
934 GtkSocketPrivate *private = socket->priv;
937 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
939 /* Oh, the trickery... */
941 gtk_widget_set_can_focus (widget, TRUE);
942 gtk_widget_grab_focus (widget);
943 gtk_widget_set_can_focus (widget, FALSE);
947 gtk_socket_focus (GtkWidget *widget,
948 GtkDirectionType direction)
950 GtkSocket *socket = GTK_SOCKET (widget);
951 GtkSocketPrivate *private = socket->priv;
953 if (private->plug_widget)
954 return gtk_widget_child_focus (private->plug_widget, direction);
956 if (!gtk_widget_is_focus (widget))
964 case GTK_DIR_TAB_BACKWARD:
965 detail = XEMBED_FOCUS_LAST;
969 case GTK_DIR_TAB_FORWARD:
970 detail = XEMBED_FOCUS_FIRST;
974 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
975 gtk_socket_claim_focus (socket, FALSE);
984 gtk_socket_remove (GtkContainer *container,
987 GtkSocket *socket = GTK_SOCKET (container);
988 GtkSocketPrivate *private = socket->priv;
990 g_return_if_fail (child == private->plug_widget);
992 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
996 gtk_socket_forall (GtkContainer *container,
997 gboolean include_internals,
998 GtkCallback callback,
999 gpointer callback_data)
1001 GtkSocket *socket = GTK_SOCKET (container);
1002 GtkSocketPrivate *private = socket->priv;
1004 if (private->plug_widget)
1005 (* callback) (private->plug_widget, callback_data);
1009 * gtk_socket_add_window:
1010 * @socket: a #GtkSocket
1011 * @xid: the native identifier for a window
1012 * @need_reparent: whether the socket's plug's window needs to be
1013 * reparented to the socket
1015 * Adds a window to a GtkSocket.
1018 gtk_socket_add_window (GtkSocket *socket,
1020 gboolean need_reparent)
1022 GtkWidget *widget = GTK_WIDGET (socket);
1023 GdkDisplay *display = gtk_widget_get_display (widget);
1024 gpointer user_data = NULL;
1025 GtkSocketPrivate *private = socket->priv;
1026 unsigned long version;
1027 unsigned long flags;
1029 if (GDK_IS_X11_DISPLAY (display))
1030 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1032 private->plug_window = NULL;
1034 if (private->plug_window)
1036 g_object_ref (private->plug_window);
1037 gdk_window_get_user_data (private->plug_window, &user_data);
1040 if (user_data) /* A widget's window in this process */
1042 GtkWidget *child_widget = user_data;
1044 if (!GTK_IS_PLUG (child_widget))
1046 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1047 private->plug_window = NULL;
1048 gdk_error_trap_pop_ignored ();
1053 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1055 else /* A foreign window */
1057 GdkDragProtocol protocol;
1059 gdk_error_trap_push ();
1061 if (!private->plug_window)
1063 if (GDK_IS_X11_DISPLAY (display))
1064 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1065 if (!private->plug_window) /* was deleted before we could get it */
1067 gdk_error_trap_pop_ignored ();
1072 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1073 GDK_WINDOW_XID (private->plug_window),
1074 StructureNotifyMask | PropertyChangeMask);
1076 if (gdk_error_trap_pop ())
1078 g_object_unref (private->plug_window);
1079 private->plug_window = NULL;
1083 /* OK, we now will reliably get destroy notification on socket->plug_window */
1085 gdk_error_trap_push ();
1089 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1090 gdk_window_reparent (private->plug_window,
1091 gtk_widget_get_window (widget),
1095 private->have_size = FALSE;
1097 private->xembed_version = -1;
1098 if (xembed_get_info (private->plug_window, &version, &flags))
1100 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1101 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1105 /* FIXME, we should probably actually check the state before we started */
1106 private->is_mapped = TRUE;
1109 private->need_map = private->is_mapped;
1111 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1113 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1116 gdk_error_trap_pop_ignored ();
1118 gdk_window_add_filter (private->plug_window,
1119 gtk_socket_filter_func,
1123 gdk_error_trap_push ();
1124 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1125 GDK_WINDOW_XID (private->plug_window),
1126 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1127 gdk_error_trap_pop_ignored ();
1129 _gtk_xembed_send_message (private->plug_window,
1130 XEMBED_EMBEDDED_NOTIFY, 0,
1131 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1132 private->xembed_version);
1134 socket_update_active (socket);
1135 socket_update_focus_in (socket);
1137 gtk_widget_queue_resize (GTK_WIDGET (socket));
1140 if (private->plug_window)
1141 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1145 * gtk_socket_handle_map_request:
1146 * @socket: a #GtkSocket
1148 * Called from the GtkSocket backend when the plug has been mapped.
1151 gtk_socket_handle_map_request (GtkSocket *socket)
1153 GtkSocketPrivate *private = socket->priv;
1154 if (!private->is_mapped)
1156 private->is_mapped = TRUE;
1157 private->need_map = TRUE;
1159 gtk_widget_queue_resize (GTK_WIDGET (socket));
1164 * gtk_socket_unmap_notify:
1165 * @socket: a #GtkSocket
1167 * Called from the GtkSocket backend when the plug has been unmapped ???
1170 gtk_socket_unmap_notify (GtkSocket *socket)
1172 GtkSocketPrivate *private = socket->priv;
1173 if (private->is_mapped)
1175 private->is_mapped = FALSE;
1176 gtk_widget_queue_resize (GTK_WIDGET (socket));
1181 * gtk_socket_advance_toplevel_focus:
1182 * @socket: a #GtkSocket
1183 * @direction: a direction
1185 * Called from the GtkSocket backend when the corresponding plug
1186 * has told the socket to move the focus.
1189 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1190 GtkDirectionType direction)
1194 GtkContainer *container;
1196 GtkWidget *focus_widget;
1197 GtkWidget *toplevel;
1198 GtkWidget *old_focus_child;
1201 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1205 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1207 gtk_widget_child_focus (toplevel,direction);
1211 container = GTK_CONTAINER (toplevel);
1212 window = GTK_WINDOW (toplevel);
1213 bin = GTK_BIN (toplevel);
1215 /* This is a copy of gtk_window_focus(), modified so that we
1216 * can detect wrap-around.
1218 old_focus_child = gtk_container_get_focus_child (container);
1220 if (old_focus_child)
1222 if (gtk_widget_child_focus (old_focus_child, direction))
1225 /* We are allowed exactly one wrap-around per sequence of focus
1228 if (_gtk_xembed_get_focus_wrapped ())
1231 _gtk_xembed_set_focus_wrapped ();
1234 focus_widget = gtk_window_get_focus (window);
1237 /* Wrapped off the end, clear the focus setting for the toplevel */
1238 parent = gtk_widget_get_parent (focus_widget);
1241 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1242 parent = gtk_widget_get_parent (parent);
1245 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1248 /* Now try to focus the first widget in the window */
1249 child = gtk_bin_get_child (bin);
1252 if (gtk_widget_child_focus (child, direction))
1258 xembed_get_info (GdkWindow *window,
1259 unsigned long *version,
1260 unsigned long *flags)
1262 GdkDisplay *display = gdk_window_get_display (window);
1263 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1266 unsigned long nitems, bytes_after;
1267 unsigned char *data;
1268 unsigned long *data_long;
1271 gdk_error_trap_push ();
1272 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1273 GDK_WINDOW_XID (window),
1276 xembed_info_atom, &type, &format,
1277 &nitems, &bytes_after, &data);
1278 gdk_error_trap_pop_ignored ();
1280 if (status != Success)
1281 return FALSE; /* Window vanished? */
1283 if (type == None) /* No info property */
1286 if (type != xembed_info_atom)
1288 g_warning ("_XEMBED_INFO property has wrong type\n");
1294 g_warning ("_XEMBED_INFO too short\n");
1299 data_long = (unsigned long *)data;
1301 *version = data_long[0];
1303 *flags = data_long[1] & XEMBED_MAPPED;
1310 handle_xembed_message (GtkSocket *socket,
1311 XEmbedMessageType message,
1317 GTK_NOTE (PLUGSOCKET,
1318 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1322 case XEMBED_EMBEDDED_NOTIFY:
1323 case XEMBED_WINDOW_ACTIVATE:
1324 case XEMBED_WINDOW_DEACTIVATE:
1325 case XEMBED_MODALITY_ON:
1326 case XEMBED_MODALITY_OFF:
1327 case XEMBED_FOCUS_IN:
1328 case XEMBED_FOCUS_OUT:
1329 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1332 case XEMBED_REQUEST_FOCUS:
1333 gtk_socket_claim_focus (socket, TRUE);
1336 case XEMBED_FOCUS_NEXT:
1337 case XEMBED_FOCUS_PREV:
1338 gtk_socket_advance_toplevel_focus (socket,
1339 (message == XEMBED_FOCUS_NEXT ?
1340 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1343 case XEMBED_GTK_GRAB_KEY:
1344 gtk_socket_add_grabbed_key (socket, data1, data2);
1346 case XEMBED_GTK_UNGRAB_KEY:
1347 gtk_socket_remove_grabbed_key (socket, data1, data2);
1350 case XEMBED_GRAB_KEY:
1351 case XEMBED_UNGRAB_KEY:
1355 GTK_NOTE (PLUGSOCKET,
1356 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1361 static GdkFilterReturn
1362 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1368 GdkDisplay *display;
1370 GtkSocketPrivate *private;
1372 GdkFilterReturn return_val;
1374 socket = GTK_SOCKET (data);
1375 private = socket->priv;
1377 return_val = GDK_FILTER_CONTINUE;
1379 if (private->plug_widget)
1382 widget = GTK_WIDGET (socket);
1383 xevent = (XEvent *)gdk_xevent;
1384 display = gtk_widget_get_display (widget);
1386 switch (xevent->type)
1389 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1391 _gtk_xembed_push_message (xevent);
1392 handle_xembed_message (socket,
1393 xevent->xclient.data.l[1],
1394 xevent->xclient.data.l[2],
1395 xevent->xclient.data.l[3],
1396 xevent->xclient.data.l[4],
1397 xevent->xclient.data.l[0]);
1398 _gtk_xembed_pop_message ();
1400 return_val = GDK_FILTER_REMOVE;
1406 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1408 if (!private->plug_window)
1410 gtk_socket_add_window (socket, xcwe->window, FALSE);
1412 if (private->plug_window)
1414 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1418 return_val = GDK_FILTER_REMOVE;
1423 case ConfigureRequest:
1425 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1427 if (!private->plug_window)
1428 gtk_socket_add_window (socket, xcre->window, FALSE);
1430 if (private->plug_window)
1432 if (xcre->value_mask & (CWWidth | CWHeight))
1434 GTK_NOTE (PLUGSOCKET,
1435 g_message ("GtkSocket - configure request: %d %d",
1436 private->request_width,
1437 private->request_height));
1439 private->resize_count++;
1440 gtk_widget_queue_resize (widget);
1442 else if (xcre->value_mask & (CWX | CWY))
1444 gtk_socket_send_configure_event (socket);
1446 /* Ignore stacking requests. */
1448 return_val = GDK_FILTER_REMOVE;
1455 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1457 /* Note that we get destroy notifies both from SubstructureNotify on
1458 * our window and StructureNotify on socket->plug_window
1460 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1464 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1466 gdk_window_destroy_notify (private->plug_window);
1467 gtk_socket_end_embedding (socket);
1469 g_object_ref (widget);
1470 g_signal_emit_by_name (widget, "plug-removed", &result);
1472 gtk_widget_destroy (widget);
1473 g_object_unref (widget);
1475 return_val = GDK_FILTER_REMOVE;
1481 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1483 gtk_socket_claim_focus (socket, TRUE);
1485 return_val = GDK_FILTER_REMOVE;
1488 return_val = GDK_FILTER_REMOVE;
1491 if (!private->plug_window)
1493 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1496 if (private->plug_window)
1498 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1500 gtk_socket_handle_map_request (socket);
1501 return_val = GDK_FILTER_REMOVE;
1504 case PropertyNotify:
1505 if (private->plug_window &&
1506 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1508 GdkDragProtocol protocol;
1510 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1512 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1513 private->have_size = FALSE;
1514 gtk_widget_queue_resize (widget);
1515 return_val = GDK_FILTER_REMOVE;
1517 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1518 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1520 gdk_error_trap_push ();
1521 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1523 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1524 private->plug_window,
1527 gdk_error_trap_pop_ignored ();
1528 return_val = GDK_FILTER_REMOVE;
1530 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1532 unsigned long flags;
1534 if (xembed_get_info (private->plug_window, NULL, &flags))
1536 gboolean was_mapped = private->is_mapped;
1537 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1539 if (was_mapped != is_mapped)
1542 gtk_socket_handle_map_request (socket);
1545 gdk_error_trap_push ();
1546 gdk_window_show (private->plug_window);
1547 gdk_error_trap_pop_ignored ();
1549 gtk_socket_unmap_notify (socket);
1553 return_val = GDK_FILTER_REMOVE;
1557 case ReparentNotify:
1560 XReparentEvent *xre = &xevent->xreparent;
1562 window = gtk_widget_get_window (widget);
1564 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1565 if (!private->plug_window &&
1566 xre->parent == GDK_WINDOW_XID (window))
1568 gtk_socket_add_window (socket, xre->window, FALSE);
1570 if (private->plug_window)
1572 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1575 return_val = GDK_FILTER_REMOVE;
1579 if (private->plug_window &&
1580 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1581 xre->parent != GDK_WINDOW_XID (window))
1585 gtk_socket_end_embedding (socket);
1587 g_object_ref (widget);
1588 g_signal_emit_by_name (widget, "plug-removed", &result);
1590 gtk_widget_destroy (widget);
1591 g_object_unref (widget);
1593 return_val = GDK_FILTER_REMOVE;
1600 if (private->plug_window &&
1601 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1603 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1605 gtk_socket_unmap_notify (socket);
1606 return_val = GDK_FILTER_REMOVE;