1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
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.
8 *
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.
13 *
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.
17 */
18
19 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
20
21 /*
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/.
26 */
27
28 #include "config.h"
29 #include <string.h>
30
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkmain.h"
33 #include "gtkmarshalers.h"
34 #include "gtkwindow.h"
35 #include "gtkplug.h"
36 #include "gtkprivate.h"
37 #include "gtksocket.h"
38 #include "gtksocketprivate.h"
39 #include "gtkdnd.h"
40
41 #include "x11/gdkx.h"
42
43 #ifdef HAVE_XFIXES
44 #include <X11/extensions/Xfixes.h>
45 #endif
46
47 #include "gtkxembed.h"
48 #include "gtkalias.h"
49
50 static gboolean xembed_get_info (GdkWindow *gdk_window,
51 unsigned long *version,
52 unsigned long *flags);
53
54 /* From Tk */
55 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
56
57 GdkNativeWindow
_gtk_socket_windowing_get_id(GtkSocket * socket)58 _gtk_socket_windowing_get_id (GtkSocket *socket)
59 {
60 return GDK_WINDOW_XWINDOW (GTK_WIDGET (socket)->window);
61 }
62
63 void
_gtk_socket_windowing_realize_window(GtkSocket * socket)64 _gtk_socket_windowing_realize_window (GtkSocket *socket)
65 {
66 GdkWindow *window = GTK_WIDGET (socket)->window;
67 XWindowAttributes xattrs;
68
69 XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
70 GDK_WINDOW_XWINDOW (window),
71 &xattrs);
72
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
80 firefox. */
81 XSelectInput (GDK_WINDOW_XDISPLAY (window),
82 GDK_WINDOW_XWINDOW (window),
83 (xattrs.your_event_mask & ~ButtonPressMask) |
84 SubstructureNotifyMask | SubstructureRedirectMask);
85 }
86
87 void
_gtk_socket_windowing_end_embedding_toplevel(GtkSocket * socket)88 _gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket)
89 {
90 gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
91 GDK_WINDOW_XWINDOW (socket->plug_window));
92 }
93
94 void
_gtk_socket_windowing_size_request(GtkSocket * socket)95 _gtk_socket_windowing_size_request (GtkSocket *socket)
96 {
97 XSizeHints hints;
98 long supplied;
99
100 gdk_error_trap_push ();
101
102 socket->request_width = 1;
103 socket->request_height = 1;
104
105 if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window),
106 GDK_WINDOW_XWINDOW (socket->plug_window),
107 &hints, &supplied))
108 {
109 if (hints.flags & PMinSize)
110 {
111 socket->request_width = MAX (hints.min_width, 1);
112 socket->request_height = MAX (hints.min_height, 1);
113 }
114 else if (hints.flags & PBaseSize)
115 {
116 socket->request_width = MAX (hints.base_width, 1);
117 socket->request_height = MAX (hints.base_height, 1);
118 }
119 }
120 socket->have_size = TRUE;
121
122 gdk_error_trap_pop ();
123 }
124
125 void
_gtk_socket_windowing_send_key_event(GtkSocket * socket,GdkEvent * gdk_event,gboolean mask_key_presses)126 _gtk_socket_windowing_send_key_event (GtkSocket *socket,
127 GdkEvent *gdk_event,
128 gboolean mask_key_presses)
129 {
130 XKeyEvent xkey;
131 GdkScreen *screen = gdk_window_get_screen (socket->plug_window);
132
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;
139 xkey.x = 0;
140 xkey.y = 0;
141 xkey.x_root = 0;
142 xkey.y_root = 0;
143 xkey.state = gdk_event->key.state;
144 xkey.keycode = gdk_event->key.hardware_keycode;
145 xkey.same_screen = True;/* FIXME ? */
146
147 gdk_error_trap_push ();
148 XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
149 GDK_WINDOW_XWINDOW (socket->plug_window),
150 False,
151 (mask_key_presses ? KeyPressMask : NoEventMask),
152 (XEvent *)&xkey);
153 gdk_display_sync (gdk_screen_get_display (screen));
154 gdk_error_trap_pop ();
155 }
156
157 void
_gtk_socket_windowing_focus_change(GtkSocket * socket,gboolean focus_in)158 _gtk_socket_windowing_focus_change (GtkSocket *socket,
159 gboolean focus_in)
160 {
161 if (focus_in)
162 _gtk_xembed_send_focus_message (socket->plug_window,
163 XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
164 else
165 _gtk_xembed_send_message (socket->plug_window,
166 XEMBED_FOCUS_OUT, 0, 0, 0);
167 }
168
169 void
_gtk_socket_windowing_update_active(GtkSocket * socket,gboolean active)170 _gtk_socket_windowing_update_active (GtkSocket *socket,
171 gboolean active)
172 {
173 _gtk_xembed_send_message (socket->plug_window,
174 active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
175 0, 0, 0);
176 }
177
178 void
_gtk_socket_windowing_update_modality(GtkSocket * socket,gboolean modality)179 _gtk_socket_windowing_update_modality (GtkSocket *socket,
180 gboolean modality)
181 {
182 _gtk_xembed_send_message (socket->plug_window,
183 modality ? XEMBED_MODALITY_ON : XEMBED_MODALITY_OFF,
184 0, 0, 0);
185 }
186
187 void
_gtk_socket_windowing_focus(GtkSocket * socket,GtkDirectionType direction)188 _gtk_socket_windowing_focus (GtkSocket *socket,
189 GtkDirectionType direction)
190 {
191 gint detail = -1;
192
193 switch (direction)
194 {
195 case GTK_DIR_UP:
196 case GTK_DIR_LEFT:
197 case GTK_DIR_TAB_BACKWARD:
198 detail = XEMBED_FOCUS_LAST;
199 break;
200 case GTK_DIR_DOWN:
201 case GTK_DIR_RIGHT:
202 case GTK_DIR_TAB_FORWARD:
203 detail = XEMBED_FOCUS_FIRST;
204 break;
205 }
206
207 _gtk_xembed_send_focus_message (socket->plug_window, XEMBED_FOCUS_IN, detail);
208 }
209
210 void
_gtk_socket_windowing_send_configure_event(GtkSocket * socket)211 _gtk_socket_windowing_send_configure_event (GtkSocket *socket)
212 {
213 XConfigureEvent xconfigure;
214 gint x, y;
215
216 g_return_if_fail (socket->plug_window != NULL);
217
218 memset (&xconfigure, 0, sizeof (xconfigure));
219 xconfigure.type = ConfigureNotify;
220
221 xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
222 xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
223
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.
227 */
228 gdk_error_trap_push ();
229 gdk_window_get_origin (socket->plug_window, &x, &y);
230 gdk_error_trap_pop ();
231
232 xconfigure.x = x;
233 xconfigure.y = y;
234 xconfigure.width = GTK_WIDGET(socket)->allocation.width;
235 xconfigure.height = GTK_WIDGET(socket)->allocation.height;
236
237 xconfigure.border_width = 0;
238 xconfigure.above = None;
239 xconfigure.override_redirect = False;
240
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 ();
247 }
248
249 void
_gtk_socket_windowing_select_plug_window_input(GtkSocket * socket)250 _gtk_socket_windowing_select_plug_window_input (GtkSocket *socket)
251 {
252 XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
253 GDK_WINDOW_XWINDOW (socket->plug_window),
254 StructureNotifyMask | PropertyChangeMask);
255 }
256
257 void
_gtk_socket_windowing_embed_get_info(GtkSocket * socket)258 _gtk_socket_windowing_embed_get_info (GtkSocket *socket)
259 {
260 unsigned long version;
261 unsigned long flags;
262
263 socket->xembed_version = -1;
264 if (xembed_get_info (socket->plug_window, &version, &flags))
265 {
266 socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
267 socket->is_mapped = (flags & XEMBED_MAPPED) != 0;
268 }
269 else
270 {
271 /* FIXME, we should probably actually check the state before we started */
272 socket->is_mapped = TRUE;
273 }
274 }
275
276 void
_gtk_socket_windowing_embed_notify(GtkSocket * socket)277 _gtk_socket_windowing_embed_notify (GtkSocket *socket)
278 {
279 #ifdef HAVE_XFIXES
280 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket));
281
282 XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display),
283 GDK_WINDOW_XWINDOW (socket->plug_window),
284 SetModeInsert, SaveSetRoot, SaveSetUnmap);
285 #endif
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);
290 }
291
292 static gboolean
xembed_get_info(GdkWindow * window,unsigned long * version,unsigned long * flags)293 xembed_get_info (GdkWindow *window,
294 unsigned long *version,
295 unsigned long *flags)
296 {
297 GdkDisplay *display = gdk_window_get_display (window);
298 Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
299 Atom type;
300 int format;
301 unsigned long nitems, bytes_after;
302 unsigned char *data;
303 unsigned long *data_long;
304 int status;
305
306 gdk_error_trap_push();
307 status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
308 GDK_WINDOW_XWINDOW (window),
309 xembed_info_atom,
310 0, 2, False,
311 xembed_info_atom, &type, &format,
312 &nitems, &bytes_after, &data);
313 gdk_error_trap_pop();
314
315 if (status != Success)
316 return FALSE; /* Window vanished? */
317
318 if (type == None) /* No info property */
319 return FALSE;
320
321 if (type != xembed_info_atom)
322 {
323 g_warning ("_XEMBED_INFO property has wrong type\n");
324 return FALSE;
325 }
326
327 if (nitems < 2)
328 {
329 g_warning ("_XEMBED_INFO too short\n");
330 XFree (data);
331 return FALSE;
332 }
333
334 data_long = (unsigned long *)data;
335 if (version)
336 *version = data_long[0];
337 if (flags)
338 *flags = data_long[1] & XEMBED_MAPPED;
339
340 XFree (data);
341 return TRUE;
342 }
343
344 gboolean
_gtk_socket_windowing_embed_get_focus_wrapped(void)345 _gtk_socket_windowing_embed_get_focus_wrapped (void)
346 {
347 return _gtk_xembed_get_focus_wrapped ();
348 }
349
350 void
_gtk_socket_windowing_embed_set_focus_wrapped(void)351 _gtk_socket_windowing_embed_set_focus_wrapped (void)
352 {
353 _gtk_xembed_set_focus_wrapped ();
354 }
355
356 static void
handle_xembed_message(GtkSocket * socket,XEmbedMessageType message,glong detail,glong data1,glong data2,guint32 time)357 handle_xembed_message (GtkSocket *socket,
358 XEmbedMessageType message,
359 glong detail,
360 glong data1,
361 glong data2,
362 guint32 time)
363 {
364 GTK_NOTE (PLUGSOCKET,
365 g_message ("GtkSocket: %s received", _gtk_xembed_message_name (message)));
366
367 switch (message)
368 {
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));
377 break;
378
379 case XEMBED_REQUEST_FOCUS:
380 _gtk_socket_claim_focus (socket, TRUE);
381 break;
382
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));
388 break;
389
390 case XEMBED_GTK_GRAB_KEY:
391 _gtk_socket_add_grabbed_key (socket, data1, data2);
392 break;
393 case XEMBED_GTK_UNGRAB_KEY:
394 _gtk_socket_remove_grabbed_key (socket, data1, data2);
395 break;
396
397 case XEMBED_GRAB_KEY:
398 case XEMBED_UNGRAB_KEY:
399 break;
400
401 default:
402 GTK_NOTE (PLUGSOCKET,
403 g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
404 break;
405 }
406 }
407
408 GdkFilterReturn
_gtk_socket_windowing_filter_func(GdkXEvent * gdk_xevent,GdkEvent * event,gpointer data)409 _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
410 GdkEvent *event,
411 gpointer data)
412 {
413 GtkSocket *socket;
414 GtkWidget *widget;
415 GdkDisplay *display;
416 XEvent *xevent;
417
418 GdkFilterReturn return_val;
419
420 socket = GTK_SOCKET (data);
421
422 return_val = GDK_FILTER_CONTINUE;
423
424 if (socket->plug_widget)
425 return return_val;
426
427 widget = GTK_WIDGET (socket);
428 xevent = (XEvent *)gdk_xevent;
429 display = gtk_widget_get_display (widget);
430
431 switch (xevent->type)
432 {
433 case ClientMessage:
434 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
435 {
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 ();
444
445 return_val = GDK_FILTER_REMOVE;
446 }
447 break;
448
449 case CreateNotify:
450 {
451 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
452
453 if (!socket->plug_window)
454 {
455 _gtk_socket_add_window (socket, xcwe->window, FALSE);
456
457 if (socket->plug_window)
458 {
459 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window created"));
460 }
461 }
462
463 return_val = GDK_FILTER_REMOVE;
464
465 break;
466 }
467
468 case ConfigureRequest:
469 {
470 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
471
472 if (!socket->plug_window)
473 _gtk_socket_add_window (socket, xcre->window, FALSE);
474
475 if (socket->plug_window)
476 {
477 GtkSocketPrivate *private = _gtk_socket_get_private (socket);
478
479 if (xcre->value_mask & (CWWidth | CWHeight))
480 {
481 GTK_NOTE (PLUGSOCKET,
482 g_message ("GtkSocket - configure request: %d %d",
483 socket->request_width,
484 socket->request_height));
485
486 private->resize_count++;
487 gtk_widget_queue_resize (widget);
488 }
489 else if (xcre->value_mask & (CWX | CWY))
490 {
491 _gtk_socket_windowing_send_configure_event (socket);
492 }
493 /* Ignore stacking requests. */
494
495 return_val = GDK_FILTER_REMOVE;
496 }
497 break;
498 }
499
500 case DestroyNotify:
501 {
502 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
503
504 /* Note that we get destroy notifies both from SubstructureNotify on
505 * our window and StructureNotify on socket->plug_window
506 */
507 if (socket->plug_window && (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
508 {
509 gboolean result;
510
511 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - destroy notify"));
512
513 gdk_window_destroy_notify (socket->plug_window);
514 _gtk_socket_end_embedding (socket);
515
516 g_object_ref (widget);
517 g_signal_emit_by_name (widget, "plug-removed", &result);
518 if (!result)
519 gtk_widget_destroy (widget);
520 g_object_unref (widget);
521
522 return_val = GDK_FILTER_REMOVE;
523 }
524 break;
525 }
526
527 case FocusIn:
528 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
529 {
530 _gtk_socket_claim_focus (socket, TRUE);
531 }
532 return_val = GDK_FILTER_REMOVE;
533 break;
534 case FocusOut:
535 return_val = GDK_FILTER_REMOVE;
536 break;
537 case MapRequest:
538 if (!socket->plug_window)
539 {
540 _gtk_socket_add_window (socket, xevent->xmaprequest.window, FALSE);
541 }
542
543 if (socket->plug_window)
544 {
545 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Map Request"));
546
547 _gtk_socket_handle_map_request (socket);
548 return_val = GDK_FILTER_REMOVE;
549 }
550 break;
551 case PropertyNotify:
552 if (socket->plug_window &&
553 xevent->xproperty.window == GDK_WINDOW_XWINDOW (socket->plug_window))
554 {
555 GdkDragProtocol protocol;
556
557 if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_NORMAL_HINTS"))
558 {
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;
563 }
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")))
566 {
567 gdk_error_trap_push ();
568 if (gdk_drag_get_protocol_for_display (display,
569 xevent->xproperty.window,
570 &protocol))
571 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
572 socket->plug_window,
573 protocol, TRUE);
574
575 gdk_display_sync (display);
576 gdk_error_trap_pop ();
577 return_val = GDK_FILTER_REMOVE;
578 }
579 else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"))
580 {
581 unsigned long flags;
582
583 if (xembed_get_info (socket->plug_window, NULL, &flags))
584 {
585 gboolean was_mapped = socket->is_mapped;
586 gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
587
588 if (was_mapped != is_mapped)
589 {
590 if (is_mapped)
591 _gtk_socket_handle_map_request (socket);
592 else
593 {
594 gdk_error_trap_push ();
595 gdk_window_show (socket->plug_window);
596 gdk_flush ();
597 gdk_error_trap_pop ();
598
599 _gtk_socket_unmap_notify (socket);
600 }
601 }
602 }
603 return_val = GDK_FILTER_REMOVE;
604 }
605 }
606 break;
607 case ReparentNotify:
608 {
609 XReparentEvent *xre = &xevent->xreparent;
610
611 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
612 if (!socket->plug_window && xre->parent == GDK_WINDOW_XWINDOW (widget->window))
613 {
614 _gtk_socket_add_window (socket, xre->window, FALSE);
615
616 if (socket->plug_window)
617 {
618 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - window reparented"));
619 }
620
621 return_val = GDK_FILTER_REMOVE;
622 }
623 else
624 {
625 if (socket->plug_window && xre->window == GDK_WINDOW_XWINDOW (socket->plug_window) && xre->parent != GDK_WINDOW_XWINDOW (widget->window))
626 {
627 gboolean result;
628
629 _gtk_socket_end_embedding (socket);
630
631 g_object_ref (widget);
632 g_signal_emit_by_name (widget, "plug-removed", &result);
633 if (!result)
634 gtk_widget_destroy (widget);
635 g_object_unref (widget);
636
637 return_val = GDK_FILTER_REMOVE;
638 }
639 }
640
641 break;
642 }
643 case UnmapNotify:
644 if (socket->plug_window &&
645 xevent->xunmap.window == GDK_WINDOW_XWINDOW (socket->plug_window))
646 {
647 GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
648
649 _gtk_socket_unmap_notify (socket);
650 return_val = GDK_FILTER_REMOVE;
651 }
652 break;
653
654 }
655
656 return return_val;
657 }
658