X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtksocket.c;h=c497f5065eaf7936323909bccb456f485ad4f570;hb=d98b4fd0b433d56f7b2148f617baf212014a3b3e;hp=8c92b9a7422ed8966779049a04c630e496276dca;hpb=b5c6904c2f77d20e64b86c48b262d7306502a880;p=~andy%2Fgtk diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c index 8c92b9a74..c497f5065 100644 --- a/gtk/gtksocket.c +++ b/gtk/gtksocket.c @@ -12,8 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * License along with this library. If not, see .Free */ /* By Owen Taylor 98/4/4 */ @@ -27,46 +26,47 @@ #include "config.h" -#include "gtksocket.h" +#include "gtksocketprivate.h" #include -#include "gtkmain.h" #include "gtkmarshalers.h" #include "gtksizerequest.h" -#include "gtkwindowprivate.h" #include "gtkplug.h" #include "gtkprivate.h" -#include "gtksocketprivate.h" #include "gtkdnd.h" #include "gtkdebug.h" #include "gtkintl.h" +#include "gtkmain.h" #include "gtkwidgetprivate.h" -#ifdef GDK_WINDOWING_X11 -#include "x11/gdkx.h" +#include +#include + +#ifdef HAVE_XFIXES +#include #endif +#include "gtkxembed.h" + /** * SECTION:gtksocket * @Short_description: Container for widgets from other processes * @Title: GtkSocket + * @include: gtk/gtkx.h * @See_also: #GtkPlug, XEmbed * - * Together with #GtkPlug, #GtkSocket provides the ability - * to embed widgets from one process into another process - * in a fashion that is transparent to the user. One - * process creates a #GtkSocket widget and passes - * that widget's window ID to the other process, - * which then creates a #GtkPlug with that window ID. - * Any widgets contained in the #GtkPlug then will appear - * inside the first application's window. + * Together with #GtkPlug, #GtkSocket provides the ability to embed + * widgets from one process into another process in a fashion that + * is transparent to the user. One process creates a #GtkSocket widget + * and passes that widget's window ID to the other process, which then + * creates a #GtkPlug with that window ID. Any widgets contained in the + * #GtkPlug then will appear inside the first application's window. * - * The socket's window ID is obtained by using - * gtk_socket_get_id(). Before using this function, - * the socket must have been realized, and for hence, - * have been added to its parent. + * The socket's window ID is obtained by using gtk_socket_get_id(). + * Before using this function, the socket must have been realized, + * and for hence, have been added to its parent. * * * Obtaining the window ID of a socket. @@ -75,9 +75,9 @@ * gtk_widget_show (socket); * gtk_container_add (GTK_CONTAINER (parent), socket); * - * /* The following call is only necessary if one of + * /* The following call is only necessary if one of * * the ancestors of the socket is not yet visible. - * */ + * */ * gtk_widget_realize (socket); * g_print ("The ID of the sockets window is %#x\n", * gtk_socket_get_id (socket)); @@ -85,32 +85,34 @@ * * * Note that if you pass the window ID of the socket to another - * process that will create a plug in the socket, you - * must make sure that the socket widget is not destroyed - * until that plug is created. Violating this rule will - * cause unpredictable consequences, the most likely - * consequence being that the plug will appear as a - * separate toplevel window. You can check if the plug - * has been created by using gtk_socket_get_plug_window(). If - * it returns a non-%NULL value, then the plug has been + * process that will create a plug in the socket, you must make + * sure that the socket widget is not destroyed until that plug + * is created. Violating this rule will cause unpredictable + * consequences, the most likely consequence being that the plug + * will appear as a separate toplevel window. You can check if + * the plug has been created by using gtk_socket_get_plug_window(). + * If it returns a non-%NULL value, then the plug has been * successfully created inside of the socket. * - * When GTK+ is notified that the embedded window has been - * destroyed, then it will destroy the socket as well. You - * should always, therefore, be prepared for your sockets - * to be destroyed at any time when the main event loop - * is running. To prevent this from happening, you can - * connect to the #GtkSocket::plug-removed signal. + * When GTK+ is notified that the embedded window has been destroyed, + * then it will destroy the socket as well. You should always, + * therefore, be prepared for your sockets to be destroyed at any + * time when the main event loop is running. To prevent this from + * happening, you can connect to the #GtkSocket::plug-removed signal. * * The communication between a #GtkSocket and a #GtkPlug follows the * XEmbed - * protocol. This protocol has also been implemented in other toolkits, e.g. - * Qt, allowing the same level of integration - * when embedding a Qt widget in GTK or vice versa. + * protocol. This protocol has also been implemented in other toolkits, + * e.g. Qt, allowing the same level of + * integration when embedding a Qt widget + * in GTK or vice versa. * * - * The #GtkPlug and #GtkSocket widgets are currently not available - * on all platforms supported by GTK+. + * The #GtkPlug and #GtkSocket widgets are only available when GTK+ + * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined. + * They can only be used on a #GdkX11Display. To use #GtkPlug and + * #GtkSocket, you need to include the gtk/gtkx.h + * header. * */ @@ -143,6 +145,19 @@ static void gtk_socket_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); +static void gtk_socket_add_window (GtkSocket *socket, + Window xid, + gboolean need_reparent); +static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data); + +static gboolean xembed_get_info (GdkWindow *gdk_window, + unsigned long *version, + unsigned long *flags); + +/* From Tk */ +#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 /* Local data */ @@ -289,7 +304,7 @@ gtk_socket_new (void) /** * gtk_socket_add_id: * @socket_: a #GtkSocket - * @window_id: the window ID of a client participating in the XEMBED protocol. + * @window: the Window of a client participating in the XEMBED protocol. * * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The * client may be in the same process or in a different process. @@ -306,7 +321,7 @@ gtk_socket_new (void) **/ void gtk_socket_add_id (GtkSocket *socket, - GdkNativeWindow window_id) + Window window) { g_return_if_fail (GTK_IS_SOCKET (socket)); g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket))); @@ -314,7 +329,7 @@ gtk_socket_add_id (GtkSocket *socket, if (!gtk_widget_get_realized (GTK_WIDGET (socket))) gtk_widget_realize (GTK_WIDGET (socket)); - _gtk_socket_add_window (socket, window_id, TRUE); + gtk_socket_add_window (socket, window, TRUE); } /** @@ -330,7 +345,7 @@ gtk_socket_add_id (GtkSocket *socket, * * Return value: the window ID for the socket **/ -GdkNativeWindow +Window gtk_socket_get_id (GtkSocket *socket) { g_return_val_if_fail (GTK_IS_SOCKET (socket), 0); @@ -339,7 +354,7 @@ gtk_socket_get_id (GtkSocket *socket) if (!gtk_widget_get_realized (GTK_WIDGET (socket))) gtk_widget_realize (GTK_WIDGET (socket)); - return _gtk_socket_windowing_get_id (socket); + return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))); } /** @@ -365,9 +380,9 @@ static void gtk_socket_realize (GtkWidget *widget) { GtkAllocation allocation; - GtkSocket *socket = GTK_SOCKET (widget); GdkWindow *window; GdkWindowAttr attributes; + XWindowAttributes xattrs; gint attributes_mask; gtk_widget_set_realized (widget, TRUE); @@ -388,15 +403,30 @@ gtk_socket_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, socket); + gtk_widget_register_window (widget, window); gtk_style_context_set_background (gtk_widget_get_style_context (widget), window); - _gtk_socket_windowing_realize_window (socket); + XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + &xattrs); + + /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects + for input on the socket with a mask of 0x0fffff (for god knows why) + which includes ButtonPressMask causing a BadAccess if someone else + also selects for this. As per the client-side windows merge we always + normally selects for button press so we can emulate it on client + side children that selects for button press. However, we don't need + this for GtkSocket, so we unselect it here, fixing the crashes in + firefox. */ + XSelectInput (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + (xattrs.your_event_mask & ~ButtonPressMask) | + SubstructureNotifyMask | SubstructureRedirectMask); gdk_window_add_filter (window, - _gtk_socket_windowing_filter_func, + gtk_socket_filter_func, widget); /* We sync here so that we make sure that if the XID for @@ -407,20 +437,15 @@ gtk_socket_realize (GtkWidget *widget) } /** - * _gtk_socket_end_embedding: - * + * gtk_socket_end_embedding: * @socket: a #GtkSocket * * Called to end the embedding of a plug in the socket. */ -void -_gtk_socket_end_embedding (GtkSocket *socket) +static void +gtk_socket_end_embedding (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); - - if (GTK_IS_WINDOW (toplevel)) - _gtk_socket_windowing_end_embedding_toplevel (socket); g_object_unref (private->plug_window); private->plug_window = NULL; @@ -445,12 +470,44 @@ gtk_socket_unrealize (GtkWidget *widget) } else if (private->plug_window) { - _gtk_socket_end_embedding (socket); + gtk_socket_end_embedding (socket); } GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget); } +static void +gtk_socket_size_request (GtkSocket *socket) +{ + GtkSocketPrivate *private = socket->priv; + XSizeHints hints; + long supplied; + + gdk_error_trap_push (); + + private->request_width = 1; + private->request_height = 1; + + if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window), + GDK_WINDOW_XID (private->plug_window), + &hints, &supplied)) + { + if (hints.flags & PMinSize) + { + private->request_width = MAX (hints.min_width, 1); + private->request_height = MAX (hints.min_height, 1); + } + else if (hints.flags & PBaseSize) + { + private->request_width = MAX (hints.base_width, 1); + private->request_height = MAX (hints.base_height, 1); + } + } + private->have_size = TRUE; + + gdk_error_trap_pop_ignored (); +} + static void gtk_socket_get_preferred_width (GtkWidget *widget, gint *minimum, @@ -466,7 +523,7 @@ gtk_socket_get_preferred_width (GtkWidget *widget, else { if (private->is_mapped && !private->have_size && private->plug_window) - _gtk_socket_windowing_size_request (socket); + gtk_socket_size_request (socket); if (private->is_mapped && private->have_size) *minimum = *natural = MAX (private->request_width, 1); @@ -490,7 +547,7 @@ gtk_socket_get_preferred_height (GtkWidget *widget, else { if (private->is_mapped && !private->have_size && private->plug_window) - _gtk_socket_windowing_size_request (socket); + gtk_socket_size_request (socket); if (private->is_mapped && private->have_size) *minimum = *natural = MAX (private->request_height, 1); @@ -499,6 +556,46 @@ gtk_socket_get_preferred_height (GtkWidget *widget, } } +static void +gtk_socket_send_configure_event (GtkSocket *socket) +{ + GtkAllocation allocation; + XConfigureEvent xconfigure; + gint x, y; + + g_return_if_fail (socket->priv->plug_window != NULL); + + memset (&xconfigure, 0, sizeof (xconfigure)); + xconfigure.type = ConfigureNotify; + + xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window); + xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window); + + /* The ICCCM says that synthetic events should have root relative + * coordinates. We still aren't really ICCCM compliant, since + * we don't send events when the real toplevel is moved. + */ + gdk_error_trap_push (); + gdk_window_get_origin (socket->priv->plug_window, &x, &y); + gdk_error_trap_pop_ignored (); + + gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation); + xconfigure.x = x; + xconfigure.y = y; + xconfigure.width = allocation.width; + xconfigure.height = allocation.height; + + xconfigure.border_width = 0; + xconfigure.above = None; + xconfigure.override_redirect = False; + + gdk_error_trap_push (); + XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window), + GDK_WINDOW_XID (socket->priv->plug_window), + False, NoEventMask, (XEvent *)&xconfigure); + gdk_error_trap_pop_ignored (); +} + static void gtk_socket_size_allocate (GtkWidget *widget, GtkAllocation *allocation) @@ -552,7 +649,7 @@ gtk_socket_size_allocate (GtkWidget *widget, while (private->resize_count) { - _gtk_socket_windowing_send_configure_event (socket); + gtk_socket_send_configure_event (socket); private->resize_count--; GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - sending synthetic configure: %d %d", @@ -564,6 +661,37 @@ gtk_socket_size_allocate (GtkWidget *widget, } } +static void +gtk_socket_send_key_event (GtkSocket *socket, + GdkEvent *gdk_event, + gboolean mask_key_presses) +{ + XKeyEvent xkey; + GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window); + + memset (&xkey, 0, sizeof (xkey)); + xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; + xkey.window = GDK_WINDOW_XID (socket->priv->plug_window); + xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen)); + xkey.subwindow = None; + xkey.time = gdk_event->key.time; + xkey.x = 0; + xkey.y = 0; + xkey.x_root = 0; + xkey.y_root = 0; + xkey.state = gdk_event->key.state; + xkey.keycode = gdk_event->key.hardware_keycode; + xkey.same_screen = True;/* FIXME ? */ + + gdk_error_trap_push (); + XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window), + GDK_WINDOW_XID (socket->priv->plug_window), + False, + (mask_key_presses ? KeyPressMask : NoEventMask), + (XEvent *)&xkey); + gdk_error_trap_pop_ignored (); +} + static gboolean activate_key (GtkAccelGroup *accel_group, GObject *acceleratable, @@ -578,7 +706,7 @@ activate_key (GtkAccelGroup *accel_group, if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window) { - _gtk_socket_windowing_send_key_event (socket, gdk_event, FALSE); + gtk_socket_send_key_event (socket, gdk_event, FALSE); retval = TRUE; } @@ -600,8 +728,7 @@ find_accel_key (GtkAccelKey *key, } /** - * _gtk_socket_add_grabbed_key: - * + * gtk_socket_add_grabbed_key: * @socket: a #GtkSocket * @keyval: a key * @modifiers: modifiers for the key @@ -609,10 +736,10 @@ find_accel_key (GtkAccelKey *key, * Called from the GtkSocket platform-specific backend when the * corresponding plug has told the socket to grab a key. */ -void -_gtk_socket_add_grabbed_key (GtkSocket *socket, - guint keyval, - GdkModifierType modifiers) +static void +gtk_socket_add_grabbed_key (GtkSocket *socket, + guint keyval, + GdkModifierType modifiers) { GClosure *closure; GrabbedKey *grabbed_key; @@ -639,8 +766,7 @@ _gtk_socket_add_grabbed_key (GtkSocket *socket, } /** - * _gtk_socket_remove_grabbed_key: - * + * gtk_socket_remove_grabbed_key: * @socket: a #GtkSocket * @keyval: a key * @modifiers: modifiers for the key @@ -648,10 +774,10 @@ _gtk_socket_add_grabbed_key (GtkSocket *socket, * Called from the GtkSocket backend when the corresponding plug has * told the socket to remove a key grab. */ -void -_gtk_socket_remove_grabbed_key (GtkSocket *socket, - guint keyval, - GdkModifierType modifiers) +static void +gtk_socket_remove_grabbed_key (GtkSocket *socket, + guint keyval, + GdkModifierType modifiers) { if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers)) g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n", @@ -678,7 +804,12 @@ socket_update_focus_in (GtkSocket *socket) { private->focus_in = focus_in; - _gtk_socket_windowing_focus_change (socket, focus_in); + if (focus_in) + _gtk_xembed_send_focus_message (private->plug_window, + XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); + else + _gtk_xembed_send_message (private->plug_window, + XEMBED_FOCUS_OUT, 0, 0, 0); } } @@ -701,7 +832,9 @@ socket_update_active (GtkSocket *socket) { private->active = active; - _gtk_socket_windowing_update_active (socket, active); + _gtk_xembed_send_message (private->plug_window, + active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE, + 0, 0, 0); } } @@ -752,7 +885,9 @@ gtk_socket_grab_notify (GtkWidget *widget, GtkSocket *socket = GTK_SOCKET (widget); if (!socket->priv->same_app) - _gtk_socket_windowing_update_modality (socket, !was_grabbed); + _gtk_xembed_send_message (socket->priv->plug_window, + was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON, + 0, 0, 0); } static gboolean @@ -764,7 +899,7 @@ gtk_socket_key_event (GtkWidget *widget, if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget) { - _gtk_socket_windowing_send_key_event (socket, (GdkEvent *) event, FALSE); + gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE); return TRUE; } @@ -776,22 +911,23 @@ static void gtk_socket_notify (GObject *object, GParamSpec *pspec) { - if (!strcmp (pspec->name, "is-focus")) - return; - socket_update_focus_in (GTK_SOCKET (object)); + if (strcmp (pspec->name, "is-focus") == 0) + socket_update_focus_in (GTK_SOCKET (object)); + + if (G_OBJECT_CLASS (gtk_socket_parent_class)->notify) + G_OBJECT_CLASS (gtk_socket_parent_class)->notify (object, pspec); } /** - * _gtk_socket_claim_focus: - * + * gtk_socket_claim_focus: * @socket: a #GtkSocket * @send_event: huh? * * Claims focus for the socket. XXX send_event? */ -void -_gtk_socket_claim_focus (GtkSocket *socket, - gboolean send_event) +static void +gtk_socket_claim_focus (GtkSocket *socket, + gboolean send_event) { GtkWidget *widget = GTK_WIDGET (socket); GtkSocketPrivate *private = socket->priv; @@ -818,8 +954,24 @@ gtk_socket_focus (GtkWidget *widget, if (!gtk_widget_is_focus (widget)) { - _gtk_socket_windowing_focus (socket, direction); - _gtk_socket_claim_focus (socket, FALSE); + gint detail = -1; + + switch (direction) + { + case GTK_DIR_UP: + case GTK_DIR_LEFT: + case GTK_DIR_TAB_BACKWARD: + detail = XEMBED_FOCUS_LAST; + break; + case GTK_DIR_DOWN: + case GTK_DIR_RIGHT: + case GTK_DIR_TAB_FORWARD: + detail = XEMBED_FOCUS_FIRST; + break; + } + + _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail); + gtk_socket_claim_focus (socket, FALSE); return TRUE; } @@ -853,30 +1005,29 @@ gtk_socket_forall (GtkContainer *container, } /** - * _gtk_socket_add_window: - * + * gtk_socket_add_window: * @socket: a #GtkSocket * @xid: the native identifier for a window * @need_reparent: whether the socket's plug's window needs to be - * reparented to the socket + * reparented to the socket * * Adds a window to a GtkSocket. */ -void -_gtk_socket_add_window (GtkSocket *socket, - GdkNativeWindow xid, - gboolean need_reparent) +static void +gtk_socket_add_window (GtkSocket *socket, + Window xid, + gboolean need_reparent) { GtkWidget *widget = GTK_WIDGET (socket); GdkDisplay *display = gtk_widget_get_display (widget); gpointer user_data = NULL; GtkSocketPrivate *private = socket->priv; + unsigned long version; + unsigned long flags; -#ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (display)) private->plug_window = gdk_x11_window_lookup_for_display (display, xid); else -#endif private->plug_window = NULL; if (private->plug_window) @@ -902,17 +1053,14 @@ _gtk_socket_add_window (GtkSocket *socket, } else /* A foreign window */ { - GtkWidget *toplevel; GdkDragProtocol protocol; gdk_error_trap_push (); if (!private->plug_window) { -#ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (display)) private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid); -#endif if (!private->plug_window) /* was deleted before we could get it */ { gdk_error_trap_pop_ignored (); @@ -920,7 +1068,9 @@ _gtk_socket_add_window (GtkSocket *socket, } } - _gtk_socket_windowing_select_plug_window_input (socket); + XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))), + GDK_WINDOW_XID (private->plug_window), + StructureNotifyMask | PropertyChangeMask); if (gdk_error_trap_pop ()) { @@ -943,27 +1093,42 @@ _gtk_socket_add_window (GtkSocket *socket, private->have_size = FALSE; - _gtk_socket_windowing_embed_get_info (socket); + private->xembed_version = -1; + if (xembed_get_info (private->plug_window, &version, &flags)) + { + private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version); + private->is_mapped = (flags & XEMBED_MAPPED) != 0; + } + else + { + /* FIXME, we should probably actually check the state before we started */ + private->is_mapped = TRUE; + } private->need_map = private->is_mapped; - if (gdk_drag_get_protocol_for_display (display, xid, &protocol)) + protocol = gdk_window_get_drag_protocol (private->plug_window, NULL); + if (protocol) gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window, protocol, TRUE); gdk_error_trap_pop_ignored (); gdk_window_add_filter (private->plug_window, - _gtk_socket_windowing_filter_func, + gtk_socket_filter_func, socket); - /* Add a pointer to the socket on our toplevel window */ - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); - if (GTK_IS_WINDOW (toplevel)) - gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid); - - _gtk_socket_windowing_embed_notify (socket); +#ifdef HAVE_XFIXES + gdk_error_trap_push (); + XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))), + GDK_WINDOW_XID (private->plug_window), + SetModeInsert, SaveSetRoot, SaveSetUnmap); + gdk_error_trap_pop_ignored (); +#endif + _gtk_xembed_send_message (private->plug_window, + XEMBED_EMBEDDED_NOTIFY, 0, + GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))), + private->xembed_version); socket_update_active (socket); socket_update_focus_in (socket); @@ -976,14 +1141,13 @@ _gtk_socket_add_window (GtkSocket *socket, } /** - * _gtk_socket_handle_map_request: - * + * gtk_socket_handle_map_request: * @socket: a #GtkSocket * * Called from the GtkSocket backend when the plug has been mapped. */ -void -_gtk_socket_handle_map_request (GtkSocket *socket) +static void +gtk_socket_handle_map_request (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; if (!private->is_mapped) @@ -996,14 +1160,13 @@ _gtk_socket_handle_map_request (GtkSocket *socket) } /** - * _gtk_socket_unmap_notify: - * + * gtk_socket_unmap_notify: * @socket: a #GtkSocket * * Called from the GtkSocket backend when the plug has been unmapped ??? */ -void -_gtk_socket_unmap_notify (GtkSocket *socket) +static void +gtk_socket_unmap_notify (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; if (private->is_mapped) @@ -1014,17 +1177,16 @@ _gtk_socket_unmap_notify (GtkSocket *socket) } /** - * _gtk_socket_advance_toplevel_focus: - * + * gtk_socket_advance_toplevel_focus: * @socket: a #GtkSocket * @direction: a direction * * Called from the GtkSocket backend when the corresponding plug * has told the socket to move the focus. */ -void -_gtk_socket_advance_toplevel_focus (GtkSocket *socket, - GtkDirectionType direction) +static void +gtk_socket_advance_toplevel_focus (GtkSocket *socket, + GtkDirectionType direction) { GtkBin *bin; GtkWindow *window; @@ -1062,10 +1224,10 @@ _gtk_socket_advance_toplevel_focus (GtkSocket *socket, /* We are allowed exactly one wrap-around per sequence of focus * events */ - if (_gtk_socket_windowing_embed_get_focus_wrapped ()) + if (_gtk_xembed_get_focus_wrapped ()) return; else - _gtk_socket_windowing_embed_set_focus_wrapped (); + _gtk_xembed_set_focus_wrapped (); } focus_widget = gtk_window_get_focus (window); @@ -1090,3 +1252,361 @@ _gtk_socket_advance_toplevel_focus (GtkSocket *socket, return; } } + +static gboolean +xembed_get_info (GdkWindow *window, + unsigned long *version, + unsigned long *flags) +{ + GdkDisplay *display = gdk_window_get_display (window); + Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"); + Atom type; + int format; + unsigned long nitems, bytes_after; + unsigned char *data; + unsigned long *data_long; + int status; + + gdk_error_trap_push (); + status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XID (window), + xembed_info_atom, + 0, 2, False, + xembed_info_atom, &type, &format, + &nitems, &bytes_after, &data); + gdk_error_trap_pop_ignored (); + + if (status != Success) + return FALSE; /* Window vanished? */ + + if (type == None) /* No info property */ + return FALSE; + + if (type != xembed_info_atom) + { + g_warning ("_XEMBED_INFO property has wrong type\n"); + return FALSE; + } + + if (nitems < 2) + { + g_warning ("_XEMBED_INFO too short\n"); + XFree (data); + return FALSE; + } + + data_long = (unsigned long *)data; + if (version) + *version = data_long[0]; + if (flags) + *flags = data_long[1] & XEMBED_MAPPED; + + XFree (data); + return TRUE; +} + +static void +handle_xembed_message (GtkSocket *socket, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2, + guint32 time) +{ + GTK_NOTE (PLUGSOCKET, + g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message))); + + switch (message) + { + case XEMBED_EMBEDDED_NOTIFY: + case XEMBED_WINDOW_ACTIVATE: + case XEMBED_WINDOW_DEACTIVATE: + case XEMBED_MODALITY_ON: + case XEMBED_MODALITY_OFF: + case XEMBED_FOCUS_IN: + case XEMBED_FOCUS_OUT: + g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message)); + break; + + case XEMBED_REQUEST_FOCUS: + gtk_socket_claim_focus (socket, TRUE); + break; + + case XEMBED_FOCUS_NEXT: + case XEMBED_FOCUS_PREV: + gtk_socket_advance_toplevel_focus (socket, + (message == XEMBED_FOCUS_NEXT ? + GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD)); + break; + + case XEMBED_GTK_GRAB_KEY: + gtk_socket_add_grabbed_key (socket, data1, data2); + break; + case XEMBED_GTK_UNGRAB_KEY: + gtk_socket_remove_grabbed_key (socket, data1, data2); + break; + + case XEMBED_GRAB_KEY: + case XEMBED_UNGRAB_KEY: + break; + + default: + GTK_NOTE (PLUGSOCKET, + g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message)); + break; + } +} + +static GdkFilterReturn +gtk_socket_filter_func (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data) +{ + GtkSocket *socket; + GtkWidget *widget; + GdkDisplay *display; + XEvent *xevent; + GtkSocketPrivate *private; + + GdkFilterReturn return_val; + + socket = GTK_SOCKET (data); + private = socket->priv; + + return_val = GDK_FILTER_CONTINUE; + + if (private->plug_widget) + return return_val; + + widget = GTK_WIDGET (socket); + xevent = (XEvent *)gdk_xevent; + display = gtk_widget_get_display (widget); + + switch (xevent->type) + { + case ClientMessage: + if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED")) + { + _gtk_xembed_push_message (xevent); + handle_xembed_message (socket, + xevent->xclient.data.l[1], + xevent->xclient.data.l[2], + xevent->xclient.data.l[3], + xevent->xclient.data.l[4], + xevent->xclient.data.l[0]); + _gtk_xembed_pop_message (); + + return_val = GDK_FILTER_REMOVE; + } + break; + + case CreateNotify: + { + XCreateWindowEvent *xcwe = &xevent->xcreatewindow; + + if (!private->plug_window) + { + gtk_socket_add_window (socket, xcwe->window, FALSE); + + if (private->plug_window) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created")); + } + } + + return_val = GDK_FILTER_REMOVE; + + break; + } + + case ConfigureRequest: + { + XConfigureRequestEvent *xcre = &xevent->xconfigurerequest; + + if (!private->plug_window) + gtk_socket_add_window (socket, xcre->window, FALSE); + + if (private->plug_window) + { + if (xcre->value_mask & (CWWidth | CWHeight)) + { + GTK_NOTE (PLUGSOCKET, + g_message ("GtkSocket - configure request: %d %d", + private->request_width, + private->request_height)); + + private->resize_count++; + gtk_widget_queue_resize (widget); + } + else if (xcre->value_mask & (CWX | CWY)) + { + gtk_socket_send_configure_event (socket); + } + /* Ignore stacking requests. */ + + return_val = GDK_FILTER_REMOVE; + } + break; + } + + case DestroyNotify: + { + XDestroyWindowEvent *xdwe = &xevent->xdestroywindow; + + /* Note that we get destroy notifies both from SubstructureNotify on + * our window and StructureNotify on socket->plug_window + */ + if (private->plug_window && (xdwe->window == GDK_WINDOW_XID (private->plug_window))) + { + gboolean result; + + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify")); + + gdk_window_destroy_notify (private->plug_window); + gtk_socket_end_embedding (socket); + + g_object_ref (widget); + g_signal_emit_by_name (widget, "plug-removed", &result); + if (!result) + gtk_widget_destroy (widget); + g_object_unref (widget); + + return_val = GDK_FILTER_REMOVE; + } + break; + } + + case FocusIn: + if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) + { + gtk_socket_claim_focus (socket, TRUE); + } + return_val = GDK_FILTER_REMOVE; + break; + case FocusOut: + return_val = GDK_FILTER_REMOVE; + break; + case MapRequest: + if (!private->plug_window) + { + gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE); + } + + if (private->plug_window) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request")); + + gtk_socket_handle_map_request (socket); + return_val = GDK_FILTER_REMOVE; + } + break; + case PropertyNotify: + if (private->plug_window && + xevent->xproperty.window == GDK_WINDOW_XID (private->plug_window)) + { + GdkDragProtocol protocol; + + if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS")) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS")); + private->have_size = FALSE; + gtk_widget_queue_resize (widget); + return_val = GDK_FILTER_REMOVE; + } + else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) || + (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO"))) + { + gdk_error_trap_push (); + protocol = gdk_window_get_drag_protocol (private->plug_window, NULL); + if (protocol) + gtk_drag_dest_set_proxy (GTK_WIDGET (socket), + private->plug_window, + protocol, TRUE); + + gdk_error_trap_pop_ignored (); + return_val = GDK_FILTER_REMOVE; + } + else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO")) + { + unsigned long flags; + + if (xembed_get_info (private->plug_window, NULL, &flags)) + { + gboolean was_mapped = private->is_mapped; + gboolean is_mapped = (flags & XEMBED_MAPPED) != 0; + + if (was_mapped != is_mapped) + { + if (is_mapped) + gtk_socket_handle_map_request (socket); + else + { + gdk_error_trap_push (); + gdk_window_show (private->plug_window); + gdk_error_trap_pop_ignored (); + + gtk_socket_unmap_notify (socket); + } + } + } + return_val = GDK_FILTER_REMOVE; + } + } + break; + case ReparentNotify: + { + GdkWindow *window; + XReparentEvent *xre = &xevent->xreparent; + + window = gtk_widget_get_window (widget); + + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received")); + if (!private->plug_window && + xre->parent == GDK_WINDOW_XID (window)) + { + gtk_socket_add_window (socket, xre->window, FALSE); + + if (private->plug_window) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented")); + } + + return_val = GDK_FILTER_REMOVE; + } + else + { + if (private->plug_window && + xre->window == GDK_WINDOW_XID (private->plug_window) && + xre->parent != GDK_WINDOW_XID (window)) + { + gboolean result; + + gtk_socket_end_embedding (socket); + + g_object_ref (widget); + g_signal_emit_by_name (widget, "plug-removed", &result); + if (!result) + gtk_widget_destroy (widget); + g_object_unref (widget); + + return_val = GDK_FILTER_REMOVE; + } + } + + break; + } + case UnmapNotify: + if (private->plug_window && + xevent->xunmap.window == GDK_WINDOW_XID (private->plug_window)) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify")); + + gtk_socket_unmap_notify (socket); + return_val = GDK_FILTER_REMOVE; + } + break; + + } + + return return_val; +}