/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 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. */ /* By Owen Taylor 98/4/4 */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include #include "gdk/gdkkeysyms.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkwindow.h" #include "gtkplug.h" #include "gtkprivate.h" #include "gtksocket.h" #include "gtksocketprivate.h" #include "gtkdnd.h" #include "x11/gdkx.h" #ifdef HAVE_XFIXES #include #endif #include "gtkxembed.h" #include "gtkalias.h" static gboolean xembed_get_info (GdkWindow *gdk_window, unsigned long *version, unsigned long *flags); /* From Tk */ #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 GdkNativeWindow _gtk_socket_windowing_get_id (GtkSocket *socket) { return GDK_WINDOW_XWINDOW (GTK_WIDGET (socket)->window); } void _gtk_socket_windowing_realize_window (GtkSocket *socket) { GdkWindow *window = GTK_WIDGET (socket)->window; XWindowAttributes xattrs; XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (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_XWINDOW (window), (xattrs.your_event_mask & ~ButtonPressMask) | SubstructureNotifyMask | SubstructureRedirectMask); } void _gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket) { gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))), GDK_WINDOW_XWINDOW (socket->plug_window)); } void _gtk_socket_windowing_size_request (GtkSocket *socket) { XSizeHints hints; long supplied; gdk_error_trap_push (); socket->request_width = 1; socket->request_height = 1; if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window), GDK_WINDOW_XWINDOW (socket->plug_window), &hints, &supplied)) { if (hints.flags & PMinSize) { socket->request_width = MAX (hints.min_width, 1); socket->request_height = MAX (hints.min_height, 1); } else if (hints.flags & PBaseSize) { socket->request_width = MAX (hints.base_width, 1); socket->request_height = MAX (hints.base_height, 1); } } socket->have_size = TRUE; gdk_error_trap_pop (); } void _gtk_socket_windowing_send_key_event (GtkSocket *socket, GdkEvent *gdk_event, gboolean mask_key_presses) { XKeyEvent xkey; GdkScreen *screen = gdk_window_get_screen (socket->plug_window); memset (&xkey, 0, sizeof (xkey)); xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window); xkey.root = GDK_WINDOW_XWINDOW (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->plug_window), GDK_WINDOW_XWINDOW (socket->plug_window), False, (mask_key_presses ? KeyPressMask : NoEventMask), (XEvent *)&xkey); gdk_display_sync (gdk_screen_get_display (screen)); gdk_error_trap_pop (); } void _gtk_socket_windowing_focus_change (GtkSocket *socket, gboolean focus_in) { if (focus_in) _gtk_xembed_send_focus_message (socket->plug_window, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); else _gtk_xembed_send_message (socket->plug_window, XEMBED_FOCUS_OUT, 0, 0, 0); } void _gtk_socket_windowing_update_active (GtkSocket *socket, gboolean active) { _gtk_xembed_send_message (socket->plug_window, active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE, 0, 0, 0); } void _gtk_socket_windowing_update_modality (GtkSocket *socket, gboolean modality) { _gtk_xembed_send_message (socket->plug_window, modality ? XEMBED_MODALITY_ON : XEMBED_MODALITY_OFF, 0, 0, 0); } void _gtk_socket_windowing_focus (GtkSocket *socket, GtkDirectionType direction) { 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 (socket->plug_window, XEMBED_FOCUS_IN, detail); } void _gtk_socket_windowing_send_configure_event (GtkSocket *socket) { XConfigureEvent xconfigure; gint x, y; g_return_if_fail (socket->plug_window != NULL); memset (&xconfigure, 0, sizeof (xconfigure)); xconfigure.type = ConfigureNotify; xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window); xconfigure.window = GDK_WINDOW_XWINDOW (socket->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->plug_window, &x, &y); gdk_error_trap_pop (); xconfigure.x = x; xconfigure.y = y; xconfigure.width = GTK_WIDGET(socket)->allocation.width; xconfigure.height = GTK_WIDGET(socket)->allocation.height; xconfigure.border_width = 0; xconfigure.above = None; xconfigure.override_redirect = False; gdk_error_trap_push (); XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window), GDK_WINDOW_XWINDOW (socket->plug_window), False, NoEventMask, (XEvent *)&xconfigure); gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (socket))); gdk_error_trap_pop (); } void _gtk_socket_windowing_select_plug_window_input (GtkSocket *socket) { XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))), GDK_WINDOW_XWINDOW (socket->plug_window), StructureNotifyMask | PropertyChangeMask); } void _gtk_socket_windowing_embed_get_info (GtkSocket *socket) { unsigned long version; unsigned long flags; socket->xembed_version = -1; if (xembed_get_info (socket->plug_window, &version, &flags)) { socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version); socket->is_mapped = (flags & XEMBED_MAPPED) != 0; } else { /* FIXME, we should probably actually check the state before we started */ socket->is_mapped = TRUE; } } void _gtk_socket_windowing_embed_notify (GtkSocket *socket) { #ifdef HAVE_XFIXES GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket)); XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XWINDOW (socket->plug_window), SetModeInsert, SaveSetRoot, SaveSetUnmap); #endif _gtk_xembed_send_message (socket->plug_window, XEMBED_EMBEDDED_NOTIFY, 0, GDK_WINDOW_XWINDOW (GTK_WIDGET (socket)->window), socket->xembed_version); } 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_XWINDOW (window), xembed_info_atom, 0, 2, False, xembed_info_atom, &type, &format, &nitems, &bytes_after, &data); gdk_error_trap_pop(); 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; } gboolean _gtk_socket_windowing_embed_get_focus_wrapped (void) { return _gtk_xembed_get_focus_wrapped (); } void _gtk_socket_windowing_embed_set_focus_wrapped (void) { _gtk_xembed_set_focus_wrapped (); } 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; } } GdkFilterReturn _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { GtkSocket *socket; GtkWidget *widget; GdkDisplay *display; XEvent *xevent; GdkFilterReturn return_val; socket = GTK_SOCKET (data); return_val = GDK_FILTER_CONTINUE; if (socket->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 (!socket->plug_window) { _gtk_socket_add_window (socket, xcwe->window, FALSE); if (socket->plug_window) { GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created")); } } return_val = GDK_FILTER_REMOVE; break; } case ConfigureRequest: { XConfigureRequestEvent *xcre = &xevent->xconfigurerequest; if (!socket->plug_window) _gtk_socket_add_window (socket, xcre->window, FALSE); if (socket->plug_window) { GtkSocketPrivate *private = _gtk_socket_get_private (socket); if (xcre->value_mask & (CWWidth | CWHeight)) { GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - configure request: %d %d", socket->request_width, socket->request_height)); private->resize_count++; gtk_widget_queue_resize (widget); } else if (xcre->value_mask & (CWX | CWY)) { _gtk_socket_windowing_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 (socket->plug_window && (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window))) { gboolean result; GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify")); gdk_window_destroy_notify (socket->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 (!socket->plug_window) { _gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE); } if (socket->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 (socket->plug_window && xevent->xproperty.window == GDK_WINDOW_XWINDOW (socket->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")); socket->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 (); if (gdk_drag_get_protocol_for_display (display, xevent->xproperty.window, &protocol)) gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window, protocol, TRUE); gdk_display_sync (display); gdk_error_trap_pop (); 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 (socket->plug_window, NULL, &flags)) { gboolean was_mapped = socket->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 (socket->plug_window); gdk_flush (); gdk_error_trap_pop (); _gtk_socket_unmap_notify (socket); } } } return_val = GDK_FILTER_REMOVE; } } break; case ReparentNotify: { XReparentEvent *xre = &xevent->xreparent; GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received")); if (!socket->plug_window && xre->parent == GDK_WINDOW_XWINDOW (widget->window)) { _gtk_socket_add_window (socket, xre->window, FALSE); if (socket->plug_window) { GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented")); } return_val = GDK_FILTER_REMOVE; } else { if (socket->plug_window && xre->window == GDK_WINDOW_XWINDOW (socket->plug_window) && xre->parent != GDK_WINDOW_XWINDOW (widget->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 (socket->plug_window && xevent->xunmap.window == GDK_WINDOW_XWINDOW (socket->plug_window)) { GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify")); _gtk_socket_unmap_notify (socket); return_val = GDK_FILTER_REMOVE; } break; } return return_val; }