From eb41026147d0966a366420bb93f7e0fcefb73536 Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Mon, 25 Aug 2003 21:46:57 +0000 Subject: [PATCH] Send the focus on to the parent when there was no focus widget before and Wed Aug 20 21:26:49 2003 Owen Taylor * gtk/gtkplug.c (gtk_plug_focus): Send the focus on to the parent when there was no focus widget before and no focus widget after - that is, when there are no focusable widgets. (#108678, help tracking it down from Padraig O'Briain, Federico Mena Quintero, ) * gtk/gtkxembed.[ch]: Move various shared utilities for the XEMBED protocol eused by GtkPlug and GtkSocket here. * gtk/gtkxembed.[ch] gtkplug.c gtksocket.c: Implement a flag bit that is sent with focus mesages to indicate that the focus has wrapped around on the toplevel; use this bit to catch infinite loops when there is no focusable widget at ll in the entire toplevel. * tests/testsocket.c (child_read_watch): Remove an extraneous unref. * gtk/gtkplug.c gtk/gtksocket.c gtk/gtkxembed.h: Up XEMBED protocol version to 1, add logic for sending the right version in XEMBED_EMBEDDED_NOTIFY. * gtk/gtksocket.c (gtk_socket_add_window): Send the embedder window in the XEMBED_EMBEDDED_NOTIFY as the spec requires. --- docs/reference/gtk/Makefile.am | 1 + gtk/Makefile.am | 4 +- gtk/gtkplug.c | 140 ++++++++-------------- gtk/gtksocket.c | 182 +++++++++++++++------------- gtk/gtkxembed.c | 210 +++++++++++++++++++++++++++++++++ gtk/gtkxembed.h | 48 ++++++++ gtk/xembed.h | 39 +++--- 7 files changed, 431 insertions(+), 193 deletions(-) create mode 100644 gtk/gtkxembed.c create mode 100644 gtk/gtkxembed.h diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am index eb4431356f..52d9bceabe 100644 --- a/docs/reference/gtk/Makefile.am +++ b/docs/reference/gtk/Makefile.am @@ -46,6 +46,7 @@ IGNORE_HFILES= \ gtktexttypes.h \ gtktextutil.h \ gtktypebuiltins.h \ + gtkxembed.h \ xembed.h # CFLAGS and LDFLAGS for compiling scan program. Only needed diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 5d1c621651..9ca5c77236 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -445,7 +445,9 @@ gtk_c_sources = \ gtk_plug_c_sources = \ gtkplug.c \ - gtksocket.c + gtksocket.c \ + gtkxembed.c \ + gtkxembed.h # we use our own built_sources variable rules to avoid automake's # BUILT_SOURCES oddities diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c index 17345563ed..d0c3496a64 100644 --- a/gtk/gtkplug.c +++ b/gtk/gtkplug.c @@ -33,7 +33,7 @@ #include "gdk/gdkkeysyms.h" #include "x11/gdkx.h" -#include "xembed.h" +#include "gtkxembed.h" static void gtk_plug_class_init (GtkPlugClass *klass); static void gtk_plug_init (GtkPlug *plug); @@ -61,12 +61,6 @@ static GdkFilterReturn gtk_plug_filter_func (GdkXEvent *gdk_xev gpointer data); static void handle_modality_off (GtkPlug *plug); -static void send_xembed_message (GtkPlug *plug, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time); static void xembed_set_info (GdkWindow *window, unsigned long flags); @@ -698,8 +692,8 @@ gtk_plug_set_focus (GtkWindow *window, if (focus && !window->has_toplevel_focus) { - send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (plug->socket_window, + XEMBED_REQUEST_FOCUS, 0, 0, 0); } } @@ -741,9 +735,8 @@ add_grabbed_key (gpointer key, gpointer val, gpointer data) if (!plug->grabbed_keys || !g_hash_table_lookup (plug->grabbed_keys, grabbed_key)) { - send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, + grabbed_key->accelerator_key, grabbed_key->accelerator_mods); } } @@ -753,9 +746,8 @@ add_grabbed_key_always (gpointer key, gpointer val, gpointer data) GrabbedKey *grabbed_key = key; GtkPlug *plug = data; - send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, + grabbed_key->accelerator_key, grabbed_key->accelerator_mods); } static void @@ -767,9 +759,8 @@ remove_grabbed_key (gpointer key, gpointer val, gpointer data) if (!plug->grabbed_keys || !g_hash_table_lookup (plug->grabbed_keys, grabbed_key)) { - send_xembed_message (plug, XEMBED_GTK_UNGRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, + grabbed_key->accelerator_key, grabbed_key->accelerator_mods); } } @@ -812,6 +803,29 @@ gtk_plug_keys_changed (GtkWindow *window) } } +static void +focus_to_parent (GtkPlug *plug, + GtkDirectionType direction) +{ + XEmbedMessageType message = XEMBED_FOCUS_PREV; /* Quiet GCC */ + + switch (direction) + { + case GTK_DIR_UP: + case GTK_DIR_LEFT: + case GTK_DIR_TAB_BACKWARD: + message = XEMBED_FOCUS_PREV; + break; + case GTK_DIR_DOWN: + case GTK_DIR_RIGHT: + case GTK_DIR_TAB_FORWARD: + message = XEMBED_FOCUS_NEXT; + break; + } + + _gtk_xembed_send_focus_message (plug->socket_window, message, 0); +} + static gboolean gtk_plug_focus (GtkWidget *widget, GtkDirectionType direction) @@ -841,40 +855,18 @@ gtk_plug_focus (GtkWidget *widget, } gtk_window_set_focus (GTK_WINDOW (container), NULL); - - if (!GTK_CONTAINER (window)->focus_child) - { - gint message = -1; - - switch (direction) - { - case GTK_DIR_UP: - case GTK_DIR_LEFT: - case GTK_DIR_TAB_BACKWARD: - message = XEMBED_FOCUS_PREV; - break; - case GTK_DIR_DOWN: - case GTK_DIR_RIGHT: - case GTK_DIR_TAB_FORWARD: - message = XEMBED_FOCUS_NEXT; - break; - } - - send_xembed_message (plug, message, 0, 0, 0, - gtk_get_current_event_time ()); - } } - - return FALSE; } else { /* Try to focus the first widget in the window */ - if (bin->child && gtk_widget_child_focus (bin->child, direction)) return TRUE; } + if (!GTK_CONTAINER (window)->focus_child) + focus_to_parent (plug, direction); + return FALSE; } @@ -887,48 +879,13 @@ gtk_plug_check_resize (GtkContainer *container) GTK_CONTAINER_CLASS (bin_class)->check_resize (container); } -static void -send_xembed_message (GtkPlug *plug, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time) -{ - if (plug->socket_window) - { - GdkDisplay *display = gdk_drawable_get_display (plug->socket_window); - XEvent xevent; - - GTK_NOTE(PLUGSOCKET, - g_message ("GtkPlug: Sending XEMBED message of type %ld", message)); - - xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window); - xevent.xclient.type = ClientMessage; - xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"); - xevent.xclient.format = 32; - xevent.xclient.data.l[0] = time; - xevent.xclient.data.l[1] = message; - xevent.xclient.data.l[2] = detail; - xevent.xclient.data.l[3] = data1; - xevent.xclient.data.l[4] = data2; - - gdk_error_trap_push (); - XSendEvent (GDK_WINDOW_XDISPLAY(plug->socket_window), - GDK_WINDOW_XWINDOW (plug->socket_window), - False, NoEventMask, &xevent); - gdk_display_sync (display); - gdk_error_trap_pop (); - } -} - static void focus_first_last (GtkPlug *plug, GtkDirectionType direction) { GtkWindow *window = GTK_WINDOW (plug); GtkWidget *parent; - + if (window->focus_widget) { parent = window->focus_widget->parent; @@ -977,7 +934,7 @@ xembed_set_info (GdkWindow *window, Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"); - buffer[1] = 0; /* Protocol version */ + buffer[1] = GTK_XEMBED_PROTOCOL_VERSION; buffer[1] = flags; XChangeProperty (GDK_DISPLAY_XDISPLAY (display), @@ -988,17 +945,17 @@ xembed_set_info (GdkWindow *window, } static void -handle_xembed_message (GtkPlug *plug, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time) +handle_xembed_message (GtkPlug *plug, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2, + guint32 time) { GtkWindow *window = GTK_WINDOW (plug); - + GTK_NOTE (PLUGSOCKET, - g_message ("GtkPlug: Message of type %ld received", message)); + g_message ("GtkPlug: Message of type %d received", message)); switch (message) { @@ -1044,12 +1001,12 @@ handle_xembed_message (GtkPlug *plug, case XEMBED_REQUEST_FOCUS: case XEMBED_FOCUS_NEXT: case XEMBED_FOCUS_PREV: - g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message); + g_warning ("GtkPlug: Invalid _XEMBED message of type %d received", message); break; default: GTK_NOTE(PLUGSOCKET, - g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message)); + g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message)); break; } } @@ -1071,14 +1028,15 @@ gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) 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 (plug, 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 GDK_FILTER_REMOVE; } else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW")) diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c index 7469a4745f..feaa594f16 100644 --- a/gtk/gtksocket.c +++ b/gtk/gtksocket.c @@ -38,7 +38,7 @@ #include "x11/gdkx.h" -#include "xembed.h" +#include "gtkxembed.h" typedef struct _GtkSocketPrivate GtkSocketPrivate; @@ -86,12 +86,6 @@ static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data); -static void send_xembed_message (GtkSocket *socket, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time); static gboolean xembed_get_info (GdkWindow *gdk_window, unsigned long *version, unsigned long *flags); @@ -690,16 +684,11 @@ socket_update_focus_in (GtkSocket *socket) socket->focus_in = focus_in; if (focus_in) - { - send_xembed_message (socket, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0, - gtk_get_current_event_time ()); - } + _gtk_xembed_send_focus_message (socket->plug_window, + XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); else - { - send_xembed_message (socket, XEMBED_FOCUS_OUT, 0, 0, 0, - gtk_get_current_event_time ()); - - } + _gtk_xembed_send_message (socket->plug_window, + XEMBED_FOCUS_OUT, 0, 0, 0); } } @@ -720,10 +709,9 @@ socket_update_active (GtkSocket *socket) { socket->active = active; - send_xembed_message (socket, - active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE, - 0, 0, 0, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (socket->plug_window, + active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE, + 0, 0, 0); } } @@ -773,9 +761,9 @@ gtk_socket_grab_notify (GtkWidget *widget, GtkSocket *socket = GTK_SOCKET (widget); if (!socket->same_app) - send_xembed_message (GTK_SOCKET (widget), - was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON, - 0, 0, 0, gtk_get_current_event_time ()); + _gtk_xembed_send_message (GTK_SOCKET (widget)->plug_window, + was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON, + 0, 0, 0); } static gboolean @@ -868,8 +856,8 @@ gtk_socket_focus (GtkWidget *widget, GtkDirectionType direction) break; } - send_xembed_message (socket, XEMBED_FOCUS_IN, detail, 0, 0, - gtk_get_current_event_time ()); + _gtk_xembed_send_focus_message (socket->plug_window, + XEMBED_FOCUS_IN, detail); gtk_socket_claim_focus (socket, FALSE); @@ -1017,7 +1005,7 @@ gtk_socket_add_window (GtkSocket *socket, socket->xembed_version = -1; if (xembed_get_info (socket->plug_window, &version, &flags)) { - socket->xembed_version = version; + socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version); socket->is_mapped = (flags & XEMBED_MAPPED) != 0; } else @@ -1045,8 +1033,10 @@ gtk_socket_add_window (GtkSocket *socket, if (toplevel && GTK_IS_WINDOW (toplevel)) gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid); - send_xembed_message (socket, XEMBED_EMBEDDED_NOTIFY, 0, 0, 0, - gtk_get_current_event_time ()); + _gtk_xembed_send_message (socket->plug_window, + XEMBED_EMBEDDED_NOTIFY, 0, + GDK_WINDOW_XWINDOW (widget->window), + socket->xembed_version); socket_update_active (socket); socket_update_focus_in (socket); @@ -1057,42 +1047,6 @@ gtk_socket_add_window (GtkSocket *socket, g_signal_emit (socket, socket_signals[PLUG_ADDED], 0); } - -static void -send_xembed_message (GtkSocket *socket, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time) -{ - GTK_NOTE(PLUGSOCKET, - g_message ("GtkSocket: Sending XEMBED message of type %ld", message)); - - if (socket->plug_window) - { - GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket)); - XEvent xevent; - - xevent.xclient.window = GDK_WINDOW_XWINDOW (socket->plug_window); - xevent.xclient.type = ClientMessage; - xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"); - xevent.xclient.format = 32; - xevent.xclient.data.l[0] = time; - xevent.xclient.data.l[1] = message; - xevent.xclient.data.l[2] = detail; - xevent.xclient.data.l[3] = data1; - xevent.xclient.data.l[4] = data2; - - gdk_error_trap_push (); - XSendEvent (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XWINDOW (socket->plug_window), - False, NoEventMask, &xevent); - gdk_display_sync (display); - gdk_error_trap_pop (); - } -} - static gboolean xembed_get_info (GdkWindow *window, unsigned long *version, @@ -1146,15 +1100,80 @@ xembed_get_info (GdkWindow *window, } static void -handle_xembed_message (GtkSocket *socket, - glong message, - glong detail, - glong data1, - glong data2, - guint32 time) +advance_toplevel_focus (GtkSocket *socket, + GtkDirectionType direction) +{ + GtkBin *bin; + GtkWindow *window; + GtkContainer *container; + GtkWidget *toplevel; + GtkWidget *old_focus_child; + GtkWidget *parent; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); + if (!toplevel) + return; + + if (!GTK_WIDGET_TOPLEVEL (toplevel) || GTK_IS_PLUG (toplevel)) + { + gtk_widget_child_focus (toplevel,direction); + return; + } + + container = GTK_CONTAINER (toplevel); + window = GTK_WINDOW (toplevel); + bin = GTK_BIN (toplevel); + + /* This is a copy of gtk_window_focus(), modified so that we + * can detect wrap-around. + */ + old_focus_child = container->focus_child; + + if (old_focus_child) + { + if (gtk_widget_child_focus (old_focus_child, direction)) + return; + + /* We are allowed exactly one wrap-around per sequence of focus + * events + */ + if (_gtk_xembed_get_focus_wrapped ()) + return; + else + _gtk_xembed_set_focus_wrapped (); + } + + if (window->focus_widget) + { + /* Wrapped off the end, clear the focus setting for the toplevel */ + parent = window->focus_widget->parent; + while (parent) + { + gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); + parent = GTK_WIDGET (parent)->parent; + } + + gtk_window_set_focus (GTK_WINDOW (container), NULL); + } + + /* Now try to focus the first widget in the window */ + if (bin->child) + { + if (gtk_widget_child_focus (bin->child, direction)) + return; + } +} + +static void +handle_xembed_message (GtkSocket *socket, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2, + guint32 time) { GTK_NOTE (PLUGSOCKET, - g_message ("GtkSocket: Message of type %ld received", message)); + g_message ("GtkSocket: Message of type %d received", message)); switch (message) { @@ -1165,7 +1184,7 @@ handle_xembed_message (GtkSocket *socket, case XEMBED_MODALITY_OFF: case XEMBED_FOCUS_IN: case XEMBED_FOCUS_OUT: - g_warning ("GtkSocket: Invalid _XEMBED message of type %ld received", message); + g_warning ("GtkSocket: Invalid _XEMBED message of type %d received", message); break; case XEMBED_REQUEST_FOCUS: @@ -1174,16 +1193,10 @@ handle_xembed_message (GtkSocket *socket, case XEMBED_FOCUS_NEXT: case XEMBED_FOCUS_PREV: - { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); - if (toplevel) - { - gtk_widget_child_focus (toplevel, - (message == XEMBED_FOCUS_NEXT ? - GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD)); - } - break; - } + advance_toplevel_focus (socket, + (message == XEMBED_FOCUS_NEXT ? + GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD)); + break; case XEMBED_GTK_GRAB_KEY: add_grabbed_key (socket, data1, data2); @@ -1198,7 +1211,7 @@ handle_xembed_message (GtkSocket *socket, default: GTK_NOTE (PLUGSOCKET, - g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %ld", message)); + g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message)); break; } } @@ -1250,13 +1263,14 @@ gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) 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; } diff --git a/gtk/gtkxembed.c b/gtk/gtkxembed.c new file mode 100644 index 0000000000..bc1f5dfa2b --- /dev/null +++ b/gtk/gtkxembed.c @@ -0,0 +1,210 @@ +/* GTK - The GIMP Toolkit + * gtkxembed.c: Utilities for the XEMBED protocol + * Copyright (C) 2001, 2003, Red Hat, 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gtkmain.h" +#include "gtkprivate.h" +#include "gtkxembed.h" + +typedef struct _GtkXEmbedMessage GtkXEmbedMessage; + +struct _GtkXEmbedMessage +{ + glong message; + glong detail; + glong data1; + glong data2; + guint32 time; +}; + +static GSList *current_messages; + + +/** + * _gtk_xembed_push_message: + * @xevent: a XEvent + * + * Adds a client message to the stack of current XEMBED events. + **/ +void +_gtk_xembed_push_message (XEvent *xevent) +{ + GtkXEmbedMessage *message = g_new (GtkXEmbedMessage, 1); + + message->time = xevent->xclient.data.l[0]; + message->message = xevent->xclient.data.l[1]; + message->detail = xevent->xclient.data.l[2]; + message->data1 = xevent->xclient.data.l[3]; + message->data2 = xevent->xclient.data.l[4]; + + current_messages = g_slist_prepend (current_messages, message); +} + +/** + * _gtk_xembed_pop_message: + * + * Removes an event added with _gtk_xembed_push_message() + **/ +void +_gtk_xembed_pop_message (void) +{ + GtkXEmbedMessage *message = current_messages->data; + current_messages = g_slist_delete_link (current_messages, current_messages); + + g_free (message); +} + +/** + * _gtk_xembed_set_focus_wrapped: + * + * Sets a flag indicating that the current focus sequence wrapped + * around to the beginning of the ultimate toplevel. + **/ +void +_gtk_xembed_set_focus_wrapped (void) +{ + GtkXEmbedMessage *message; + + g_return_if_fail (current_messages != NULL); + message = current_messages->data; + g_return_if_fail (message->message == XEMBED_FOCUS_PREV || message->message == XEMBED_FOCUS_NEXT); + + message->data1 |= XEMBED_FOCUS_WRAPAROUND; +} + +/** + * _gtk_xembed_get_focus_wrapped: + * + * Gets whether the current focus sequence has wrapped around + * to the beginning of the ultimate toplevel. + * + * Return value: %TRUE if the focus sequence has wrapped around. + **/ +gboolean +_gtk_xembed_get_focus_wrapped (void) +{ + GtkXEmbedMessage *message; + + g_return_val_if_fail (current_messages != NULL, FALSE); + message = current_messages->data; + + return (message->data1 & XEMBED_FOCUS_WRAPAROUND) != 0; +} + +static guint32 +gtk_xembed_get_time (void) +{ + if (current_messages) + { + GtkXEmbedMessage *message = current_messages->data; + return message->time; + } + else + return gtk_get_current_event_time (); +} + +/** + * _gtk_xembed_send_message: + * @recipient: window to which to send the window, or %NULL + * in which case nothing wil be sent + * @message: type of message + * @detail: detail field of message + * @data1: data1 field of message + * @data2: data2 field of message + * + * Sends a generic XEMBED message to a particular window. + **/ +void +_gtk_xembed_send_message (GdkWindow *recipient, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2) +{ + GdkDisplay *display; + XEvent xevent; + + if (!recipient) + return; + + g_return_if_fail (GDK_IS_WINDOW (recipient)); + + display = gdk_drawable_get_display (recipient); + GTK_NOTE (PLUGSOCKET, + g_message ("Sending XEMBED message of type %d", message)); + + xevent.xclient.window = GDK_WINDOW_XWINDOW (recipient); + xevent.xclient.type = ClientMessage; + xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"); + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = gtk_xembed_get_time (); + xevent.xclient.data.l[1] = message; + xevent.xclient.data.l[2] = detail; + xevent.xclient.data.l[3] = data1; + xevent.xclient.data.l[4] = data2; + + gdk_error_trap_push (); + XSendEvent (GDK_WINDOW_XDISPLAY(recipient), + GDK_WINDOW_XWINDOW (recipient), + False, NoEventMask, &xevent); + gdk_display_sync (display); + gdk_error_trap_pop (); +} + +/** + * _gtk_xembed_send_focus_message: + * @recipient: window to which to send the window, or %NULL + * in which case nothing wil be sent + * @message: type of message + * @detail: detail field of message + * + * Sends a XEMBED message for moving the focus along the focus + * chain to a window. The flags field that these messages share + * will be correctly filled in. + **/ +void +_gtk_xembed_send_focus_message (GdkWindow *recipient, + XEmbedMessageType message, + glong detail) +{ + gulong flags = 0; + + g_return_if_fail (GDK_IS_WINDOW (recipient)); + g_return_if_fail (message == XEMBED_FOCUS_IN || + message == XEMBED_FOCUS_NEXT || + message == XEMBED_FOCUS_PREV); + + if (current_messages) + { + GtkXEmbedMessage *message = current_messages->data; + switch (message->message) + { + case XEMBED_FOCUS_IN: + case XEMBED_FOCUS_NEXT: + case XEMBED_FOCUS_PREV: + flags = message->data1 & XEMBED_FOCUS_WRAPAROUND; + break; + default: + break; + } + } + + _gtk_xembed_send_message (recipient, message, detail, flags, 0); +} + diff --git a/gtk/gtkxembed.h b/gtk/gtkxembed.h new file mode 100644 index 0000000000..d18e81a121 --- /dev/null +++ b/gtk/gtkxembed.h @@ -0,0 +1,48 @@ +/* GTK - The GIMP Toolkit + * gtkxembed.c: Utilities for the XEMBED protocol + * Copyright (C) 2003, Red Hat, 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_XEMBED_H__ +#define __GTK_XEMBED_H__ + +#include "xembed.h" +#include "x11/gdkx.h" + +G_BEGIN_DECLS + +/* Latest version we implement */ +#define GTK_XEMBED_PROTOCOL_VERSION 1 + +void _gtk_xembed_send_message (GdkWindow *recipient, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2); +void _gtk_xembed_send_focus_message (GdkWindow *recipient, + XEmbedMessageType message, + glong detail); + +void _gtk_xembed_push_message (XEvent *xevent); +void _gtk_xembed_pop_message (void); +void _gtk_xembed_set_focus_wrapped (void); +gboolean _gtk_xembed_get_focus_wrapped (void); + +G_END_DECLS + +#endif /* __GTK_XEMBED_H__ */ diff --git a/gtk/xembed.h b/gtk/xembed.h index 14d55e604d..106392951e 100644 --- a/gtk/xembed.h +++ b/gtk/xembed.h @@ -1,26 +1,31 @@ /* XEMBED messages */ -#define XEMBED_EMBEDDED_NOTIFY 0 -#define XEMBED_WINDOW_ACTIVATE 1 -#define XEMBED_WINDOW_DEACTIVATE 2 -#define XEMBED_REQUEST_FOCUS 3 -#define XEMBED_FOCUS_IN 4 -#define XEMBED_FOCUS_OUT 5 -#define XEMBED_FOCUS_NEXT 6 -#define XEMBED_FOCUS_PREV 7 -#define XEMBED_GRAB_KEY 8 -#define XEMBED_UNGRAB_KEY 9 -#define XEMBED_MODALITY_ON 10 -#define XEMBED_MODALITY_OFF 11 +typedef enum { + XEMBED_EMBEDDED_NOTIFY = 0, + XEMBED_WINDOW_ACTIVATE = 1, + XEMBED_WINDOW_DEACTIVATE = 2, + XEMBED_REQUEST_FOCUS = 3, + XEMBED_FOCUS_IN = 4, + XEMBED_FOCUS_OUT = 5, + XEMBED_FOCUS_NEXT = 6, + XEMBED_FOCUS_PREV = 7, + XEMBED_GRAB_KEY = 8, + XEMBED_UNGRAB_KEY = 9, + XEMBED_MODALITY_ON = 10, + XEMBED_MODALITY_OFF = 11, /* Non standard messages*/ -#define XEMBED_GTK_GRAB_KEY 108 -#define XEMBED_GTK_UNGRAB_KEY 109 + XEMBED_GTK_GRAB_KEY = 108, + XEMBED_GTK_UNGRAB_KEY = 109 +} XEmbedMessageType; /* Details for XEMBED_FOCUS_IN: */ -#define XEMBED_FOCUS_CURRENT 0 -#define XEMBED_FOCUS_FIRST 1 -#define XEMBED_FOCUS_LAST 2 +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 +/* Flags for XEMBED_FOCUS_IN, XEMBED_FOCUS_NEXT, XEMBED_FOCUS_PREV */ +#define XEMBED_FOCUS_WRAPAROUND (1 << 0) /* Flags for _XEMBED_INFO */ #define XEMBED_MAPPED (1 << 0) +