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