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));
920 * gtk_socket_claim_focus:
921 * @socket: a #GtkSocket
924 * Claims focus for the socket. XXX send_event?
927 gtk_socket_claim_focus (GtkSocket *socket,
930 GtkWidget *widget = GTK_WIDGET (socket);
931 GtkSocketPrivate *private = socket->priv;
934 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
936 /* Oh, the trickery... */
938 gtk_widget_set_can_focus (widget, TRUE);
939 gtk_widget_grab_focus (widget);
940 gtk_widget_set_can_focus (widget, FALSE);
944 gtk_socket_focus (GtkWidget *widget,
945 GtkDirectionType direction)
947 GtkSocket *socket = GTK_SOCKET (widget);
948 GtkSocketPrivate *private = socket->priv;
950 if (private->plug_widget)
951 return gtk_widget_child_focus (private->plug_widget, direction);
953 if (!gtk_widget_is_focus (widget))
961 case GTK_DIR_TAB_BACKWARD:
962 detail = XEMBED_FOCUS_LAST;
966 case GTK_DIR_TAB_FORWARD:
967 detail = XEMBED_FOCUS_FIRST;
971 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
972 gtk_socket_claim_focus (socket, FALSE);
981 gtk_socket_remove (GtkContainer *container,
984 GtkSocket *socket = GTK_SOCKET (container);
985 GtkSocketPrivate *private = socket->priv;
987 g_return_if_fail (child == private->plug_widget);
989 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
993 gtk_socket_forall (GtkContainer *container,
994 gboolean include_internals,
995 GtkCallback callback,
996 gpointer callback_data)
998 GtkSocket *socket = GTK_SOCKET (container);
999 GtkSocketPrivate *private = socket->priv;
1001 if (private->plug_widget)
1002 (* callback) (private->plug_widget, callback_data);
1006 * gtk_socket_add_window:
1007 * @socket: a #GtkSocket
1008 * @xid: the native identifier for a window
1009 * @need_reparent: whether the socket's plug's window needs to be
1010 * reparented to the socket
1012 * Adds a window to a GtkSocket.
1015 gtk_socket_add_window (GtkSocket *socket,
1017 gboolean need_reparent)
1019 GtkWidget *widget = GTK_WIDGET (socket);
1020 GdkDisplay *display = gtk_widget_get_display (widget);
1021 gpointer user_data = NULL;
1022 GtkSocketPrivate *private = socket->priv;
1023 unsigned long version;
1024 unsigned long flags;
1026 if (GDK_IS_X11_DISPLAY (display))
1027 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1029 private->plug_window = NULL;
1031 if (private->plug_window)
1033 g_object_ref (private->plug_window);
1034 gdk_window_get_user_data (private->plug_window, &user_data);
1037 if (user_data) /* A widget's window in this process */
1039 GtkWidget *child_widget = user_data;
1041 if (!GTK_IS_PLUG (child_widget))
1043 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1044 private->plug_window = NULL;
1045 gdk_error_trap_pop_ignored ();
1050 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1052 else /* A foreign window */
1054 GdkDragProtocol protocol;
1056 gdk_error_trap_push ();
1058 if (!private->plug_window)
1060 if (GDK_IS_X11_DISPLAY (display))
1061 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1062 if (!private->plug_window) /* was deleted before we could get it */
1064 gdk_error_trap_pop_ignored ();
1069 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1070 GDK_WINDOW_XID (private->plug_window),
1071 StructureNotifyMask | PropertyChangeMask);
1073 if (gdk_error_trap_pop ())
1075 g_object_unref (private->plug_window);
1076 private->plug_window = NULL;
1080 /* OK, we now will reliably get destroy notification on socket->plug_window */
1082 gdk_error_trap_push ();
1086 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1087 gdk_window_reparent (private->plug_window,
1088 gtk_widget_get_window (widget),
1092 private->have_size = FALSE;
1094 private->xembed_version = -1;
1095 if (xembed_get_info (private->plug_window, &version, &flags))
1097 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1098 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1102 /* FIXME, we should probably actually check the state before we started */
1103 private->is_mapped = TRUE;
1106 private->need_map = private->is_mapped;
1108 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1110 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1113 gdk_error_trap_pop_ignored ();
1115 gdk_window_add_filter (private->plug_window,
1116 gtk_socket_filter_func,
1120 gdk_error_trap_push ();
1121 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1122 GDK_WINDOW_XID (private->plug_window),
1123 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1124 gdk_error_trap_pop_ignored ();
1126 _gtk_xembed_send_message (private->plug_window,
1127 XEMBED_EMBEDDED_NOTIFY, 0,
1128 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1129 private->xembed_version);
1131 socket_update_active (socket);
1132 socket_update_focus_in (socket);
1134 gtk_widget_queue_resize (GTK_WIDGET (socket));
1137 if (private->plug_window)
1138 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1142 * gtk_socket_handle_map_request:
1143 * @socket: a #GtkSocket
1145 * Called from the GtkSocket backend when the plug has been mapped.
1148 gtk_socket_handle_map_request (GtkSocket *socket)
1150 GtkSocketPrivate *private = socket->priv;
1151 if (!private->is_mapped)
1153 private->is_mapped = TRUE;
1154 private->need_map = TRUE;
1156 gtk_widget_queue_resize (GTK_WIDGET (socket));
1161 * gtk_socket_unmap_notify:
1162 * @socket: a #GtkSocket
1164 * Called from the GtkSocket backend when the plug has been unmapped ???
1167 gtk_socket_unmap_notify (GtkSocket *socket)
1169 GtkSocketPrivate *private = socket->priv;
1170 if (private->is_mapped)
1172 private->is_mapped = FALSE;
1173 gtk_widget_queue_resize (GTK_WIDGET (socket));
1178 * gtk_socket_advance_toplevel_focus:
1179 * @socket: a #GtkSocket
1180 * @direction: a direction
1182 * Called from the GtkSocket backend when the corresponding plug
1183 * has told the socket to move the focus.
1186 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1187 GtkDirectionType direction)
1191 GtkContainer *container;
1193 GtkWidget *focus_widget;
1194 GtkWidget *toplevel;
1195 GtkWidget *old_focus_child;
1198 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1202 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1204 gtk_widget_child_focus (toplevel,direction);
1208 container = GTK_CONTAINER (toplevel);
1209 window = GTK_WINDOW (toplevel);
1210 bin = GTK_BIN (toplevel);
1212 /* This is a copy of gtk_window_focus(), modified so that we
1213 * can detect wrap-around.
1215 old_focus_child = gtk_container_get_focus_child (container);
1217 if (old_focus_child)
1219 if (gtk_widget_child_focus (old_focus_child, direction))
1222 /* We are allowed exactly one wrap-around per sequence of focus
1225 if (_gtk_xembed_get_focus_wrapped ())
1228 _gtk_xembed_set_focus_wrapped ();
1231 focus_widget = gtk_window_get_focus (window);
1234 /* Wrapped off the end, clear the focus setting for the toplevel */
1235 parent = gtk_widget_get_parent (focus_widget);
1238 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1239 parent = gtk_widget_get_parent (parent);
1242 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1245 /* Now try to focus the first widget in the window */
1246 child = gtk_bin_get_child (bin);
1249 if (gtk_widget_child_focus (child, direction))
1255 xembed_get_info (GdkWindow *window,
1256 unsigned long *version,
1257 unsigned long *flags)
1259 GdkDisplay *display = gdk_window_get_display (window);
1260 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1263 unsigned long nitems, bytes_after;
1264 unsigned char *data;
1265 unsigned long *data_long;
1268 gdk_error_trap_push ();
1269 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1270 GDK_WINDOW_XID (window),
1273 xembed_info_atom, &type, &format,
1274 &nitems, &bytes_after, &data);
1275 gdk_error_trap_pop_ignored ();
1277 if (status != Success)
1278 return FALSE; /* Window vanished? */
1280 if (type == None) /* No info property */
1283 if (type != xembed_info_atom)
1285 g_warning ("_XEMBED_INFO property has wrong type\n");
1291 g_warning ("_XEMBED_INFO too short\n");
1296 data_long = (unsigned long *)data;
1298 *version = data_long[0];
1300 *flags = data_long[1] & XEMBED_MAPPED;
1307 handle_xembed_message (GtkSocket *socket,
1308 XEmbedMessageType message,
1314 GTK_NOTE (PLUGSOCKET,
1315 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1319 case XEMBED_EMBEDDED_NOTIFY:
1320 case XEMBED_WINDOW_ACTIVATE:
1321 case XEMBED_WINDOW_DEACTIVATE:
1322 case XEMBED_MODALITY_ON:
1323 case XEMBED_MODALITY_OFF:
1324 case XEMBED_FOCUS_IN:
1325 case XEMBED_FOCUS_OUT:
1326 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1329 case XEMBED_REQUEST_FOCUS:
1330 gtk_socket_claim_focus (socket, TRUE);
1333 case XEMBED_FOCUS_NEXT:
1334 case XEMBED_FOCUS_PREV:
1335 gtk_socket_advance_toplevel_focus (socket,
1336 (message == XEMBED_FOCUS_NEXT ?
1337 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1340 case XEMBED_GTK_GRAB_KEY:
1341 gtk_socket_add_grabbed_key (socket, data1, data2);
1343 case XEMBED_GTK_UNGRAB_KEY:
1344 gtk_socket_remove_grabbed_key (socket, data1, data2);
1347 case XEMBED_GRAB_KEY:
1348 case XEMBED_UNGRAB_KEY:
1352 GTK_NOTE (PLUGSOCKET,
1353 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1358 static GdkFilterReturn
1359 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1365 GdkDisplay *display;
1367 GtkSocketPrivate *private;
1369 GdkFilterReturn return_val;
1371 socket = GTK_SOCKET (data);
1372 private = socket->priv;
1374 return_val = GDK_FILTER_CONTINUE;
1376 if (private->plug_widget)
1379 widget = GTK_WIDGET (socket);
1380 xevent = (XEvent *)gdk_xevent;
1381 display = gtk_widget_get_display (widget);
1383 switch (xevent->type)
1386 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1388 _gtk_xembed_push_message (xevent);
1389 handle_xembed_message (socket,
1390 xevent->xclient.data.l[1],
1391 xevent->xclient.data.l[2],
1392 xevent->xclient.data.l[3],
1393 xevent->xclient.data.l[4],
1394 xevent->xclient.data.l[0]);
1395 _gtk_xembed_pop_message ();
1397 return_val = GDK_FILTER_REMOVE;
1403 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1405 if (!private->plug_window)
1407 gtk_socket_add_window (socket, xcwe->window, FALSE);
1409 if (private->plug_window)
1411 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1415 return_val = GDK_FILTER_REMOVE;
1420 case ConfigureRequest:
1422 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1424 if (!private->plug_window)
1425 gtk_socket_add_window (socket, xcre->window, FALSE);
1427 if (private->plug_window)
1429 if (xcre->value_mask & (CWWidth | CWHeight))
1431 GTK_NOTE (PLUGSOCKET,
1432 g_message ("GtkSocket - configure request: %d %d",
1433 private->request_width,
1434 private->request_height));
1436 private->resize_count++;
1437 gtk_widget_queue_resize (widget);
1439 else if (xcre->value_mask & (CWX | CWY))
1441 gtk_socket_send_configure_event (socket);
1443 /* Ignore stacking requests. */
1445 return_val = GDK_FILTER_REMOVE;
1452 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1454 /* Note that we get destroy notifies both from SubstructureNotify on
1455 * our window and StructureNotify on socket->plug_window
1457 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1461 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1463 gdk_window_destroy_notify (private->plug_window);
1464 gtk_socket_end_embedding (socket);
1466 g_object_ref (widget);
1467 g_signal_emit_by_name (widget, "plug-removed", &result);
1469 gtk_widget_destroy (widget);
1470 g_object_unref (widget);
1472 return_val = GDK_FILTER_REMOVE;
1478 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1480 gtk_socket_claim_focus (socket, TRUE);
1482 return_val = GDK_FILTER_REMOVE;
1485 return_val = GDK_FILTER_REMOVE;
1488 if (!private->plug_window)
1490 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1493 if (private->plug_window)
1495 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1497 gtk_socket_handle_map_request (socket);
1498 return_val = GDK_FILTER_REMOVE;
1501 case PropertyNotify:
1502 if (private->plug_window &&
1503 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1505 GdkDragProtocol protocol;
1507 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1509 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1510 private->have_size = FALSE;
1511 gtk_widget_queue_resize (widget);
1512 return_val = GDK_FILTER_REMOVE;
1514 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1515 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1517 gdk_error_trap_push ();
1518 protocol = gdk_window_get_drag_protocol (private->plug_window, NULL);
1520 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1521 private->plug_window,
1524 gdk_error_trap_pop_ignored ();
1525 return_val = GDK_FILTER_REMOVE;
1527 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1529 unsigned long flags;
1531 if (xembed_get_info (private->plug_window, NULL, &flags))
1533 gboolean was_mapped = private->is_mapped;
1534 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1536 if (was_mapped != is_mapped)
1539 gtk_socket_handle_map_request (socket);
1542 gdk_error_trap_push ();
1543 gdk_window_show (private->plug_window);
1544 gdk_error_trap_pop_ignored ();
1546 gtk_socket_unmap_notify (socket);
1550 return_val = GDK_FILTER_REMOVE;
1554 case ReparentNotify:
1557 XReparentEvent *xre = &xevent->xreparent;
1559 window = gtk_widget_get_window (widget);
1561 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1562 if (!private->plug_window &&
1563 xre->parent == GDK_WINDOW_XID (window))
1565 gtk_socket_add_window (socket, xre->window, FALSE);
1567 if (private->plug_window)
1569 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1572 return_val = GDK_FILTER_REMOVE;
1576 if (private->plug_window &&
1577 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1578 xre->parent != GDK_WINDOW_XID (window))
1582 gtk_socket_end_embedding (socket);
1584 g_object_ref (widget);
1585 g_signal_emit_by_name (widget, "plug-removed", &result);
1587 gtk_widget_destroy (widget);
1588 g_object_unref (widget);
1590 return_val = GDK_FILTER_REMOVE;
1597 if (private->plug_window &&
1598 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1600 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1602 gtk_socket_unmap_notify (socket);
1603 return_val = GDK_FILTER_REMOVE;