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 #include "gdk/gdkkeysyms.h"
23 #include "gtkwindow.h"
24 #include "gtksignal.h"
25 #include "gtksocket.h"
28 /* Forward declararations */
30 static void gtk_socket_class_init (GtkSocketClass *klass);
31 static void gtk_socket_init (GtkSocket *socket);
32 static void gtk_socket_realize (GtkWidget *widget);
33 static void gtk_socket_unrealize (GtkWidget *widget);
34 static void gtk_socket_size_request (GtkWidget *widget,
35 GtkRequisition *requisition);
36 static void gtk_socket_size_allocate (GtkWidget *widget,
37 GtkAllocation *allocation);
38 static gint gtk_socket_focus_in_event (GtkWidget *widget,
39 GdkEventFocus *event);
40 static void gtk_socket_claim_focus (GtkSocket *socket);
41 static gint gtk_socket_focus_out_event (GtkWidget *widget,
42 GdkEventFocus *event);
43 static void gtk_socket_send_configure_event (GtkSocket *socket);
44 static gint gtk_socket_focus (GtkContainer *container,
45 GtkDirectionType direction);
46 static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent,
50 #ifdef DEBUG_PLUGSOCKET
51 #define DPRINTF(arg) g_print arg
57 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
61 static GtkWidgetClass *parent_class = NULL;
64 gtk_socket_get_type ()
66 static guint socket_type = 0;
70 GtkTypeInfo socket_info =
74 sizeof (GtkSocketClass),
75 (GtkClassInitFunc) gtk_socket_class_init,
76 (GtkObjectInitFunc) gtk_socket_init,
81 socket_type = gtk_type_unique (gtk_container_get_type (), &socket_info);
88 gtk_socket_class_init (GtkSocketClass *class)
90 GtkObjectClass *object_class;
91 GtkWidgetClass *widget_class;
92 GtkContainerClass *container_class;
94 object_class = (GtkObjectClass*) class;
95 widget_class = (GtkWidgetClass*) class;
96 container_class = (GtkContainerClass*) class;
98 parent_class = gtk_type_class (gtk_widget_get_type ());
100 widget_class->realize = gtk_socket_realize;
101 widget_class->unrealize = gtk_socket_unrealize;
102 widget_class->size_request = gtk_socket_size_request;
103 widget_class->size_allocate = gtk_socket_size_allocate;
104 widget_class->focus_in_event = gtk_socket_focus_in_event;
105 widget_class->focus_out_event = gtk_socket_focus_out_event;
107 container_class->focus = gtk_socket_focus;
111 gtk_socket_init (GtkSocket *socket)
113 socket->request_width = 0;
114 socket->request_height = 0;
115 socket->current_width = 0;
116 socket->current_height = 0;
118 socket->plug_window = NULL;
119 socket->same_app = FALSE;
120 socket->focus_in = FALSE;
121 socket->have_size = FALSE;
122 socket->need_map = FALSE;
130 socket = gtk_type_new (gtk_socket_get_type ());
132 return GTK_WIDGET (socket);
136 gtk_socket_steal (GtkSocket *socket, guint32 id)
140 widget = GTK_WIDGET (socket);
142 socket->plug_window = gdk_window_lookup (id);
144 if (socket->plug_window && socket->plug_window->user_data)
146 GtkWidget *child_widget = GTK_WIDGET (socket->plug_window->user_data);
148 g_warning("Stealing from same app not yet implemented");
150 socket->same_app = TRUE;
154 socket->plug_window = gdk_window_foreign_new (id);
155 socket->same_app = FALSE;
156 socket->have_size = FALSE;
158 XSelectInput (GDK_DISPLAY (),
159 GDK_WINDOW_XWINDOW(socket->plug_window),
160 StructureNotifyMask | PropertyChangeMask);
162 gtk_widget_queue_resize (widget);
165 gdk_window_hide (socket->plug_window);
166 gdk_window_reparent (socket->plug_window, widget->window, 0, 0);
167 socket->need_map = TRUE;
171 gtk_socket_realize (GtkWidget *widget)
174 GdkWindowAttr attributes;
175 gint attributes_mask;
176 XWindowAttributes xattrs;
178 g_return_if_fail (widget != NULL);
179 g_return_if_fail (GTK_IS_SOCKET (widget));
181 socket = GTK_SOCKET (widget);
182 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
184 attributes.window_type = GDK_WINDOW_CHILD;
185 attributes.x = widget->allocation.x;
186 attributes.y = widget->allocation.y;
187 attributes.width = widget->allocation.width;
188 attributes.height = widget->allocation.height;
189 attributes.wclass = GDK_INPUT_OUTPUT;
190 attributes.visual = gtk_widget_get_visual (widget);
191 attributes.colormap = gtk_widget_get_colormap (widget);
192 attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
194 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
196 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
197 &attributes, attributes_mask);
198 gdk_window_set_user_data (widget->window, socket);
200 widget->style = gtk_style_attach (widget->style, widget->window);
201 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
203 XGetWindowAttributes (GDK_DISPLAY (),
204 GDK_WINDOW_XWINDOW (widget->window),
207 XSelectInput (GDK_DISPLAY (),
208 GDK_WINDOW_XWINDOW(widget->window),
209 xattrs.your_event_mask |
210 SubstructureNotifyMask | SubstructureRedirectMask);
212 gdk_window_add_filter (widget->window, gtk_socket_filter_func, widget);
214 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
216 /* We sync here so that we make sure that if the XID for
217 * our window is passed to another application, SubstructureRedirectMask
218 * will be set by the time the other app creates its window.
224 gtk_socket_unrealize (GtkWidget *widget)
228 g_return_if_fail (widget != NULL);
229 g_return_if_fail (GTK_IS_SOCKET (widget));
231 socket = GTK_SOCKET (widget);
233 if (socket->plug_window)
235 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
236 if (toplevel && GTK_IS_WINDOW (toplevel))
237 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel),
238 GDK_WINDOW_XWINDOW (socket->plug_window));
241 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
242 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
246 gtk_socket_size_request (GtkWidget *widget,
247 GtkRequisition *requisition)
251 g_return_if_fail (widget != NULL);
252 g_return_if_fail (GTK_IS_SOCKET (widget));
253 g_return_if_fail (requisition != NULL);
255 socket = GTK_SOCKET (widget);
257 if (!socket->have_size && socket->plug_window)
262 if (XGetWMNormalHints (GDK_DISPLAY(),
263 GDK_WINDOW_XWINDOW (socket->plug_window),
266 /* This is obsolete, according the X docs, but many programs
268 if (hints.flags & (PSize | USSize))
270 socket->request_width = hints.width;
271 socket->request_height = hints.height;
273 else if (hints.flags & PMinSize)
275 socket->request_width = hints.min_width;
276 socket->request_height = hints.min_height;
278 else if (hints.flags & PBaseSize)
280 socket->request_width = hints.base_width;
281 socket->request_height = hints.base_height;
284 socket->have_size = TRUE; /* don't check again? */
287 requisition->width = socket->request_width;
288 requisition->height = socket->request_height;
292 gtk_socket_size_allocate (GtkWidget *widget,
293 GtkAllocation *allocation)
297 g_return_if_fail (widget != NULL);
298 g_return_if_fail (GTK_IS_SOCKET (widget));
299 g_return_if_fail (allocation != NULL);
301 socket = GTK_SOCKET (widget);
303 widget->allocation = *allocation;
304 if (GTK_WIDGET_REALIZED (widget))
306 gdk_window_move_resize (widget->window,
307 allocation->x, allocation->y,
308 allocation->width, allocation->height);
310 if (socket->plug_window)
312 if (!socket->need_map &&
313 (allocation->width == socket->current_width) &&
314 (allocation->height == socket->current_height))
316 gtk_socket_send_configure_event (socket);
317 DPRINTF(( "No change: %d %d\n",
318 allocation->width, allocation->height));
322 gdk_window_move_resize (socket->plug_window,
324 allocation->width, allocation->height);
325 DPRINTF(("configuring: %d %d\n",
326 allocation->width, allocation->height));
327 socket->current_width = allocation->width;
328 socket->current_height = allocation->height;
331 if (socket->need_map)
333 gdk_window_show (socket->plug_window);
334 socket->need_map = FALSE;
342 gtk_socket_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
345 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
346 socket = GTK_SOCKET (widget);
348 DPRINTF (( "Got focus\n"));
350 if (socket->focus_in && socket->plug_window)
351 XSetInputFocus (GDK_DISPLAY (),
352 GDK_WINDOW_XWINDOW (socket->plug_window),
353 RevertToParent, GDK_CURRENT_TIME);
359 gtk_socket_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
364 g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
365 socket = GTK_SOCKET (widget);
367 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
371 XSetInputFocus (GDK_DISPLAY (),
372 GDK_WINDOW_XWINDOW (toplevel->window),
373 RevertToParent, CurrentTime); /* FIXME? */
376 socket->focus_in = FALSE;
382 gtk_socket_claim_focus (GtkSocket *socket)
385 socket->focus_in = TRUE;
387 /* Oh, the trickery... */
389 GTK_WIDGET_SET_FLAGS (socket, GTK_CAN_FOCUS);
390 gtk_widget_grab_focus (GTK_WIDGET (socket));
391 GTK_WIDGET_UNSET_FLAGS (socket, GTK_CAN_FOCUS);
393 /* FIXME: we might grab the focus even if we don't have
394 * it as an app... (and see _focus_in ()) */
395 if (socket->plug_window)
396 XSetInputFocus (GDK_DISPLAY (),
397 GDK_WINDOW_XWINDOW (socket->plug_window),
398 RevertToParent, GDK_CURRENT_TIME);
402 gtk_socket_focus (GtkContainer *container, GtkDirectionType direction)
406 g_return_val_if_fail (GTK_IS_SOCKET (container), FALSE);
408 socket = GTK_SOCKET (container);
410 if (!socket->focus_in && socket->plug_window)
414 gtk_socket_claim_focus (socket);
416 xevent.xkey.type = KeyPress;
417 xevent.xkey.display = GDK_DISPLAY ();
418 xevent.xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
419 xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
420 xevent.xkey.time = GDK_CURRENT_TIME; /* FIXME */
421 /* FIXME, the following might cause big problems for
425 xevent.xkey.x_root = 0;
426 xevent.xkey.y_root = 0;
427 xevent.xkey.state = 0;
428 xevent.xkey.same_screen = TRUE; /* FIXME ? */
433 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Up);
436 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Down);
439 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Left);
442 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Right);
444 case GTK_DIR_TAB_FORWARD:
445 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
447 case GTK_DIR_TAB_BACKWARD:
448 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
449 xevent.xkey.state = ShiftMask;
454 XSendEvent (gdk_display,
455 GDK_WINDOW_XWINDOW (socket->plug_window),
456 False, NoEventMask, &xevent);
466 gtk_socket_send_configure_event (GtkSocket *socket)
470 g_return_if_fail (socket->plug_window != NULL);
472 event.xconfigure.type = ConfigureNotify;
473 event.xconfigure.display = gdk_display;
475 event.xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
476 event.xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
478 event.xconfigure.x = 0;
479 event.xconfigure.y = 0;
480 event.xconfigure.width = GTK_WIDGET(socket)->allocation.width;
481 event.xconfigure.height = GTK_WIDGET(socket)->allocation.height;
483 event.xconfigure.border_width = 0;
484 event.xconfigure.above = None;
485 event.xconfigure.override_redirect = False;
487 XSendEvent (gdk_display,
488 GDK_WINDOW_XWINDOW (socket->plug_window),
489 False, NoEventMask, &event);
493 gtk_socket_add_window (GtkSocket *socket, guint32 xid)
495 socket->plug_window = gdk_window_lookup (xid);
496 socket->same_app = TRUE;
498 if (!socket->plug_window)
501 GdkDragProtocol protocol;
503 socket->plug_window = gdk_window_foreign_new (xid);
504 socket->same_app = FALSE;
506 XSelectInput (GDK_DISPLAY (),
507 GDK_WINDOW_XWINDOW(socket->plug_window),
508 StructureNotifyMask | PropertyChangeMask);
510 if (gdk_drag_get_protocol (xid, &protocol))
511 gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window,
514 gdk_window_add_filter (socket->plug_window,
515 gtk_socket_filter_func, socket);
517 /* Add a pointer to the socket on our toplevel window */
519 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
520 if (toplevel && GTK_IS_WINDOW (toplevel))
522 gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
527 static GdkFilterReturn
528 gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
534 GdkFilterReturn return_val;
536 socket = GTK_SOCKET (data);
537 widget = GTK_WIDGET (socket);
538 xevent = (XEvent *)gdk_xevent;
540 return_val = GDK_FILTER_CONTINUE;
542 switch (xevent->type)
546 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
548 if (!socket->plug_window)
552 gtk_socket_add_window (socket, xcwe->window);
554 gdk_window_move_resize(socket->plug_window,
556 widget->allocation.width,
557 widget->allocation.height);
559 socket->request_width = xcwe->width;
560 socket->request_height = xcwe->height;
561 socket->have_size = TRUE;
563 DPRINTF(("Window created with size: %d %d\n",
564 socket->request_width,
565 socket->request_height));
567 gtk_widget_queue_resize (widget);
570 return_val = GDK_FILTER_REMOVE;
575 case ConfigureRequest:
577 XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
579 if (!socket->plug_window)
580 gtk_socket_add_window (socket, xcre->window);
582 if (xcre->window == GDK_WINDOW_XWINDOW (socket->plug_window))
584 if (xcre->value_mask & (CWWidth | CWHeight))
586 socket->request_width = xcre->width;
587 socket->request_height = xcre->height;
588 socket->have_size = TRUE;
590 DPRINTF(("Configure request: %d %d\n",
591 socket->request_width,
592 socket->request_height));
594 gtk_widget_queue_resize (widget);
596 else if (xcre->value_mask & (CWX | CWY))
598 gtk_socket_send_configure_event (socket);
600 /* Ignore stacking requests. */
602 return_val = GDK_FILTER_REMOVE;
609 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
611 if (socket->plug_window &&
612 (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
616 DPRINTF(("Destroy Notify\n"));
618 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
619 if (toplevel && GTK_IS_WINDOW (toplevel))
620 gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), xdwe->window);
621 gtk_widget_destroy (widget);
622 gdk_window_destroy_notify (socket->plug_window);
624 socket->plug_window = NULL;
626 return_val = GDK_FILTER_REMOVE;
632 if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
634 gtk_socket_claim_focus (socket);
636 else if (xevent->xfocus.detail == NotifyInferior)
640 toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
644 XSetInputFocus (GDK_DISPLAY (),
645 GDK_WINDOW_XWINDOW (toplevel->window),
646 RevertToParent, CurrentTime); /* FIXME? */
650 return_val = GDK_FILTER_REMOVE;
653 return_val = GDK_FILTER_REMOVE;
656 if (!socket->plug_window)
657 gtk_socket_add_window (socket, xevent->xmaprequest.window);
659 if (xevent->xmaprequest.window ==
660 GDK_WINDOW_XWINDOW (socket->plug_window))
662 DPRINTF(("Map Request\n"));
664 gdk_window_show (socket->plug_window);
666 return_val = GDK_FILTER_REMOVE;
670 if (xevent->xproperty.window ==
671 GDK_WINDOW_XWINDOW (socket->plug_window))
673 GdkDragProtocol protocol;
675 if ((xevent->xproperty.atom == gdk_atom_intern ("XdndAware", FALSE)) ||
676 (xevent->xproperty.atom == gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE)))
678 if (gdk_drag_get_protocol (xevent->xproperty.window, &protocol))
679 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
683 return_val = GDK_FILTER_REMOVE;