]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkmain-x11.c
gtk/docs/debugging.txt, gdk/gdk.c, gdk/gdkinternals.h,
[~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 <ctype.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <errno.h>
36
37 #include <X11/Xatom.h>
38 #include <X11/Xlib.h>
39 #include <X11/Xutil.h>
40
41 #ifdef HAVE_XKB
42 #include <X11/XKBlib.h>
43 #endif
44
45 #include "gdk.h"
46
47 #include "gdkprivate-x11.h"
48 #include "gdkinternals.h"
49 #include "gdkregion-generic.h"
50 #include "gdkinputprivate.h"
51
52 #include <pango/pangox.h>
53
54 typedef struct _GdkPredicate  GdkPredicate;
55 typedef struct _GdkErrorTrap  GdkErrorTrap;
56
57 struct _GdkPredicate
58 {
59   GdkEventFunc func;
60   gpointer data;
61 };
62
63 struct _GdkErrorTrap
64 {
65   gint error_warnings;
66   gint error_code;
67 };
68
69 /* 
70  * Private function declarations
71  */
72
73 #ifndef HAVE_XCONVERTCASE
74 static void      gdkx_XConvertCase      (KeySym        symbol,
75                                          KeySym       *lower,
76                                          KeySym       *upper);
77 #define XConvertCase gdkx_XConvertCase
78 #endif
79
80 static int          gdk_x_error                  (Display     *display, 
81                                                   XErrorEvent *error);
82 static int          gdk_x_io_error               (Display     *display);
83
84 /* Private variable declarations
85  */
86 static int gdk_initialized = 0;                     /* 1 if the library is initialized,
87                                                      * 0 otherwise.
88                                                      */
89
90 static gint autorepeat;
91 static gboolean gdk_synchronize = FALSE;
92
93 #ifdef G_ENABLE_DEBUG
94 static const GDebugKey gdk_debug_keys[] = {
95   {"events",        GDK_DEBUG_EVENTS},
96   {"misc",          GDK_DEBUG_MISC},
97   {"dnd",           GDK_DEBUG_DND},
98   {"xim",           GDK_DEBUG_XIM}
99 };
100
101 static const int gdk_ndebug_keys = sizeof(gdk_debug_keys)/sizeof(GDebugKey);
102
103 #endif /* G_ENABLE_DEBUG */
104
105 GdkArgDesc _gdk_windowing_args[] = {
106   { "display",     GDK_ARG_STRING,   &gdk_display_name,    (GdkArgFunc)NULL   },
107   { "sync",        GDK_ARG_BOOL,     &gdk_synchronize,     (GdkArgFunc)NULL   },
108   { "no-xshm",     GDK_ARG_NOBOOL,   &gdk_use_xshm,        (GdkArgFunc)NULL   },
109   { "class",       GDK_ARG_STRING,   &gdk_progclass,       (GdkArgFunc)NULL   },
110   { "gxid-host",   GDK_ARG_STRING,   &gdk_input_gxid_host, (GdkArgFunc)NULL   },
111   { "gxid-port",   GDK_ARG_INT,      &gdk_input_gxid_port, (GdkArgFunc)NULL   },
112   { NULL }
113 };
114
115 gboolean
116 _gdk_windowing_init_check (int argc, char **argv)
117 {
118   XKeyboardState keyboard_state;
119   XClassHint *class_hint;
120   guint pid;
121   
122   XSetErrorHandler (gdk_x_error);
123   XSetIOErrorHandler (gdk_x_io_error);
124   
125   gdk_display = XOpenDisplay (gdk_display_name);
126   if (!gdk_display)
127     return FALSE;
128   
129   if (gdk_synchronize)
130     XSynchronize (gdk_display, True);
131   
132   gdk_screen = DefaultScreen (gdk_display);
133   gdk_root_window = RootWindow (gdk_display, gdk_screen);
134   
135   gdk_leader_window = XCreateSimpleWindow(gdk_display, gdk_root_window,
136                                           10, 10, 10, 10, 0, 0 , 0);
137   class_hint = XAllocClassHint();
138   class_hint->res_name = g_get_prgname ();
139   if (gdk_progclass == NULL)
140     {
141       gdk_progclass = g_strdup (g_get_prgname ());
142       gdk_progclass[0] = toupper (gdk_progclass[0]);
143     }
144   class_hint->res_class = gdk_progclass;
145   XmbSetWMProperties (gdk_display, gdk_leader_window,
146                       NULL, NULL, argv, argc, 
147                       NULL, NULL, class_hint);
148   XFree (class_hint);
149
150   pid = getpid();
151   XChangeProperty (gdk_display, gdk_leader_window,
152                    gdk_atom_intern ("_NET_WM_PID", FALSE),
153                    XA_CARDINAL, 32,
154                    PropModeReplace,
155                    (guchar *)&pid, 1);
156   
157   gdk_wm_delete_window = gdk_atom_intern ("WM_DELETE_WINDOW", FALSE);
158   gdk_wm_take_focus = gdk_atom_intern ("WM_TAKE_FOCUS", FALSE);
159   gdk_wm_protocols = gdk_atom_intern ("WM_PROTOCOLS", FALSE);
160   gdk_wm_window_protocols[0] = gdk_wm_delete_window;
161   gdk_wm_window_protocols[1] = gdk_wm_take_focus;
162   gdk_wm_window_protocols[2] = gdk_atom_intern ("_NET_WM_PING", FALSE);
163   gdk_selection_property = gdk_atom_intern ("GDK_SELECTION", FALSE);
164   
165   XGetKeyboardControl (gdk_display, &keyboard_state);
166   autorepeat = keyboard_state.global_auto_repeat;
167
168 #ifdef HAVE_XKB
169   {
170     gint xkb_major = XkbMajorVersion;
171     gint xkb_minor = XkbMinorVersion;
172     if (XkbLibraryVersion (&xkb_major, &xkb_minor))
173       {
174         xkb_major = XkbMajorVersion;
175         xkb_minor = XkbMinorVersion;
176             
177         if (XkbQueryExtension (gdk_display, NULL, &_gdk_xkb_event_type, NULL,
178                                &xkb_major, &xkb_minor))
179           {
180             Bool detectable_autorepeat_supported;
181
182             _gdk_use_xkb = TRUE;
183
184             XkbSelectEvents (gdk_display,
185                              XkbUseCoreKbd,
186                              XkbMapNotifyMask | XkbStateNotifyMask,
187                              XkbMapNotifyMask | XkbStateNotifyMask);
188
189             XkbSetDetectableAutoRepeat (gdk_display,
190                                         True,
191                                         &detectable_autorepeat_supported);
192
193             GDK_NOTE (MISC, g_message ("Detectable autorepeat %s.",
194                                        detectable_autorepeat_supported ? "supported" : "not supported"));
195             
196             _gdk_have_xkb_autorepeat = detectable_autorepeat_supported;
197           }
198       }
199   }
200 #endif
201   
202   return TRUE;
203 }
204
205 void
206 gdk_set_use_xshm (gboolean use_xshm)
207 {
208   gdk_use_xshm = use_xshm;
209 }
210
211 gboolean
212 gdk_get_use_xshm (void)
213 {
214   return gdk_use_xshm;
215 }
216
217 static GdkGrabStatus
218 gdk_x11_convert_grab_status (gint status)
219 {
220   switch (status)
221     {
222     case GrabSuccess:
223       return GDK_GRAB_SUCCESS;
224     case AlreadyGrabbed:
225       return GDK_GRAB_ALREADY_GRABBED;
226     case GrabInvalidTime:
227       return GDK_GRAB_INVALID_TIME;
228     case GrabNotViewable:
229       return GDK_GRAB_NOT_VIEWABLE;
230     case GrabFrozen:
231       return GDK_GRAB_FROZEN;
232     }
233
234   g_assert_not_reached();
235
236   return 0;
237 }
238
239 /*
240  *--------------------------------------------------------------
241  * gdk_pointer_grab
242  *
243  *   Grabs the pointer 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  *   "event_mask" masks only interesting events
250  *   "confine_to" limits the cursor movement to the specified window
251  *   "cursor" changes the cursor for the duration of the grab
252  *   "time" specifies the time
253  *
254  * Results:
255  *
256  * Side effects:
257  *   requires a corresponding call to gdk_pointer_ungrab
258  *
259  *--------------------------------------------------------------
260  */
261
262 GdkGrabStatus
263 gdk_pointer_grab (GdkWindow *     window,
264                   gboolean        owner_events,
265                   GdkEventMask    event_mask,
266                   GdkWindow *     confine_to,
267                   GdkCursor *     cursor,
268                   guint32         time)
269 {
270   gint return_val;
271   GdkCursorPrivate *cursor_private;
272   guint xevent_mask;
273   Window xwindow;
274   Window xconfine_to;
275   Cursor xcursor;
276   int i;
277   
278   g_return_val_if_fail (window != NULL, 0);
279   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
280   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
281   
282   cursor_private = (GdkCursorPrivate*) cursor;
283   
284   xwindow = GDK_WINDOW_XID (window);
285   
286   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
287     xconfine_to = None;
288   else
289     xconfine_to = GDK_WINDOW_XID (confine_to);
290   
291   if (!cursor)
292     xcursor = None;
293   else
294     xcursor = cursor_private->xcursor;
295   
296   
297   xevent_mask = 0;
298   for (i = 0; i < gdk_nevent_masks; i++)
299     {
300       if (event_mask & (1 << (i + 1)))
301         xevent_mask |= gdk_event_mask_table[i];
302     }
303   
304   return_val = _gdk_input_grab_pointer (window,
305                                         owner_events,
306                                         event_mask,
307                                         confine_to,
308                                         time);
309
310   if (return_val == GrabSuccess)
311     {
312       if (!GDK_WINDOW_DESTROYED (window))
313         return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (window),
314                                    xwindow,
315                                    owner_events,
316                                    xevent_mask,
317                                    GrabModeAsync, GrabModeAsync,
318                                    xconfine_to,
319                                    xcursor,
320                                    time);
321       else
322         return_val = AlreadyGrabbed;
323     }
324   
325   if (return_val == GrabSuccess)
326     gdk_xgrab_window = (GdkWindowObject *)window;
327
328   return gdk_x11_convert_grab_status (return_val);
329 }
330
331 /*
332  *--------------------------------------------------------------
333  * gdk_pointer_ungrab
334  *
335  *   Releases any pointer grab
336  *
337  * Arguments:
338  *
339  * Results:
340  *
341  * Side effects:
342  *
343  *--------------------------------------------------------------
344  */
345
346 void
347 gdk_pointer_ungrab (guint32 time)
348 {
349   _gdk_input_ungrab_pointer (time);
350   
351   XUngrabPointer (gdk_display, time);
352   gdk_xgrab_window = NULL;
353 }
354
355 /*
356  *--------------------------------------------------------------
357  * gdk_pointer_is_grabbed
358  *
359  *   Tell wether there is an active x pointer grab in effect
360  *
361  * Arguments:
362  *
363  * Results:
364  *
365  * Side effects:
366  *
367  *--------------------------------------------------------------
368  */
369
370 gboolean
371 gdk_pointer_is_grabbed (void)
372 {
373   return gdk_xgrab_window != NULL;
374 }
375
376 /*
377  *--------------------------------------------------------------
378  * gdk_keyboard_grab
379  *
380  *   Grabs the keyboard to a specific window
381  *
382  * Arguments:
383  *   "window" is the window which will receive the grab
384  *   "owner_events" specifies whether events will be reported as is,
385  *     or relative to "window"
386  *   "time" specifies the time
387  *
388  * Results:
389  *
390  * Side effects:
391  *   requires a corresponding call to gdk_keyboard_ungrab
392  *
393  *--------------------------------------------------------------
394  */
395
396 GdkGrabStatus
397 gdk_keyboard_grab (GdkWindow *     window,
398                    gboolean        owner_events,
399                    guint32         time)
400 {
401   gint return_val;
402   
403   g_return_val_if_fail (window != NULL, 0);
404   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
405   
406   if (!GDK_WINDOW_DESTROYED (window))
407     return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (window),
408                                 GDK_WINDOW_XID (window),
409                                 owner_events,
410                                 GrabModeAsync, GrabModeAsync,
411                                 time);
412   else
413     return_val = AlreadyGrabbed;
414
415   return gdk_x11_convert_grab_status (return_val);
416 }
417
418 /*
419  *--------------------------------------------------------------
420  * gdk_keyboard_ungrab
421  *
422  *   Releases any keyboard grab
423  *
424  * Arguments:
425  *
426  * Results:
427  *
428  * Side effects:
429  *
430  *--------------------------------------------------------------
431  */
432
433 void
434 gdk_keyboard_ungrab (guint32 time)
435 {
436   XUngrabKeyboard (gdk_display, time);
437 }
438
439 /*
440  *--------------------------------------------------------------
441  * gdk_screen_width
442  *
443  *   Return the width of the screen.
444  *
445  * Arguments:
446  *
447  * Results:
448  *
449  * Side effects:
450  *
451  *--------------------------------------------------------------
452  */
453
454 gint
455 gdk_screen_width (void)
456 {
457   gint return_val;
458   
459   return_val = DisplayWidth (gdk_display, gdk_screen);
460   
461   return return_val;
462 }
463
464 /*
465  *--------------------------------------------------------------
466  * gdk_screen_height
467  *
468  *   Return the height of the screen.
469  *
470  * Arguments:
471  *
472  * Results:
473  *
474  * Side effects:
475  *
476  *--------------------------------------------------------------
477  */
478
479 gint
480 gdk_screen_height (void)
481 {
482   gint return_val;
483   
484   return_val = DisplayHeight (gdk_display, gdk_screen);
485   
486   return return_val;
487 }
488
489 /*
490  *--------------------------------------------------------------
491  * gdk_screen_width_mm
492  *
493  *   Return the width of the screen in millimeters.
494  *
495  * Arguments:
496  *
497  * Results:
498  *
499  * Side effects:
500  *
501  *--------------------------------------------------------------
502  */
503
504 gint
505 gdk_screen_width_mm (void)
506 {
507   gint return_val;
508   
509   return_val = DisplayWidthMM (gdk_display, gdk_screen);
510   
511   return return_val;
512 }
513
514 /*
515  *--------------------------------------------------------------
516  * gdk_screen_height
517  *
518  *   Return the height of the screen in millimeters.
519  *
520  * Arguments:
521  *
522  * Results:
523  *
524  * Side effects:
525  *
526  *--------------------------------------------------------------
527  */
528
529 gint
530 gdk_screen_height_mm (void)
531 {
532   gint return_val;
533   
534   return_val = DisplayHeightMM (gdk_display, gdk_screen);
535   
536   return return_val;
537 }
538
539 /*
540  *--------------------------------------------------------------
541  * gdk_set_sm_client_id
542  *
543  *   Set the SM_CLIENT_ID property on the WM_CLIENT_LEADER window
544  *   so that the window manager can save our state using the
545  *   X11R6 ICCCM session management protocol. A NULL value should 
546  *   be set following disconnection from the session manager to
547  *   remove the SM_CLIENT_ID property.
548  *
549  * Arguments:
550  * 
551  *   "sm_client_id" specifies the client id assigned to us by the
552  *   session manager or NULL to remove the property.
553  *
554  * Results:
555  *
556  * Side effects:
557  *
558  *--------------------------------------------------------------
559  */
560
561 void
562 gdk_set_sm_client_id (const gchar* sm_client_id)
563 {
564   if (sm_client_id && strcmp (sm_client_id, ""))
565     {
566       XChangeProperty (gdk_display, gdk_leader_window,
567                        gdk_atom_intern ("SM_CLIENT_ID", FALSE),
568                        XA_STRING, 8, PropModeReplace,
569                        sm_client_id, strlen(sm_client_id));
570     }
571   else
572      XDeleteProperty (gdk_display, gdk_leader_window,
573                       gdk_atom_intern ("SM_CLIENT_ID", FALSE));
574 }
575
576 void
577 gdk_beep (void)
578 {
579   XBell(gdk_display, 0);
580 }
581
582 void
583 gdk_windowing_exit (void)
584 {
585   pango_x_shutdown_display (gdk_display);
586   
587   XCloseDisplay (gdk_display);
588 }
589
590 /*
591  *--------------------------------------------------------------
592  * gdk_x_error
593  *
594  *   The X error handling routine.
595  *
596  * Arguments:
597  *   "display" is the X display the error orignated from.
598  *   "error" is the XErrorEvent that we are handling.
599  *
600  * Results:
601  *   Either we were expecting some sort of error to occur,
602  *   in which case we set the "gdk_error_code" flag, or this
603  *   error was unexpected, in which case we will print an
604  *   error message and exit. (Since trying to continue will
605  *   most likely simply lead to more errors).
606  *
607  * Side effects:
608  *
609  *--------------------------------------------------------------
610  */
611
612 static int
613 gdk_x_error (Display     *display,
614              XErrorEvent *error)
615 {
616   if (error->error_code)
617     {
618       if (gdk_error_warnings)
619         {
620           gchar buf[64];
621           gchar *msg;
622           
623           XGetErrorText (display, error->error_code, buf, 63);
624
625           msg =
626             g_strdup_printf ("The program '%s' received an X Window System error.\n"
627                              "This probably reflects a bug in the program.\n"
628                              "The error was '%s'.\n"
629                              "  (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
630                              "  (Note to programmers: normally, X errors are reported asynchronously;\n"
631                              "   that is, you will receive the error a while after causing it.\n"
632                              "   To debug your program, run it with the --sync command line\n"
633                              "   option to change this behavior. You can then get a meaningful\n"
634                              "   backtrace from your debugger if you break on the gdk_x_error() function.)",
635                              g_get_prgname (),
636                              buf,
637                              error->serial, 
638                              error->error_code, 
639                              error->request_code,
640                              error->minor_code);
641           
642 #ifdef G_ENABLE_DEBUG     
643           g_error ("%s", msg);
644 #else /* !G_ENABLE_DEBUG */
645           fprintf (stderr, "%s\n", msg);
646
647           exit(1);
648 #endif /* G_ENABLE_DEBUG */
649         }
650       gdk_error_code = error->error_code;
651     }
652   
653   return 0;
654 }
655
656 /*
657  *--------------------------------------------------------------
658  * gdk_x_io_error
659  *
660  *   The X I/O error handling routine.
661  *
662  * Arguments:
663  *   "display" is the X display the error orignated from.
664  *
665  * Results:
666  *   An X I/O error basically means we lost our connection
667  *   to the X server. There is not much we can do to
668  *   continue, so simply print an error message and exit.
669  *
670  * Side effects:
671  *
672  *--------------------------------------------------------------
673  */
674
675 static int
676 gdk_x_io_error (Display *display)
677 {
678   /* This is basically modelled after the code in XLib. We need
679    * an explicit error handler here, so we can disable our atexit()
680    * which would otherwise cause a nice segfault.
681    * We fprintf(stderr, instead of g_warning() because g_warning()
682    * could possibly be redirected to a dialog
683    */
684   if (errno == EPIPE)
685     {
686       fprintf (stderr,
687                "The application '%s' lost its connection to the display %s;\n"
688                "most likely the X server was shut down or you killed/destroyed\n"
689                "the application.\n",
690                g_get_prgname (),
691                gdk_display ? DisplayString (gdk_display) : gdk_get_display());
692     }
693   else
694     {
695       fprintf (stderr, "%s: Fatal IO error %d (%s) on X server %s.\n",
696                g_get_prgname (),
697                errno, g_strerror (errno),
698                gdk_display ? DisplayString (gdk_display) : gdk_get_display());
699     }
700
701   /* Disable the atexit shutdown for GDK */
702   gdk_initialized = 0;
703   
704   exit(1);
705 }
706
707 gchar *
708 gdk_get_display (void)
709 {
710   return (gchar *)XDisplayName (gdk_display_name);
711 }
712
713 gint 
714 gdk_send_xevent (Window window, gboolean propagate, glong event_mask,
715                  XEvent *event_send)
716 {
717   Status result;
718   gint old_warnings = gdk_error_warnings;
719   
720   gdk_error_code = 0;
721   
722   gdk_error_warnings = 0;
723   result = XSendEvent (gdk_display, window, propagate, event_mask, event_send);
724   XSync (gdk_display, False);
725   gdk_error_warnings = old_warnings;
726   
727   return result && !gdk_error_code;
728 }
729
730 void
731 _gdk_region_get_xrectangles (GdkRegion   *region,
732                              gint         x_offset,
733                              gint         y_offset,
734                              XRectangle **rects,
735                              gint        *n_rects)
736 {
737   XRectangle *rectangles = g_new (XRectangle, region->numRects);
738   GdkRegionBox *boxes = region->rects;
739   gint i;
740   
741   for (i = 0; i < region->numRects; i++)
742     {
743       rectangles[i].x = CLAMP (boxes[i].x1 + x_offset, G_MINSHORT, G_MAXSHORT);
744       rectangles[i].y = CLAMP (boxes[i].y1 + y_offset, G_MINSHORT, G_MAXSHORT);
745       rectangles[i].width = CLAMP (boxes[i].x2 + x_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].x;
746       rectangles[i].height = CLAMP (boxes[i].y2 + y_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].y;
747     }
748
749   *rects = rectangles;
750   *n_rects = region->numRects;
751 }
752
753 /* FIXME put in GdkDisplay */
754 static gint grab_count = 0;
755 void
756 gdk_x11_grab_server (void)
757
758   if (grab_count == 0)
759     XGrabServer (gdk_display);
760   ++grab_count;
761 }
762
763 void
764 gdk_x11_ungrab_server (void)
765 {
766   g_return_if_fail (grab_count > 0);
767   
768   --grab_count;
769   if (grab_count == 0)
770     XUngrabServer (gdk_display);
771 }