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