]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkmain-x11.c
get directfb building with csw
[~andy/gtk] / gdk / x11 / gdkmain-x11.c
1 /* GDK - The GIMP Drawing Kit
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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <glib/gprintf.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <errno.h>
35
36 #include <X11/Xatom.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xutil.h>
39
40 #ifdef HAVE_XKB
41 #include <X11/XKBlib.h>
42 #endif
43
44 #include "gdk.h"
45
46 #include "gdkx.h"
47 #include "gdkasync.h"
48 #include "gdkdisplay-x11.h"
49 #include "gdkinternals.h"
50 #include "gdkintl.h"
51 #include "gdkregion-generic.h"
52 #include "gdkinputprivate.h"
53 #include "gdkalias.h"
54
55 typedef struct _GdkPredicate  GdkPredicate;
56 typedef struct _GdkErrorTrap  GdkErrorTrap;
57
58 struct _GdkPredicate
59 {
60   GdkEventFunc func;
61   gpointer data;
62 };
63
64 struct _GdkErrorTrap
65 {
66   int (*old_handler) (Display *, XErrorEvent *);
67   gint error_warnings;
68   gint error_code;
69 };
70
71 /* 
72  * Private function declarations
73  */
74
75 #ifndef HAVE_XCONVERTCASE
76 static void      gdkx_XConvertCase      (KeySym        symbol,
77                                          KeySym       *lower,
78                                          KeySym       *upper);
79 #define XConvertCase gdkx_XConvertCase
80 #endif
81
82 static int          gdk_x_error                  (Display     *display, 
83                                                   XErrorEvent *error);
84 static int          gdk_x_io_error               (Display     *display);
85
86 /* Private variable declarations
87  */
88 static GSList *gdk_error_traps = NULL;               /* List of error traps */
89 static GSList *gdk_error_trap_free_list = NULL;      /* Free list */
90
91 const GOptionEntry _gdk_windowing_args[] = {
92   { "sync", 0, 0, G_OPTION_ARG_NONE, &_gdk_synchronize, 
93     /* Description of --sync in --help output */ N_("Make X calls synchronous"), NULL },
94   { NULL }
95 };
96
97 void
98 _gdk_windowing_init (void)
99 {
100   _gdk_x11_initialize_locale ();
101   
102   XSetErrorHandler (gdk_x_error);
103   XSetIOErrorHandler (gdk_x_io_error);
104
105   _gdk_selection_property = gdk_atom_intern_static_string ("GDK_SELECTION");
106 }
107
108 void
109 gdk_set_use_xshm (gboolean use_xshm)
110 {
111 }
112
113 gboolean
114 gdk_get_use_xshm (void)
115 {
116   return GDK_DISPLAY_X11 (gdk_display_get_default ())->use_xshm;
117 }
118
119 static GdkGrabStatus
120 gdk_x11_convert_grab_status (gint status)
121 {
122   switch (status)
123     {
124     case GrabSuccess:
125       return GDK_GRAB_SUCCESS;
126     case AlreadyGrabbed:
127       return GDK_GRAB_ALREADY_GRABBED;
128     case GrabInvalidTime:
129       return GDK_GRAB_INVALID_TIME;
130     case GrabNotViewable:
131       return GDK_GRAB_NOT_VIEWABLE;
132     case GrabFrozen:
133       return GDK_GRAB_FROZEN;
134     }
135
136   g_assert_not_reached();
137
138   return 0;
139 }
140
141 static void
142 has_pointer_grab_callback (GdkDisplay *display,
143                            gpointer data,
144                            gulong serial)
145 {
146   _gdk_display_pointer_grab_update (display, serial);
147 }
148
149 GdkGrabStatus
150 _gdk_windowing_pointer_grab (GdkWindow *window,
151                              GdkWindow *native,
152                              gboolean owner_events,
153                              GdkEventMask event_mask,
154                              GdkWindow *confine_to,
155                              GdkCursor *cursor,
156                              guint32 time)
157 {
158   gint return_val;
159   GdkCursorPrivate *cursor_private;
160   GdkDisplayX11 *display_x11;
161   guint xevent_mask;
162   Window xwindow;
163   Window xconfine_to;
164   Cursor xcursor;
165   int i;
166
167   if (confine_to)
168     confine_to = _gdk_window_get_impl_window (confine_to);
169
170   display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (native));
171
172   cursor_private = (GdkCursorPrivate*) cursor;
173
174   xwindow = GDK_WINDOW_XID (native);
175
176   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
177     xconfine_to = None;
178   else
179     xconfine_to = GDK_WINDOW_XID (confine_to);
180
181   if (!cursor)
182     xcursor = None;
183   else
184     {
185       _gdk_x11_cursor_update_theme (cursor);
186       xcursor = cursor_private->xcursor;
187     }
188
189   xevent_mask = 0;
190   for (i = 0; i < _gdk_nenvent_masks; i++)
191     {
192       if (event_mask & (1 << (i + 1)))
193         xevent_mask |= _gdk_event_mask_table[i];
194     }
195
196   /* We don't want to set a native motion hint mask, as we're emulating motion
197    * hints. If we set a native one we just wouldn't get any events.
198    */
199   xevent_mask &= ~PointerMotionHintMask;
200
201   return_val = _gdk_input_grab_pointer (window,
202                                         native,
203                                         owner_events,
204                                         event_mask,
205                                         confine_to,
206                                         time);
207
208   if (return_val == GrabSuccess ||
209       G_UNLIKELY (!display_x11->trusted_client && return_val == AlreadyGrabbed))
210     {
211       if (!GDK_WINDOW_DESTROYED (native))
212         {
213 #ifdef G_ENABLE_DEBUG
214           if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
215             return_val = GrabSuccess;
216           else
217 #endif
218             return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (native),
219                                        xwindow,
220                                        owner_events,
221                                        xevent_mask,
222                                        GrabModeAsync, GrabModeAsync,
223                                        xconfine_to,
224                                        xcursor,
225                                        time);
226         }
227       else
228         return_val = AlreadyGrabbed;
229     }
230
231   if (return_val == GrabSuccess)
232     _gdk_x11_roundtrip_async (GDK_DISPLAY_OBJECT (display_x11),
233                               has_pointer_grab_callback,
234                               NULL);
235
236   return gdk_x11_convert_grab_status (return_val);
237 }
238
239 /*
240  *--------------------------------------------------------------
241  * gdk_keyboard_grab
242  *
243  *   Grabs the keyboard to a specific window
244  *
245  * Arguments:
246  *   "window" is the window which will receive the grab
247  *   "owner_events" specifies whether events will be reported as is,
248  *     or relative to "window"
249  *   "time" specifies the time
250  *
251  * Results:
252  *
253  * Side effects:
254  *   requires a corresponding call to gdk_keyboard_ungrab
255  *
256  *--------------------------------------------------------------
257  */
258
259 GdkGrabStatus
260 gdk_keyboard_grab (GdkWindow *     window,
261                    gboolean        owner_events,
262                    guint32         time)
263 {
264   gint return_val;
265   unsigned long serial;
266   GdkDisplay *display;
267   GdkDisplayX11 *display_x11;
268   GdkWindow *native;
269
270   g_return_val_if_fail (window != NULL, 0);
271   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
272
273   native = gdk_window_get_toplevel (window);
274
275   /* TODO: What do we do for offscreens and  children? We need to proxy the grab somehow */
276   if (!GDK_IS_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (native)->impl))
277     return GDK_GRAB_SUCCESS;
278
279   display = GDK_WINDOW_DISPLAY (native);
280   display_x11 = GDK_DISPLAY_X11 (display);
281
282   serial = NextRequest (GDK_WINDOW_XDISPLAY (native));
283
284   if (!GDK_WINDOW_DESTROYED (native))
285     {
286 #ifdef G_ENABLE_DEBUG
287       if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
288         return_val = GrabSuccess;
289       else
290 #endif
291         return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (native),
292                                     GDK_WINDOW_XID (native),
293                                     owner_events,
294                                     GrabModeAsync, GrabModeAsync,
295                                     time);
296         if (G_UNLIKELY (!display_x11->trusted_client && 
297                         return_val == AlreadyGrabbed))
298           /* we can't grab the keyboard, but we can do a GTK-local grab */
299           return_val = GrabSuccess;
300     }
301   else
302     return_val = AlreadyGrabbed;
303
304   if (return_val == GrabSuccess)
305     _gdk_display_set_has_keyboard_grab (display,
306                                         window, native,
307                                         owner_events,
308                                         serial, time);
309
310   return gdk_x11_convert_grab_status (return_val);
311 }
312
313 /**
314  * _gdk_xgrab_check_unmap:
315  * @window: a #GdkWindow
316  * @serial: serial from Unmap event (or from NextRequest(display)
317  *   if the unmap is being done by this client.)
318  * 
319  * Checks to see if an unmap request or event causes the current
320  * grab window to become not viewable, and if so, clear the
321  * the pointer we keep to it.
322  **/
323 void
324 _gdk_xgrab_check_unmap (GdkWindow *window,
325                         gulong     serial)
326 {
327   GdkDisplay *display = gdk_drawable_get_display (window);
328
329   _gdk_display_end_pointer_grab (display, serial, window, TRUE);
330
331   if (display->keyboard_grab.window &&
332       serial >= display->keyboard_grab.serial)
333     {
334       GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
335       GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->keyboard_grab.window);
336
337       while (tmp && tmp != private)
338         tmp = tmp->parent;
339
340       if (tmp)
341         _gdk_display_unset_has_keyboard_grab (display, TRUE);
342     }
343 }
344
345 /**
346  * _gdk_xgrab_check_destroy:
347  * @window: a #GdkWindow
348  * 
349  * Checks to see if window is the current grab window, and if
350  * so, clear the current grab window.
351  **/
352 void
353 _gdk_xgrab_check_destroy (GdkWindow *window)
354 {
355   GdkDisplay *display = gdk_drawable_get_display (window);
356   GdkPointerGrabInfo *grab;
357
358   /* Make sure there is no lasting grab in this native
359      window */
360   grab = _gdk_display_get_last_pointer_grab (display);
361   if (grab && grab->native_window == window)
362     {
363       /* We don't know the actual serial to end, but it
364          doesn't really matter as this only happens
365          after we get told of the destroy from the
366          server so we know its ended in the server,
367          just make sure its ended. */
368       grab->serial_end = grab->serial_start;
369       grab->implicit_ungrab = TRUE;
370     }
371   
372   if (window == display->keyboard_grab.native_window &&
373       display->keyboard_grab.window != NULL)
374     _gdk_display_unset_has_keyboard_grab (display, TRUE);
375 }
376
377 void
378 _gdk_windowing_display_set_sm_client_id (GdkDisplay  *display,
379                                          const gchar *sm_client_id)
380 {
381   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
382
383   if (display->closed)
384     return;
385
386   if (sm_client_id && strcmp (sm_client_id, ""))
387     {
388       XChangeProperty (display_x11->xdisplay, display_x11->leader_window,
389                        gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"),
390                        XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id,
391                        strlen (sm_client_id));
392     }
393   else
394     XDeleteProperty (display_x11->xdisplay, display_x11->leader_window,
395                      gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"));
396 }
397
398 /* Close all open displays
399  */
400 void
401 _gdk_windowing_exit (void)
402 {
403   GSList *tmp_list = _gdk_displays;
404     
405   while (tmp_list)
406     {
407       XCloseDisplay (GDK_DISPLAY_XDISPLAY (tmp_list->data));
408       
409       tmp_list = tmp_list->next;
410   }
411 }
412
413 /*
414  *--------------------------------------------------------------
415  * gdk_x_error
416  *
417  *   The X error handling routine.
418  *
419  * Arguments:
420  *   "display" is the X display the error orignated from.
421  *   "error" is the XErrorEvent that we are handling.
422  *
423  * Results:
424  *   Either we were expecting some sort of error to occur,
425  *   in which case we set the "_gdk_error_code" flag, or this
426  *   error was unexpected, in which case we will print an
427  *   error message and exit. (Since trying to continue will
428  *   most likely simply lead to more errors).
429  *
430  * Side effects:
431  *
432  *--------------------------------------------------------------
433  */
434
435 static int
436 gdk_x_error (Display     *display,
437              XErrorEvent *error)
438 {
439   if (error->error_code)
440     {
441       if (_gdk_error_warnings)
442         {
443           gchar buf[64];
444           gchar *msg;
445           
446           XGetErrorText (display, error->error_code, buf, 63);
447
448           msg =
449             g_strdup_printf ("The program '%s' received an X Window System error.\n"
450                              "This probably reflects a bug in the program.\n"
451                              "The error was '%s'.\n"
452                              "  (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
453                              "  (Note to programmers: normally, X errors are reported asynchronously;\n"
454                              "   that is, you will receive the error a while after causing it.\n"
455                              "   To debug your program, run it with the --sync command line\n"
456                              "   option to change this behavior. You can then get a meaningful\n"
457                              "   backtrace from your debugger if you break on the gdk_x_error() function.)",
458                              g_get_prgname (),
459                              buf,
460                              error->serial, 
461                              error->error_code, 
462                              error->request_code,
463                              error->minor_code);
464           
465 #ifdef G_ENABLE_DEBUG     
466           g_error ("%s", msg);
467 #else /* !G_ENABLE_DEBUG */
468           g_fprintf (stderr, "%s\n", msg);
469
470           exit (1);
471 #endif /* G_ENABLE_DEBUG */
472         }
473       _gdk_error_code = error->error_code;
474     }
475   
476   return 0;
477 }
478
479 /*
480  *--------------------------------------------------------------
481  * gdk_x_io_error
482  *
483  *   The X I/O error handling routine.
484  *
485  * Arguments:
486  *   "display" is the X display the error orignated from.
487  *
488  * Results:
489  *   An X I/O error basically means we lost our connection
490  *   to the X server. There is not much we can do to
491  *   continue, so simply print an error message and exit.
492  *
493  * Side effects:
494  *
495  *--------------------------------------------------------------
496  */
497
498 static int
499 gdk_x_io_error (Display *display)
500 {
501   /* This is basically modelled after the code in XLib. We need
502    * an explicit error handler here, so we can disable our atexit()
503    * which would otherwise cause a nice segfault.
504    * We fprintf(stderr, instead of g_warning() because g_warning()
505    * could possibly be redirected to a dialog
506    */
507   if (errno == EPIPE)
508     {
509       g_fprintf (stderr,
510                "The application '%s' lost its connection to the display %s;\n"
511                "most likely the X server was shut down or you killed/destroyed\n"
512                "the application.\n",
513                g_get_prgname (),
514                display ? DisplayString (display) : gdk_get_display_arg_name ());
515     }
516   else
517     {
518       g_fprintf (stderr, "%s: Fatal IO error %d (%s) on X server %s.\n",
519                g_get_prgname (),
520                errno, g_strerror (errno),
521                display ? DisplayString (display) : gdk_get_display_arg_name ());
522     }
523
524   exit(1);
525 }
526
527 /*************************************************************
528  * gdk_error_trap_push:
529  *     Push an error trap. X errors will be trapped until
530  *     the corresponding gdk_error_pop(), which will return
531  *     the error code, if any.
532  *   arguments:
533  *     
534  *   results:
535  *************************************************************/
536
537 void
538 gdk_error_trap_push (void)
539 {
540   GSList *node;
541   GdkErrorTrap *trap;
542
543   if (gdk_error_trap_free_list)
544     {
545       node = gdk_error_trap_free_list;
546       gdk_error_trap_free_list = gdk_error_trap_free_list->next;
547     }
548   else
549     {
550       node = g_slist_alloc ();
551       node->data = g_new (GdkErrorTrap, 1);
552     }
553
554   node->next = gdk_error_traps;
555   gdk_error_traps = node;
556   
557   trap = node->data;
558   trap->old_handler = XSetErrorHandler (gdk_x_error);
559   trap->error_code = _gdk_error_code;
560   trap->error_warnings = _gdk_error_warnings;
561
562   _gdk_error_code = 0;
563   _gdk_error_warnings = 0;
564 }
565
566 /*************************************************************
567  * gdk_error_trap_pop:
568  *     Pop an error trap added with gdk_error_push()
569  *   arguments:
570  *     
571  *   results:
572  *     0, if no error occured, otherwise the error code.
573  *************************************************************/
574
575 gint
576 gdk_error_trap_pop (void)
577 {
578   GSList *node;
579   GdkErrorTrap *trap;
580   gint result;
581
582   g_return_val_if_fail (gdk_error_traps != NULL, 0);
583
584   node = gdk_error_traps;
585   gdk_error_traps = gdk_error_traps->next;
586
587   node->next = gdk_error_trap_free_list;
588   gdk_error_trap_free_list = node;
589   
590   result = _gdk_error_code;
591   
592   trap = node->data;
593   _gdk_error_code = trap->error_code;
594   _gdk_error_warnings = trap->error_warnings;
595   XSetErrorHandler (trap->old_handler);
596   
597   return result;
598 }
599
600 gchar *
601 gdk_get_display (void)
602 {
603   return g_strdup (gdk_display_get_name (gdk_display_get_default ()));
604 }
605
606 /**
607  * _gdk_send_xevent:
608  * @display: #GdkDisplay which @window is on
609  * @window: window ID to which to send the event
610  * @propagate: %TRUE if the event should be propagated if the target window
611  *             doesn't handle it.
612  * @event_mask: event mask to match against, or 0 to send it to @window
613  *              without regard to event masks.
614  * @event_send: #XEvent to send
615  * 
616  * Send an event, like XSendEvent(), but trap errors and check
617  * the result.
618  * 
619  * Return value: %TRUE if sending the event succeeded.
620  **/
621 gint 
622 _gdk_send_xevent (GdkDisplay *display,
623                   Window      window, 
624                   gboolean    propagate, 
625                   glong       event_mask,
626                   XEvent     *event_send)
627 {
628   gboolean result;
629
630   if (display->closed)
631     return FALSE;
632
633   gdk_error_trap_push ();
634   result = XSendEvent (GDK_DISPLAY_XDISPLAY (display), window, 
635                        propagate, event_mask, event_send);
636   XSync (GDK_DISPLAY_XDISPLAY (display), False);
637   
638   if (gdk_error_trap_pop ())
639     return FALSE;
640  
641   return result;
642 }
643
644 void
645 _gdk_region_get_xrectangles (const GdkRegion *region,
646                              gint             x_offset,
647                              gint             y_offset,
648                              XRectangle     **rects,
649                              gint            *n_rects)
650 {
651   XRectangle *rectangles = g_new (XRectangle, region->numRects);
652   GdkRegionBox *boxes = region->rects;
653   gint i;
654   
655   for (i = 0; i < region->numRects; i++)
656     {
657       rectangles[i].x = CLAMP (boxes[i].x1 + x_offset, G_MINSHORT, G_MAXSHORT);
658       rectangles[i].y = CLAMP (boxes[i].y1 + y_offset, G_MINSHORT, G_MAXSHORT);
659       rectangles[i].width = CLAMP (boxes[i].x2 + x_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].x;
660       rectangles[i].height = CLAMP (boxes[i].y2 + y_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].y;
661     }
662
663   *rects = rectangles;
664   *n_rects = region->numRects;
665 }
666
667 /**
668  * gdk_x11_grab_server:
669  * 
670  * Call gdk_x11_display_grab() on the default display. 
671  * To ungrab the server again, use gdk_x11_ungrab_server(). 
672  *
673  * gdk_x11_grab_server()/gdk_x11_ungrab_server() calls can be nested.
674  **/ 
675 void
676 gdk_x11_grab_server (void)
677 {
678   gdk_x11_display_grab (gdk_display_get_default ());
679 }
680
681 /**
682  * gdk_x11_ungrab_server:
683  *
684  * Ungrab the default display after it has been grabbed with 
685  * gdk_x11_grab_server(). 
686  **/
687 void
688 gdk_x11_ungrab_server (void)
689 {
690   gdk_x11_display_ungrab (gdk_display_get_default ());
691 }
692
693 /**
694  * gdk_x11_get_default_screen:
695  * 
696  * Gets the default GTK+ screen number.
697  * 
698  * Return value: returns the screen number specified by
699  *   the --display command line option or the DISPLAY environment
700  *   variable when gdk_init() calls XOpenDisplay().
701  **/
702 gint
703 gdk_x11_get_default_screen (void)
704 {
705   return gdk_screen_get_number (gdk_screen_get_default ());
706 }
707
708 /**
709  * gdk_x11_get_default_root_xwindow:
710  * 
711  * Gets the root window of the default screen 
712  * (see gdk_x11_get_default_screen()).  
713  * 
714  * Return value: an Xlib <type>Window</type>.
715  **/
716 Window
717 gdk_x11_get_default_root_xwindow (void)
718 {
719   return GDK_SCREEN_XROOTWIN (gdk_screen_get_default ());
720 }
721
722 /**
723  * gdk_x11_get_default_xdisplay:
724  * 
725  * Gets the default GTK+ display.
726  * 
727  * Return value: the Xlib <type>Display*</type> for the display
728  * specified in the <option>--display</option> command line option 
729  * or the <envar>DISPLAY</envar> environment variable.
730  **/
731 Display *
732 gdk_x11_get_default_xdisplay (void)
733 {
734   return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
735 }
736
737 #define __GDK_MAIN_X11_C__
738 #include "gdkaliasdef.c"