1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 #include "gdk/gdkkeysyms.h"
33 #include "gtkmarshalers.h"
34 #include "gtkwindow.h"
36 #include "gtkprivate.h"
37 #include "gtksocket.h"
38 #include "gtksocketprivate.h"
44 #include <X11/extensions/Xfixes.h>
47 #include "gtkxembed.h"
50 static gboolean xembed_get_info (GdkWindow *gdk_window,
51 unsigned long *version,
52 unsigned long *flags);
55 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
58 _gtk_socket_windowing_get_id (GtkSocket *socket)
60 return GDK_WINDOW_XWINDOW (GTK_WIDGET (socket)->window);
64 _gtk_socket_windowing_realize_window (GtkSocket *socket)
66 GdkWindow *window = GTK_WIDGET (socket)->window;
67 XWindowAttributes xattrs;
69 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
70 GDK_WINDOW_XWINDOW (window),
73 /* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
74 for input on the socket with a mask of 0x0fffff (for god knows why)
75 which includes ButtonPressMask causing a BadAccess if someone else
76 also selects for this. As per the client-side windows merge we always
77 normally selects for button press so we can emulate it on client
78 side children that selects for button press. However, we don't need
79 this for GtkSocket, so we unselect it here, fixing the crashes in
81 XSelectInput (GDK_WINDOW_XDISPLAY (window),
82 GDK_WINDOW_XWINDOW (window),
83 (xattrs.your_event_mask & ~ButtonPressMask) |
84 SubstructureNotifyMask | SubstructureRedirectMask);
88 _gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket)
90 gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
91 GDK_WINDOW_XWINDOW (socket->plug_window));
95 _gtk_socket_windowing_size_request (GtkSocket *socket)
100 gdk_error_trap_push ();
102 socket->request_width = 1;
103 socket->request_height = 1;
105 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window),
106 GDK_WINDOW_XWINDOW (socket->plug_window),
109 if (hints.flags & PMinSize)
111 socket->request_width = MAX (hints.min_width, 1);
112 socket->request_height = MAX (hints.min_height, 1);
114 else if (hints.flags & PBaseSize)
116 socket->request_width = MAX (hints.base_width, 1);
117 socket->request_height = MAX (hints.base_height, 1);
120 socket->have_size = TRUE;
122 gdk_error_trap_pop ();
126 _gtk_socket_windowing_send_key_event (GtkSocket *socket,
128 gboolean mask_key_presses)
131 GdkScreen *screen = gdk_drawable_get_screen (socket->plug_window);
133 memset (&xkey, 0, sizeof (xkey));
134 xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
135 xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
136 xkey.root = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
137 xkey.subwindow = None;
138 xkey.time = gdk_event->key.time;
143 xkey.state = gdk_event->key.state;
144 xkey.keycode = gdk_event->key.hardware_keycode;
145 xkey.same_screen = True;/* FIXME ? */
147 gdk_error_trap_push ();
148 XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
149 GDK_WINDOW_XWINDOW (socket->plug_window),
151 (mask_key_presses ? KeyPressMask : NoEventMask),
153 gdk_display_sync (gdk_screen_get_display (screen));
154 gdk_error_trap_pop ();
158 _gtk_socket_windowing_focus_change (GtkSocket *socket,
162 _gtk_xembed_send_focus_message (socket->plug_window,
163 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
165 _gtk_xembed_send_message (socket->plug_window,
166 XEMBED_FOCUS_OUT, 0, 0, 0);
170 _gtk_socket_windowing_update_active (GtkSocket *socket,
173 _gtk_xembed_send_message (socket->plug_window,
174 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
179 _gtk_socket_windowing_update_modality (GtkSocket *socket,
182 _gtk_xembed_send_message (socket->plug_window,
183 modality ? XEMBED_MODALITY_ON : XEMBED_MODALITY_OFF,
188 _gtk_socket_windowing_focus (GtkSocket *socket,
189 GtkDirectionType direction)
197 case GTK_DIR_TAB_BACKWARD:
198 detail = XEMBED_FOCUS_LAST;
202 case GTK_DIR_TAB_FORWARD:
203 detail = XEMBED_FOCUS_FIRST;
207 _gtk_xembed_send_focus_message (socket->plug_window, XEMBED_FOCUS_IN, detail);
211 _gtk_socket_windowing_send_configure_event (GtkSocket *socket)
213 XConfigureEvent xconfigure;
216 g_return_if_fail (socket->plug_window != NULL);
218 memset (&xconfigure, 0, sizeof (xconfigure));
219 xconfigure.type = ConfigureNotify;
221 xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
222 xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
224 /* The ICCCM says that synthetic events should have root relative
225 * coordinates. We still aren't really ICCCM compliant, since
226 * we don't send events when the real toplevel is moved.
228 gdk_error_trap_push ();
229 gdk_window_get_origin (socket->plug_window, &x, &y);
230 gdk_error_trap_pop ();
234 xconfigure.width = GTK_WIDGET(socket)->allocation.width;
235 xconfigure.height = GTK_WIDGET(socket)->allocation.height;
237 xconfigure.border_width = 0;
238 xconfigure.above = None;
239 xconfigure.override_redirect = False;
241 gdk_error_trap_push ();
242 XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
243 GDK_WINDOW_XWINDOW (socket->plug_window),
244 False, NoEventMask, (XEvent *)&xconfigure);
245 gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (socket)));
246 gdk_error_trap_pop ();
250 _gtk_socket_windowing_select_plug_window_input (GtkSocket *socket)
252 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
253 GDK_WINDOW_XWINDOW (socket->plug_window),
254 StructureNotifyMask | PropertyChangeMask);
258 _gtk_socket_windowing_embed_get_info (GtkSocket *socket)
260 unsigned long version;
263 socket->xembed_version = -1;
264 if (xembed_get_info (socket->plug_window, &version, &flags))
266 socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
267 socket->is_mapped = (flags & XEMBED_MAPPED) != 0;
271 /* FIXME, we should probably actually check the state before we started */
272 socket->is_mapped = TRUE;
277 _gtk_socket_windowing_embed_notify (GtkSocket *socket)
280 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket));
282 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display),
283 GDK_WINDOW_XWINDOW (socket->plug_window),
284 SetModeInsert, SaveSetRoot, SaveSetUnmap);
286 _gtk_xembed_send_message (socket->plug_window,
287 XEMBED_EMBEDDED_NOTIFY, 0,
288 GDK_WINDOW_XWINDOW (GTK_WIDGET (socket)->window),
289 socket->xembed_version);
293 xembed_get_info (GdkWindow *window,
294 unsigned long *version,
295 unsigned long *flags)
297 GdkDisplay *display = gdk_drawable_get_display (window);
298 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
301 unsigned long nitems, bytes_after;
303 unsigned long *data_long;
306 gdk_error_trap_push();
307 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
308 GDK_WINDOW_XWINDOW (window),
311 xembed_info_atom, &type, &format,
312 &nitems, &bytes_after, &data);
313 gdk_error_trap_pop();
315 if (status != Success)
316 return FALSE; /* Window vanished? */
318 if (type == None) /* No info property */
321 if (type != xembed_info_atom)
323 g_warning ("_XEMBED_INFO property has wrong type\n");
329 g_warning ("_XEMBED_INFO too short\n");
334 data_long = (unsigned long *)data;
336 *version = data_long[0];
338 *flags = data_long[1] & XEMBED_MAPPED;
345 _gtk_socket_windowing_embed_get_focus_wrapped (void)
347 return _gtk_xembed_get_focus_wrapped ();
351 _gtk_socket_windowing_embed_set_focus_wrapped (void)
353 _gtk_xembed_set_focus_wrapped ();
357 handle_xembed_message (GtkSocket *socket,
358 XEmbedMessageType message,
364 GTK_NOTE (PLUGSOCKET,
365 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
369 case XEMBED_EMBEDDED_NOTIFY:
370 case XEMBED_WINDOW_ACTIVATE:
371 case XEMBED_WINDOW_DEACTIVATE:
372 case XEMBED_MODALITY_ON:
373 case XEMBED_MODALITY_OFF:
374 case XEMBED_FOCUS_IN:
375 case XEMBED_FOCUS_OUT:
376 g_warning ("GtkSocket: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message));
379 case XEMBED_REQUEST_FOCUS:
380 _gtk_socket_claim_focus (socket, TRUE);
383 case XEMBED_FOCUS_NEXT:
384 case XEMBED_FOCUS_PREV:
385 _gtk_socket_advance_toplevel_focus (socket,
386 (message == XEMBED_FOCUS_NEXT ?
387 GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
390 case XEMBED_GTK_GRAB_KEY:
391 _gtk_socket_add_grabbed_key (socket, data1, data2);
393 case XEMBED_GTK_UNGRAB_KEY:
394 _gtk_socket_remove_grabbed_key (socket, data1, data2);
397 case XEMBED_GRAB_KEY:
398 case XEMBED_UNGRAB_KEY:
402 GTK_NOTE (PLUGSOCKET,
403 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
409 _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
418 GdkFilterReturn return_val;
420 socket = GTK_SOCKET (data);
422 return_val = GDK_FILTER_CONTINUE;
424 if (socket->plug_widget)
427 widget = GTK_WIDGET (socket);
428 xevent = (XEvent *)gdk_xevent;
429 display = gtk_widget_get_display (widget);
431 switch (xevent->type)
434 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
436 _gtk_xembed_push_message (xevent);
437 handle_xembed_message (socket,
438 xevent->xclient.data.l[1],
439 xevent->xclient.data.l[2],
440 xevent->xclient.data.l[3],
441 xevent->xclient.data.l[4],
442 xevent->xclient.data.l[0]);
443 _gtk_xembed_pop_message ();
445 return_val = GDK_FILTER_REMOVE;
451 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
453 if (!socket->plug_window)
455 _gtk_socket_add_window (socket, xcwe->window, FALSE);
457 if (socket->plug_window)
459 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
463 return_val = GDK_FILTER_REMOVE;
468 case ConfigureRequest:
470 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
472 if (!socket->plug_window)
473 _gtk_socket_add_window (socket, xcre->window, FALSE);
475 if (socket->plug_window)
477 GtkSocketPrivate *private = _gtk_socket_get_private (socket);
479 if (xcre->value_mask & (CWWidth | CWHeight))
481 GTK_NOTE (PLUGSOCKET,
482 g_message ("GtkSocket - configure request: %d %d",
483 socket->request_width,
484 socket->request_height));
486 private->resize_count++;
487 gtk_widget_queue_resize (widget);
489 else if (xcre->value_mask & (CWX | CWY))
491 _gtk_socket_windowing_send_configure_event (socket);
493 /* Ignore stacking requests. */
495 return_val = GDK_FILTER_REMOVE;
502 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
504 /* Note that we get destroy notifies both from SubstructureNotify on
505 * our window and StructureNotify on socket->plug_window
507 if (socket->plug_window && (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
511 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
513 gdk_window_destroy_notify (socket->plug_window);
514 _gtk_socket_end_embedding (socket);
516 g_object_ref (widget);
517 g_signal_emit_by_name (widget, "plug-removed", &result);
519 gtk_widget_destroy (widget);
520 g_object_unref (widget);
522 return_val = GDK_FILTER_REMOVE;
528 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
530 _gtk_socket_claim_focus (socket, TRUE);
532 return_val = GDK_FILTER_REMOVE;
535 return_val = GDK_FILTER_REMOVE;
538 if (!socket->plug_window)
540 _gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
543 if (socket->plug_window)
545 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
547 _gtk_socket_handle_map_request (socket);
548 return_val = GDK_FILTER_REMOVE;
552 if (socket->plug_window &&
553 xevent->xproperty.window == GDK_WINDOW_XWINDOW (socket->plug_window))
555 GdkDragProtocol protocol;
557 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
559 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - received PropertyNotify for plug's WM_NORMAL_HINTS"));
560 socket->have_size = FALSE;
561 gtk_widget_queue_resize (widget);
562 return_val = GDK_FILTER_REMOVE;
564 else if ((xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndAware")) ||
565 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO")))
567 gdk_error_trap_push ();
568 if (gdk_drag_get_protocol_for_display (display,
569 xevent->xproperty.window,
571 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
575 gdk_display_sync (display);
576 gdk_error_trap_pop ();
577 return_val = GDK_FILTER_REMOVE;
579 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
583 if (xembed_get_info (socket->plug_window, NULL, &flags))
585 gboolean was_mapped = socket->is_mapped;
586 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
588 if (was_mapped != is_mapped)
591 _gtk_socket_handle_map_request (socket);
594 gdk_error_trap_push ();
595 gdk_window_show (socket->plug_window);
597 gdk_error_trap_pop ();
599 _gtk_socket_unmap_notify (socket);
603 return_val = GDK_FILTER_REMOVE;
609 XReparentEvent *xre = &xevent->xreparent;
611 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
612 if (!socket->plug_window && xre->parent == GDK_WINDOW_XWINDOW (widget->window))
614 _gtk_socket_add_window (socket, xre->window, FALSE);
616 if (socket->plug_window)
618 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
621 return_val = GDK_FILTER_REMOVE;
627 if (socket->plug_window &&
628 xevent->xunmap.window == GDK_WINDOW_XWINDOW (socket->plug_window))
630 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
632 _gtk_socket_unmap_notify (socket);
633 return_val = GDK_FILTER_REMOVE;