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 * @include: gtk/gtkx.h
60 * @See_also: #GtkPlug, <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
62 * Together with #GtkPlug, #GtkSocket provides the ability to embed
63 * widgets from one process into another process in a fashion that
64 * is transparent to the user. One process creates a #GtkSocket widget
65 * and passes that widget's window ID to the other process, which then
66 * creates a #GtkPlug with that window ID. Any widgets contained in the
67 * #GtkPlug then will appear inside the first application's window.
69 * The socket's window ID is obtained by using gtk_socket_get_id().
70 * Before using this function, the socket must have been realized,
71 * and for hence, have been added to its parent.
74 * <title>Obtaining the window ID of a socket.</title>
76 * GtkWidget *socket = gtk_socket_new (<!-- -->);
77 * gtk_widget_show (socket);
78 * gtk_container_add (GTK_CONTAINER (parent), socket);
80 * /* The following call is only necessary if one of
81 * * the ancestors of the socket is not yet visible.
83 * gtk_widget_realize (socket);
84 * g_print ("The ID of the sockets window is %#x\n",
85 * gtk_socket_get_id (socket));
89 * Note that if you pass the window ID of the socket to another
90 * process that will create a plug in the socket, you must make
91 * sure that the socket widget is not destroyed until that plug
92 * is created. Violating this rule will cause unpredictable
93 * consequences, the most likely consequence being that the plug
94 * will appear as a separate toplevel window. You can check if
95 * the plug has been created by using gtk_socket_get_plug_window().
96 * If it returns a non-%NULL value, then the plug has been
97 * successfully created inside of the socket.
99 * When GTK+ is notified that the embedded window has been destroyed,
100 * then it will destroy the socket as well. You should always,
101 * therefore, be prepared for your sockets to be destroyed at any
102 * time when the main event loop is running. To prevent this from
103 * happening, you can connect to the #GtkSocket::plug-removed signal.
105 * The communication between a #GtkSocket and a #GtkPlug follows the
106 * <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
107 * protocol. This protocol has also been implemented in other toolkits,
108 * e.g. <application>Qt</application>, allowing the same level of
109 * integration when embedding a <application>Qt</application> widget
110 * in GTK or vice versa.
113 * The #GtkPlug and #GtkSocket widgets are only available when GTK+
114 * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined.
115 * They can only be used on a #GdkX11Display. To use #GtkPlug and
116 * #GtkSocket, you need to include the <filename>gtk/gtkx.h</filename>
121 /* Forward declararations */
123 static void gtk_socket_finalize (GObject *object);
124 static void gtk_socket_notify (GObject *object,
126 static void gtk_socket_realize (GtkWidget *widget);
127 static void gtk_socket_unrealize (GtkWidget *widget);
128 static void gtk_socket_get_preferred_width (GtkWidget *widget,
131 static void gtk_socket_get_preferred_height (GtkWidget *widget,
134 static void gtk_socket_size_allocate (GtkWidget *widget,
135 GtkAllocation *allocation);
136 static void gtk_socket_hierarchy_changed (GtkWidget *widget,
137 GtkWidget *old_toplevel);
138 static void gtk_socket_grab_notify (GtkWidget *widget,
139 gboolean was_grabbed);
140 static gboolean gtk_socket_key_event (GtkWidget *widget,
142 static gboolean gtk_socket_focus (GtkWidget *widget,
143 GtkDirectionType direction);
144 static void gtk_socket_remove (GtkContainer *container,
146 static void gtk_socket_forall (GtkContainer *container,
147 gboolean include_internals,
148 GtkCallback callback,
149 gpointer callback_data);
150 static void gtk_socket_add_window (GtkSocket *socket,
152 gboolean need_reparent);
153 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
157 static gboolean xembed_get_info (GdkWindow *gdk_window,
158 unsigned long *version,
159 unsigned long *flags);
162 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
170 GdkModifierType accel_mods;
179 static guint socket_signals[LAST_SIGNAL] = { 0 };
181 G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
184 gtk_socket_finalize (GObject *object)
186 GtkSocket *socket = GTK_SOCKET (object);
187 GtkSocketPrivate *priv = socket->priv;
189 g_object_unref (priv->accel_group);
191 G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
195 gtk_socket_class_init (GtkSocketClass *class)
197 GtkWidgetClass *widget_class;
198 GtkContainerClass *container_class;
199 GObjectClass *gobject_class;
201 gobject_class = (GObjectClass *) class;
202 widget_class = (GtkWidgetClass*) class;
203 container_class = (GtkContainerClass*) class;
205 gobject_class->finalize = gtk_socket_finalize;
206 gobject_class->notify = gtk_socket_notify;
208 widget_class->realize = gtk_socket_realize;
209 widget_class->unrealize = gtk_socket_unrealize;
210 widget_class->get_preferred_width = gtk_socket_get_preferred_width;
211 widget_class->get_preferred_height = gtk_socket_get_preferred_height;
212 widget_class->size_allocate = gtk_socket_size_allocate;
213 widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
214 widget_class->grab_notify = gtk_socket_grab_notify;
215 widget_class->key_press_event = gtk_socket_key_event;
216 widget_class->key_release_event = gtk_socket_key_event;
217 widget_class->focus = gtk_socket_focus;
219 /* We don't want to show_all the in-process plug, if any.
221 widget_class->show_all = gtk_widget_show;
223 container_class->remove = gtk_socket_remove;
224 container_class->forall = gtk_socket_forall;
227 * GtkSocket::plug-added:
228 * @socket_: the object which received the signal
230 * This signal is emitted when a client is successfully
231 * added to the socket.
233 socket_signals[PLUG_ADDED] =
234 g_signal_new (I_("plug-added"),
235 G_OBJECT_CLASS_TYPE (class),
237 G_STRUCT_OFFSET (GtkSocketClass, plug_added),
239 _gtk_marshal_VOID__VOID,
243 * GtkSocket::plug-removed:
244 * @socket_: the object which received the signal
246 * This signal is emitted when a client is removed from the socket.
247 * The default action is to destroy the #GtkSocket widget, so if you
248 * want to reuse it you must add a signal handler that returns %TRUE.
250 * Return value: %TRUE to stop other handlers from being invoked.
252 socket_signals[PLUG_REMOVED] =
253 g_signal_new (I_("plug-removed"),
254 G_OBJECT_CLASS_TYPE (class),
256 G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
257 _gtk_boolean_handled_accumulator, NULL,
258 _gtk_marshal_BOOLEAN__VOID,
261 g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate));
265 gtk_socket_init (GtkSocket *socket)
267 GtkSocketPrivate *priv;
269 priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
273 priv->request_width = 0;
274 priv->request_height = 0;
275 priv->current_width = 0;
276 priv->current_height = 0;
278 priv->plug_window = NULL;
279 priv->plug_widget = NULL;
280 priv->focus_in = FALSE;
281 priv->have_size = FALSE;
282 priv->need_map = FALSE;
283 priv->active = FALSE;
285 priv->accel_group = gtk_accel_group_new ();
286 g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
292 * Create a new empty #GtkSocket.
294 * Return value: the new #GtkSocket.
297 gtk_socket_new (void)
301 socket = g_object_new (GTK_TYPE_SOCKET, NULL);
303 return GTK_WIDGET (socket);
308 * @socket_: a #GtkSocket
309 * @window: the Window of a client participating in the XEMBED protocol.
311 * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The
312 * client may be in the same process or in a different process.
314 * To embed a #GtkPlug in a #GtkSocket, you can either create the
315 * #GtkPlug with <literal>gtk_plug_new (0)</literal>, call
316 * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
317 * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
318 * window ID for the socket, and call gtk_plug_new() passing in that
321 * The #GtkSocket must have already be added into a toplevel window
322 * before you can make this call.
325 gtk_socket_add_id (GtkSocket *socket,
328 g_return_if_fail (GTK_IS_SOCKET (socket));
329 g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
331 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
332 gtk_widget_realize (GTK_WIDGET (socket));
334 gtk_socket_add_window (socket, window, TRUE);
339 * @socket_: a #GtkSocket.
341 * Gets the window ID of a #GtkSocket widget, which can then
342 * be used to create a client embedded inside the socket, for
343 * instance with gtk_plug_new().
345 * The #GtkSocket must have already be added into a toplevel window
346 * before you can make this call.
348 * Return value: the window ID for the socket
351 gtk_socket_get_id (GtkSocket *socket)
353 g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
354 g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
356 if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
357 gtk_widget_realize (GTK_WIDGET (socket));
359 return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
363 * gtk_socket_get_plug_window:
364 * @socket_: a #GtkSocket.
366 * Retrieves the window of the plug. Use this to check if the plug has
367 * been created inside of the socket.
369 * Return value: (transfer none): the window of the plug if available, or %NULL
374 gtk_socket_get_plug_window (GtkSocket *socket)
376 g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);
378 return socket->priv->plug_window;
382 gtk_socket_realize (GtkWidget *widget)
384 GtkAllocation allocation;
385 GtkSocket *socket = GTK_SOCKET (widget);
387 GdkWindowAttr attributes;
388 XWindowAttributes xattrs;
389 gint attributes_mask;
391 gtk_widget_set_realized (widget, TRUE);
393 gtk_widget_get_allocation (widget, &allocation);
395 attributes.window_type = GDK_WINDOW_CHILD;
396 attributes.x = allocation.x;
397 attributes.y = allocation.y;
398 attributes.width = allocation.width;
399 attributes.height = allocation.height;
400 attributes.wclass = GDK_INPUT_OUTPUT;
401 attributes.visual = gtk_widget_get_visual (widget);
402 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
404 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
406 window = gdk_window_new (gtk_widget_get_parent_window (widget),
407 &attributes, attributes_mask);
408 gtk_widget_set_window (widget, window);
409 gdk_window_set_user_data (window, socket);
411 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
414 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
415 GDK_WINDOW_XID (window),
418 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
419 for input on the socket with a mask of 0x0fffff (for god knows why)
420 which includes ButtonPressMask causing a BadAccess if someone else
421 also selects for this. As per the client-side windows merge we always
422 normally selects for button press so we can emulate it on client
423 side children that selects for button press. However, we don't need
424 this for GtkSocket, so we unselect it here, fixing the crashes in
426 XSelectInput (GDK_WINDOW_XDISPLAY (window),
427 GDK_WINDOW_XID (window),
428 (xattrs.your_event_mask & ~ButtonPressMask) |
429 SubstructureNotifyMask | SubstructureRedirectMask);
431 gdk_window_add_filter (window,
432 gtk_socket_filter_func,
435 /* We sync here so that we make sure that if the XID for
436 * our window is passed to another application, SubstructureRedirectMask
437 * will be set by the time the other app creates its window.
439 gdk_display_sync (gtk_widget_get_display (widget));
443 * gtk_socket_end_embedding:
445 * @socket: a #GtkSocket
447 * Called to end the embedding of a plug in the socket.
450 gtk_socket_end_embedding (GtkSocket *socket)
452 GtkSocketPrivate *private = socket->priv;
453 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
455 if (GTK_IS_WINDOW (toplevel))
456 _gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
457 GDK_WINDOW_XID (socket->priv->plug_window));
459 g_object_unref (private->plug_window);
460 private->plug_window = NULL;
461 private->current_width = 0;
462 private->current_height = 0;
463 private->resize_count = 0;
465 gtk_accel_group_disconnect (private->accel_group, NULL);
469 gtk_socket_unrealize (GtkWidget *widget)
471 GtkSocket *socket = GTK_SOCKET (widget);
472 GtkSocketPrivate *private = socket->priv;
474 gtk_widget_set_realized (widget, FALSE);
476 if (private->plug_widget)
478 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
480 else if (private->plug_window)
482 gtk_socket_end_embedding (socket);
485 GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
489 gtk_socket_size_request (GtkSocket *socket)
491 GtkSocketPrivate *private = socket->priv;
495 gdk_error_trap_push ();
497 private->request_width = 1;
498 private->request_height = 1;
500 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
501 GDK_WINDOW_XID (private->plug_window),
504 if (hints.flags & PMinSize)
506 private->request_width = MAX (hints.min_width, 1);
507 private->request_height = MAX (hints.min_height, 1);
509 else if (hints.flags & PBaseSize)
511 private->request_width = MAX (hints.base_width, 1);
512 private->request_height = MAX (hints.base_height, 1);
515 private->have_size = TRUE;
517 gdk_error_trap_pop_ignored ();
521 gtk_socket_get_preferred_width (GtkWidget *widget,
525 GtkSocket *socket = GTK_SOCKET (widget);
526 GtkSocketPrivate *private = socket->priv;
528 if (private->plug_widget)
530 gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
534 if (private->is_mapped && !private->have_size && private->plug_window)
535 gtk_socket_size_request (socket);
537 if (private->is_mapped && private->have_size)
538 *minimum = *natural = MAX (private->request_width, 1);
540 *minimum = *natural = 1;
545 gtk_socket_get_preferred_height (GtkWidget *widget,
549 GtkSocket *socket = GTK_SOCKET (widget);
550 GtkSocketPrivate *private = socket->priv;
552 if (private->plug_widget)
554 gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
558 if (private->is_mapped && !private->have_size && private->plug_window)
559 gtk_socket_size_request (socket);
561 if (private->is_mapped && private->have_size)
562 *minimum = *natural = MAX (private->request_height, 1);
564 *minimum = *natural = 1;
569 gtk_socket_send_configure_event (GtkSocket *socket)
571 GtkAllocation allocation;
572 XConfigureEvent xconfigure;
575 g_return_if_fail (socket->priv->plug_window != NULL);
577 memset (&xconfigure, 0, sizeof (xconfigure));
578 xconfigure.type = ConfigureNotify;
580 xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
581 xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);
583 /* The ICCCM says that synthetic events should have root relative
584 * coordinates. We still aren't really ICCCM compliant, since
585 * we don't send events when the real toplevel is moved.
587 gdk_error_trap_push ();
588 gdk_window_get_origin (socket->priv->plug_window, &x, &y);
589 gdk_error_trap_pop_ignored ();
591 gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
594 xconfigure.width = allocation.width;
595 xconfigure.height = allocation.height;
597 xconfigure.border_width = 0;
598 xconfigure.above = None;
599 xconfigure.override_redirect = False;
601 gdk_error_trap_push ();
602 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
603 GDK_WINDOW_XID (socket->priv->plug_window),
604 False, NoEventMask, (XEvent *)&xconfigure);
605 gdk_error_trap_pop_ignored ();
609 gtk_socket_size_allocate (GtkWidget *widget,
610 GtkAllocation *allocation)
612 GtkSocket *socket = GTK_SOCKET (widget);
613 GtkSocketPrivate *private = socket->priv;
615 gtk_widget_set_allocation (widget, allocation);
616 if (gtk_widget_get_realized (widget))
618 gdk_window_move_resize (gtk_widget_get_window (widget),
619 allocation->x, allocation->y,
620 allocation->width, allocation->height);
622 if (private->plug_widget)
624 GtkAllocation child_allocation;
626 child_allocation.x = 0;
627 child_allocation.y = 0;
628 child_allocation.width = allocation->width;
629 child_allocation.height = allocation->height;
631 gtk_widget_size_allocate (private->plug_widget, &child_allocation);
633 else if (private->plug_window)
635 gdk_error_trap_push ();
637 if (allocation->width != private->current_width ||
638 allocation->height != private->current_height)
640 gdk_window_move_resize (private->plug_window,
642 allocation->width, allocation->height);
643 if (private->resize_count)
644 private->resize_count--;
646 GTK_NOTE (PLUGSOCKET,
647 g_message ("GtkSocket - allocated: %d %d",
648 allocation->width, allocation->height));
649 private->current_width = allocation->width;
650 private->current_height = allocation->height;
653 if (private->need_map)
655 gdk_window_show (private->plug_window);
656 private->need_map = FALSE;
659 while (private->resize_count)
661 gtk_socket_send_configure_event (socket);
662 private->resize_count--;
663 GTK_NOTE (PLUGSOCKET,
664 g_message ("GtkSocket - sending synthetic configure: %d %d",
665 allocation->width, allocation->height));
668 gdk_error_trap_pop_ignored ();
674 gtk_socket_send_key_event (GtkSocket *socket,
676 gboolean mask_key_presses)
679 GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);
681 memset (&xkey, 0, sizeof (xkey));
682 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
683 xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
684 xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
685 xkey.subwindow = None;
686 xkey.time = gdk_event->key.time;
691 xkey.state = gdk_event->key.state;
692 xkey.keycode = gdk_event->key.hardware_keycode;
693 xkey.same_screen = True;/* FIXME ? */
695 gdk_error_trap_push ();
696 XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
697 GDK_WINDOW_XID (socket->priv->plug_window),
699 (mask_key_presses ? KeyPressMask : NoEventMask),
701 gdk_error_trap_pop_ignored ();
705 activate_key (GtkAccelGroup *accel_group,
706 GObject *acceleratable,
708 GdkModifierType accel_mods,
709 GrabbedKey *grabbed_key)
711 GdkEvent *gdk_event = gtk_get_current_event ();
713 GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
714 gboolean retval = FALSE;
716 if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
718 gtk_socket_send_key_event (socket, gdk_event, FALSE);
723 gdk_event_free (gdk_event);
729 find_accel_key (GtkAccelKey *key,
733 GrabbedKey *grabbed_key = data;
735 return (key->accel_key == grabbed_key->accel_key &&
736 key->accel_mods == grabbed_key->accel_mods);
740 * gtk_socket_add_grabbed_key:
742 * @socket: a #GtkSocket
744 * @modifiers: modifiers for the key
746 * Called from the GtkSocket platform-specific backend when the
747 * corresponding plug has told the socket to grab a key.
750 gtk_socket_add_grabbed_key (GtkSocket *socket,
752 GdkModifierType modifiers)
755 GrabbedKey *grabbed_key;
757 grabbed_key = g_new (GrabbedKey, 1);
759 grabbed_key->accel_key = keyval;
760 grabbed_key->accel_mods = modifiers;
762 if (gtk_accel_group_find (socket->priv->accel_group,
766 g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
768 g_free (grabbed_key);
772 closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
774 gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
779 * gtk_socket_remove_grabbed_key:
781 * @socket: a #GtkSocket
783 * @modifiers: modifiers for the key
785 * Called from the GtkSocket backend when the corresponding plug has
786 * told the socket to remove a key grab.
789 gtk_socket_remove_grabbed_key (GtkSocket *socket,
791 GdkModifierType modifiers)
793 if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
794 g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
799 socket_update_focus_in (GtkSocket *socket)
801 GtkSocketPrivate *private = socket->priv;
802 gboolean focus_in = FALSE;
804 if (private->plug_window)
806 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
808 if (gtk_widget_is_toplevel (toplevel) &&
809 gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
810 gtk_widget_is_focus (GTK_WIDGET (socket)))
814 if (focus_in != private->focus_in)
816 private->focus_in = focus_in;
819 _gtk_xembed_send_focus_message (private->plug_window,
820 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
822 _gtk_xembed_send_message (private->plug_window,
823 XEMBED_FOCUS_OUT, 0, 0, 0);
828 socket_update_active (GtkSocket *socket)
830 GtkSocketPrivate *private = socket->priv;
831 gboolean active = FALSE;
833 if (private->plug_window)
835 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
837 if (gtk_widget_is_toplevel (toplevel) &&
838 gtk_window_is_active (GTK_WINDOW (toplevel)))
842 if (active != private->active)
844 private->active = active;
846 _gtk_xembed_send_message (private->plug_window,
847 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
853 gtk_socket_hierarchy_changed (GtkWidget *widget,
854 GtkWidget *old_toplevel)
856 GtkSocket *socket = GTK_SOCKET (widget);
857 GtkSocketPrivate *private = socket->priv;
858 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
860 if (toplevel && !GTK_IS_WINDOW (toplevel))
863 if (toplevel != private->toplevel)
865 if (private->toplevel)
867 gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
868 g_signal_handlers_disconnect_by_func (private->toplevel,
869 socket_update_focus_in,
871 g_signal_handlers_disconnect_by_func (private->toplevel,
872 socket_update_active,
876 private->toplevel = toplevel;
880 gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
881 g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
882 G_CALLBACK (socket_update_focus_in), socket);
883 g_signal_connect_swapped (private->toplevel, "notify::is-active",
884 G_CALLBACK (socket_update_active), socket);
887 socket_update_focus_in (socket);
888 socket_update_active (socket);
893 gtk_socket_grab_notify (GtkWidget *widget,
894 gboolean was_grabbed)
896 GtkSocket *socket = GTK_SOCKET (widget);
898 if (!socket->priv->same_app)
899 _gtk_xembed_send_message (socket->priv->plug_window,
900 was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
905 gtk_socket_key_event (GtkWidget *widget,
908 GtkSocket *socket = GTK_SOCKET (widget);
909 GtkSocketPrivate *private = socket->priv;
911 if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
913 gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
922 gtk_socket_notify (GObject *object,
925 if (!strcmp (pspec->name, "is-focus"))
927 socket_update_focus_in (GTK_SOCKET (object));
931 * gtk_socket_claim_focus:
933 * @socket: a #GtkSocket
936 * Claims focus for the socket. XXX send_event?
939 gtk_socket_claim_focus (GtkSocket *socket,
942 GtkWidget *widget = GTK_WIDGET (socket);
943 GtkSocketPrivate *private = socket->priv;
946 private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */
948 /* Oh, the trickery... */
950 gtk_widget_set_can_focus (widget, TRUE);
951 gtk_widget_grab_focus (widget);
952 gtk_widget_set_can_focus (widget, FALSE);
956 gtk_socket_focus (GtkWidget *widget,
957 GtkDirectionType direction)
959 GtkSocket *socket = GTK_SOCKET (widget);
960 GtkSocketPrivate *private = socket->priv;
962 if (private->plug_widget)
963 return gtk_widget_child_focus (private->plug_widget, direction);
965 if (!gtk_widget_is_focus (widget))
973 case GTK_DIR_TAB_BACKWARD:
974 detail = XEMBED_FOCUS_LAST;
978 case GTK_DIR_TAB_FORWARD:
979 detail = XEMBED_FOCUS_FIRST;
983 _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
984 gtk_socket_claim_focus (socket, FALSE);
993 gtk_socket_remove (GtkContainer *container,
996 GtkSocket *socket = GTK_SOCKET (container);
997 GtkSocketPrivate *private = socket->priv;
999 g_return_if_fail (child == private->plug_widget);
1001 _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
1005 gtk_socket_forall (GtkContainer *container,
1006 gboolean include_internals,
1007 GtkCallback callback,
1008 gpointer callback_data)
1010 GtkSocket *socket = GTK_SOCKET (container);
1011 GtkSocketPrivate *private = socket->priv;
1013 if (private->plug_widget)
1014 (* callback) (private->plug_widget, callback_data);
1018 * gtk_socket_add_window:
1020 * @socket: a #GtkSocket
1021 * @xid: the native identifier for a window
1022 * @need_reparent: whether the socket's plug's window needs to be
1023 * reparented to the socket
1025 * Adds a window to a GtkSocket.
1028 gtk_socket_add_window (GtkSocket *socket,
1030 gboolean need_reparent)
1032 GtkWidget *widget = GTK_WIDGET (socket);
1033 GdkDisplay *display = gtk_widget_get_display (widget);
1034 gpointer user_data = NULL;
1035 GtkSocketPrivate *private = socket->priv;
1036 unsigned long version;
1037 unsigned long flags;
1039 if (GDK_IS_X11_DISPLAY (display))
1040 private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
1042 private->plug_window = NULL;
1044 if (private->plug_window)
1046 g_object_ref (private->plug_window);
1047 gdk_window_get_user_data (private->plug_window, &user_data);
1050 if (user_data) /* A widget's window in this process */
1052 GtkWidget *child_widget = user_data;
1054 if (!GTK_IS_PLUG (child_widget))
1056 g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
1057 private->plug_window = NULL;
1058 gdk_error_trap_pop_ignored ();
1063 _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
1065 else /* A foreign window */
1067 GtkWidget *toplevel;
1068 GdkDragProtocol protocol;
1070 gdk_error_trap_push ();
1072 if (!private->plug_window)
1074 if (GDK_IS_X11_DISPLAY (display))
1075 private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
1076 if (!private->plug_window) /* was deleted before we could get it */
1078 gdk_error_trap_pop_ignored ();
1083 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1084 GDK_WINDOW_XID (private->plug_window),
1085 StructureNotifyMask | PropertyChangeMask);
1087 if (gdk_error_trap_pop ())
1089 g_object_unref (private->plug_window);
1090 private->plug_window = NULL;
1094 /* OK, we now will reliably get destroy notification on socket->plug_window */
1096 gdk_error_trap_push ();
1100 gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
1101 gdk_window_reparent (private->plug_window,
1102 gtk_widget_get_window (widget),
1106 private->have_size = FALSE;
1108 private->xembed_version = -1;
1109 if (xembed_get_info (private->plug_window, &version, &flags))
1111 private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
1112 private->is_mapped = (flags & XEMBED_MAPPED) != 0;
1116 /* FIXME, we should probably actually check the state before we started */
1117 private->is_mapped = TRUE;
1120 private->need_map = private->is_mapped;
1122 if (gdk_drag_get_protocol_for_display (display, xid, &protocol))
1123 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window,
1126 gdk_error_trap_pop_ignored ();
1128 gdk_window_add_filter (private->plug_window,
1129 gtk_socket_filter_func,
1132 /* Add a pointer to the socket on our toplevel window */
1134 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1135 if (GTK_IS_WINDOW (toplevel))
1136 _gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
1139 gdk_error_trap_push ();
1140 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
1141 GDK_WINDOW_XID (private->plug_window),
1142 SetModeInsert, SaveSetRoot, SaveSetUnmap);
1143 gdk_error_trap_pop_ignored ();
1145 _gtk_xembed_send_message (private->plug_window,
1146 XEMBED_EMBEDDED_NOTIFY, 0,
1147 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
1148 private->xembed_version);
1150 socket_update_active (socket);
1151 socket_update_focus_in (socket);
1153 gtk_widget_queue_resize (GTK_WIDGET (socket));
1156 if (private->plug_window)
1157 g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
1161 * gtk_socket_handle_map_request:
1163 * @socket: a #GtkSocket
1165 * Called from the GtkSocket backend when the plug has been mapped.
1168 gtk_socket_handle_map_request (GtkSocket *socket)
1170 GtkSocketPrivate *private = socket->priv;
1171 if (!private->is_mapped)
1173 private->is_mapped = TRUE;
1174 private->need_map = TRUE;
1176 gtk_widget_queue_resize (GTK_WIDGET (socket));
1181 * gtk_socket_unmap_notify:
1183 * @socket: a #GtkSocket
1185 * Called from the GtkSocket backend when the plug has been unmapped ???
1188 gtk_socket_unmap_notify (GtkSocket *socket)
1190 GtkSocketPrivate *private = socket->priv;
1191 if (private->is_mapped)
1193 private->is_mapped = FALSE;
1194 gtk_widget_queue_resize (GTK_WIDGET (socket));
1199 * gtk_socket_advance_toplevel_focus:
1201 * @socket: a #GtkSocket
1202 * @direction: a direction
1204 * Called from the GtkSocket backend when the corresponding plug
1205 * has told the socket to move the focus.
1208 gtk_socket_advance_toplevel_focus (GtkSocket *socket,
1209 GtkDirectionType direction)
1213 GtkContainer *container;
1215 GtkWidget *focus_widget;
1216 GtkWidget *toplevel;
1217 GtkWidget *old_focus_child;
1220 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
1224 if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel))
1226 gtk_widget_child_focus (toplevel,direction);
1230 container = GTK_CONTAINER (toplevel);
1231 window = GTK_WINDOW (toplevel);
1232 bin = GTK_BIN (toplevel);
1234 /* This is a copy of gtk_window_focus(), modified so that we
1235 * can detect wrap-around.
1237 old_focus_child = gtk_container_get_focus_child (container);
1239 if (old_focus_child)
1241 if (gtk_widget_child_focus (old_focus_child, direction))
1244 /* We are allowed exactly one wrap-around per sequence of focus
1247 if (_gtk_xembed_get_focus_wrapped ())
1250 _gtk_xembed_set_focus_wrapped ();
1253 focus_widget = gtk_window_get_focus (window);
1256 /* Wrapped off the end, clear the focus setting for the toplevel */
1257 parent = gtk_widget_get_parent (focus_widget);
1260 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
1261 parent = gtk_widget_get_parent (parent);
1264 gtk_window_set_focus (GTK_WINDOW (container), NULL);
1267 /* Now try to focus the first widget in the window */
1268 child = gtk_bin_get_child (bin);
1271 if (gtk_widget_child_focus (child, direction))
1277 xembed_get_info (GdkWindow *window,
1278 unsigned long *version,
1279 unsigned long *flags)
1281 GdkDisplay *display = gdk_window_get_display (window);
1282 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
1285 unsigned long nitems, bytes_after;
1286 unsigned char *data;
1287 unsigned long *data_long;
1290 gdk_error_trap_push ();
1291 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1292 GDK_WINDOW_XID (window),
1295 xembed_info_atom, &type, &format,
1296 &nitems, &bytes_after, &data);
1297 gdk_error_trap_pop_ignored ();
1299 if (status != Success)
1300 return FALSE; /* Window vanished? */
1302 if (type == None) /* No info property */
1305 if (type != xembed_info_atom)
1307 g_warning ("_XEMBED_INFO property has wrong type\n");
1313 g_warning ("_XEMBED_INFO too short\n");
1318 data_long = (unsigned long *)data;
1320 *version = data_long[0];
1322 *flags = data_long[1] & XEMBED_MAPPED;
1329 handle_xembed_message (GtkSocket *socket,
1330 XEmbedMessageType message,
1336 GTK_NOTE (PLUGSOCKET,
1337 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
1341 case XEMBED_EMBEDDED_NOTIFY:
1342 case XEMBED_WINDOW_ACTIVATE:
1343 case XEMBED_WINDOW_DEACTIVATE:
1344 case XEMBED_MODALITY_ON:
1345 case XEMBED_MODALITY_OFF:
1346 case XEMBED_FOCUS_IN:
1347 case XEMBED_FOCUS_OUT:
1348 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
1351 case XEMBED_REQUEST_FOCUS:
1352 gtk_socket_claim_focus (socket, TRUE);
1355 case XEMBED_FOCUS_NEXT:
1356 case XEMBED_FOCUS_PREV:
1357 gtk_socket_advance_toplevel_focus (socket,
1358 (message == XEMBED_FOCUS_NEXT ?
1359 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
1362 case XEMBED_GTK_GRAB_KEY:
1363 gtk_socket_add_grabbed_key (socket, data1, data2);
1365 case XEMBED_GTK_UNGRAB_KEY:
1366 gtk_socket_remove_grabbed_key (socket, data1, data2);
1369 case XEMBED_GRAB_KEY:
1370 case XEMBED_UNGRAB_KEY:
1374 GTK_NOTE (PLUGSOCKET,
1375 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
1380 static GdkFilterReturn
1381 gtk_socket_filter_func (GdkXEvent *gdk_xevent,
1387 GdkDisplay *display;
1389 GtkSocketPrivate *private;
1391 GdkFilterReturn return_val;
1393 socket = GTK_SOCKET (data);
1394 private = socket->priv;
1396 return_val = GDK_FILTER_CONTINUE;
1398 if (private->plug_widget)
1401 widget = GTK_WIDGET (socket);
1402 xevent = (XEvent *)gdk_xevent;
1403 display = gtk_widget_get_display (widget);
1405 switch (xevent->type)
1408 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1410 _gtk_xembed_push_message (xevent);
1411 handle_xembed_message (socket,
1412 xevent->xclient.data.l[1],
1413 xevent->xclient.data.l[2],
1414 xevent->xclient.data.l[3],
1415 xevent->xclient.data.l[4],
1416 xevent->xclient.data.l[0]);
1417 _gtk_xembed_pop_message ();
1419 return_val = GDK_FILTER_REMOVE;
1425 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
1427 if (!private->plug_window)
1429 gtk_socket_add_window (socket, xcwe->window, FALSE);
1431 if (private->plug_window)
1433 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
1437 return_val = GDK_FILTER_REMOVE;
1442 case ConfigureRequest:
1444 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
1446 if (!private->plug_window)
1447 gtk_socket_add_window (socket, xcre->window, FALSE);
1449 if (private->plug_window)
1451 if (xcre->value_mask & (CWWidth | CWHeight))
1453 GTK_NOTE (PLUGSOCKET,
1454 g_message ("GtkSocket - configure request: %d %d",
1455 private->request_width,
1456 private->request_height));
1458 private->resize_count++;
1459 gtk_widget_queue_resize (widget);
1461 else if (xcre->value_mask & (CWX | CWY))
1463 gtk_socket_send_configure_event (socket);
1465 /* Ignore stacking requests. */
1467 return_val = GDK_FILTER_REMOVE;
1474 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
1476 /* Note that we get destroy notifies both from SubstructureNotify on
1477 * our window and StructureNotify on socket->plug_window
1479 if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window)))
1483 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
1485 gdk_window_destroy_notify (private->plug_window);
1486 gtk_socket_end_embedding (socket);
1488 g_object_ref (widget);
1489 g_signal_emit_by_name (widget, "plug-removed", &result);
1491 gtk_widget_destroy (widget);
1492 g_object_unref (widget);
1494 return_val = GDK_FILTER_REMOVE;
1500 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
1502 gtk_socket_claim_focus (socket, TRUE);
1504 return_val = GDK_FILTER_REMOVE;
1507 return_val = GDK_FILTER_REMOVE;
1510 if (!private->plug_window)
1512 gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
1515 if (private->plug_window)
1517 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
1519 gtk_socket_handle_map_request (socket);
1520 return_val = GDK_FILTER_REMOVE;
1523 case PropertyNotify:
1524 if (private->plug_window &&
1525 xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window))
1527 GdkDragProtocol protocol;
1529 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
1531 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
1532 private->have_size = FALSE;
1533 gtk_widget_queue_resize (widget);
1534 return_val = GDK_FILTER_REMOVE;
1536 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
1537 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
1539 gdk_error_trap_push ();
1540 if (gdk_drag_get_protocol_for_display (display,
1541 xevent->xproperty.window,
1543 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
1544 private->plug_window,
1547 gdk_error_trap_pop_ignored ();
1548 return_val = GDK_FILTER_REMOVE;
1550 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
1552 unsigned long flags;
1554 if (xembed_get_info (private->plug_window, NULL, &flags))
1556 gboolean was_mapped = private->is_mapped;
1557 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
1559 if (was_mapped != is_mapped)
1562 gtk_socket_handle_map_request (socket);
1565 gdk_error_trap_push ();
1566 gdk_window_show (private->plug_window);
1567 gdk_error_trap_pop_ignored ();
1569 gtk_socket_unmap_notify (socket);
1573 return_val = GDK_FILTER_REMOVE;
1577 case ReparentNotify:
1580 XReparentEvent *xre = &xevent->xreparent;
1582 window = gtk_widget_get_window (widget);
1584 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
1585 if (!private->plug_window &&
1586 xre->parent == GDK_WINDOW_XID (window))
1588 gtk_socket_add_window (socket, xre->window, FALSE);
1590 if (private->plug_window)
1592 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
1595 return_val = GDK_FILTER_REMOVE;
1599 if (private->plug_window &&
1600 xre->window == GDK_WINDOW_XID (private->plug_window) &&
1601 xre->parent != GDK_WINDOW_XID (window))
1605 gtk_socket_end_embedding (socket);
1607 g_object_ref (widget);
1608 g_signal_emit_by_name (widget, "plug-removed", &result);
1610 gtk_widget_destroy (widget);
1611 g_object_unref (widget);
1613 return_val = GDK_FILTER_REMOVE;
1620 if (private->plug_window &&
1621 xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window))
1623 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
1625 gtk_socket_unmap_notify (socket);
1626 return_val = GDK_FILTER_REMOVE;