/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * Copyright (C) 2005 Novell, Inc. * * 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 Tor Lillqvist 2005 */ /* * Modified by the GTK+ Team and others 1997-2005. 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 "gtkmarshalers.h" #include "gtkplug.h" #include "gtkplugprivate.h" #include "win32/gdkwin32.h" #include "gtkwin32embed.h" #if defined(_MSC_VER) && (WINVER < 0x0500) #ifndef GA_PARENT #define GA_PARENT 1 #endif WINUSERAPI HWND WINAPI GetAncestor(HWND,UINT); #endif GdkNativeWindow _gtk_plug_windowing_get_id (GtkPlug *plug) { return (GdkNativeWindow) GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (plug))); } void _gtk_plug_windowing_realize_toplevel (GtkPlug *plug) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) { _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_PARENT_NOTIFY, (WPARAM) GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (plug))), GTK_WIN32_EMBED_PROTOCOL_VERSION); _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_EVENT_PLUG_MAPPED, 0, 0); } } void _gtk_plug_windowing_map_toplevel (GtkPlug *plug) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_EVENT_PLUG_MAPPED, 1, 0); } void _gtk_plug_windowing_unmap_toplevel (GtkPlug *plug) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_EVENT_PLUG_MAPPED, 0, 0); } void _gtk_plug_windowing_set_focus (GtkPlug *plug) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_REQUEST_FOCUS, 0, 0); } void _gtk_plug_windowing_add_grabbed_key (GtkPlug *plug, guint accelerator_key, GdkModifierType accelerator_mods) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_GRAB_KEY, accelerator_key, accelerator_mods); } void _gtk_plug_windowing_remove_grabbed_key (GtkPlug *plug, guint accelerator_key, GdkModifierType accelerator_mods) { GtkPlugPrivate *priv = plug->priv; if (priv->socket_window) _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_UNGRAB_KEY, accelerator_key, accelerator_mods); } void _gtk_plug_windowing_focus_to_parent (GtkPlug *plug, GtkDirectionType direction) { GtkPlugPrivate *priv = plug->priv; GtkWin32EmbedMessageType message = GTK_WIN32_EMBED_FOCUS_PREV; switch (direction) { case GTK_DIR_UP: case GTK_DIR_LEFT: case GTK_DIR_TAB_BACKWARD: message = GTK_WIN32_EMBED_FOCUS_PREV; break; case GTK_DIR_DOWN: case GTK_DIR_RIGHT: case GTK_DIR_TAB_FORWARD: message = GTK_WIN32_EMBED_FOCUS_NEXT; break; } _gtk_win32_embed_send_focus_message (priv->socket_window, message, 0); } GdkFilterReturn _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { GtkPlug *plug = GTK_PLUG (data); GtkPlugPrivate *priv = plug->priv; MSG *msg = (MSG *) gdk_xevent; GdkFilterReturn return_val = GDK_FILTER_CONTINUE; switch (msg->message) { /* What message should we look for to notice the reparenting? * Maybe WM_WINDOWPOSCHANGED will work? This is handled in the * X11 implementation by handling ReparentNotify. Handle this * only for cross-process embedding, otherwise we get odd * crashes in testsocket. */ case WM_WINDOWPOSCHANGED: if (!priv->same_app) { HWND parent = GetAncestor (msg->hwnd, GA_PARENT); gboolean was_embedded = priv->socket_window != NULL; GdkScreen *screen = gdk_window_get_screen (event->any.window); GdkDisplay *display = gdk_screen_get_display (screen); GTK_NOTE (PLUGSOCKET, g_printerr ("WM_WINDOWPOSCHANGED: hwnd=%p GA_PARENT=%p socket_window=%p\n", msg->hwnd, parent, priv->socket_window)); g_object_ref (plug); if (was_embedded) { /* End of embedding protocol for previous socket */ if (parent != GDK_WINDOW_HWND (priv->socket_window)) { GtkWidget *widget = GTK_WIDGET (plug); GTK_NOTE (PLUGSOCKET, g_printerr ("was_embedded, current parent != socket_window\n")); gdk_window_set_user_data (priv->socket_window, NULL); g_object_unref (priv->socket_window); priv->socket_window = NULL; /* Emit a delete window, as if the user attempted to * close the toplevel. Only do this if we are being * reparented to the desktop window. Moving from one * embedder to another should be invisible to the app. */ if (parent == GetDesktopWindow ()) { GTK_NOTE (PLUGSOCKET, g_printerr ("current parent is root window\n")); _gtk_plug_send_delete_event (widget); return_val = GDK_FILTER_REMOVE; } } else { GTK_NOTE (PLUGSOCKET, g_printerr ("still same parent\n")); goto done; } } if (parent != GetDesktopWindow ()) { /* Start of embedding protocol */ GTK_NOTE (PLUGSOCKET, g_printerr ("start of embedding\n")); priv->socket_window = gdk_window_lookup_for_display (display, (GdkNativeWindow) parent); if (priv->socket_window) { gpointer user_data = NULL; GTK_NOTE (PLUGSOCKET, g_printerr ("already had socket_window\n")); gdk_window_get_user_data (priv->socket_window, &user_data); if (user_data) { g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process"); priv->socket_window = NULL; break; } g_object_ref (priv->socket_window); } else { priv->socket_window = gdk_window_foreign_new_for_display (display, (GdkNativeWindow) parent); if (!priv->socket_window) /* Already gone */ break; } _gtk_plug_add_all_grabbed_keys (plug); if (!was_embedded) g_signal_emit_by_name (plug, "embedded"); } done: g_object_unref (plug); } break; case WM_SIZE: if (!priv->same_app && priv->socket_window) { _gtk_win32_embed_send (priv->socket_window, GTK_WIN32_EMBED_PLUG_RESIZED, 0, 0); } break; default: if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_WINDOW_ACTIVATE)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: WINDOW_ACTIVATE received\n")); _gtk_win32_embed_push_message (msg); _gtk_window_set_is_active (GTK_WINDOW (plug), TRUE); _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } else if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_WINDOW_DEACTIVATE)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: WINDOW_DEACTIVATE received\n")); _gtk_win32_embed_push_message (msg); _gtk_window_set_is_active (GTK_WINDOW (plug), FALSE); _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } else if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_FOCUS_IN)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: FOCUS_IN received\n")); _gtk_win32_embed_push_message (msg); _gtk_window_set_has_toplevel_focus (GTK_WINDOW (plug), TRUE); switch (msg->wParam) { case GTK_WIN32_EMBED_FOCUS_CURRENT: break; case GTK_WIN32_EMBED_FOCUS_FIRST: _gtk_plug_focus_first_last (plug, GTK_DIR_TAB_FORWARD); break; case GTK_WIN32_EMBED_FOCUS_LAST: _gtk_plug_focus_first_last (plug, GTK_DIR_TAB_BACKWARD); break; } _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } else if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_FOCUS_OUT)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: FOCUS_OUT received\n")); _gtk_win32_embed_push_message (msg); _gtk_window_set_has_toplevel_focus (GTK_WINDOW (plug), FALSE); _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } else if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_MODALITY_ON)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: MODALITY_ON received\n")); _gtk_win32_embed_push_message (msg); _gtk_plug_handle_modality_on (plug); _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } else if (msg->message == _gtk_win32_embed_message_type (GTK_WIN32_EMBED_MODALITY_OFF)) { GTK_NOTE (PLUGSOCKET, g_printerr ("GtkPlug: MODALITY_OFF received\n")); _gtk_win32_embed_push_message (msg); _gtk_plug_handle_modality_off (plug); _gtk_win32_embed_pop_message (); return_val = GDK_FILTER_REMOVE; } break; } return return_val; }