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