gtkplug-x11.c 11.3 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include "config.h"

#ifdef XINPUT_2

/* Hack to have keyboard events interpreted
 * regardless of the default device manager
 */
#define GDK_COMPILATION
#include "x11/gdkdevicemanager-core.h"
#include "x11/gdkdevicemanager-xi2.h"
#include "x11/gdkeventtranslator.h"
#undef GDK_COMPILATION

#endif /* XINPUT_2 */

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkplug.h"
#include "gtkprivate.h"
#include "gtkplugprivate.h"

#include "x11/gdkx.h"

#include "gtkxembed.h"

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

GdkNativeWindow
_gtk_plug_windowing_get_id (GtkPlug *plug)
{
59
  return GDK_WINDOW_XWINDOW (gtk_widget_get_window (GTK_WIDGET (plug)));
60 61 62 63 64
}

void
_gtk_plug_windowing_realize_toplevel (GtkPlug *plug)
{
65
  xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0);
66 67 68 69 70
}

void
_gtk_plug_windowing_map_toplevel (GtkPlug *plug)
{
71
  xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), XEMBED_MAPPED);
72 73 74 75 76
}

void
_gtk_plug_windowing_unmap_toplevel (GtkPlug *plug)
{
77
  xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0);
78 79 80 81 82
}

void
_gtk_plug_windowing_set_focus (GtkPlug *plug)
{
83 84 85
  GtkPlugPrivate *priv = plug->priv;

  _gtk_xembed_send_message (priv->socket_window,
86 87 88 89 90 91 92 93
			    XEMBED_REQUEST_FOCUS, 0, 0, 0);
}

void
_gtk_plug_windowing_add_grabbed_key (GtkPlug        *plug,
				     guint           accelerator_key,
				     GdkModifierType accelerator_mods)
{
94 95 96
  GtkPlugPrivate *priv = plug->priv;

  _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_GRAB_KEY, 0,
97 98 99 100 101 102 103 104
			    accelerator_key, accelerator_mods);
}

void
_gtk_plug_windowing_remove_grabbed_key (GtkPlug        *plug,
					guint           accelerator_key,
					GdkModifierType accelerator_mods)
{
105 106 107
  GtkPlugPrivate *priv = plug->priv;

  _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_UNGRAB_KEY, 0,
108 109 110 111 112 113 114
			    accelerator_key, accelerator_mods);
}

void
_gtk_plug_windowing_focus_to_parent (GtkPlug         *plug,
				     GtkDirectionType direction)
{
115
  GtkPlugPrivate *priv = plug->priv;
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
  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;
    }
131 132

  _gtk_xembed_send_focus_message (priv->socket_window, message, 0);
133 134 135 136 137 138
}

static void
xembed_set_info (GdkWindow     *window,
		 unsigned long  flags)
{
139
  GdkDisplay *display = gdk_window_get_display (window);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
  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_XWINDOW (window),
		   xembed_info_atom, xembed_info_atom, 32,
		   PropModeReplace,
		   (unsigned char *)buffer, 2);
}

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;
    }
}

GdkFilterReturn
_gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
				 GdkEvent  *event,
				 gpointer   data)
{
226
  GdkScreen *screen = gdk_window_get_screen (event->any.window);
227 228
  GdkDisplay *display = gdk_screen_get_display (screen);
  GtkPlug *plug = GTK_PLUG (data);
229
  GtkPlugPrivate *priv = plug->priv;
230 231 232
  XEvent *xevent = (XEvent *)gdk_xevent;

  GdkFilterReturn return_val;
233

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  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
	   */

258
	  return_val = GDK_FILTER_REMOVE;
259 260 261 262 263
	}
      break;
    case ReparentNotify:
      {
	XReparentEvent *xre = &xevent->xreparent;
264
        gboolean was_embedded = priv->socket_window != NULL;
265

266
	GTK_NOTE (PLUGSOCKET, g_message("GtkPlug: ReparentNotify received"));
267 268 269 270 271 272 273 274 275

	return_val = GDK_FILTER_REMOVE;
	
	g_object_ref (plug);
	
	if (was_embedded)
	  {
	    /* End of embedding protocol for previous socket */
	    
276
	    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: end of embedding"));
277 278 279 280
	    /* 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
	     */
281 282

            if (xre->parent != GDK_WINDOW_XWINDOW (priv->socket_window))
283 284 285
	      {
		GtkWidget *widget = GTK_WIDGET (plug);

286 287 288
                gdk_window_set_user_data (priv->socket_window, NULL);
		g_object_unref (priv->socket_window);
		priv->socket_window = NULL;
289 290 291 292 293 294 295 296 297 298 299 300

		/* 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_XWINDOW (gdk_screen_get_root_window (screen)))
		  {
301
		    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: calling gtk_plug_send_delete_event()"));
302
		    _gtk_plug_send_delete_event (widget);
303 304

		    g_object_notify (G_OBJECT (plug), "embedded");
305 306 307 308 309 310 311 312 313 314
		  }
	      }
	    else
	      goto done;
	  }

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

315
	    GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: start of embedding"));
316 317 318

            priv->socket_window = gdk_window_lookup_for_display (display, xre->parent);
            if (priv->socket_window)
319 320
	      {
		gpointer user_data = NULL;
321
		gdk_window_get_user_data (priv->socket_window, &user_data);
322 323 324 325

		if (user_data)
		  {
		    g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process");
326
                    priv->socket_window = NULL;
327
		    break; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */
328 329
		  }

330
		g_object_ref (priv->socket_window);
331 332 333
	      }
	    else
	      {
334 335
		priv->socket_window = gdk_window_foreign_new_for_display (display, xre->parent);
		if (!priv->socket_window) /* Already gone */
336
		  break; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */
337 338 339 340 341 342
	      }

	    _gtk_plug_add_all_grabbed_keys (plug);

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

	    g_object_notify (G_OBJECT (plug), "embedded");
345 346 347 348 349 350 351
	  }

      done:
	g_object_unref (plug);
	
	break;
      }
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

#ifdef XINPUT_2
    case KeyPress:
    case KeyRelease:
      {
        static GdkDeviceManager *core_device_manager = NULL;
        GdkDeviceManager *device_manager;
        GdkEvent *translated_event;
        GList *devices, *d;
        GdkDevice *keyboard = NULL;

        device_manager = gdk_display_get_device_manager (display);

        /* bail out if the device manager already
         * interprets core keyboard events.
         */
        if (!GDK_IS_DEVICE_MANAGER_XI2 (device_manager))
          return GDK_FILTER_CONTINUE;

        /* Find out the first keyboard device, the
         * generated event will be assigned to it.
         */
        devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);

        for (d = devices; d; d = d->next)
          {
            GdkDevice *device = d->data;

            if (device->source == GDK_SOURCE_KEYBOARD)
              keyboard = device;
          }

        g_list_free (devices);

        if (!keyboard)
          return GDK_FILTER_CONTINUE;

        /* This is a crude hack so key events
         * are interpreted as if there was a
         * GdkDeviceManagerCore available.
         */
        if (G_UNLIKELY (!core_device_manager))
          core_device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
                                         "display", display,
                                         NULL);

        translated_event = gdk_event_translator_translate (GDK_EVENT_TRANSLATOR (core_device_manager), display, xevent);
        gdk_event_set_device (translated_event, keyboard);

        gtk_main_do_event (translated_event);
        gdk_event_free (translated_event);

        return_val = GDK_FILTER_REMOVE;
      }
#endif
407 408
    }

409
  return return_val;
410
}