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 /* Forward declararations */
37 static void gtk_socket_class_init (GtkSocketClass *klass);
38 static void gtk_socket_init (GtkSocket *socket);
39 static void gtk_socket_realize (GtkWidget *widget);
40 static void gtk_socket_unrealize (GtkWidget *widget);
41 static void gtk_socket_size_request (GtkWidget *widget,
42 GtkRequisition *requisition);
43 static void gtk_socket_size_allocate (GtkWidget *widget,
44 GtkAllocation *allocation);
45 static gint gtk_socket_focus_in_event (GtkWidget *widget,
46 GdkEventFocus *event);
47 static void gtk_socket_claim_focus (GtkSocket *socket);
48 static gint gtk_socket_focus_out_event (GtkWidget *widget,
49 GdkEventFocus *event);
50 static void gtk_socket_send_configure_event (GtkSocket *socket);
51 static gint gtk_socket_focus (GtkContainer *container,
52 GtkDirectionType direction);
53 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
58 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
62 static GtkWidgetClass *parent_class = NULL;
65 gtk_socket_get_type ()
67 static guint socket_type = 0;
71 static const GtkTypeInfo socket_info =
75 sizeof (GtkSocketClass),
76 (GtkClassInitFunc) gtk_socket_class_init,
77 (GtkObjectInitFunc) gtk_socket_init,
82 socket_type = gtk_type_unique (gtk_container_get_type (), &socket_info);
89 gtk_socket_class_init (GtkSocketClass *class)
91 GtkObjectClass *object_class;
92 GtkWidgetClass *widget_class;
93 GtkContainerClass *container_class;
95 object_class = (GtkObjectClass*) class;
96 widget_class = (GtkWidgetClass*) class;
97 container_class = (GtkContainerClass*) class;
99 parent_class = gtk_type_class (gtk_widget_get_type ());
101 widget_class->realize = gtk_socket_realize;
102 widget_class->unrealize = gtk_socket_unrealize;
103 widget_class->size_request = gtk_socket_size_request;
104 widget_class->size_allocate = gtk_socket_size_allocate;
105 widget_class->focus_in_event = gtk_socket_focus_in_event;
106 widget_class->focus_out_event = gtk_socket_focus_out_event;
108 container_class->focus = gtk_socket_focus;
112 gtk_socket_init (GtkSocket *socket)
114 socket->request_width = 0;
115 socket->request_height = 0;
116 socket->current_width = 0;
117 socket->current_height = 0;
119 socket->plug_window = NULL;
120 socket->same_app = FALSE;
121 socket->focus_in = FALSE;
122 socket->have_size = FALSE;
123 socket->need_map = FALSE;
131 socket = gtk_type_new (gtk_socket_get_type ());
133 return GTK_WIDGET (socket);
137 gtk_socket_steal (GtkSocket *socket, guint32 id)
141 widget = GTK_WIDGET (socket);
143 socket->plug_window = gdk_window_lookup (id);
145 gdk_error_trap_push ();
147 if (socket->plug_window && socket->plug_window->user_data)
150 GtkWidget *child_widget;
152 child_widget = GTK_WIDGET (socket->plug_window->user_data);
155 g_warning("Stealing from same app not yet implemented");
157 socket->same_app = TRUE;
161 socket->plug_window = gdk_window_foreign_new (id);
162 if (!socket->plug_window) /* was deleted before we could get it */
164 gdk_error_trap_pop ();
168 socket->same_app = FALSE;
169 socket->have_size = FALSE;
171 XSelectInput (GDK_DISPLAY (),
172 GDK_WINDOW_XWINDOW(socket->plug_window),
173 StructureNotifyMask | PropertyChangeMask);
175 gtk_widget_queue_resize (widget);
178 gdk_window_hide (socket->plug_window);
179 gdk_window_reparent (socket->plug_window, widget->window, 0, 0);
182 gdk_error_trap_pop ();
184 socket->need_map = TRUE;
188 gtk_socket_realize (GtkWidget *widget)
191 GdkWindowAttr attributes;
192 gint attributes_mask;
193 XWindowAttributes xattrs;
195 g_return_if_fail (widget != NULL);
196 g_return_if_fail (GTK_IS_SOCKET (widget));
198 socket = GTK_SOCKET (widget);
199 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
201 attributes.window_type = GDK_WINDOW_CHILD;
202 attributes.x = widget->allocation.x;
203 attributes.y = widget->allocation.y;
204 attributes.width = widget->allocation.width;
205 attributes.height = widget->allocation.height;
206 attributes.wclass = GDK_INPUT_OUTPUT;
207 attributes.visual = gtk_widget_get_visual (widget);
208 attributes.colormap = gtk_widget_get_colormap (widget);
209 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
211 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
213 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
214 &attributes, attributes_mask);
215 gdk_window_set_user_data (widget->window, socket);
217 widget->style = gtk_style_attach (widget->style, widget->window);
218 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
220 XGetWindowAttributes (GDK_DISPLAY (),
221 GDK_WINDOW_XWINDOW (widget->window),
224 XSelectInput (GDK_DISPLAY (),
225 GDK_WINDOW_XWINDOW(widget->window),
226 xattrs.your_event_mask |
227 SubstructureNotifyMask | SubstructureRedirectMask);
229 gdk_window_add_filter (widget->window, gtk_socket_filter_func, widget);
231 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
233 /* We sync here so that we make sure that if the XID for
234 * our window is passed to another application, SubstructureRedirectMask
235 * will be set by the time the other app creates its window.
241 gtk_socket_unrealize (GtkWidget *widget)
245 g_return_if_fail (widget != NULL);
246 g_return_if_fail (GTK_IS_SOCKET (widget));
248 socket = GTK_SOCKET (widget);
250 if (socket->plug_window)
252 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
253 if (toplevel && GTK_IS_WINDOW (toplevel))
254 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel),
255 GDK_WINDOW_XWINDOW (socket->plug_window));
258 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
259 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
263 gtk_socket_size_request (GtkWidget *widget,
264 GtkRequisition *requisition)
268 g_return_if_fail (widget != NULL);
269 g_return_if_fail (GTK_IS_SOCKET (widget));
270 g_return_if_fail (requisition != NULL);
272 socket = GTK_SOCKET (widget);
274 if (!socket->have_size && socket->plug_window)
279 gdk_error_trap_push ();
281 if (XGetWMNormalHints (GDK_DISPLAY(),
282 GDK_WINDOW_XWINDOW (socket->plug_window),
285 /* This is obsolete, according the X docs, but many programs
287 if (hints.flags & (PSize | USSize))
289 socket->request_width = hints.width;
290 socket->request_height = hints.height;
292 else if (hints.flags & PMinSize)
294 socket->request_width = hints.min_width;
295 socket->request_height = hints.min_height;
297 else if (hints.flags & PBaseSize)
299 socket->request_width = hints.base_width;
300 socket->request_height = hints.base_height;
303 socket->have_size = TRUE; /* don't check again? */
305 gdk_error_trap_pop ();
308 requisition->width = socket->request_width;
309 requisition->height = socket->request_height;
313 gtk_socket_size_allocate (GtkWidget *widget,
314 GtkAllocation *allocation)
318 g_return_if_fail (widget != NULL);
319 g_return_if_fail (GTK_IS_SOCKET (widget));
320 g_return_if_fail (allocation != NULL);
322 socket = GTK_SOCKET (widget);
324 widget->allocation = *allocation;
325 if (GTK_WIDGET_REALIZED (widget))
327 gdk_window_move_resize (widget->window,
328 allocation->x, allocation->y,
329 allocation->width, allocation->height);
331 if (socket->plug_window)
333 gdk_error_trap_push ();
335 if (!socket->need_map &&
336 (allocation->width == socket->current_width) &&
337 (allocation->height == socket->current_height))
339 gtk_socket_send_configure_event (socket);
341 g_message ("GtkSocket - allocated no change: %d %d",
342 allocation->width, allocation->height));
346 gdk_window_move_resize (socket->plug_window,
348 allocation->width, allocation->height);
350 g_message ("GtkSocket - allocated: %d %d",
351 allocation->width, allocation->height));
352 socket->current_width = allocation->width;
353 socket->current_height = allocation->height;
356 if (socket->need_map)
358 gdk_window_show (socket->plug_window);
359 socket->need_map = FALSE;
363 gdk_error_trap_pop ();
369 gtk_socket_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
372 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
373 socket = GTK_SOCKET (widget);
375 if (socket->focus_in && socket->plug_window)
377 gdk_error_trap_push ();
378 XSetInputFocus (GDK_DISPLAY (),
379 GDK_WINDOW_XWINDOW (socket->plug_window),
380 RevertToParent, GDK_CURRENT_TIME);
382 gdk_error_trap_pop ();
389 gtk_socket_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
394 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
395 socket = GTK_SOCKET (widget);
397 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
401 XSetInputFocus (GDK_DISPLAY (),
402 GDK_WINDOW_XWINDOW (toplevel->window),
403 RevertToParent, CurrentTime); /* FIXME? */
406 socket->focus_in = FALSE;
412 gtk_socket_claim_focus (GtkSocket *socket)
415 socket->focus_in = TRUE;
417 /* Oh, the trickery... */
419 GTK_WIDGET_SET_FLAGS (socket, GTK_CAN_FOCUS);
420 gtk_widget_grab_focus (GTK_WIDGET (socket));
421 GTK_WIDGET_UNSET_FLAGS (socket, GTK_CAN_FOCUS);
423 /* FIXME: we might grab the focus even if we don't have
424 * it as an app... (and see _focus_in ()) */
425 if (socket->plug_window)
427 gdk_error_trap_push ();
428 XSetInputFocus (GDK_DISPLAY (),
429 GDK_WINDOW_XWINDOW (socket->plug_window),
430 RevertToParent, GDK_CURRENT_TIME);
432 gdk_error_trap_pop ();
437 gtk_socket_focus (GtkContainer *container, GtkDirectionType direction)
441 g_return_val_if_fail (GTK_IS_SOCKET (container), FALSE);
443 socket = GTK_SOCKET (container);
445 if (!socket->focus_in && socket->plug_window)
449 gtk_socket_claim_focus (socket);
451 xevent.xkey.type = KeyPress;
452 xevent.xkey.display = GDK_DISPLAY ();
453 xevent.xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
454 xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
455 xevent.xkey.time = GDK_CURRENT_TIME; /* FIXME */
456 /* FIXME, the following might cause big problems for
460 xevent.xkey.x_root = 0;
461 xevent.xkey.y_root = 0;
462 xevent.xkey.state = 0;
463 xevent.xkey.same_screen = TRUE; /* FIXME ? */
468 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Up);
471 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Down);
474 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Left);
477 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Right);
479 case GTK_DIR_TAB_FORWARD:
480 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
482 case GTK_DIR_TAB_BACKWARD:
483 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
484 xevent.xkey.state = ShiftMask;
489 gdk_error_trap_push ();
490 XSendEvent (gdk_display,
491 GDK_WINDOW_XWINDOW (socket->plug_window),
492 False, NoEventMask, &xevent);
494 gdk_error_trap_pop ();
505 gtk_socket_send_configure_event (GtkSocket *socket)
509 g_return_if_fail (socket->plug_window != NULL);
511 event.xconfigure.type = ConfigureNotify;
512 event.xconfigure.display = gdk_display;
514 event.xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
515 event.xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
517 event.xconfigure.x = 0;
518 event.xconfigure.y = 0;
519 event.xconfigure.width = GTK_WIDGET(socket)->allocation.width;
520 event.xconfigure.height = GTK_WIDGET(socket)->allocation.height;
522 event.xconfigure.border_width = 0;
523 event.xconfigure.above = None;
524 event.xconfigure.override_redirect = False;
526 gdk_error_trap_push ();
527 XSendEvent (gdk_display,
528 GDK_WINDOW_XWINDOW (socket->plug_window),
529 False, NoEventMask, &event);
531 gdk_error_trap_pop ();
535 gtk_socket_add_window (GtkSocket *socket, guint32 xid)
537 socket->plug_window = gdk_window_lookup (xid);
538 socket->same_app = TRUE;
540 if (!socket->plug_window)
543 GdkDragProtocol protocol;
545 socket->plug_window = gdk_window_foreign_new (xid);
546 if (!socket->plug_window) /* Already gone */
549 socket->same_app = FALSE;
551 gdk_error_trap_push ();
552 XSelectInput (GDK_DISPLAY (),
553 GDK_WINDOW_XWINDOW(socket->plug_window),
554 StructureNotifyMask | PropertyChangeMask);
556 if (gdk_drag_get_protocol (xid, &protocol))
557 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window,
560 gdk_error_trap_pop ();
562 gdk_window_add_filter (socket->plug_window,
563 gtk_socket_filter_func, socket);
565 /* Add a pointer to the socket on our toplevel window */
567 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
568 if (toplevel && GTK_IS_WINDOW (toplevel))
570 gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
575 static GdkFilterReturn
576 gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
582 GdkFilterReturn return_val;
584 socket = GTK_SOCKET (data);
585 widget = GTK_WIDGET (socket);
586 xevent = (XEvent *)gdk_xevent;
588 return_val = GDK_FILTER_CONTINUE;
590 switch (xevent->type)
594 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
596 if (!socket->plug_window)
598 gtk_socket_add_window (socket, xcwe->window);
600 gdk_error_trap_push ();
601 gdk_window_move_resize(socket->plug_window,
603 widget->allocation.width,
604 widget->allocation.height);
606 gdk_error_trap_pop ();
608 socket->request_width = xcwe->width;
609 socket->request_height = xcwe->height;
610 socket->have_size = TRUE;
613 g_message ("GtkSocket - window created with size: %d %d",
614 socket->request_width,
615 socket->request_height));
617 gtk_widget_queue_resize (widget);
620 return_val = GDK_FILTER_REMOVE;
625 case ConfigureRequest:
627 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
629 if (!socket->plug_window)
630 gtk_socket_add_window (socket, xcre->window);
632 if (xcre->window == GDK_WINDOW_XWINDOW (socket->plug_window))
634 if (xcre->value_mask & (CWWidth | CWHeight))
636 socket->request_width = xcre->width;
637 socket->request_height = xcre->height;
638 socket->have_size = TRUE;
641 g_message ("GtkSocket - configure request: %d %d",
642 socket->request_width,
643 socket->request_height));
645 gtk_widget_queue_resize (widget);
647 else if (xcre->value_mask & (CWX | CWY))
649 gtk_socket_send_configure_event (socket);
651 /* Ignore stacking requests. */
653 return_val = GDK_FILTER_REMOVE;
660 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
662 if (socket->plug_window &&
663 (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
668 g_message ("GtkSocket - destroy notify"));
670 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
671 if (toplevel && GTK_IS_WINDOW (toplevel))
672 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), xdwe->window);
673 gdk_window_destroy_notify (socket->plug_window);
674 gtk_widget_destroy (widget);
676 socket->plug_window = NULL;
678 return_val = GDK_FILTER_REMOVE;
684 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
686 gtk_socket_claim_focus (socket);
688 else if (xevent->xfocus.detail == NotifyInferior)
692 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
696 XSetInputFocus (GDK_DISPLAY (),
697 GDK_WINDOW_XWINDOW (toplevel->window),
698 RevertToParent, CurrentTime); /* FIXME? */
702 return_val = GDK_FILTER_REMOVE;
705 return_val = GDK_FILTER_REMOVE;
708 if (!socket->plug_window)
709 gtk_socket_add_window (socket, xevent->xmaprequest.window);
711 if (xevent->xmaprequest.window ==
712 GDK_WINDOW_XWINDOW (socket->plug_window))
715 g_message ("GtkSocket - Map Request"));
717 gdk_error_trap_push ();
718 gdk_window_show (socket->plug_window);
720 gdk_error_trap_pop ();
722 return_val = GDK_FILTER_REMOVE;
726 if (xevent->xproperty.window ==
727 GDK_WINDOW_XWINDOW (socket->plug_window))
729 GdkDragProtocol protocol;
731 if ((xevent->xproperty.atom == gdk_atom_intern ("XdndAware", FALSE)) ||
732 (xevent->xproperty.atom == gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE)))
734 gdk_error_trap_push ();
735 if (gdk_drag_get_protocol (xevent->xproperty.window, &protocol))
736 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
740 gdk_error_trap_pop ();
742 return_val = GDK_FILTER_REMOVE;