gtksocket.c 45.2 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
15 16 17 18 19 20
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

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

28
#include "config.h"
29

30
#include "gtksocketprivate.h"
31

32 33
#include <string.h>

34
#include "gtkmainprivate.h"
35
#include "gtkmarshalers.h"
36
#include "gtksizerequest.h"
37
#include "gtkplug.h"
38
#include "gtkprivate.h"
39
#include "gtkdnd.h"
40
#include "gtkdebug.h"
Matthias Clasen's avatar
Matthias Clasen committed
41
#include "gtkintl.h"
Benjamin Otte's avatar
Benjamin Otte committed
42
#include "gtkwidgetprivate.h"
43

44
#include <gdk/gdkx.h>
45 46 47 48 49 50 51
#include <gdk/gdkprivate.h>

#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif

#include "gtkxembed.h"
52

53

54 55 56 57
/**
 * SECTION:gtksocket
 * @Short_description: Container for widgets from other processes
 * @Title: GtkSocket
58
 * @include: gtk/gtkx.h
59 60
 * @See_also: #GtkPlug, <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
 *
61 62 63 64 65 66
 * Together with #GtkPlug, #GtkSocket provides the ability to embed
 * widgets from one process into another process in a fashion that
 * is transparent to the user. One process creates a #GtkSocket widget
 * and passes that widget's window ID to the other process, which then
 * creates a #GtkPlug with that window ID. Any widgets contained in the
 * #GtkPlug then will appear inside the first application's window.
67
 *
68 69 70
 * The socket's window ID is obtained by using gtk_socket_get_id().
 * Before using this function, the socket must have been realized,
 * and for hence, have been added to its parent.
71 72 73 74 75 76 77 78
 *
 * <example>
 * <title>Obtaining the window ID of a socket.</title>
 * <programlisting>
 * GtkWidget *socket = gtk_socket_new (<!-- -->);
 * gtk_widget_show (socket);
 * gtk_container_add (GTK_CONTAINER (parent), socket);
 *
79
 * /&ast; The following call is only necessary if one of
80
 *  * the ancestors of the socket is not yet visible.
81
 *  &ast;/
82 83 84 85 86 87 88
 * gtk_widget_realize (socket);
 * g_print ("The ID of the sockets window is %#x\n",
 *          gtk_socket_get_id (socket));
 * </programlisting>
 * </example>
 *
 * Note that if you pass the window ID of the socket to another
89 90 91 92 93 94 95
 * process that will create a plug in the socket, you must make
 * sure that the socket widget is not destroyed until that plug
 * is created. Violating this rule will cause unpredictable
 * consequences, the most likely consequence being that the plug
 * will appear as a separate toplevel window. You can check if
 * the plug has been created by using gtk_socket_get_plug_window().
 * If it returns a non-%NULL value, then the plug has been
96 97
 * successfully created inside of the socket.
 *
98 99 100 101 102
 * When GTK+ is notified that the embedded window has been destroyed,
 * then it will destroy the socket as well. You should always,
 * therefore, be prepared for your sockets to be destroyed at any
 * time when the main event loop is running. To prevent this from
 * happening, you can connect to the #GtkSocket::plug-removed signal.
103 104 105
 *
 * The communication between a #GtkSocket and a #GtkPlug follows the
 * <ulink url="http://www.freedesktop.org/Standards/xembed-spec">XEmbed</ulink>
106 107 108 109
 * protocol. This protocol has also been implemented in other toolkits,
 * e.g. <application>Qt</application>, allowing the same level of
 * integration when embedding a <application>Qt</application> widget
 * in GTK or vice versa.
110 111
 *
 * <note>
112 113 114 115 116
 * The #GtkPlug and #GtkSocket widgets are only available when GTK+
 * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined.
 * They can only be used on a #GdkX11Display. To use #GtkPlug and
 * #GtkSocket, you need to include the <filename>gtk/gtkx.h</filename>
 * header.
117 118 119
 * </note>
 */

120 121
/* Forward declararations */

122
static void     gtk_socket_finalize             (GObject          *object);
123 124
static void     gtk_socket_notify               (GObject          *object,
						 GParamSpec       *pspec);
125 126
static void     gtk_socket_realize              (GtkWidget        *widget);
static void     gtk_socket_unrealize            (GtkWidget        *widget);
127 128 129 130 131 132
static void     gtk_socket_get_preferred_width  (GtkWidget        *widget,
                                                 gint             *minimum,
                                                 gint             *natural);
static void     gtk_socket_get_preferred_height (GtkWidget        *widget,
                                                 gint             *minimum,
                                                 gint             *natural);
133 134 135 136 137 138
static void     gtk_socket_size_allocate        (GtkWidget        *widget,
						 GtkAllocation    *allocation);
static void     gtk_socket_hierarchy_changed    (GtkWidget        *widget,
						 GtkWidget        *old_toplevel);
static void     gtk_socket_grab_notify          (GtkWidget        *widget,
						 gboolean          was_grabbed);
139
static gboolean gtk_socket_key_event            (GtkWidget        *widget,
140 141 142 143 144 145 146 147 148
						 GdkEventKey      *event);
static gboolean gtk_socket_focus                (GtkWidget        *widget,
						 GtkDirectionType  direction);
static void     gtk_socket_remove               (GtkContainer     *container,
						 GtkWidget        *widget);
static void     gtk_socket_forall               (GtkContainer     *container,
						 gboolean          include_internals,
						 GtkCallback       callback,
						 gpointer          callback_data);
149
static void     gtk_socket_add_window           (GtkSocket        *socket,
150
                                                 Window            xid,
151 152 153 154 155 156 157 158 159 160 161
                                                 gboolean          need_reparent);
static GdkFilterReturn gtk_socket_filter_func   (GdkXEvent        *gdk_xevent,
                                                 GdkEvent         *event,
                                                 gpointer          data);

static gboolean xembed_get_info                 (GdkWindow        *gdk_window,
                                                 unsigned long    *version,
                                                 unsigned long    *flags);

/* From Tk */
#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
162 163


164 165
/* Local data */

166 167 168 169 170 171
typedef struct
{
  guint			 accel_key;
  GdkModifierType	 accel_mods;
} GrabbedKey;

172 173 174 175 176 177 178 179
enum {
  PLUG_ADDED,
  PLUG_REMOVED,
  LAST_SIGNAL
}; 

static guint socket_signals[LAST_SIGNAL] = { 0 };

Matthias Clasen's avatar
Matthias Clasen committed
180
G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
181

182 183 184 185
static void
gtk_socket_finalize (GObject *object)
{
  GtkSocket *socket = GTK_SOCKET (object);
186 187 188
  GtkSocketPrivate *priv = socket->priv;

  g_object_unref (priv->accel_group);
189

Matthias Clasen's avatar
Matthias Clasen committed
190
  G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object);
191 192
}

193 194 195 196 197
static void
gtk_socket_class_init (GtkSocketClass *class)
{
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
198
  GObjectClass *gobject_class;
199

200
  gobject_class = (GObjectClass *) class;
201 202 203
  widget_class = (GtkWidgetClass*) class;
  container_class = (GtkContainerClass*) class;

204
  gobject_class->finalize = gtk_socket_finalize;
205
  gobject_class->notify = gtk_socket_notify;
206

207 208
  widget_class->realize = gtk_socket_realize;
  widget_class->unrealize = gtk_socket_unrealize;
209 210
  widget_class->get_preferred_width = gtk_socket_get_preferred_width;
  widget_class->get_preferred_height = gtk_socket_get_preferred_height;
211
  widget_class->size_allocate = gtk_socket_size_allocate;
212 213
  widget_class->hierarchy_changed = gtk_socket_hierarchy_changed;
  widget_class->grab_notify = gtk_socket_grab_notify;
214 215
  widget_class->key_press_event = gtk_socket_key_event;
  widget_class->key_release_event = gtk_socket_key_event;
216
  widget_class->focus = gtk_socket_focus;
217

218
  /* We don't want to show_all the in-process plug, if any.
219 220
   */
  widget_class->show_all = gtk_widget_show;
221

222 223
  container_class->remove = gtk_socket_remove;
  container_class->forall = gtk_socket_forall;
224

Matthias Clasen's avatar
Matthias Clasen committed
225 226 227 228 229 230 231
  /**
   * GtkSocket::plug-added:
   * @socket_: the object which received the signal
   *
   * This signal is emitted when a client is successfully
   * added to the socket. 
   */
232
  socket_signals[PLUG_ADDED] =
233
    g_signal_new (I_("plug-added"),
234 235 236 237
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkSocketClass, plug_added),
		  NULL, NULL,
238
		  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
239
		  G_TYPE_NONE, 0);
Matthias Clasen's avatar
Matthias Clasen committed
240 241 242 243 244 245 246 247 248 249 250

  /**
   * GtkSocket::plug-removed:
   * @socket_: the object which received the signal
   *
   * This signal is emitted when a client is removed from the socket. 
   * The default action is to destroy the #GtkSocket widget, so if you 
   * want to reuse it you must add a signal handler that returns %TRUE. 
   *
   * Return value: %TRUE to stop other handlers from being invoked.
   */
251
  socket_signals[PLUG_REMOVED] =
252
    g_signal_new (I_("plug-removed"),
253 254 255 256
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkSocketClass, plug_removed),
                  _gtk_boolean_handled_accumulator, NULL,
257
		  _gtk_marshal_BOOLEAN__VOID,
258
		  G_TYPE_BOOLEAN, 0);
259 260

  g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate));
261 262 263 264 265
}

static void
gtk_socket_init (GtkSocket *socket)
{
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
  GtkSocketPrivate *priv;

  priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
                                      GTK_TYPE_SOCKET,
                                      GtkSocketPrivate);
  socket->priv = priv;
  priv->request_width = 0;
  priv->request_height = 0;
  priv->current_width = 0;
  priv->current_height = 0;

  priv->plug_window = NULL;
  priv->plug_widget = NULL;
  priv->focus_in = FALSE;
  priv->have_size = FALSE;
  priv->need_map = FALSE;
  priv->active = FALSE;

  priv->accel_group = gtk_accel_group_new ();
  g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket);
286 287
}

288 289 290 291 292 293 294
/**
 * gtk_socket_new:
 * 
 * Create a new empty #GtkSocket.
 * 
 * Return value:  the new #GtkSocket.
 **/
295
GtkWidget*
Tim Janik's avatar
Tim Janik committed
296
gtk_socket_new (void)
297 298 299
{
  GtkSocket *socket;

300
  socket = g_object_new (GTK_TYPE_SOCKET, NULL);
301 302 303 304

  return GTK_WIDGET (socket);
}

305 306
/**
 * gtk_socket_add_id:
307
 * @socket_: a #GtkSocket
308
 * @window: the Window of a client participating in the XEMBED protocol.
309 310 311 312 313
 *
 * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket.  The
 * client may be in the same process or in a different process. 
 * 
 * To embed a #GtkPlug in a #GtkSocket, you can either create the
314 315
 * #GtkPlug with <literal>gtk_plug_new (0)</literal>, call 
 * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the
316 317 318 319 320 321 322 323
 * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the
 * window ID for the socket, and call gtk_plug_new() passing in that
 * ID.
 *
 * The #GtkSocket must have already be added into a toplevel window
 *  before you can make this call.
 **/
void           
324
gtk_socket_add_id (GtkSocket      *socket,
325
		   Window          window)
326 327
{
  g_return_if_fail (GTK_IS_SOCKET (socket));
328
  g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)));
329

330
  if (!gtk_widget_get_realized (GTK_WIDGET (socket)))
331 332
    gtk_widget_realize (GTK_WIDGET (socket));

333
  gtk_socket_add_window (socket, window, TRUE);
334 335 336 337
}

/**
 * gtk_socket_get_id:
338
 * @socket_: a #GtkSocket.
339 340 341
 * 
 * Gets the window ID of a #GtkSocket widget, which can then
 * be used to create a client embedded inside the socket, for
342
 * instance with gtk_plug_new(). 
Matthias Clasen's avatar
Matthias Clasen committed
343 344 345
 *
 * The #GtkSocket must have already be added into a toplevel window 
 * before you can make this call.
346 347 348
 * 
 * Return value: the window ID for the socket
 **/
349
Window
350 351 352
gtk_socket_get_id (GtkSocket *socket)
{
  g_return_val_if_fail (GTK_IS_SOCKET (socket), 0);
353
  g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0);
354

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

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

Tim Janik's avatar
Tim Janik committed
361 362 363 364 365 366 367
/**
 * gtk_socket_get_plug_window:
 * @socket_: a #GtkSocket.
 *
 * Retrieves the window of the plug. Use this to check if the plug has
 * been created inside of the socket.
 *
368
 * Return value: (transfer none): the window of the plug if available, or %NULL
Tim Janik's avatar
Tim Janik committed
369
 *
370
 * Since:  2.14
Tim Janik's avatar
Tim Janik committed
371 372 373 374 375 376
 **/
GdkWindow*
gtk_socket_get_plug_window (GtkSocket *socket)
{
  g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL);

377
  return socket->priv->plug_window;
Tim Janik's avatar
Tim Janik committed
378 379
}

380 381 382
static void
gtk_socket_realize (GtkWidget *widget)
{
383
  GtkAllocation allocation;
384
  GtkSocket *socket = GTK_SOCKET (widget);
385
  GdkWindow *window;
386
  GdkWindowAttr attributes;
387
  XWindowAttributes xattrs;
388 389
  gint attributes_mask;

390
  gtk_widget_set_realized (widget, TRUE);
391

392 393
  gtk_widget_get_allocation (widget, &allocation);

394
  attributes.window_type = GDK_WINDOW_CHILD;
395 396 397 398
  attributes.x = allocation.x;
  attributes.y = allocation.y;
  attributes.width = allocation.width;
  attributes.height = allocation.height;
399 400 401 402
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.event_mask = GDK_FOCUS_CHANGE_MASK;

403
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
404

405 406 407 408
  window = gdk_window_new (gtk_widget_get_parent_window (widget),
                           &attributes, attributes_mask);
  gtk_widget_set_window (widget, window);
  gdk_window_set_user_data (window, socket);
409

410 411
  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
                                    window);
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
  XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
			GDK_WINDOW_XID (window),
			&xattrs);

  /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
     for input on the socket with a mask of 0x0fffff (for god knows why)
     which includes ButtonPressMask causing a BadAccess if someone else
     also selects for this. As per the client-side windows merge we always
     normally selects for button press so we can emulate it on client
     side children that selects for button press. However, we don't need
     this for GtkSocket, so we unselect it here, fixing the crashes in
     firefox. */
  XSelectInput (GDK_WINDOW_XDISPLAY (window),
		GDK_WINDOW_XID (window), 
		(xattrs.your_event_mask & ~ButtonPressMask) |
		SubstructureNotifyMask | SubstructureRedirectMask);
429

430
  gdk_window_add_filter (window,
431
			 gtk_socket_filter_func,
432
			 widget);
433 434 435 436 437

  /* We sync here so that we make sure that if the XID for
   * our window is passed to another application, SubstructureRedirectMask
   * will be set by the time the other app creates its window.
   */
438
  gdk_display_sync (gtk_widget_get_display (widget));
439 440
}

441
/**
442
 * gtk_socket_end_embedding:
443 444 445 446 447
 *
 * @socket: a #GtkSocket
 *
 * Called to end the embedding of a plug in the socket.
 */
448 449
static void
gtk_socket_end_embedding (GtkSocket *socket)
450
{
451
  GtkSocketPrivate *private = socket->priv;
452

453 454 455 456
  g_object_unref (private->plug_window);
  private->plug_window = NULL;
  private->current_width = 0;
  private->current_height = 0;
457
  private->resize_count = 0;
458

459
  gtk_accel_group_disconnect (private->accel_group, NULL);
460 461
}

462 463 464
static void
gtk_socket_unrealize (GtkWidget *widget)
{
465
  GtkSocket *socket = GTK_SOCKET (widget);
466
  GtkSocketPrivate *private = socket->priv;
467

468
  gtk_widget_set_realized (widget, FALSE);
469

470
  if (private->plug_widget)
471
    {
472
      _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
473
    }
474
  else if (private->plug_window)
475
    {
476
      gtk_socket_end_embedding (socket);
477 478
    }

479
  GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget);
480
}
481

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
static void
gtk_socket_size_request (GtkSocket *socket)
{
  GtkSocketPrivate *private = socket->priv;
  XSizeHints hints;
  long supplied;
	  
  gdk_error_trap_push ();

  private->request_width = 1;
  private->request_height = 1;
	  
  if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (private->plug_window),
			 GDK_WINDOW_XID (private->plug_window),
			 &hints, &supplied))
    {
      if (hints.flags & PMinSize)
	{
	  private->request_width = MAX (hints.min_width, 1);
	  private->request_height = MAX (hints.min_height, 1);
	}
      else if (hints.flags & PBaseSize)
	{
	  private->request_width = MAX (hints.base_width, 1);
	  private->request_height = MAX (hints.base_height, 1);
	}
    }
  private->have_size = TRUE;
  
  gdk_error_trap_pop_ignored ();
}

514
static void
515 516 517
gtk_socket_get_preferred_width (GtkWidget *widget,
                                gint      *minimum,
                                gint      *natural)
518
{
519
  GtkSocket *socket = GTK_SOCKET (widget);
520
  GtkSocketPrivate *private = socket->priv;
521

522
  if (private->plug_widget)
523
    {
524
      gtk_widget_get_preferred_width (private->plug_widget, minimum, natural);
525 526 527
    }
  else
    {
528
      if (private->is_mapped && !private->have_size && private->plug_window)
529
        gtk_socket_size_request (socket);
530

531 532
      if (private->is_mapped && private->have_size)
        *minimum = *natural = MAX (private->request_width, 1);
533
      else
534 535 536 537 538 539 540 541 542 543
        *minimum = *natural = 1;
    }
}

static void
gtk_socket_get_preferred_height (GtkWidget *widget,
                                 gint      *minimum,
                                 gint      *natural)
{
  GtkSocket *socket = GTK_SOCKET (widget);
544
  GtkSocketPrivate *private = socket->priv;
545

546
  if (private->plug_widget)
547
    {
548
      gtk_widget_get_preferred_height (private->plug_widget, minimum, natural);
549 550 551
    }
  else
    {
552
      if (private->is_mapped && !private->have_size && private->plug_window)
553
        gtk_socket_size_request (socket);
554

555 556
      if (private->is_mapped && private->have_size)
        *minimum = *natural = MAX (private->request_height, 1);
557 558
      else
        *minimum = *natural = 1;
559 560 561
    }
}

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
static void
gtk_socket_send_configure_event (GtkSocket *socket)
{
  GtkAllocation allocation;
  XConfigureEvent xconfigure;
  gint x, y;

  g_return_if_fail (socket->priv->plug_window != NULL);

  memset (&xconfigure, 0, sizeof (xconfigure));
  xconfigure.type = ConfigureNotify;

  xconfigure.event = GDK_WINDOW_XID (socket->priv->plug_window);
  xconfigure.window = GDK_WINDOW_XID (socket->priv->plug_window);

  /* The ICCCM says that synthetic events should have root relative
   * coordinates. We still aren't really ICCCM compliant, since
   * we don't send events when the real toplevel is moved.
   */
  gdk_error_trap_push ();
  gdk_window_get_origin (socket->priv->plug_window, &x, &y);
  gdk_error_trap_pop_ignored ();

  gtk_widget_get_allocation (GTK_WIDGET(socket), &allocation);
  xconfigure.x = x;
  xconfigure.y = y;
  xconfigure.width = allocation.width;
  xconfigure.height = allocation.height;

  xconfigure.border_width = 0;
  xconfigure.above = None;
  xconfigure.override_redirect = False;

  gdk_error_trap_push ();
  XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
	      GDK_WINDOW_XID (socket->priv->plug_window),
	      False, NoEventMask, (XEvent *)&xconfigure);
  gdk_error_trap_pop_ignored ();
}

602 603 604 605
static void
gtk_socket_size_allocate (GtkWidget     *widget,
			  GtkAllocation *allocation)
{
606
  GtkSocket *socket = GTK_SOCKET (widget);
607
  GtkSocketPrivate *private = socket->priv;
608

609
  gtk_widget_set_allocation (widget, allocation);
610
  if (gtk_widget_get_realized (widget))
611
    {
612
      gdk_window_move_resize (gtk_widget_get_window (widget),
613 614 615
			      allocation->x, allocation->y,
			      allocation->width, allocation->height);

616
      if (private->plug_widget)
617 618 619 620 621 622 623 624
	{
	  GtkAllocation child_allocation;

	  child_allocation.x = 0;
	  child_allocation.y = 0;
	  child_allocation.width = allocation->width;
	  child_allocation.height = allocation->height;

625
	  gtk_widget_size_allocate (private->plug_widget, &child_allocation);
626
	}
627
      else if (private->plug_window)
628
	{
629
	  gdk_error_trap_push ();
630 631 632

	  if (allocation->width != private->current_width ||
	      allocation->height != private->current_height)
633
	    {
634
	      gdk_window_move_resize (private->plug_window,
635 636
				      0, 0,
				      allocation->width, allocation->height);
637 638 639
	      if (private->resize_count)
		private->resize_count--;
	      
640 641 642
	      GTK_NOTE (PLUGSOCKET,
			g_message ("GtkSocket - allocated: %d %d",
				   allocation->width, allocation->height));
643 644
	      private->current_width = allocation->width;
	      private->current_height = allocation->height;
645 646
	    }

647
	  if (private->need_map)
648
	    {
649 650
	      gdk_window_show (private->plug_window);
	      private->need_map = FALSE;
651 652
	    }

653 654
	  while (private->resize_count)
 	    {
655
 	      gtk_socket_send_configure_event (socket);
656
 	      private->resize_count--;
657 658 659
 	      GTK_NOTE (PLUGSOCKET,
			g_message ("GtkSocket - sending synthetic configure: %d %d",
				   allocation->width, allocation->height));
660
 	    }
Matthias Clasen's avatar
Matthias Clasen committed
661

662
	  gdk_error_trap_pop_ignored ();
663 664 665 666
	}
    }
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
static void
gtk_socket_send_key_event (GtkSocket *socket,
			   GdkEvent  *gdk_event,
			   gboolean   mask_key_presses)
{
  XKeyEvent xkey;
  GdkScreen *screen = gdk_window_get_screen (socket->priv->plug_window);

  memset (&xkey, 0, sizeof (xkey));
  xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
  xkey.window = GDK_WINDOW_XID (socket->priv->plug_window);
  xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
  xkey.subwindow = None;
  xkey.time = gdk_event->key.time;
  xkey.x = 0;
  xkey.y = 0;
  xkey.x_root = 0;
  xkey.y_root = 0;
  xkey.state = gdk_event->key.state;
  xkey.keycode = gdk_event->key.hardware_keycode;
  xkey.same_screen = True;/* FIXME ? */

  gdk_error_trap_push ();
  XSendEvent (GDK_WINDOW_XDISPLAY (socket->priv->plug_window),
	      GDK_WINDOW_XID (socket->priv->plug_window),
	      False,
	      (mask_key_presses ? KeyPressMask : NoEventMask),
	      (XEvent *)&xkey);
  gdk_error_trap_pop_ignored ();
}

698 699 700 701 702 703
static gboolean
activate_key (GtkAccelGroup  *accel_group,
	      GObject        *acceleratable,
	      guint           accel_key,
	      GdkModifierType accel_mods,
	      GrabbedKey     *grabbed_key)
704
{
705
  GdkEvent *gdk_event = gtk_get_current_event ();
706
  
707
  GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket");
708
  gboolean retval = FALSE;
709

710
  if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window)
711
    {
712
      gtk_socket_send_key_event (socket, gdk_event, FALSE);
713
      retval = TRUE;
714 715 716 717
    }

  if (gdk_event)
    gdk_event_free (gdk_event);
718 719

  return retval;
720 721 722
}

static gboolean
723 724 725
find_accel_key (GtkAccelKey *key,
		GClosure    *closure,
		gpointer     data)
726
{
727 728 729 730
  GrabbedKey *grabbed_key = data;
  
  return (key->accel_key == grabbed_key->accel_key &&
	  key->accel_mods == grabbed_key->accel_mods);
731 732
}

733
/**
734
 * gtk_socket_add_grabbed_key:
735 736 737 738 739 740 741 742
 *
 * @socket: a #GtkSocket
 * @keyval: a key
 * @modifiers: modifiers for the key
 *
 * Called from the GtkSocket platform-specific backend when the
 * corresponding plug has told the socket to grab a key.
 */
743 744 745 746
static void
gtk_socket_add_grabbed_key (GtkSocket       *socket,
			    guint            keyval,
			    GdkModifierType  modifiers)
747
{
748 749
  GClosure *closure;
  GrabbedKey *grabbed_key;
750

751 752 753 754
  grabbed_key = g_new (GrabbedKey, 1);
  
  grabbed_key->accel_key = keyval;
  grabbed_key->accel_mods = modifiers;
755

756
  if (gtk_accel_group_find (socket->priv->accel_group,
757 758 759 760 761 762 763
			    find_accel_key,
			    &grabbed_key))
    {
      g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n",
		 keyval, modifiers);
      g_free (grabbed_key);
      return;
764 765
    }

766
  closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free);
767

768
  gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED,
769
			   closure);
770 771
}

772
/**
773
 * gtk_socket_remove_grabbed_key:
774 775 776 777 778 779 780 781
 *
 * @socket: a #GtkSocket
 * @keyval: a key
 * @modifiers: modifiers for the key
 *
 * Called from the GtkSocket backend when the corresponding plug has
 * told the socket to remove a key grab.
 */
782 783 784 785
static void
gtk_socket_remove_grabbed_key (GtkSocket      *socket,
			       guint           keyval,
			       GdkModifierType modifiers)
786
{
787
  if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers))
788 789
    g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n",
	       keyval, modifiers);
790 791
}

792 793
static void
socket_update_focus_in (GtkSocket *socket)
794
{
795
  GtkSocketPrivate *private = socket->priv;
796 797
  gboolean focus_in = FALSE;

798
  if (private->plug_window)
799 800
    {
      GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
801

802
      if (gtk_widget_is_toplevel (toplevel) &&
803
	  gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) &&
804 805 806 807
	  gtk_widget_is_focus (GTK_WIDGET (socket)))
	focus_in = TRUE;
    }

808
  if (focus_in != private->focus_in)
809
    {
810
      private->focus_in = focus_in;
811

812 813 814 815 816 817
      if (focus_in)
        _gtk_xembed_send_focus_message (private->plug_window,
                                        XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
      else
        _gtk_xembed_send_message (private->plug_window,
                                  XEMBED_FOCUS_OUT, 0, 0, 0);
818
    }
819 820
}

821 822
static void
socket_update_active (GtkSocket *socket)
823
{
824
  GtkSocketPrivate *private = socket->priv;
825
  gboolean active = FALSE;
826

827
  if (private->plug_window)
828 829
    {
      GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
830

831
      if (gtk_widget_is_toplevel (toplevel) &&
832
	  gtk_window_is_active  (GTK_WINDOW (toplevel)))
833 834 835
	active = TRUE;
    }

836
  if (active != private->active)
837
    {
838
      private->active = active;
839

840 841 842
      _gtk_xembed_send_message (private->plug_window,
                                active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
                                0, 0, 0);
843
    }
844 845 846
}

static void
847 848
gtk_socket_hierarchy_changed (GtkWidget *widget,
			      GtkWidget *old_toplevel)
849 850
{
  GtkSocket *socket = GTK_SOCKET (widget);
851
  GtkSocketPrivate *private = socket->priv;
852 853 854 855 856
  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);

  if (toplevel && !GTK_IS_WINDOW (toplevel))
    toplevel = NULL;

857
  if (toplevel != private->toplevel)
858
    {
859
      if (private->toplevel)
860
	{
861 862
	  gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
	  g_signal_handlers_disconnect_by_func (private->toplevel,
Manish Singh's avatar
Manish Singh committed
863 864
						socket_update_focus_in,
						socket);
865
	  g_signal_handlers_disconnect_by_func (private->toplevel,
Manish Singh's avatar
Manish Singh committed
866 867
						socket_update_active,
						socket);
868 869
	}

870
      private->toplevel = toplevel;
871 872 873

      if (toplevel)
	{
874 875
	  gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group);
	  g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus",
876
				    G_CALLBACK (socket_update_focus_in), socket);
877
	  g_signal_connect_swapped (private->toplevel, "notify::is-active",
878
				    G_CALLBACK (socket_update_active), socket);
879
	}
880 881 882

      socket_update_focus_in (socket);
      socket_update_active (socket);
883 884 885 886 887 888 889
    }
}

static void
gtk_socket_grab_notify (GtkWidget *widget,
			gboolean   was_grabbed)
{
890 891
  GtkSocket *socket = GTK_SOCKET (widget);

892
  if (!socket->priv->same_app)
893 894 895
    _gtk_xembed_send_message (socket->priv->plug_window,
                              was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
                              0, 0, 0);
896 897 898
}

static gboolean
899 900
gtk_socket_key_event (GtkWidget   *widget,
                      GdkEventKey *event)
901 902
{
  GtkSocket *socket = GTK_SOCKET (widget);
903
  GtkSocketPrivate *private = socket->priv;
904
  
905
  if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget)
906
    {
907
      gtk_socket_send_key_event (socket, (GdkEvent *) event, FALSE);
908

909 910 911 912 913 914
      return TRUE;
    }
  else
    return FALSE;
}

915 916 917
static void
gtk_socket_notify (GObject    *object,
		   GParamSpec *pspec)
918
{
919
  if (!strcmp (pspec->name, "is-focus"))
920 921
    return;
  socket_update_focus_in (GTK_SOCKET (object));
922 923
}

924
/**
925
 * gtk_socket_claim_focus:
926 927 928 929 930 931
 *
 * @socket: a #GtkSocket
 * @send_event: huh?
 *
 * Claims focus for the socket. XXX send_event?
 */
932 933 934
static void
gtk_socket_claim_focus (GtkSocket *socket,
			gboolean   send_event)
935
{
936
  GtkWidget *widget = GTK_WIDGET (socket);
937
  GtkSocketPrivate *private = socket->priv;
938

939
  if (!send_event)
940
    private->focus_in = TRUE;	/* Otherwise, our notify handler will send FOCUS_IN  */
941 942 943
      
  /* Oh, the trickery... */
  
944 945 946
  gtk_widget_set_can_focus (widget, TRUE);
  gtk_widget_grab_focus (widget);
  gtk_widget_set_can_focus (widget, FALSE);
947 948
}

949
static gboolean
950 951
gtk_socket_focus (GtkWidget       *widget,
		  GtkDirectionType direction)
952
{
953
  GtkSocket *socket = GTK_SOCKET (widget);
954
  GtkSocketPrivate *private = socket->priv;
955

956 957
  if (private->plug_widget)
    return gtk_widget_child_focus (private->plug_widget, direction);
958

959
  if (!gtk_widget_is_focus (widget))
960
    {
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
      gint detail = -1;

      switch (direction)
        {
        case GTK_DIR_UP:
        case GTK_DIR_LEFT:
        case GTK_DIR_TAB_BACKWARD:
          detail = XEMBED_FOCUS_LAST;
          break;
        case GTK_DIR_DOWN:
        case GTK_DIR_RIGHT:
        case GTK_DIR_TAB_FORWARD:
          detail = XEMBED_FOCUS_FIRST;
          break;
        }
      
      _gtk_xembed_send_focus_message (private->plug_window, XEMBED_FOCUS_IN, detail);
      gtk_socket_claim_focus (socket, FALSE);
979 980 981 982 983
 
      return TRUE;
    }
  else
    return FALSE;
984 985
}

986 987 988 989 990
static void
gtk_socket_remove (GtkContainer *container,
		   GtkWidget    *child)
{
  GtkSocket *socket = GTK_SOCKET (container);
991
  GtkSocketPrivate *private = socket->priv;
992

993
  g_return_if_fail (child == private->plug_widget);
994

995
  _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket);
996 997 998 999 1000 1001 1002 1003 1004
}

static void
gtk_socket_forall (GtkContainer *container,
		   gboolean      include_internals,
		   GtkCallback   callback,
		   gpointer      callback_data)
{
  GtkSocket *socket = GTK_SOCKET (container);
1005
  GtkSocketPrivate *private = socket->priv;
1006

1007 1008
  if (private->plug_widget)
    (* callback) (private->plug_widget, callback_data);
1009 1010
}

1011
/**
1012
 * gtk_socket_add_window:
1013 1014 1015 1016 1017 1018 1019 1020
 *
 * @socket: a #GtkSocket
 * @xid: the native identifier for a window
 * @need_reparent: whether the socket's plug's window needs to be
 *		   reparented to the socket
 *
 * Adds a window to a GtkSocket.
 */
1021 1022
static void
gtk_socket_add_window (GtkSocket       *socket,
1023
		       Window           xid,
1024
		       gboolean         need_reparent)
1025
{
1026
  GtkWidget *widget = GTK_WIDGET (socket);
1027
  GdkDisplay *display = gtk_widget_get_display (widget);
1028
  gpointer user_data = NULL;
1029
  GtkSocketPrivate *private = socket->priv;
1030 1031
  unsigned long version;
  unsigned long flags;
1032

1033
  if (GDK_IS_X11_DISPLAY (display))
1034 1035 1036
    private->plug_window = gdk_x11_window_lookup_for_display (display, xid);
  else
    private->plug_window = NULL;
1037

1038
  if (private->plug_window)
1039
    {
1040 1041
      g_object_ref (private->plug_window);
      gdk_window_get_user_data (private->plug_window, &user_data);
1042 1043
    }

1044
  if (user_data) /* A widget's window in this process */
1045 1046 1047 1048
    {
      GtkWidget *child_widget = user_data;

      if (!GTK_IS_PLUG (child_widget))
1049 1050 1051 1052 1053 1054 1055
        {
          g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket");
          private->plug_window = NULL;
          gdk_error_trap_pop_ignored ();

          return;
        }
1056 1057 1058

      _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket);
    }
1059
  else  /* A foreign window */
1060 1061
    {
      GdkDragProtocol protocol;
1062 1063

      gdk_error_trap_push ();
1064

1065
      if (!private->plug_window)
1066
        {
1067
          if (GDK_IS_X11_DISPLAY (display))
1068 1069 1070 1071 1072 1073 1074 1075
            private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid);
          if (!private->plug_window) /* was deleted before we could get it */
            {
              gdk_error_trap_pop_ignored ();
              return;
            }
        }

1076 1077 1078
      XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
                    GDK_WINDOW_XID (private->plug_window),
                    StructureNotifyMask | PropertyChangeMask);
1079

1080 1081
      if (gdk_error_trap_pop ())
	{
1082 1083
	  g_object_unref (private->plug_window);
	  private->plug_window = NULL;
1084 1085 1086 1087 1088 1089 1090 1091 1092
	  return;
	}
      
      /* OK, we now will reliably get destroy notification on socket->plug_window */

      gdk_error_trap_push ();

      if (need_reparent)
	{
1093 1094
	  gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
	  gdk_window_reparent (private->plug_window,
1095 1096
                               gtk_widget_get_window (widget),
                               0, 0);
1097 1098
	}

1099
      private->have_size = FALSE;
1100

1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
      private->xembed_version = -1;
      if (xembed_get_info (private->plug_window, &version, &flags))
        {
          private->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
          private->is_mapped = (flags & XEMBED_MAPPED) != 0;
        }
      else
        {
          /* FIXME, we should probably actually check the state before we started */
          private->is_mapped = TRUE;
        }
1112

1113
      private->need_map = private->is_mapped;