gtkplug.c 39.5 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
16 17 18 19
 */

/* By Owen Taylor <otaylor@gtk.org>              98/4/4 */

20
/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * 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/. 
 */

27
#include "config.h"
28

29
#include "gtkdebug.h"
30
#include "gtkmain.h"
31
#include "gtkmarshalers.h"
32
#include "gtkplug.h"
33
#include "gtkintl.h"
34
#include "gtkprivate.h"
35
#include "gtkrender.h"
36
#include "gtksocketprivate.h"
Emmanuele Bassi's avatar
Emmanuele Bassi committed
37
#include "gtkwidgetprivate.h"
38
#include "gtkwindowgroup.h"
39
#include "gtkwindowprivate.h"
40
#include "gtkxembed.h"
41

42
#include <gdk/gdkx.h>
43

44 45 46 47
/**
 * SECTION:gtkplug
 * @Short_description: Toplevel for embedding into other processes
 * @Title: GtkPlug
48
 * @include: gtk/gtkx.h
49 50
 * @See_also: #GtkSocket
 *
51 52 53
 * Together with #GtkSocket, #GtkPlug 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
54
 * and passes the ID of that widget’s window to the other process,
55 56
 * which then creates a #GtkPlug with that window ID. Any widgets
 * contained in the #GtkPlug then will appear inside the first
57
 * application’s window.
58
 *
59
 * The communication between a #GtkSocket and a #GtkPlug follows the
60
 * [XEmbed Protocol](http://www.freedesktop.org/Standards/xembed-spec).
61 62
 * This protocol has also been implemented in other toolkits, e.g. Qt,
 * allowing the same level of integration when embedding a Qt widget
63
 * in GTK+ or vice versa.
64
 *
65 66 67
 * 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
68
 * #GtkSocket, you need to include the `gtk/gtkx.h` header.
69 70
 */

71 72 73 74 75 76 77 78 79 80 81 82
struct _GtkPlugPrivate
{
  GtkWidget      *modality_window;
  GtkWindowGroup *modality_group;

  GdkWindow      *socket_window;

  GHashTable     *grabbed_keys;

  guint  same_app : 1;
};

83 84 85 86
static void            gtk_plug_get_property          (GObject     *object,
						       guint        prop_id,
						       GValue      *value,
						       GParamSpec  *pspec);
87
static void            gtk_plug_finalize              (GObject          *object);
88 89
static void            gtk_plug_realize               (GtkWidget        *widget);
static void            gtk_plug_unrealize             (GtkWidget        *widget);
90 91 92 93
static void            gtk_plug_show                  (GtkWidget        *widget);
static void            gtk_plug_hide                  (GtkWidget        *widget);
static void            gtk_plug_map                   (GtkWidget        *widget);
static void            gtk_plug_unmap                 (GtkWidget        *widget);
94 95
static gboolean        gtk_plug_draw                  (GtkWidget        *widget,
						       cairo_t          *cr);
96 97
static gboolean        gtk_plug_key_press_event       (GtkWidget        *widget,
						       GdkEventKey      *event);
98 99
static gboolean        gtk_plug_focus_event           (GtkWidget        *widget,
						       GdkEventFocus    *event);
100 101
static void            gtk_plug_set_focus             (GtkWindow        *window,
						       GtkWidget        *focus);
102
static gboolean        gtk_plug_focus                 (GtkWidget        *widget,
103
						       GtkDirectionType  direction);
104
static void            gtk_plug_check_resize          (GtkContainer     *container);
105
static void            gtk_plug_keys_changed          (GtkWindow        *window);
106

107 108 109
static void            xembed_set_info                (GdkWindow        *window,
				                       unsigned long     flags);

110
static GtkBinClass *bin_class = NULL;
111

112 113 114 115 116 117
typedef struct
{
  guint			 accelerator_key;
  GdkModifierType	 accelerator_mods;
} GrabbedKey;

118 119 120
enum {
  PROP_0,
  PROP_EMBEDDED,
121
  PROP_SOCKET_WINDOW
122 123
};

124 125 126 127 128 129 130
enum {
  EMBEDDED,
  LAST_SIGNAL
}; 

static guint plug_signals[LAST_SIGNAL] = { 0 };

131
G_DEFINE_TYPE_WITH_PRIVATE (GtkPlug, gtk_plug, GTK_TYPE_WINDOW)
132

133 134 135 136 137 138 139
static void
gtk_plug_get_property (GObject    *object,
		       guint       prop_id,
		       GValue     *value,
		       GParamSpec *pspec)
{
  GtkPlug *plug = GTK_PLUG (object);
140
  GtkPlugPrivate *priv = plug->priv;
141 142 143 144

  switch (prop_id)
    {
    case PROP_EMBEDDED:
145
      g_value_set_boolean (value, priv->socket_window != NULL);
146
      break;
147
    case PROP_SOCKET_WINDOW:
148
      g_value_set_object (value, priv->socket_window);
149
      break;
150 151 152 153 154 155
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

156 157 158
static void
gtk_plug_class_init (GtkPlugClass *class)
{
159
  GObjectClass *gobject_class = (GObjectClass *)class;
160 161
  GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
  GtkWindowClass *window_class = (GtkWindowClass *)class;
162
  GtkContainerClass *container_class = (GtkContainerClass *)class;
163

Manish Singh's avatar
Manish Singh committed
164
  bin_class = g_type_class_peek (GTK_TYPE_BIN);
165

166
  gobject_class->get_property = gtk_plug_get_property;
167 168
  gobject_class->finalize = gtk_plug_finalize;
  
169
  widget_class->realize = gtk_plug_realize;
170
  widget_class->unrealize = gtk_plug_unrealize;
171
  widget_class->key_press_event = gtk_plug_key_press_event;
172 173
  widget_class->focus_in_event = gtk_plug_focus_event;
  widget_class->focus_out_event = gtk_plug_focus_event;
174

175 176 177 178
  widget_class->show = gtk_plug_show;
  widget_class->hide = gtk_plug_hide;
  widget_class->map = gtk_plug_map;
  widget_class->unmap = gtk_plug_unmap;
179
  widget_class->draw = gtk_plug_draw;
180 181
  widget_class->focus = gtk_plug_focus;

182 183
  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL);

184
  container_class->check_resize = gtk_plug_check_resize;
185 186

  window_class->set_focus = gtk_plug_set_focus;
187
  window_class->keys_changed = gtk_plug_keys_changed;
188

189 190 191 192 193 194 195 196 197 198 199
  /**
   * GtkPlug:embedded:
   *
   * %TRUE if the plug is embedded in a socket.
   *
   * Since: 2.12
   */
  g_object_class_install_property (gobject_class,
				   PROP_EMBEDDED,
				   g_param_spec_boolean ("embedded",
							 P_("Embedded"),
200
							 P_("Whether the plug is embedded"),
201 202 203
							 FALSE,
							 GTK_PARAM_READABLE));

204 205 206 207 208
  /**
   * GtkPlug:socket-window:
   *
   * The window of the socket the plug is embedded in.
   *
209
   * Since: 2.14
210 211 212
   */
  g_object_class_install_property (gobject_class,
				   PROP_SOCKET_WINDOW,
213
				   g_param_spec_object ("socket-window",
214 215 216 217 218
							P_("Socket Window"),
							P_("The window of the socket the plug is embedded in"),
							GDK_TYPE_WINDOW,
							GTK_PARAM_READABLE));

Matthias Clasen's avatar
Matthias Clasen committed
219 220 221 222
  /**
   * GtkPlug::embedded:
   * @plug: the object on which the signal was emitted
   *
223
   * Gets emitted when the plug becomes embedded in a socket.
Matthias Clasen's avatar
Matthias Clasen committed
224
   */ 
225
  plug_signals[EMBEDDED] =
226
    g_signal_new (I_("embedded"),
227 228 229 230
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkPlugClass, embedded),
		  NULL, NULL,
231
		  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
232
		  G_TYPE_NONE, 0);
233 234 235 236 237
}

static void
gtk_plug_init (GtkPlug *plug)
{
238
  plug->priv = gtk_plug_get_instance_private (plug);
239 240

  gtk_window_set_decorated (GTK_WINDOW (plug), FALSE);
241
}
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
/**
 * gtk_plug_handle_modality_on:
 * @plug: a #GtkPlug
 *
 * Called from the GtkPlug backend when the corresponding socket has
 * told the plug that it modality has toggled on.
 */
static void
gtk_plug_handle_modality_on (GtkPlug *plug)
{
  GtkPlugPrivate *priv = plug->priv;

  if (!priv->modality_window)
    {
      priv->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
      gtk_window_set_screen (GTK_WINDOW (priv->modality_window),
			     gtk_widget_get_screen (GTK_WIDGET (plug)));
      gtk_widget_realize (priv->modality_window);
      gtk_window_group_add_window (priv->modality_group, GTK_WINDOW (priv->modality_window));
      gtk_grab_add (priv->modality_window);
    }
}

/**
 * gtk_plug_handle_modality_off:
 * @plug: a #GtkPlug
 *
 * Called from the GtkPlug backend when the corresponding socket has
 * told the plug that it modality has toggled off.
 */
static void
gtk_plug_handle_modality_off (GtkPlug *plug)
{
  GtkPlugPrivate *priv = plug->priv;

  if (priv->modality_window)
    {
      gtk_widget_destroy (priv->modality_window);
      priv->modality_window = NULL;
    }
}

285 286 287 288
static void
gtk_plug_set_is_child (GtkPlug  *plug,
		       gboolean  is_child)
{
289
  GtkPlugPrivate *priv = plug->priv;
290 291 292 293
  GtkWidget *widget = GTK_WIDGET (plug);

  g_assert (!gtk_widget_get_parent (widget));

294 295
  if (is_child)
    {
296
      if (priv->modality_window)
297
	gtk_plug_handle_modality_off (plug);
298

299
      if (priv->modality_group)
300
	{
301 302 303
	  gtk_window_group_remove_window (priv->modality_group, GTK_WINDOW (plug));
	  g_object_unref (priv->modality_group);
	  priv->modality_group = NULL;
304 305
	}
      
306 307 308 309 310
      /* As a toplevel, the MAPPED flag doesn't correspond
       * to whether the widget->window is mapped; we unmap
       * here, but don't bother remapping -- we will get mapped
       * by gtk_widget_set_parent ().
       */
311 312 313
      if (gtk_widget_get_mapped (widget))
	gtk_widget_unmap (widget);

314
      _gtk_window_set_is_toplevel (GTK_WINDOW (plug), FALSE);
315
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
316
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT);
317
      G_GNUC_END_IGNORE_DEPRECATIONS;
318

319
      _gtk_widget_propagate_hierarchy_changed (widget, widget);
320 321 322
    }
  else
    {
323
      if (gtk_window_get_focus (GTK_WINDOW (plug)))
324
	gtk_window_set_focus (GTK_WINDOW (plug), NULL);
325
      if (gtk_window_get_default_widget (GTK_WINDOW (plug)))
326
	gtk_window_set_default (GTK_WINDOW (plug), NULL);
327 328 329 330

      priv->modality_group = gtk_window_group_new ();
      gtk_window_group_add_window (priv->modality_group, GTK_WINDOW (plug));

331
      _gtk_window_set_is_toplevel (GTK_WINDOW (plug), TRUE);
332
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
333
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_QUEUE);
334
      G_GNUC_END_IGNORE_DEPRECATIONS;
335 336

      _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), NULL);
337 338 339
    }
}

340 341 342 343 344 345 346 347
/**
 * gtk_plug_get_id:
 * @plug: a #GtkPlug.
 * 
 * Gets the window ID of a #GtkPlug widget, which can then
 * be used to embed this window inside another window, for
 * instance with gtk_socket_add_id().
 * 
348
 * Returns: the window ID for the plug
349
 **/
350
Window
351 352 353 354
gtk_plug_get_id (GtkPlug *plug)
{
  g_return_val_if_fail (GTK_IS_PLUG (plug), 0);

355
  if (!gtk_widget_get_realized (GTK_WIDGET (plug)))
356 357
    gtk_widget_realize (GTK_WIDGET (plug));

358
  return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (plug)));
359 360
}

Tim Janik's avatar
Tim Janik committed
361 362 363 364 365
/**
 * gtk_plug_get_embedded:
 * @plug: a #GtkPlug
 *
 * Determines whether the plug is embedded in a socket.
366
 *
367
 * Returns: %TRUE if the plug is embedded in a socket
368 369
 *
 * Since: 2.14
Tim Janik's avatar
Tim Janik committed
370 371 372 373
 **/
gboolean
gtk_plug_get_embedded (GtkPlug *plug)
{
374
  g_return_val_if_fail (GTK_IS_PLUG (plug), FALSE);
Tim Janik's avatar
Tim Janik committed
375

376
  return plug->priv->socket_window != NULL;
Tim Janik's avatar
Tim Janik committed
377 378
}

379 380 381 382 383 384
/**
 * gtk_plug_get_socket_window:
 * @plug: a #GtkPlug
 *
 * Retrieves the socket the plug is embedded in.
 *
385
 * Returns: (transfer none): the window of the socket, or %NULL
386 387
 *
 * Since: 2.14
388 389 390 391 392 393
 **/
GdkWindow *
gtk_plug_get_socket_window (GtkPlug *plug)
{
  g_return_val_if_fail (GTK_IS_PLUG (plug), NULL);

394
  return plug->priv->socket_window;
395 396
}

397 398 399
/**
 * _gtk_plug_add_to_socket:
 * @plug: a #GtkPlug
400
 * @socket_: a #GtkSocket
401
 * 
402
 * Adds a plug to a socket within the same application.
403
 **/
404
void
405
_gtk_plug_add_to_socket (GtkPlug   *plug,
Matthias Clasen's avatar
Matthias Clasen committed
406
			 GtkSocket *socket_)
407
{
408
  GtkPlugPrivate *priv;
409
  GtkWidget *widget;
410 411
  
  g_return_if_fail (GTK_IS_PLUG (plug));
Matthias Clasen's avatar
Matthias Clasen committed
412
  g_return_if_fail (GTK_IS_SOCKET (socket_));
413
  g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (socket_)));
414

415
  priv = plug->priv;
416 417
  widget = GTK_WIDGET (plug);

418
  gtk_plug_set_is_child (plug, TRUE);
419
  priv->same_app = TRUE;
420 421
  socket_->priv->same_app = TRUE;
  socket_->priv->plug_widget = widget;
422

423 424
  priv->socket_window = gtk_widget_get_window (GTK_WIDGET (socket_));
  g_object_ref (priv->socket_window);
425 426
  g_signal_emit (plug, plug_signals[EMBEDDED], 0);
  g_object_notify (G_OBJECT (plug), "embedded");
427

428
  if (gtk_widget_get_realized (widget))
429
    {
430 431 432
      GdkWindow *window;

      window = gtk_widget_get_window (widget);
433 434 435
      gdk_window_reparent (window, priv->socket_window,
                           -gdk_window_get_width (window),
                           -gdk_window_get_height (window));
436
    }
437

Matthias Clasen's avatar
Matthias Clasen committed
438
  gtk_widget_set_parent (widget, GTK_WIDGET (socket_));
439

440
  g_signal_emit_by_name (socket_, "plug-added");
441 442
}

443 444
/*
 * gtk_plug_send_delete_event:
Matthias Clasen's avatar
Matthias Clasen committed
445
 * @widget: a #GtkWidget
446 447 448 449 450
 *
 * Send a GDK_DELETE event to the @widget and destroy it if
 * necessary. Internal GTK function, called from this file or the
 * backend-specific GtkPlug implementation.
 */
451 452
static void
gtk_plug_send_delete_event (GtkWidget *widget)
453 454
{
  GdkEvent *event = gdk_event_new (GDK_DELETE);
455

456
  event->any.window = g_object_ref (gtk_widget_get_window (widget));
457 458
  event->any.send_event = FALSE;

459 460
  g_object_ref (widget);

461 462
  if (!gtk_widget_event (widget, event))
    gtk_widget_destroy (widget);
463 464

  g_object_unref (widget);
465 466 467 468

  gdk_event_free (event);
}

469
/**
470
 * _gtk_plug_remove_from_socket:
471
 * @plug: a #GtkPlug
Matthias Clasen's avatar
Matthias Clasen committed
472
 * @socket_: a #GtkSocket
473
 * 
474
 * Removes a plug from a socket within the same application.
475 476 477
 **/
void
_gtk_plug_remove_from_socket (GtkPlug   *plug,
Matthias Clasen's avatar
Matthias Clasen committed
478
			      GtkSocket *socket_)
479
{
480
  GtkPlugPrivate *priv;
481
  GtkWidget *widget;
482
  GdkWindow *window;
483
  GdkWindow *root_window;
484 485 486 487
  gboolean result;
  gboolean widget_was_visible;

  g_return_if_fail (GTK_IS_PLUG (plug));
Matthias Clasen's avatar
Matthias Clasen committed
488
  g_return_if_fail (GTK_IS_SOCKET (socket_));
489
  g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (plug)));
490

491
  priv = plug->priv;
492 493
  widget = GTK_WIDGET (plug);

494
  if (_gtk_widget_get_in_reparent (widget))
495 496
    return;

497
  g_object_ref (plug);
Matthias Clasen's avatar
Matthias Clasen committed
498
  g_object_ref (socket_);
499

500
  widget_was_visible = gtk_widget_get_visible (widget);
501
  window = gtk_widget_get_window (widget);
502
  root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
503 504

  gdk_window_hide (window);
505
  _gtk_widget_set_in_reparent (widget, TRUE);
506
  gdk_window_reparent (window, root_window, 0, 0);
507
  gtk_widget_unparent (GTK_WIDGET (plug));
508
  _gtk_widget_set_in_reparent (widget, FALSE);
509
  
510 511
  socket_->priv->plug_widget = NULL;
  if (socket_->priv->plug_window != NULL)
512
    {
513 514
      g_object_unref (socket_->priv->plug_window);
      socket_->priv->plug_window = NULL;
515
    }
516
  
517
  socket_->priv->same_app = FALSE;
518

519 520
  priv->same_app = FALSE;
  if (priv->socket_window != NULL)
521
    {
522 523
      g_object_unref (priv->socket_window);
      priv->socket_window = NULL;
524
    }
525
  gtk_plug_set_is_child (plug, FALSE);
526

527
  g_signal_emit_by_name (socket_, "plug-removed", &result);
528
  if (!result)
Matthias Clasen's avatar
Matthias Clasen committed
529
    gtk_widget_destroy (GTK_WIDGET (socket_));
530

531
  if (window)
532
    gtk_plug_send_delete_event (widget);
533

534 535
  g_object_unref (plug);

536
  if (widget_was_visible && gtk_widget_get_visible (GTK_WIDGET (socket_)))
Matthias Clasen's avatar
Matthias Clasen committed
537
    gtk_widget_queue_resize (GTK_WIDGET (socket_));
538

Matthias Clasen's avatar
Matthias Clasen committed
539
  g_object_unref (socket_);
540 541
}

542 543 544
/**
 * gtk_plug_construct:
 * @plug: a #GtkPlug.
545
 * @socket_id: the XID of the socket’s window.
546
 *
547
 * Finish the initialization of @plug for a given #GtkSocket identified by
548 549
 * @socket_id. This function will generally only be used by classes deriving from #GtkPlug.
 **/
550
void
551 552
gtk_plug_construct (GtkPlug *plug,
		    Window   socket_id)
553
{
Owen Taylor's avatar
Owen Taylor committed
554
  gtk_plug_construct_for_display (plug, gdk_display_get_default (), socket_id);
555 556 557 558
}

/**
 * gtk_plug_construct_for_display:
559
 * @plug: a #GtkPlug.
560
 * @display: the #GdkDisplay associated with @socket_id’s 
561
 *	     #GtkSocket.
562
 * @socket_id: the XID of the socket’s window.
563
 *
564
 * Finish the initialization of @plug for a given #GtkSocket identified by
565 566
 * @socket_id which is currently displayed on @display.
 * This function will generally only be used by classes deriving from #GtkPlug.
567 568
 *
 * Since: 2.2
569 570
 **/
void
571 572 573
gtk_plug_construct_for_display (GtkPlug    *plug,
				GdkDisplay *display,
				Window      socket_id)
574
{
575 576 577 578 579 580 581
  GtkPlugPrivate *priv;

  g_return_if_fail (GTK_IS_PLUG (plug));
  g_return_if_fail (GDK_IS_DISPLAY (display));

  priv = plug->priv;

582
  if (socket_id)
583
    {
584 585
      gpointer user_data = NULL;

586
      if (GDK_IS_X11_DISPLAY (display))
587 588 589 590
        priv->socket_window = gdk_x11_window_lookup_for_display (display, socket_id);
      else
        priv->socket_window = NULL;

591
      if (priv->socket_window)
592
	{
593
	  gdk_window_get_user_data (priv->socket_window, &user_data);
594 595

	  if (user_data)
596
	    {
597 598 599 600 601
	      if (GTK_IS_SOCKET (user_data))
		_gtk_plug_add_to_socket (plug, user_data);
	      else
		{
		  g_warning (G_STRLOC "Can't create GtkPlug as child of non-GtkSocket");
602
		  priv->socket_window = NULL;
603
		}
604
	    }
605
	  else
606
	    g_object_ref (priv->socket_window);
607
	}
608
      else if (GDK_IS_X11_DISPLAY (display))
609
        priv->socket_window = gdk_x11_window_foreign_new_for_display (display, socket_id);
610

611
      if (priv->socket_window) {
Manish Singh's avatar
Manish Singh committed
612
	g_signal_emit (plug, plug_signals[EMBEDDED], 0);
613 614 615

        g_object_notify (G_OBJECT (plug), "embedded");
      }
616 617
    }
}
618

619 620 621 622 623
/**
 * gtk_plug_new:
 * @socket_id:  the window ID of the socket, or 0.
 * 
 * Creates a new plug widget inside the #GtkSocket identified
624
 * by @socket_id. If @socket_id is 0, the plug is left “unplugged” and
625 626
 * can later be plugged into a #GtkSocket by  gtk_socket_add_id().
 * 
627
 * Returns: the new #GtkPlug widget.
628
 **/
629
GtkWidget*
630
gtk_plug_new (Window socket_id)
631
{
Owen Taylor's avatar
Owen Taylor committed
632
  return gtk_plug_new_for_display (gdk_display_get_default (), socket_id);
633 634 635 636
}

/**
 * gtk_plug_new_for_display:
637
 * @display: the #GdkDisplay on which @socket_id is displayed
638
 * @socket_id: the XID of the socket’s window.
639
 * 
640
 * Create a new plug widget inside the #GtkSocket identified by socket_id.
641
 *
642
 * Returns: the new #GtkPlug widget.
643 644
 *
 * Since: 2.2
645 646
 */
GtkWidget*
647 648
gtk_plug_new_for_display (GdkDisplay *display,
			  Window      socket_id)
649 650 651
{
  GtkPlug *plug;

652 653
  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);

Manish Singh's avatar
Manish Singh committed
654
  plug = g_object_new (GTK_TYPE_PLUG, NULL);
655
  gtk_plug_construct_for_display (plug, display, socket_id);
656 657 658
  return GTK_WIDGET (plug);
}

659 660 661 662
static void
gtk_plug_finalize (GObject *object)
{
  GtkPlug *plug = GTK_PLUG (object);
663
  GtkPlugPrivate *priv = plug->priv;
664

665
  if (priv->grabbed_keys)
666 667
    g_hash_table_destroy (priv->grabbed_keys);

Matthias Clasen's avatar
Matthias Clasen committed
668
  G_OBJECT_CLASS (gtk_plug_parent_class)->finalize (object);
669 670
}

671 672 673
static void
gtk_plug_unrealize (GtkWidget *widget)
{
674
  GtkPlug *plug = GTK_PLUG (widget);
675
  GtkPlugPrivate *priv = plug->priv;
676

677
  if (priv->socket_window != NULL)
678
    {
679 680
      g_object_unref (priv->socket_window);
      priv->socket_window = NULL;
681 682

      g_object_notify (G_OBJECT (widget), "embedded");
683 684
    }

685
  if (!priv->same_app)
686
    {
687
      if (priv->modality_window)
688
	gtk_plug_handle_modality_off (plug);
689

690 691
      gtk_window_group_remove_window (priv->modality_group, GTK_WINDOW (plug));
      g_object_unref (priv->modality_group);
692
    }
693

694
  GTK_WIDGET_CLASS (gtk_plug_parent_class)->unrealize (widget);
695 696
}

697 698 699 700 701 702 703 704 705 706 707 708
static gboolean
gtk_plug_draw (GtkWidget *widget,
	       cairo_t   *cr)
{
  gtk_render_background (gtk_widget_get_style_context (widget), cr,
                         0, 0,
                         gtk_widget_get_allocated_width (widget),
                         gtk_widget_get_allocated_height (widget));

  return GTK_WIDGET_CLASS (gtk_plug_parent_class)->draw (widget, cr);
}

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
static void
xembed_set_info (GdkWindow     *window,
		 unsigned long  flags)
{
  GdkDisplay *display = gdk_window_get_display (window);
  unsigned long buffer[2];

  Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");

  buffer[0] = GTK_XEMBED_PROTOCOL_VERSION;
  buffer[1] = flags;

  XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
		   GDK_WINDOW_XID (window),
		   xembed_info_atom, xembed_info_atom, 32,
		   PropModeReplace,
		   (unsigned char *)buffer, 2);
}

/**
 * gtk_plug_focus_first_last:
 * @plug: a #GtkPlug
 * @direction: a direction
 *
 * Called from the GtkPlug backend when the corresponding socket has
 * told the plug that it has received the focus.
 */
static void
gtk_plug_focus_first_last (GtkPlug          *plug,
			   GtkDirectionType  direction)
{
  GtkWindow *window = GTK_WINDOW (plug);
  GtkWidget *focus_widget;
  GtkWidget *parent;

  focus_widget = gtk_window_get_focus (window);
  if (focus_widget)
    {
      parent = gtk_widget_get_parent (focus_widget);
      while (parent)
	{
	  gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
	  parent = gtk_widget_get_parent (parent);
	}
      
      gtk_window_set_focus (GTK_WINDOW (plug), NULL);
    }

  gtk_widget_child_focus (GTK_WIDGET (plug), direction);
}

static void
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: %s received", _gtk_xembed_message_name (message)));
  
  switch (message)
    {
    case XEMBED_EMBEDDED_NOTIFY:
      break;
    case XEMBED_WINDOW_ACTIVATE:
      _gtk_window_set_is_active (window, TRUE);
      break;
    case XEMBED_WINDOW_DEACTIVATE:
      _gtk_window_set_is_active (window, FALSE);
      break;
      
    case XEMBED_MODALITY_ON:
      gtk_plug_handle_modality_on (plug);
      break;
    case XEMBED_MODALITY_OFF:
      gtk_plug_handle_modality_off (plug);
      break;

    case XEMBED_FOCUS_IN:
      _gtk_window_set_has_toplevel_focus (window, TRUE);
      switch (detail)
	{
	case XEMBED_FOCUS_FIRST:
	  gtk_plug_focus_first_last (plug, GTK_DIR_TAB_FORWARD);
	  break;
	case XEMBED_FOCUS_LAST:
	  gtk_plug_focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
	  break;
	case XEMBED_FOCUS_CURRENT:
	  break;
	}
      break;

    case XEMBED_FOCUS_OUT:
      _gtk_window_set_has_toplevel_focus (window, FALSE);
      break;
      
    case XEMBED_GRAB_KEY:
    case XEMBED_UNGRAB_KEY:
    case XEMBED_GTK_GRAB_KEY:
    case XEMBED_GTK_UNGRAB_KEY:
    case XEMBED_REQUEST_FOCUS:
    case XEMBED_FOCUS_NEXT:
    case XEMBED_FOCUS_PREV:
      g_warning ("GtkPlug: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
      break;
      
    default:
      GTK_NOTE(PLUGSOCKET,
	       g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message));
      break;
    }
}
static GdkFilterReturn
gtk_plug_filter_func (GdkXEvent *gdk_xevent,
		      GdkEvent  *event,
		      gpointer   data)
{
  GdkScreen *screen = gdk_window_get_screen (event->any.window);
  GdkDisplay *display = gdk_screen_get_display (screen);
  GtkPlug *plug = GTK_PLUG (data);
  GtkPlugPrivate *priv = plug->priv;
  XEvent *xevent = (XEvent *)gdk_xevent;
  GHashTableIter iter;
  gpointer key;
  GdkFilterReturn return_val;

  return_val = GDK_FILTER_CONTINUE;

  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 (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_val = GDK_FILTER_REMOVE;
	}
      else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
	{
	  /* We filter these out because we take being reparented back to the
	   * root window as the reliable end of the embedding protocol
	   */

	  return_val = GDK_FILTER_REMOVE;
	}
      break;
    case ReparentNotify:
      {
	XReparentEvent *xre = &xevent->xreparent;
        gboolean was_embedded = priv->socket_window != NULL;

	GTK_NOTE (PLUGSOCKET, g_message("GtkPlug: ReparentNotify received"));

	return_val = GDK_FILTER_REMOVE;
	
	g_object_ref (plug);
	
	if (was_embedded)
	  {
	    /* End of embedding protocol for previous socket */
	    
	    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: end of embedding"));
	    /* FIXME: race if we remove from another socket and
	     * then add to a local window before we get notification
	     * Probably need check in _gtk_plug_add_to_socket
	     */

            if (xre->parent != GDK_WINDOW_XID (priv->socket_window))
	      {
		GtkWidget *widget = GTK_WIDGET (plug);

		g_object_unref (priv->socket_window);
		priv->socket_window = NULL;

		/* Emit a delete window, as if the user attempted
		 * to close the toplevel. Simple as to how we
		 * handle WM_DELETE_WINDOW, if it isn't handled
		 * we destroy the widget. BUt only do this if
		 * we are being reparented to the root window.
		 * Moving from one embedder to another should
		 * be invisible to the app.
		 */

		if (xre->parent == GDK_WINDOW_XID (gdk_screen_get_root_window (screen)))
		  {
		    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: calling gtk_plug_send_delete_event()"));
907
		    gtk_plug_send_delete_event (widget);
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943

		    g_object_notify (G_OBJECT (plug), "embedded");
		  }
	      }
	    else
	      goto done;
	  }

	if (xre->parent != GDK_WINDOW_XID (gdk_screen_get_root_window (screen)))
	  {
	    /* Start of embedding protocol */

	    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: start of embedding"));

            priv->socket_window = gdk_x11_window_lookup_for_display (display, xre->parent);
            if (priv->socket_window)
	      {
		gpointer user_data = NULL;
		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; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */
		  }

		g_object_ref (priv->socket_window);
	      }
	    else
	      {
		priv->socket_window = gdk_x11_window_foreign_new_for_display (display, xre->parent);
		if (!priv->socket_window) /* Already gone */
		  break; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */
	      }

944
            if (priv->grabbed_keys)
945
              {
946 947 948 949 950 951 952 953 954
                g_hash_table_iter_init (&iter, priv->grabbed_keys);
                while (g_hash_table_iter_next (&iter, &key, NULL))
                  {
                    GrabbedKey *grabbed_key = key;

                    _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_GRAB_KEY, 0,
                                              grabbed_key->accelerator_key,
                                              grabbed_key->accelerator_mods);
                  }
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
              }

	    if (!was_embedded)
	      g_signal_emit_by_name (plug, "embedded");

	    g_object_notify (G_OBJECT (plug), "embedded");
	  }

      done:
	g_object_unref (plug);
	
	break;
      }
    case KeyPress:
    case KeyRelease:
      {
        GdkModifierType state, consumed;
        GdkDeviceManager *device_manager;
        GdkDevice *pointer, *keyboard;
        GdkKeymap *keymap;

        if (xevent->type == KeyPress)
          event->key.type = GDK_KEY_PRESS;
        else
          event->key.type = GDK_KEY_RELEASE;

        event->key.window = gdk_x11_window_lookup_for_display (display, xevent->xany.window);
        event->key.send_event = TRUE;
        event->key.time = xevent->xkey.time;
        event->key.state = (GdkModifierType) xevent->xkey.state;
        event->key.hardware_keycode = xevent->xkey.keycode;
        event->key.keyval = GDK_KEY_VoidSymbol;

        device_manager = gdk_display_get_device_manager (display);
        pointer = gdk_device_manager_get_client_pointer (device_manager);
        keyboard = gdk_device_get_associated_device (pointer);
        gdk_event_set_device (event, keyboard);

        keymap = gdk_keymap_get_for_display (display);
994 995 996 997

        event->key.group = gdk_x11_keymap_get_group_for_state (keymap, xevent->xkey.state);
        event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, event->key.hardware_keycode);

998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
        gdk_keymap_translate_keyboard_state (keymap,
                                             event->key.hardware_keycode,
                                             event->key.state,
                                             event->key.group,
                                             &event->key.keyval,
                                             NULL, NULL, &consumed);

        state = event->key.state & ~consumed;
        gdk_keymap_add_virtual_modifiers (keymap, &state);
        event->key.state |= state;

        event->key.length = 0;
        event->key.string = g_strdup ("");

        return_val = GDK_FILTER_TRANSLATE;
      }
    }

  return return_val;
}
1018 1019 1020
static void
gtk_plug_realize (GtkWidget *widget)
{
1021
  GtkAllocation allocation;
1022
  GtkPlug *plug = GTK_PLUG (widget);
1023
  GtkPlugPrivate *priv = plug->priv;
1024 1025
  GtkWindow *window = GTK_WINDOW (widget);
  GdkWindow *gdk_window;
1026
  GdkWindowAttr attributes;
1027 1028
  const gchar *title;
  gchar *wmclass_name, *wmclass_class;
1029
  gint attributes_mask;
1030
  GdkScreen *screen;
1031

1032
  gtk_widget_set_realized (widget, TRUE);
1033

1034 1035 1036 1037
  screen = gtk_widget_get_screen (widget);
  if (!GDK_IS_X11_SCREEN (screen))
    g_warning ("GtkPlug only works under X11");

1038 1039
  title = gtk_window_get_title (window);
  _gtk_window_get_wmclass (window, &wmclass_name, &wmclass_class);
1040 1041
  gtk_widget_get_allocation (widget, &allocation);

1042
  attributes.window_type = GDK_WINDOW_CHILD;	/* XXX GDK_WINDOW_PLUG ? */
1043 1044 1045
  attributes.title = (gchar *) title;
  attributes.wmclass_name = wmclass_name;
  attributes.wmclass_class = wmclass_class;
1046 1047
  attributes.width = allocation.width;
  attributes.height = allocation.height;
1048 1049 1050 1051 1052 1053 1054 1055
  attributes.wclass = GDK_INPUT_OUTPUT;

  /* this isn't right - we should match our parent's visual/colormap.
   * though that will require handling "foreign" colormaps */
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_KEY_PRESS_MASK |
1056
			    GDK_KEY_RELEASE_MASK |
1057 1058 1059 1060
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
			    GDK_STRUCTURE_MASK);

1061
  attributes_mask = GDK_WA_VISUAL;
1062
  attributes_mask |= (title ? GDK_WA_TITLE : 0);
1063
  attributes_mask |= (wmclass_name ? GDK_WA_WMCLASS : 0);
1064

1065
  if (gtk_widget_is_toplevel (widget))
1066
    {
1067
      GdkWindow *root_window;
1068 1069
      attributes.window_type = GDK_WINDOW_TOPLEVEL;

1070
      root_window = gdk_screen_get_root_window (screen);
1071

1072
      gdk_error_trap_push ();
1073 1074
      if (priv->socket_window)
        gdk_window = gdk_window_new (priv->socket_window,
1075
                                     &attributes, attributes_mask);
1076
      else /* If it's a passive plug, we use the root window */
1077
        gdk_window = gdk_window_new (root_window,
1078
                                     &attributes, attributes_mask);
1079 1080 1081 1082 1083 1084 1085
      /* Because the window isn't known to the window manager,
       * frame sync won't work. In theory, XEMBED could be extended
       * so that embedder did frame sync like a window manager, but
       * it's just not worth the effort considering the current
       * minimal use of XEMBED.
       */
      gdk_x11_window_set_frame_sync_enabled (gdk_window, FALSE);
1086
      gtk_widget_set_window (widget, gdk_window);
1087 1088

      gdk_display_sync (gtk_widget_get_display (widget));
1089 1090 1091
      if (gdk_error_trap_pop ()) /* Uh-oh */
	{
	  gdk_error_trap_push ();
1092
	  gdk_window_destroy (gdk_window);
1093
	  gdk_error_trap_pop_ignored ();
1094 1095
	  gdk_window = gdk_window_new (root_window,
                                       &attributes, attributes_mask);
1096
          gtk_widget_set_window (widget, gdk_window);
1097
	}
1098 1099

      gdk_window_add_filter (gdk_window,
1100
			     gtk_plug_filter_func,
1101
			     widget);
1102

1103 1104 1105
      priv->modality_group = gtk_window_group_new ();
      gtk_window_group_add_window (priv->modality_group, window);

1106
      xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0);
1107
    }
1108
  else
1109 1110 1111 1112 1113 1114
    {
      gdk_window = gdk_window_new (gtk_widget_get_parent_window (widget),
                                   &attributes, attributes_mask);
      gtk_widget_set_window (widget, gdk_window);
    }

1115
  gtk_widget_register_window (widget, gdk_window);
1116 1117 1118 1119 1120
}

static void
gtk_plug_show (GtkWidget *widget)
{
1121
  if (gtk_widget_is_toplevel (widget))
Matthias Clasen's avatar
Matthias Clasen committed
1122
    GTK_WIDGET_CLASS (gtk_plug_parent_class)->show (widget);
1123 1124 1125 1126 1127 1128 1129
  else
    GTK_WIDGET_CLASS (bin_class)->show (widget);
}

static void
gtk_plug_hide (GtkWidget *widget)
{
1130
  if (gtk_widget_is_toplevel (widget))
Matthias Clasen's avatar
Matthias Clasen committed
1131
    GTK_WIDGET_CLASS (gtk_plug_parent_class)->hide (widget);
1132 1133 1134 1135
  else
    GTK_WIDGET_CLASS (bin_class)->hide (widget);
}

1136
/* From gdkprivate.h */
1137 1138 1139 1140 1141 1142 1143
void gdk_synthesize_window_state (GdkWindow     *window,
                                  GdkWindowState unset_flags,
                                  GdkWindowState set_flags);

static void
gtk_plug_map (GtkWidget *widget)
{
1144
  if (gtk_widget_is_toplevel (widget))
1145 1146
    {
      GtkBin *bin = GTK_BIN (widget);
1147
      GtkPlug *plug = GTK_PLUG (widget);
Javier Jardón's avatar
Javier Jardón committed
1148
      GtkWidget *child;
1149

1150
      gtk_widget_set_mapped (widget, TRUE);
1151

Javier Jardón's avatar
Javier Jardón committed
1152
      child = gtk_bin_get_child (bin);
1153 1154
      if (child != NULL &&
          gtk_widget_get_visible (child) &&
1155 1156
          !gtk_widget_get_mapped (child))
        gtk_widget_map (child);
1157

1158
      xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), XEMBED_MAPPED);
1159 1160

      gdk_synthesize_window_state (gtk_widget_get_window (widget),
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
				   GDK_WINDOW_STATE_WITHDRAWN,
				   0);
    }
  else
    GTK_WIDGET_CLASS (bin_class)->map (widget);
}

static void
gtk_plug_unmap (GtkWidget *widget)
{
1171
  if (gtk_widget_is_toplevel (widget))
1172
    {
1173
      GtkPlug *plug = GTK_PLUG (widget);
1174
      GdkWindow *window;
1175
      GtkWidget *child;
1176 1177

      window = gtk_widget_get_window (widget);
1178

1179
      gtk_widget_set_mapped (widget, FALSE);
1180

1181
      gdk_window_hide (window);
1182

1183 1184 1185 1186
      child = gtk_bin_get_child (GTK_BIN (widget));
      if (child != NULL)
        gtk_widget_unmap (child);

1187
      xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0);
1188 1189

      gdk_synthesize_window_state (window,
1190 1191 1192 1193 1194 1195
				   0,
				   GDK_WINDOW_STATE_WITHDRAWN);
    }
  else
    GTK_WIDGET_CLASS (bin_class)->unmap (widget);
}
1196 1197

static gboolean
1198 1199 1200
gtk_plug_key_press_event (GtkWidget   *widget,
			  GdkEventKey *event)
{
1201
  if (gtk_widget_is_toplevel (widget))
Matthias Clasen's avatar
Matthias Clasen committed
1202
    return GTK_WIDGET_CLASS (gtk_plug_parent_class)->key_press_event (widget, event);
1203
  else
1204
    return FALSE;
1205 1206
}

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
static gboolean
gtk_plug_focus_event (GtkWidget      *widget,
		      GdkEventFocus  *event)
{
  /* We eat focus-in events and focus-out events, since they
   * can be generated by something like a keyboard grab on
   * a child of the plug.
   */
  return FALSE;
}

1218 1219 1220
static void
gtk_plug_set_focus (GtkWindow *window,
		    GtkWidget *focus)
1221
{
1222
  GtkPlug *plug = GTK_PLUG (window);
1223
  GtkPlugPrivate *priv = plug->priv;
1224

Matthias Clasen's avatar
Matthias Clasen committed
1225
  GTK_WINDOW_CLASS (gtk_plug_parent_class)->set_focus (window, focus);
1226 1227
  
  /* Ask for focus from embedder
1228
   */
1229

1230
  if (focus && !gtk_window_has_toplevel_focus (window))
1231 1232
    _gtk_xembed_send_message (priv->socket_window,
                              XEMBED_REQUEST_FOCUS, 0, 0, 0);
1233 1234
}

1235 1236 1237 1238 1239 1240 1241 1242 1243
static guint
grabbed_key_hash (gconstpointer a)
{
  const GrabbedKey *key = a;
  guint h;
  
  h = key->accelerator_key << 16;
  h ^= key->accelerator_key >> 16;
  h ^= key->accelerator_mods;
1244

1245 1246
  return h;
}
1247

1248 1249 1250 1251 1252
static gboolean
grabbed_key_equal (gconstpointer a, gconstpointer b)
{
  const GrabbedKey *keya = a;
  const GrabbedKey *keyb = b;
1253

1254 1255 1256
  return (keya->accelerator_key == keyb->accelerator_key &&
	  keya->accelerator_mods == keyb->accelerator_mods);
}
1257

1258
static void
1259
add_grabbed_key (gpointer key, gpointer val, gpointer data)
1260 1261 1262
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;
1263
  GtkPlugPrivate *priv = plug->priv;
1264

1265 1266
  if (!priv->grabbed_keys ||
      !g_hash_table_lookup (priv->grabbed_keys, grabbed_key))
1267
    {
1268 1269 1270
      _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_GRAB_KEY, 0,
                                grabbed_key->accelerator_key,
                                grabbed_key->accelerator_mods);
1271
    }
1272
}
1273

1274 1275
static void
remove_grabbed_key (gpointer key, gpointer val, gpointer data)
1276 1277 1278
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;
1279
  GtkPlugPrivate *priv = plug->priv;
1280

1281 1282
  if (!priv->grabbed_keys ||
      !g_hash_table_lookup (priv->grabbed_keys, grabbed_key))
1283
    {
1284 1285 1286
      _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_UNGRAB_KEY, 0,
                                grabbed_key->accelerator_key,
                                grabbed_key->accelerator_mods);
1287
    }
1288 1289 1290
}

static void
1291 1292 1293 1294 1295
keys_foreach (GtkWindow      *window,
	      guint           keyval,
	      GdkModifierType modifiers,
	      gboolean        is_mnemonic,
	      gpointer        data)
1296
{
1297
  GHashTable *new_grabbed_keys = data;
1298
  GrabbedKey *key = g_slice_new (GrabbedKey);
1299 1300 1301 1302 1303

  key->accelerator_key = keyval;
  key->accelerator_mods = modifiers;
  
  g_hash_table_replace (new_grabbed_keys, key, key);
1304
}
1305

1306 1307 1308 1309 1310 1311
static void
grabbed_key_free (gpointer data)
{
  g_slice_free (GrabbedKey, data);
}

1312
static void
1313
gtk_plug_keys_changed (GtkWindow *window)
1314 1315 1316
{
  GHashTable *new_grabbed_keys, *old_grabbed_keys;
  GtkPlug *plug = GTK_PLUG (window);