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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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-1999. 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/.
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkwindow.h"
31 #include "gtksignal.h"
32 #include "gtksocket.h"
35 #ifdef GDK_WINDOWING_X11
37 /* Forward declararations */
39 static void gtk_socket_class_init (GtkSocketClass *klass);
40 static void gtk_socket_init (GtkSocket *socket);
41 static void gtk_socket_realize (GtkWidget *widget);
42 static void gtk_socket_unrealize (GtkWidget *widget);
43 static void gtk_socket_size_request (GtkWidget *widget,
44 GtkRequisition *requisition);
45 static void gtk_socket_size_allocate (GtkWidget *widget,
46 GtkAllocation *allocation);
47 static gint gtk_socket_focus_in_event (GtkWidget *widget,
48 GdkEventFocus *event);
49 static void gtk_socket_claim_focus (GtkSocket *socket);
50 static gint gtk_socket_focus_out_event (GtkWidget *widget,
51 GdkEventFocus *event);
52 static void gtk_socket_send_configure_event (GtkSocket *socket);
53 static gint gtk_socket_focus (GtkContainer *container,
54 GtkDirectionType direction);
55 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
60 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
64 static GtkWidgetClass *parent_class = NULL;
67 gtk_socket_get_type ()
69 static GtkType socket_type = 0;
73 static const GtkTypeInfo socket_info =
77 sizeof (GtkSocketClass),
78 (GtkClassInitFunc) gtk_socket_class_init,
79 (GtkObjectInitFunc) gtk_socket_init,
84 socket_type = gtk_type_unique (gtk_container_get_type (), &socket_info);
91 gtk_socket_class_init (GtkSocketClass *class)
93 GtkObjectClass *object_class;
94 GtkWidgetClass *widget_class;
95 GtkContainerClass *container_class;
97 object_class = (GtkObjectClass*) class;
98 widget_class = (GtkWidgetClass*) class;
99 container_class = (GtkContainerClass*) class;
101 parent_class = gtk_type_class (gtk_widget_get_type ());
103 widget_class->realize = gtk_socket_realize;
104 widget_class->unrealize = gtk_socket_unrealize;
105 widget_class->size_request = gtk_socket_size_request;
106 widget_class->size_allocate = gtk_socket_size_allocate;
107 widget_class->focus_in_event = gtk_socket_focus_in_event;
108 widget_class->focus_out_event = gtk_socket_focus_out_event;
110 container_class->focus = gtk_socket_focus;
114 gtk_socket_init (GtkSocket *socket)
116 socket->request_width = 0;
117 socket->request_height = 0;
118 socket->current_width = 0;
119 socket->current_height = 0;
121 socket->plug_window = NULL;
122 socket->same_app = FALSE;
123 socket->focus_in = FALSE;
124 socket->have_size = FALSE;
125 socket->need_map = FALSE;
133 socket = gtk_type_new (gtk_socket_get_type ());
135 return GTK_WIDGET (socket);
139 gtk_socket_steal (GtkSocket *socket, guint32 id)
143 widget = GTK_WIDGET (socket);
145 socket->plug_window = gdk_window_lookup (id);
147 gdk_error_trap_push ();
149 if (socket->plug_window && socket->plug_window->user_data)
152 GtkWidget *child_widget;
154 child_widget = GTK_WIDGET (socket->plug_window->user_data);
157 g_warning("Stealing from same app not yet implemented");
159 socket->same_app = TRUE;
163 socket->plug_window = gdk_window_foreign_new (id);
164 if (!socket->plug_window) /* was deleted before we could get it */
166 gdk_error_trap_pop ();
170 socket->same_app = FALSE;
171 socket->have_size = FALSE;
173 XSelectInput (GDK_DISPLAY (),
174 GDK_WINDOW_XWINDOW(socket->plug_window),
175 StructureNotifyMask | PropertyChangeMask);
177 gtk_widget_queue_resize (widget);
180 gdk_window_hide (socket->plug_window);
181 gdk_window_reparent (socket->plug_window, widget->window, 0, 0);
184 gdk_error_trap_pop ();
186 socket->need_map = TRUE;
190 gtk_socket_realize (GtkWidget *widget)
193 GdkWindowAttr attributes;
194 gint attributes_mask;
195 XWindowAttributes xattrs;
197 g_return_if_fail (widget != NULL);
198 g_return_if_fail (GTK_IS_SOCKET (widget));
200 socket = GTK_SOCKET (widget);
201 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
203 attributes.window_type = GDK_WINDOW_CHILD;
204 attributes.x = widget->allocation.x;
205 attributes.y = widget->allocation.y;
206 attributes.width = widget->allocation.width;
207 attributes.height = widget->allocation.height;
208 attributes.wclass = GDK_INPUT_OUTPUT;
209 attributes.visual = gtk_widget_get_visual (widget);
210 attributes.colormap = gtk_widget_get_colormap (widget);
211 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
213 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
215 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
216 &attributes, attributes_mask);
217 gdk_window_set_user_data (widget->window, socket);
219 widget->style = gtk_style_attach (widget->style, widget->window);
220 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
222 XGetWindowAttributes (GDK_DISPLAY (),
223 GDK_WINDOW_XWINDOW (widget->window),
226 XSelectInput (GDK_DISPLAY (),
227 GDK_WINDOW_XWINDOW(widget->window),
228 xattrs.your_event_mask |
229 SubstructureNotifyMask | SubstructureRedirectMask);
231 gdk_window_add_filter (widget->window, gtk_socket_filter_func, widget);
233 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
235 /* We sync here so that we make sure that if the XID for
236 * our window is passed to another application, SubstructureRedirectMask
237 * will be set by the time the other app creates its window.
243 gtk_socket_unrealize (GtkWidget *widget)
247 g_return_if_fail (widget != NULL);
248 g_return_if_fail (GTK_IS_SOCKET (widget));
250 socket = GTK_SOCKET (widget);
252 if (socket->plug_window)
254 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
255 if (toplevel && GTK_IS_WINDOW (toplevel))
256 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel),
257 GDK_WINDOW_XWINDOW (socket->plug_window));
260 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
261 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
265 gtk_socket_size_request (GtkWidget *widget,
266 GtkRequisition *requisition)
270 g_return_if_fail (widget != NULL);
271 g_return_if_fail (GTK_IS_SOCKET (widget));
272 g_return_if_fail (requisition != NULL);
274 socket = GTK_SOCKET (widget);
276 if (!socket->have_size && socket->plug_window)
281 gdk_error_trap_push ();
283 if (XGetWMNormalHints (GDK_DISPLAY(),
284 GDK_WINDOW_XWINDOW (socket->plug_window),
287 /* This is obsolete, according the X docs, but many programs
289 if (hints.flags & (PSize | USSize))
291 socket->request_width = hints.width;
292 socket->request_height = hints.height;
294 else if (hints.flags & PMinSize)
296 socket->request_width = hints.min_width;
297 socket->request_height = hints.min_height;
299 else if (hints.flags & PBaseSize)
301 socket->request_width = hints.base_width;
302 socket->request_height = hints.base_height;
305 socket->have_size = TRUE; /* don't check again? */
307 gdk_error_trap_pop ();
310 requisition->width = socket->request_width;
311 requisition->height = socket->request_height;
315 gtk_socket_size_allocate (GtkWidget *widget,
316 GtkAllocation *allocation)
320 g_return_if_fail (widget != NULL);
321 g_return_if_fail (GTK_IS_SOCKET (widget));
322 g_return_if_fail (allocation != NULL);
324 socket = GTK_SOCKET (widget);
326 widget->allocation = *allocation;
327 if (GTK_WIDGET_REALIZED (widget))
329 gdk_window_move_resize (widget->window,
330 allocation->x, allocation->y,
331 allocation->width, allocation->height);
333 if (socket->plug_window)
335 gdk_error_trap_push ();
337 if (!socket->need_map &&
338 (allocation->width == socket->current_width) &&
339 (allocation->height == socket->current_height))
341 gtk_socket_send_configure_event (socket);
343 g_message ("GtkSocket - allocated no change: %d %d",
344 allocation->width, allocation->height));
348 gdk_window_move_resize (socket->plug_window,
350 allocation->width, allocation->height);
352 g_message ("GtkSocket - allocated: %d %d",
353 allocation->width, allocation->height));
354 socket->current_width = allocation->width;
355 socket->current_height = allocation->height;
358 if (socket->need_map)
360 gdk_window_show (socket->plug_window);
361 socket->need_map = FALSE;
365 gdk_error_trap_pop ();
371 gtk_socket_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
374 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
375 socket = GTK_SOCKET (widget);
377 if (socket->focus_in && socket->plug_window)
379 gdk_error_trap_push ();
380 XSetInputFocus (GDK_DISPLAY (),
381 GDK_WINDOW_XWINDOW (socket->plug_window),
382 RevertToParent, GDK_CURRENT_TIME);
384 gdk_error_trap_pop ();
391 gtk_socket_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
396 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
397 socket = GTK_SOCKET (widget);
399 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
403 XSetInputFocus (GDK_DISPLAY (),
404 GDK_WINDOW_XWINDOW (toplevel->window),
405 RevertToParent, CurrentTime); /* FIXME? */
408 socket->focus_in = FALSE;
414 gtk_socket_claim_focus (GtkSocket *socket)
417 socket->focus_in = TRUE;
419 /* Oh, the trickery... */
421 GTK_WIDGET_SET_FLAGS (socket, GTK_CAN_FOCUS);
422 gtk_widget_grab_focus (GTK_WIDGET (socket));
423 GTK_WIDGET_UNSET_FLAGS (socket, GTK_CAN_FOCUS);
425 /* FIXME: we might grab the focus even if we don't have
426 * it as an app... (and see _focus_in ()) */
427 if (socket->plug_window)
429 gdk_error_trap_push ();
430 XSetInputFocus (GDK_DISPLAY (),
431 GDK_WINDOW_XWINDOW (socket->plug_window),
432 RevertToParent, GDK_CURRENT_TIME);
434 gdk_error_trap_pop ();
439 gtk_socket_focus (GtkContainer *container, GtkDirectionType direction)
443 g_return_val_if_fail (GTK_IS_SOCKET (container), FALSE);
445 socket = GTK_SOCKET (container);
447 if (!socket->focus_in && socket->plug_window)
451 gtk_socket_claim_focus (socket);
453 xevent.xkey.type = KeyPress;
454 xevent.xkey.display = GDK_DISPLAY ();
455 xevent.xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
456 xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
457 xevent.xkey.time = GDK_CURRENT_TIME; /* FIXME */
458 /* FIXME, the following might cause big problems for
462 xevent.xkey.x_root = 0;
463 xevent.xkey.y_root = 0;
464 xevent.xkey.state = 0;
465 xevent.xkey.same_screen = TRUE; /* FIXME ? */
470 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Up);
473 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Down);
476 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Left);
479 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Right);
481 case GTK_DIR_TAB_FORWARD:
482 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
484 case GTK_DIR_TAB_BACKWARD:
485 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
486 xevent.xkey.state = ShiftMask;
491 gdk_error_trap_push ();
492 XSendEvent (gdk_display,
493 GDK_WINDOW_XWINDOW (socket->plug_window),
494 False, NoEventMask, &xevent);
496 gdk_error_trap_pop ();
507 gtk_socket_send_configure_event (GtkSocket *socket)
511 g_return_if_fail (socket->plug_window != NULL);
513 event.xconfigure.type = ConfigureNotify;
514 event.xconfigure.display = gdk_display;
516 event.xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
517 event.xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
519 event.xconfigure.x = 0;
520 event.xconfigure.y = 0;
521 event.xconfigure.width = GTK_WIDGET(socket)->allocation.width;
522 event.xconfigure.height = GTK_WIDGET(socket)->allocation.height;
524 event.xconfigure.border_width = 0;
525 event.xconfigure.above = None;
526 event.xconfigure.override_redirect = False;
528 gdk_error_trap_push ();
529 XSendEvent (gdk_display,
530 GDK_WINDOW_XWINDOW (socket->plug_window),
531 False, NoEventMask, &event);
533 gdk_error_trap_pop ();
537 gtk_socket_add_window (GtkSocket *socket, guint32 xid)
539 socket->plug_window = gdk_window_lookup (xid);
540 socket->same_app = TRUE;
542 if (!socket->plug_window)
545 GdkDragProtocol protocol;
547 socket->plug_window = gdk_window_foreign_new (xid);
548 if (!socket->plug_window) /* Already gone */
551 socket->same_app = FALSE;
553 gdk_error_trap_push ();
554 XSelectInput (GDK_DISPLAY (),
555 GDK_WINDOW_XWINDOW(socket->plug_window),
556 StructureNotifyMask | PropertyChangeMask);
558 if (gdk_drag_get_protocol (xid, &protocol))
559 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window,
562 gdk_error_trap_pop ();
564 gdk_window_add_filter (socket->plug_window,
565 gtk_socket_filter_func, socket);
567 /* Add a pointer to the socket on our toplevel window */
569 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
570 if (toplevel && GTK_IS_WINDOW (toplevel))
572 gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
577 static GdkFilterReturn
578 gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
584 GdkFilterReturn return_val;
586 socket = GTK_SOCKET (data);
587 widget = GTK_WIDGET (socket);
588 xevent = (XEvent *)gdk_xevent;
590 return_val = GDK_FILTER_CONTINUE;
592 switch (xevent->type)
596 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
598 if (!socket->plug_window)
600 gtk_socket_add_window (socket, xcwe->window);
602 gdk_error_trap_push ();
603 gdk_window_move_resize(socket->plug_window,
605 widget->allocation.width,
606 widget->allocation.height);
608 gdk_error_trap_pop ();
610 socket->request_width = xcwe->width;
611 socket->request_height = xcwe->height;
612 socket->have_size = TRUE;
615 g_message ("GtkSocket - window created with size: %d %d",
616 socket->request_width,
617 socket->request_height));
619 gtk_widget_queue_resize (widget);
622 return_val = GDK_FILTER_REMOVE;
627 case ConfigureRequest:
629 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
631 if (!socket->plug_window)
632 gtk_socket_add_window (socket, xcre->window);
634 if (xcre->window == GDK_WINDOW_XWINDOW (socket->plug_window))
636 if (xcre->value_mask & (CWWidth | CWHeight))
638 socket->request_width = xcre->width;
639 socket->request_height = xcre->height;
640 socket->have_size = TRUE;
643 g_message ("GtkSocket - configure request: %d %d",
644 socket->request_width,
645 socket->request_height));
647 gtk_widget_queue_resize (widget);
649 else if (xcre->value_mask & (CWX | CWY))
651 gtk_socket_send_configure_event (socket);
653 /* Ignore stacking requests. */
655 return_val = GDK_FILTER_REMOVE;
662 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
664 if (socket->plug_window &&
665 (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
670 g_message ("GtkSocket - destroy notify"));
672 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
673 if (toplevel && GTK_IS_WINDOW (toplevel))
674 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), xdwe->window);
675 gdk_window_destroy_notify (socket->plug_window);
676 gtk_widget_destroy (widget);
678 socket->plug_window = NULL;
680 return_val = GDK_FILTER_REMOVE;
686 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
688 gtk_socket_claim_focus (socket);
690 else if (xevent->xfocus.detail == NotifyInferior)
694 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
698 XSetInputFocus (GDK_DISPLAY (),
699 GDK_WINDOW_XWINDOW (toplevel->window),
700 RevertToParent, CurrentTime); /* FIXME? */
704 return_val = GDK_FILTER_REMOVE;
707 return_val = GDK_FILTER_REMOVE;
710 if (!socket->plug_window)
711 gtk_socket_add_window (socket, xevent->xmaprequest.window);
713 if (xevent->xmaprequest.window ==
714 GDK_WINDOW_XWINDOW (socket->plug_window))
717 g_message ("GtkSocket - Map Request"));
719 gdk_error_trap_push ();
720 gdk_window_show (socket->plug_window);
722 gdk_error_trap_pop ();
724 return_val = GDK_FILTER_REMOVE;
728 if (xevent->xproperty.window ==
729 GDK_WINDOW_XWINDOW (socket->plug_window))
731 GdkDragProtocol protocol;
733 if ((xevent->xproperty.atom == gdk_atom_intern ("XdndAware", FALSE)) ||
734 (xevent->xproperty.atom == gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE)))
736 gdk_error_trap_push ();
737 if (gdk_drag_get_protocol (xevent->xproperty.window, &protocol))
738 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
742 gdk_error_trap_pop ();
744 return_val = GDK_FILTER_REMOVE;
751 #elif defined (GDK_WINDOWING_WIN32)
754 gtk_socket_get_type ()
756 g_error ("GtkSocket not implemented");
763 g_error ("GtkSocket not implemented");
768 gtk_socket_steal (GtkSocket *socket, guint32 id)
770 g_error ("GtkSocket not implemented");
773 #endif /* GDK_WINDOWING */