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