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
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkplug.h"
#include "gtkprivate.h"
#include "gtkplugprivate.h"
48
#include "gtkdebug.h"
49 50 51 52 53 54 55 56 57 58 59

#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)
{
60
  return GDK_WINDOW_XWINDOW (gtk_widget_get_window (GTK_WIDGET (plug)));
61 62 63 64 65
}

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

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

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

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

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

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

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

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

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

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

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

static void
xembed_set_info (GdkWindow     *window,
		 unsigned long  flags)
{
140
  GdkDisplay *display = gdk_window_get_display (window);
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 226
  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)
{
227
  GdkScreen *screen = gdk_window_get_screen (event->any.window);
228 229
  GdkDisplay *display = gdk_screen_get_display (screen);
  GtkPlug *plug = GTK_PLUG (data);
230
  GtkPlugPrivate *priv = plug->priv;
231 232 233
  XEvent *xevent = (XEvent *)gdk_xevent;

  GdkFilterReturn return_val;
234

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

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

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

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

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

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

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

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

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

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

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

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

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

	    _gtk_plug_add_all_grabbed_keys (plug);

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

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

      done:
	g_object_unref (plug);
	
	break;
      }
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 407

#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
408 409
    }

410
  return return_val;
411
}