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