1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30 #include "gtksocketprivate.h"
34 #include "gtkmainprivate.h"
35 #include "gtkmarshalers.h"
36 #include "gtksizerequest.h"
37 #include "gtkwindowprivate.h"
39 #include "gtkprivate.h"
43 #include "gtkwidgetprivate.h"
46 #include <gdk/gdkprivate.h>
49 #include <X11/extensions/Xfixes.h>
52 #include "gtkxembed.h"
57 * @Short_description: Container for widgets from other processes
59 * @See_also: #GtkPlug, <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
61 * Together with #GtkPlug, #GtkSocket provides the ability
62 * to embed widgets from one process into another process
63 * in a fashion that is transparent to the user. One
64 * process creates a #GtkSocket widget and passes
65 * that widget's window ID to the other process,
66 * which then creates a #GtkPlug with that window ID.
67 * Any widgets contained in the #GtkPlug then will appear
68 * inside the first application's window.
70 * The socket's window ID is obtained by using
71 * gtk_socket_get_id(). Before using this function,
72 * the socket must have been realized, and for hence,
73 * have been added to its parent.
76 * <title>Obtaining the window ID of a socket.</title>
78 * GtkWidget *socket = gtk_socket_new (<!-- -->);
79 * gtk_widget_show (socket);
80 * gtk_container_add (GTK_CONTAINER (parent), socket);
82 * /<!---->* The following call is only necessary if one of
83 * * the ancestors of the socket is not yet visible.
85 * gtk_widget_realize (socket);
86 * g_print ("The ID of the sockets window is %#x\n",
87 * gtk_socket_get_id (socket));
91 * Note that if you pass the window ID of the socket to another
92 * process that will create a plug in the socket, you
93 * must make sure that the socket widget is not destroyed
94 * until that plug is created. Violating this rule will
95 * cause unpredictable consequences, the most likely
96 * consequence being that the plug will appear as a
97 * separate toplevel window. You can check if the plug
98 * has been created by using gtk_socket_get_plug_window(). If
99 * it returns a non-%NULL value, then the plug has been
100 * successfully created inside of the socket.
102 * When GTK+ is notified that the embedded window has been
103 * destroyed, then it will destroy the socket as well. You
104 * should always, therefore, be prepared for your sockets
105 * to be destroyed at any time when the main event loop
106 * is running. To prevent this from happening, you can
107 * connect to the #GtkSocket::plug-removed signal.
109 * The communication between a #GtkSocket and a #GtkPlug follows the
110 * <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
111 * protocol. This protocol has also been implemented in other toolkits, e.g.
112 * <application>Qt</application>, allowing the same level of integration
113 * when embedding a <application>Qt</application> widget in GTK or vice versa.
116 * The #GtkPlug and #GtkSocket widgets are only available when GTK is
117 * compiled for the X11 platform and %GDK_WINDOWING_X11 is defined.
118 * They can only be used on a #GdkX11Display.
122 /* Forward declararations */
124 static void gtk_socket_finalize (GObject *object);
125 static void gtk_socket_notify (GObject *object,
127 static void gtk_socket_realize (GtkWidget *widget);
128 static void gtk_socket_unrealize (GtkWidget *widget);
129 static void gtk_socket_get_preferred_width (GtkWidget *widget,
132 static void gtk_socket_get_preferred_height (GtkWidget *widget,
135 static void gtk_socket_size_allocate (GtkWidget *widget,
136 GtkAllocation *allocation);
137 static void gtk_socket_hierarchy_changed (GtkWidget *widget,
138 GtkWidget *old_toplevel);
139 static void gtk_socket_grab_notify (GtkWidget *widget,
140 gboolean was_grabbed);
141 static gboolean gtk_socket_key_event (GtkWidget *widget,
143 static gboolean gtk_socket_focus (GtkWidget *widget,
144 GtkDirectionType direction);
145 static void gtk_socket_remove (GtkContainer *container,
147 static void gtk_socket_forall (GtkContainer *container,
148 gboolean include_internals,
149 GtkCallback callback,
150 gpointer callback_data);
151 static void gtk_socket_add_window (GtkSocket *socket,
153 gboolean need_reparent);
154 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
158 static gboolean xembed_get_info (GdkWindow *gdk_window,
159 unsigned long *version,
160 unsigned long *flags);
163 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
171 GdkModifierType accel_mods;
180 static guint socket_signals[LAST_SIGNAL] = { 0 };
182 G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
185 gtk_socket_finalize (GObject *object)
187 GtkSocket *socket = GTK_SOCKET (object);
188 GtkSocketPrivate *priv = socket->priv;
190 g_object_unref (priv->accel_group);
192 G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
196 gtk_socket_class_init (GtkSocketClass *class)
198 GtkWidgetClass *widget_class;
199 GtkContainerClass *container_class;
200 GObjectClass *gobject_class;
202 gobject_class = (GObjectClass *) class;
203 widget_class = (GtkWidgetClass*) class;
204 container_class = (GtkContainerClass*) class;
206 gobject_class->finalize = gtk_socket_finalize;
207 gobject_class->notify = gtk_socket_notify;
209 widget_class->realize = gtk_socket_realize;
210 widget_class->unrealize = gtk_socket_unrealize;
211 widget_class->get_preferred_width = gtk_socket_get_preferred_width;
212 widget_class->get_preferred_height = gtk_socket_get_preferred_height;
213 widget_class->size_allocate = gtk_socket_size_allocate;
214 widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
215 widget_class->grab_notify = gtk_socket_grab_notify;
216 widget_class->key_press_event = gtk_socket_key_event;
217 widget_class->key_release_event = gtk_socket_key_event;
218 widget_class->focus = gtk_socket_focus;
220 /* We don't want to show_all the in-process plug, if any.
222 widget_class->show_all = gtk_widget_show;
224 container_class->remove = gtk_socket_remove;
225 container_class->forall = gtk_socket_forall;
228 * GtkSocket::plug-added:
229 * @socket_: the object which received the signal
231 * This signal is emitted when a client is successfully
232 * added to the socket.
234 socket_signals[PLUG_ADDED] =
235 g_signal_new (I_("plug-added"),
236 G_OBJECT_CLASS_TYPE (class),
238 G_STRUCT_OFFSET (GtkSocketClass, plug_added),
240 _gtk_marshal_VOID__VOID,
244 * GtkSocket::plug-removed:
245 * @socket_: the object which received the signal
247 * This signal is emitted when a client is removed from the socket.
248 * The default action is to destroy the #GtkSocket widget, so if you
249 * want to reuse it you must add a signal handler that returns %TRUE.
251 * Return value: %TRUE to stop other handlers from being invoked.
253 socket_signals[PLUG_REMOVED] =
254 g_signal_new (I_("plug-removed"),
255 G_OBJECT_CLASS_TYPE (class),
257 G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
258 _gtk_boolean_handled_accumulator, NULL,
259 _gtk_marshal_BOOLEAN__VOID,
262 g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate));
266 gtk_socket_init (GtkSocket *socket)
268 GtkSocketPrivate *priv;
270 priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
274 priv->request_width = 0;
275 priv->request_height = 0;
276 priv->current_width = 0;
277 priv->current_height = 0;
279 priv->plug_window = NULL;
280 priv->plug_widget = NULL;
281 priv->focus_in = FALSE;
282 priv->have_size = FALSE;
283 priv->need_map = FALSE;
284 priv->active = FALSE;
286 priv->accel_group = gtk_accel_group_new ();
287 g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
293 * Create a new empty #GtkSocket.
295 * Return value: the new #GtkSocket.
298 gtk_socket_new (void)
302 socket = g_object_new (GTK_TYPE_SOCKET, NULL);
304 return GTK_WIDGET (socket);
309 * @socket_: a #GtkSocket
310 * @window: the Window of a client participating in the XEMBED protocol.
312 * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The
313 * client may be in the same process or in a different process.
315 * To embed a #GtkPlug in a #GtkSocket, you can either create the
316 * #GtkPlug with <literal>gtk_plug_new (0)</literal>, call
317 * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
318 * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
319 * window ID for the socket, and call gtk_plug_new() passing in that
322 * The #GtkSocket must have already be added into a toplevel window
323 * before you can make this call.
326 gtk_socket_add_id (GtkSocket *socket,
329 g_return_if_fail (GTK_IS_SOCKET (socket));
330 g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
332 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
333 gtk_widget_realize (GTK_WIDGET (socket));
335 gtk_socket_add_window (socket, window, TRUE);
340 * @socket_: a #GtkSocket.
342 * Gets the window ID of a #GtkSocket widget, which can then
343 * be used to create a client embedded inside the socket, for
344 * instance with gtk_plug_new().
346 * The #GtkSocket must have already be added into a toplevel window
347 * before you can make this call.
349 * Return value: the window ID for the socket
352 gtk_socket_get_id (GtkSocket *socket)
354 g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
355 g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
357 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
358 gtk_widget_realize (GTK_WIDGET (socket));
360 return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
364 * gtk_socket_get_plug_window:
365 * @socket_: a #GtkSocket.
367 * Retrieves the window of the plug. Use this to check if the plug has
368 * been created inside of the socket.
370 * Return value: (transfer none): the window of the plug if available, or %NULL
375 gtk_socket_get_plug_window (GtkSocket *socket)
377 g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);
379 return socket->priv->plug_window;
383 gtk_socket_realize (GtkWidget *widget)
385 GtkAllocation allocation;
386 GtkSocket *socket = GTK_SOCKET (widget);
388 GdkWindowAttr attributes;
389 XWindowAttributes xattrs;
390 gint attributes_mask;
392 gtk_widget_set_realized (widget, TRUE);
394 gtk_widget_get_allocation (widget, &allocation);
396 attributes.window_type = GDK_WINDOW_CHILD;
397 attributes.x = allocation.x;
398 attributes.y = allocation.y;
399 attributes.width = allocation.width;
400 attributes.height = allocation.height;
401 attributes.wclass = GDK_INPUT_OUTPUT;
402 attributes.visual = gtk_widget_get_visual (widget);
403 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
405 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
407 window = gdk_window_new (gtk_widget_get_parent_window (widget),
408 &attributes, attributes_mask);
409 gtk_widget_set_window (widget, window);
410 gdk_window_set_user_data (window, socket);
412 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
415 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
416 GDK_WINDOW_XID (window),
419 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
420 for input on the socket with a mask of 0x0fffff (for god knows why)
421 which includes ButtonPressMask causing a BadAccess if someone else
422 also selects for this. As per the client-side windows merge we always
423 normally selects for button press so we can emulate it on client
424 side children that selects for button press. However, we don't need
425 this for GtkSocket, so we unselect it here, fixing the crashes in
427 XSelectInput (GDK_WINDOW_XDISPLAY (window),
428 GDK_WINDOW_XID (window),
429 (xattrs.your_event_mask & ~ButtonPressMask) |
430 SubstructureNotifyMask | SubstructureRedirectMask);
432 gdk_window_add_filter (window,
433 gtk_socket_filter_func,
436 /* We sync here so that we make sure that if the XID for
437 * our window is passed to another application, SubstructureRedirectMask
438 * will be set by the time the other app creates its window.
440 gdk_display_sync (gtk_widget_get_display (widget));
444 * gtk_socket_end_embedding:
446 * @socket: a #GtkSocket
448 * Called to end the embedding of a plug in the socket.
451 gtk_socket_end_embedding (GtkSocket *socket)
453 GtkSocketPrivate *private = socket->priv;
454 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
456 if (GTK_IS_WINDOW (toplevel))
457 _gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
458 GDK_WINDOW_XID (socket->priv->plug_window));
460 g_object_unref (private->plug_window);
461 private->plug_window = NULL;
462 private->current_width = 0;
463 private->current_height = 0;
464 private->resize_count = 0;
466 gtk_accel_group_disconnect (private->accel_group, NULL);
470 gtk_socket_unrealize (GtkWidget *widget)
472 GtkSocket *socket = GTK_SOCKET (widget);
473 GtkSocketPrivate *private = socket->priv;
475 gtk_widget_set_realized (widget, FALSE);
477 if (private->plug_widget)
479 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
481 else if (private->plug_window)
483 gtk_socket_end_embedding (socket);
486 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
490 gtk_socket_size_request (GtkSocket *socket)
492 GtkSocketPrivate *private = socket->priv;
496 gdk_error_trap_push ();
498 private->request_width = 1;
499 private->request_height = 1;
501 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
502 GDK_WINDOW_XID (private->plug_window),
505 if (hints.flags & PMinSize)
507 private->request_width = MAX (hints.min_width, 1);
508 private->request_height = MAX (hints.min_height, 1);
510 else if (hints.flags & PBaseSize)
512 private->request_width = MAX (hints.base_width, 1);
513 private->request_height = MAX (hints.base_height, 1);
516 private->have_size = TRUE;
518 gdk_error_trap_pop_ignored ();
522 gtk_socket_get_preferred_width (GtkWidget *widget,
526 GtkSocket *socket = GTK_SOCKET (widget);
527 GtkSocketPrivate *private = socket->priv;
529 if (private->plug_widget)
531 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
535 if (private->is_mapped && !private->have_size && private->plug_window)
536 gtk_socket_size_request (socket);
538 if (private->is_mapped && private->have_size)
539 *minimum = *natural = MAX (private->request_width, 1);
541 *minimum = *natural = 1;
546 gtk_socket_get_preferred_height (GtkWidget *widget,
550 GtkSocket *socket = GTK_SOCKET (widget);
551 GtkSocketPrivate *private = socket->priv;
553 if (private->plug_widget)
555 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
559 if (private->is_mapped && !private->have_size && private->plug_window)
560 gtk_socket_size_request (socket);
562 if (private->is_mapped && private->have_size)
563 *minimum = *natural = MAX (private->request_height, 1);
565 *minimum = *natural = 1;
570 gtk_socket_send_configure_event (GtkSocket *socket)
572 GtkAllocation allocation;
573 XConfigureEvent xconfigure;
576 g_return_if_fail (socket->priv->plug_window != NULL);
578 memset (&xconfigure, 0, sizeof (xconfigure));
579 xconfigure.type = ConfigureNotify;
581 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
582 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
584 /* The ICCCM says that synthetic events should have root relative
585 * coordinates. We still aren't really ICCCM compliant, since
586 * we don't send events when the real toplevel is moved.
588 gdk_error_trap_push ();
589 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
590 gdk_error_trap_pop_ignored ();
592 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
595 xconfigure.width = allocation.width;
596 xconfigure.height = allocation.height;
598 xconfigure.border_width = 0;
599 xconfigure.above = None;
600 xconfigure.override_redirect = False;
602 gdk_error_trap_push ();
603 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
604 GDK_WINDOW_XID (socket->priv->plug_window),
605 False, NoEventMask, (XEvent *)&xconfigure);
606 gdk_error_trap_pop_ignored ();
610 gtk_socket_size_allocate (GtkWidget *widget,
611 GtkAllocation *allocation)
613 GtkSocket *socket = GTK_SOCKET (widget);
614 GtkSocketPrivate *private = socket->priv;
616 gtk_widget_set_allocation (widget, allocation);
617 if (gtk_widget_get_realized (widget))
619 gdk_window_move_resize (gtk_widget_get_window (widget),
620 allocation->x, allocation->y,
621 allocation->width, allocation->height);
623 if (private->plug_widget)
625 GtkAllocation child_allocation;
627 child_allocation.x = 0;
628 child_allocation.y = 0;
629 child_allocation.width = allocation->width;
630 child_allocation.height = allocation->height;
632 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
634 else if (private->plug_window)
636 gdk_error_trap_push ();
638 if (allocation->width != private->current_width ||
639 allocation->height != private->current_height)
641 gdk_window_move_resize (private->plug_window,
643 allocation->width, allocation->height);
644 if (private->resize_count)
645 private->resize_count--;
647 GTK_NOTE (PLUGSOCKET,
648 g_message ("GtkSocket - allocated: %d %d",
649 allocation->width, allocation->height));
650 private->current_width = allocation->width;
651 private->current_height = allocation->height;
654 if (private->need_map)
656 gdk_window_show (private->plug_window);
657 private->need_map = FALSE;
660 while (private->resize_count)
662 gtk_socket_send_configure_event (socket);
663 private->resize_count--;
664 GTK_NOTE (PLUGSOCKET,
665 g_message ("GtkSocket - sending synthetic configure: %d %d",
666 allocation->width, allocation->height));
669 gdk_error_trap_pop_ignored ();
675 gtk_socket_send_key_event (GtkSocket *socket,
677 gboolean mask_key_presses)
680 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
682 memset (&xkey, 0, sizeof (xkey));
683 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
684 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
685 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
686 xkey.subwindow = None;
687 xkey.time = gdk_event->key.time;
692 xkey.state = gdk_event->key.state;
693 xkey.keycode = gdk_event->key.hardware_keycode;
694 xkey.same_screen = True;/* FIXME ? */
696 gdk_error_trap_push ();
697 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
698 GDK_WINDOW_XID (socket->priv->plug_window),
700 (mask_key_presses ? KeyPressMask : NoEventMask),
702 gdk_error_trap_pop_ignored ();
706 activate_key (GtkAccelGroup *accel_group,
707 GObject *acceleratable,
709 GdkModifierType accel_mods,
710 GrabbedKey *grabbed_key)
712 GdkEvent *gdk_event = gtk_get_current_event ();
714 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
715 gboolean retval = FALSE;
717 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
719 gtk_socket_send_key_event (socket, gdk_event, FALSE);
724 gdk_event_free (gdk_event);
730 find_accel_key (GtkAccelKey *key,
734 GrabbedKey *grabbed_key = data;
736 return (key->accel_key == grabbed_key->accel_key &&
737 key->accel_mods == grabbed_key->accel_mods);
741 * gtk_socket_add_grabbed_key:
743 * @socket: a #GtkSocket
745 * @modifiers: modifiers for the key
747 * Called from the GtkSocket platform-specific backend when the
748 * corresponding plug has told the socket to grab a key.
751 gtk_socket_add_grabbed_key (GtkSocket *socket,
753 GdkModifierType modifiers)
756 GrabbedKey *grabbed_key;
758 grabbed_key = g_new (GrabbedKey, 1);
760 grabbed_key->accel_key = keyval;
761 grabbed_key->accel_mods = modifiers;
763 if (gtk_accel_group_find (socket->priv->accel_group,
767 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
769 g_free (grabbed_key);
773 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
775 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
780 * gtk_socket_remove_grabbed_key:
782 * @socket: a #GtkSocket
784 * @modifiers: modifiers for the key
786 * Called from the GtkSocket backend when the corresponding plug has
787 * told the socket to remove a key grab.
790 gtk_socket_remove_grabbed_key (GtkSocket *socket,
792 GdkModifierType modifiers)
794 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
795 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
800 socket_update_focus_in (GtkSocket *socket)
802 GtkSocketPrivate *private = socket->priv;
803 gboolean focus_in = FALSE;
805 if (private->plug_window)
807 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
809 if (gtk_widget_is_toplevel (toplevel) &&
810 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
811 gtk_widget_is_focus (GTK_WIDGET (socket)))
815 if (focus_in != private->focus_in)
817 private->focus_in = focus_in;
820 _gtk_xembed_send_focus_message (private->plug_window,
821 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
823 _gtk_xembed_send_message (private->plug_window,
824 XEMBED_FOCUS_OUT, 0, 0, 0);
829 socket_update_active (GtkSocket *socket)
831 GtkSocketPrivate *private = socket->priv;
832 gboolean active = FALSE;
834 if (private->plug_window)
836 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
838 if (gtk_widget_is_toplevel (toplevel) &&
839 gtk_window_is_active (GTK_WINDOW (toplevel)))
843 if (active != private->active)
845 private->active = active;
847 _gtk_xembed_send_message (private->plug_window,
848 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
854 gtk_socket_hierarchy_changed (GtkWidget *widget,
855 GtkWidget *old_toplevel)
857 GtkSocket *socket = GTK_SOCKET (widget);
858 GtkSocketPrivate *private = socket->priv;
859 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
861 if (toplevel && !GTK_IS_WINDOW (toplevel))
864 if (toplevel != private->toplevel)
866 if (private->toplevel)
868 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
869 g_signal_handlers_disconnect_by_func (private->toplevel,
870 socket_update_focus_in,
872 g_signal_handlers_disconnect_by_func (private->toplevel,
873 socket_update_active,
877 private->toplevel = toplevel;
881 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
882 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
883 G_CALLBACK (socket_update_focus_in), socket);
884 g_signal_connect_swapped (private->toplevel, "notify::is-active",
885 G_CALLBACK (socket_update_active), socket);
888 socket_update_focus_in (socket);
889 socket_update_active (socket);
894 gtk_socket_grab_notify (GtkWidget *widget,
895 gboolean was_grabbed)
897 GtkSocket *socket = GTK_SOCKET (widget);
899 if (!socket->priv->same_app)
900 _gtk_xembed_send_message (socket->priv->plug_window,
901 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
906 gtk_socket_key_event (GtkWidget *widget,
909 GtkSocket *socket = GTK_SOCKET (widget);
910 GtkSocketPrivate *private = socket->priv;
912 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
914 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
923 gtk_socket_notify (GObject *object,
926 if (!strcmp (pspec->name, "is-focus"))
928 socket_update_focus_in (GTK_SOCKET (object));
932 * gtk_socket_claim_focus:
934 * @socket: a #GtkSocket
937 * Claims focus for the socket. XXX send_event?
940 gtk_socket_claim_focus (GtkSocket *socket,
943 GtkWidget *widget = GTK_WIDGET (socket);
944 GtkSocketPrivate *private = socket->priv;
947 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
949 /* Oh, the trickery... */
951 gtk_widget_set_can_focus (widget, TRUE);
952 gtk_widget_grab_focus (widget);
953 gtk_widget_set_can_focus (widget, FALSE);
957 gtk_socket_focus (GtkWidget *widget,
958 GtkDirectionType direction)
960 GtkSocket *socket = GTK_SOCKET (widget);
961 GtkSocketPrivate *private = socket->priv;
963 if (private->plug_widget)
964 return gtk_widget_child_focus (private->plug_widget, direction);
966 if (!gtk_widget_is_focus (widget))
974 case GTK_DIR_TAB_BACKWARD:
975 detail = XEMBED_FOCUS_LAST;
979 case GTK_DIR_TAB_FORWARD:
980 detail = XEMBED_FOCUS_FIRST;
984 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
985 gtk_socket_claim_focus (socket, FALSE);
994 gtk_socket_remove (GtkContainer *container,
997 GtkSocket *socket = GTK_SOCKET (container);
998 GtkSocketPrivate *private = socket->priv;
1000 g_return_if_fail (child == private->plug_widget);
1002 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
1006 gtk_socket_forall (GtkContainer *container,
1007 gboolean include_internals,
1008 GtkCallback callback,
1009 gpointer callback_data)
1011 GtkSocket *socket = GTK_SOCKET (container);
1012 GtkSocketPrivate *private = socket->priv;
1014 if (private->plug_widget)
1015 (* callback) (private->plug_widget, callback_data);
1019 * gtk_socket_add_window:
1021 * @socket: a #GtkSocket
1022 * @xid: the native identifier for a window
1023 * @need_reparent: whether the socket's plug's window needs to be
1024 * reparented to the socket
1026 * Adds a window to a GtkSocket.
1029 gtk_socket_add_window (GtkSocket *socket,
1031 gboolean need_reparent)
1033 GtkWidget *widget = GTK_WIDGET (socket);
1034 GdkDisplay *display = gtk_widget_get_display (widget);
1035 gpointer user_data = NULL;
1036 GtkSocketPrivate *private = socket->priv;
1037 unsigned long version;
1038 unsigned long flags;
1040 if (GDK_IS_X11_DISPLAY (display))
1041 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1043 private->plug_window = NULL;
1045 if (private->plug_window)
1047 g_object_ref (private->plug_window);
1048 gdk_window_get_user_data (private->plug_window, &user_data);
1051 if (user_data) /* A widget's window in this process */
1053 GtkWidget *child_widget = user_data;
1055 if (!GTK_IS_PLUG (child_widget))
1057 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1058 private->plug_window = NULL;
1059 gdk_error_trap_pop_ignored ();
1064 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1066 else /* A foreign window */
1068 GtkWidget *toplevel;
1069 GdkDragProtocol protocol;
1071 gdk_error_trap_push ();
1073 if (!private->plug_window)
1075 if (GDK_IS_X11_DISPLAY (display))
1076 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1077 if (!private->plug_window) /* was deleted before we could get it */
1079 gdk_error_trap_pop_ignored ();
1084 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1085 GDK_WINDOW_XID (private->plug_window),
1086 StructureNotifyMask | PropertyChangeMask);
1088 if (gdk_error_trap_pop ())
1090 g_object_unref (private->plug_window);
1091 private->plug_window = NULL;
1095 /* OK, we now will reliably get destroy notification on socket->plug_window */
1097 gdk_error_trap_push ();
1101 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1102 gdk_window_reparent (private->plug_window,
1103 gtk_widget_get_window (widget),
1107 private->have_size = FALSE;
1109 private->xembed_version = -1;
1110 if (xembed_get_info (private->plug_window, &version, &flags))
1112 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1113 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1117 /* FIXME, we should probably actually check the state before we started */
1118 private->is_mapped = TRUE;
1121 private->need_map = private->is_mapped;
1123 if (gdk_drag_get_protocol_for_display (display, xid, &protocol))
1124 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1127 gdk_error_trap_pop_ignored ();
1129 gdk_window_add_filter (private->plug_window,
1130 gtk_socket_filter_func,
1133 /* Add a pointer to the socket on our toplevel window */
1135 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1136 if (GTK_IS_WINDOW (toplevel))
1137 _gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
1140 gdk_error_trap_push ();
1141 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1142 GDK_WINDOW_XID (private->plug_window),
1143 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1144 gdk_error_trap_pop_ignored ();
1146 _gtk_xembed_send_message (private->plug_window,
1147 XEMBED_EMBEDDED_NOTIFY, 0,
1148 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1149 private->xembed_version);
1151 socket_update_active (socket);
1152 socket_update_focus_in (socket);
1154 gtk_widget_queue_resize (GTK_WIDGET (socket));
1157 if (private->plug_window)
1158 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1162 * gtk_socket_handle_map_request:
1164 * @socket: a #GtkSocket
1166 * Called from the GtkSocket backend when the plug has been mapped.
1169 gtk_socket_handle_map_request (GtkSocket *socket)
1171 GtkSocketPrivate *private = socket->priv;
1172 if (!private->is_mapped)
1174 private->is_mapped = TRUE;
1175 private->need_map = TRUE;
1177 gtk_widget_queue_resize (GTK_WIDGET (socket));
1182 * gtk_socket_unmap_notify:
1184 * @socket: a #GtkSocket
1186 * Called from the GtkSocket backend when the plug has been unmapped ???
1189 gtk_socket_unmap_notify (GtkSocket *socket)
1191 GtkSocketPrivate *private = socket->priv;
1192 if (private->is_mapped)
1194 private->is_mapped = FALSE;
1195 gtk_widget_queue_resize (GTK_WIDGET (socket));
1200 * gtk_socket_advance_toplevel_focus:
1202 * @socket: a #GtkSocket
1203 * @direction: a direction
1205 * Called from the GtkSocket backend when the corresponding plug
1206 * has told the socket to move the focus.
1209 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1210 GtkDirectionType direction)
1214 GtkContainer *container;
1216 GtkWidget *focus_widget;
1217 GtkWidget *toplevel;
1218 GtkWidget *old_focus_child;
1221 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1225 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1227 gtk_widget_child_focus (toplevel,direction);
1231 container = GTK_CONTAINER (toplevel);
1232 window = GTK_WINDOW (toplevel);
1233 bin = GTK_BIN (toplevel);
1235 /* This is a copy of gtk_window_focus(), modified so that we
1236 * can detect wrap-around.
1238 old_focus_child = gtk_container_get_focus_child (container);
1240 if (old_focus_child)
1242 if (gtk_widget_child_focus (old_focus_child, direction))
1245 /* We are allowed exactly one wrap-around per sequence of focus
1248 if (_gtk_xembed_get_focus_wrapped ())
1251 _gtk_xembed_set_focus_wrapped ();
1254 focus_widget = gtk_window_get_focus (window);
1257 /* Wrapped off the end, clear the focus setting for the toplevel */
1258 parent = gtk_widget_get_parent (focus_widget);
1261 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1262 parent = gtk_widget_get_parent (parent);
1265 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1268 /* Now try to focus the first widget in the window */
1269 child = gtk_bin_get_child (bin);
1272 if (gtk_widget_child_focus (child, direction))
1278 xembed_get_info (GdkWindow *window,
1279 unsigned long *version,
1280 unsigned long *flags)
1282 GdkDisplay *display = gdk_window_get_display (window);
1283 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1286 unsigned long nitems, bytes_after;
1287 unsigned char *data;
1288 unsigned long *data_long;
1291 gdk_error_trap_push ();
1292 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1293 GDK_WINDOW_XID (window),
1296 xembed_info_atom, &type, &format,
1297 &nitems, &bytes_after, &data);
1298 gdk_error_trap_pop_ignored ();
1300 if (status != Success)
1301 return FALSE; /* Window vanished? */
1303 if (type == None) /* No info property */
1306 if (type != xembed_info_atom)
1308 g_warning ("_XEMBED_INFO property has wrong type\n");
1314 g_warning ("_XEMBED_INFO too short\n");
1319 data_long = (unsigned long *)data;
1321 *version = data_long[0];
1323 *flags = data_long[1] & XEMBED_MAPPED;
1330 handle_xembed_message (GtkSocket *socket,
1331 XEmbedMessageType message,
1337 GTK_NOTE (PLUGSOCKET,
1338 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1342 case XEMBED_EMBEDDED_NOTIFY:
1343 case XEMBED_WINDOW_ACTIVATE:
1344 case XEMBED_WINDOW_DEACTIVATE:
1345 case XEMBED_MODALITY_ON:
1346 case XEMBED_MODALITY_OFF:
1347 case XEMBED_FOCUS_IN:
1348 case XEMBED_FOCUS_OUT:
1349 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1352 case XEMBED_REQUEST_FOCUS:
1353 gtk_socket_claim_focus (socket, TRUE);
1356 case XEMBED_FOCUS_NEXT:
1357 case XEMBED_FOCUS_PREV:
1358 gtk_socket_advance_toplevel_focus (socket,
1359 (message == XEMBED_FOCUS_NEXT ?
1360 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1363 case XEMBED_GTK_GRAB_KEY:
1364 gtk_socket_add_grabbed_key (socket, data1, data2);
1366 case XEMBED_GTK_UNGRAB_KEY:
1367 gtk_socket_remove_grabbed_key (socket, data1, data2);
1370 case XEMBED_GRAB_KEY:
1371 case XEMBED_UNGRAB_KEY:
1375 GTK_NOTE (PLUGSOCKET,
1376 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1381 static GdkFilterReturn
1382 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1388 GdkDisplay *display;
1390 GtkSocketPrivate *private;
1392 GdkFilterReturn return_val;
1394 socket = GTK_SOCKET (data);
1395 private = socket->priv;
1397 return_val = GDK_FILTER_CONTINUE;
1399 if (private->plug_widget)
1402 widget = GTK_WIDGET (socket);
1403 xevent = (XEvent *)gdk_xevent;
1404 display = gtk_widget_get_display (widget);
1406 switch (xevent->type)
1409 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1411 _gtk_xembed_push_message (xevent);
1412 handle_xembed_message (socket,
1413 xevent->xclient.data.l[1],
1414 xevent->xclient.data.l[2],
1415 xevent->xclient.data.l[3],
1416 xevent->xclient.data.l[4],
1417 xevent->xclient.data.l[0]);
1418 _gtk_xembed_pop_message ();
1420 return_val = GDK_FILTER_REMOVE;
1426 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1428 if (!private->plug_window)
1430 gtk_socket_add_window (socket, xcwe->window, FALSE);
1432 if (private->plug_window)
1434 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1438 return_val = GDK_FILTER_REMOVE;
1443 case ConfigureRequest:
1445 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1447 if (!private->plug_window)
1448 gtk_socket_add_window (socket, xcre->window, FALSE);
1450 if (private->plug_window)
1452 if (xcre->value_mask & (CWWidth | CWHeight))
1454 GTK_NOTE (PLUGSOCKET,
1455 g_message ("GtkSocket - configure request: %d %d",
1456 private->request_width,
1457 private->request_height));
1459 private->resize_count++;
1460 gtk_widget_queue_resize (widget);
1462 else if (xcre->value_mask & (CWX | CWY))
1464 gtk_socket_send_configure_event (socket);
1466 /* Ignore stacking requests. */
1468 return_val = GDK_FILTER_REMOVE;
1475 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1477 /* Note that we get destroy notifies both from SubstructureNotify on
1478 * our window and StructureNotify on socket->plug_window
1480 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1484 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1486 gdk_window_destroy_notify (private->plug_window);
1487 gtk_socket_end_embedding (socket);
1489 g_object_ref (widget);
1490 g_signal_emit_by_name (widget, "plug-removed", &result);
1492 gtk_widget_destroy (widget);
1493 g_object_unref (widget);
1495 return_val = GDK_FILTER_REMOVE;
1501 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1503 gtk_socket_claim_focus (socket, TRUE);
1505 return_val = GDK_FILTER_REMOVE;
1508 return_val = GDK_FILTER_REMOVE;
1511 if (!private->plug_window)
1513 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1516 if (private->plug_window)
1518 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1520 gtk_socket_handle_map_request (socket);
1521 return_val = GDK_FILTER_REMOVE;
1524 case PropertyNotify:
1525 if (private->plug_window &&
1526 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1528 GdkDragProtocol protocol;
1530 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1532 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1533 private->have_size = FALSE;
1534 gtk_widget_queue_resize (widget);
1535 return_val = GDK_FILTER_REMOVE;
1537 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1538 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1540 gdk_error_trap_push ();
1541 if (gdk_drag_get_protocol_for_display (display,
1542 xevent->xproperty.window,
1544 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1545 private->plug_window,
1548 gdk_error_trap_pop_ignored ();
1549 return_val = GDK_FILTER_REMOVE;
1551 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1553 unsigned long flags;
1555 if (xembed_get_info (private->plug_window, NULL, &flags))
1557 gboolean was_mapped = private->is_mapped;
1558 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1560 if (was_mapped != is_mapped)
1563 gtk_socket_handle_map_request (socket);
1566 gdk_error_trap_push ();
1567 gdk_window_show (private->plug_window);
1568 gdk_error_trap_pop_ignored ();
1570 gtk_socket_unmap_notify (socket);
1574 return_val = GDK_FILTER_REMOVE;
1578 case ReparentNotify:
1581 XReparentEvent *xre = &xevent->xreparent;
1583 window = gtk_widget_get_window (widget);
1585 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1586 if (!private->plug_window &&
1587 xre->parent == GDK_WINDOW_XID (window))
1589 gtk_socket_add_window (socket, xre->window, FALSE);
1591 if (private->plug_window)
1593 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1596 return_val = GDK_FILTER_REMOVE;
1600 if (private->plug_window &&
1601 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1602 xre->parent != GDK_WINDOW_XID (window))
1606 gtk_socket_end_embedding (socket);
1608 g_object_ref (widget);
1609 g_signal_emit_by_name (widget, "plug-removed", &result);
1611 gtk_widget_destroy (widget);
1612 g_object_unref (widget);
1614 return_val = GDK_FILTER_REMOVE;
1621 if (private->plug_window &&
1622 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1624 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1626 gtk_socket_unmap_notify (socket);
1627 return_val = GDK_FILTER_REMOVE;