/* GDK - The GIMP Drawing Kit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_XKB #include #endif #include "gdk.h" #include "gdkx.h" #include "gdkasync.h" #include "gdkdisplay-x11.h" #include "gdkinternals.h" #include "gdkintl.h" #include "gdkregion-generic.h" #include "gdkinputprivate.h" #include "gdkalias.h" typedef struct _GdkPredicate GdkPredicate; typedef struct _GdkErrorTrap GdkErrorTrap; struct _GdkPredicate { GdkEventFunc func; gpointer data; }; struct _GdkErrorTrap { int (*old_handler) (Display *, XErrorEvent *); gint error_warnings; gint error_code; }; /* * Private function declarations */ #ifndef HAVE_XCONVERTCASE static void gdkx_XConvertCase (KeySym symbol, KeySym *lower, KeySym *upper); #define XConvertCase gdkx_XConvertCase #endif static int gdk_x_error (Display *display, XErrorEvent *error); static int gdk_x_io_error (Display *display); /* Private variable declarations */ static GSList *gdk_error_traps = NULL; /* List of error traps */ static GSList *gdk_error_trap_free_list = NULL; /* Free list */ const GOptionEntry _gdk_windowing_args[] = { { "sync", 0, 0, G_OPTION_ARG_NONE, &_gdk_synchronize, /* Description of --sync in --help output */ N_("Make X calls synchronous"), NULL }, { NULL } }; void _gdk_windowing_init (void) { _gdk_x11_initialize_locale (); XSetErrorHandler (gdk_x_error); XSetIOErrorHandler (gdk_x_io_error); _gdk_selection_property = gdk_atom_intern_static_string ("GDK_SELECTION"); } void gdk_set_use_xshm (gboolean use_xshm) { } gboolean gdk_get_use_xshm (void) { return GDK_DISPLAY_X11 (gdk_display_get_default ())->use_xshm; } static GdkGrabStatus gdk_x11_convert_grab_status (gint status) { switch (status) { case GrabSuccess: return GDK_GRAB_SUCCESS; case AlreadyGrabbed: return GDK_GRAB_ALREADY_GRABBED; case GrabInvalidTime: return GDK_GRAB_INVALID_TIME; case GrabNotViewable: return GDK_GRAB_NOT_VIEWABLE; case GrabFrozen: return GDK_GRAB_FROZEN; } g_assert_not_reached(); return 0; } static void generate_grab_broken_event (GdkWindow *window, gboolean keyboard, gboolean implicit, GdkWindow *grab_window) { g_return_if_fail (window != NULL); if (!GDK_WINDOW_DESTROYED (window)) { GdkEvent event; event.type = GDK_GRAB_BROKEN; event.grab_broken.window = window; event.grab_broken.send_event = 0; event.grab_broken.keyboard = keyboard; event.grab_broken.implicit = implicit; event.grab_broken.grab_window = grab_window; gdk_event_put (&event); } } struct XPointerGrabInfo { GdkDisplay *display; GdkWindow *window; GdkWindow *native_window; gboolean owner_events; gulong serial; guint event_mask; guint32 time; }; static void has_pointer_grab_callback (gpointer _data) { struct XPointerGrabInfo *data = _data; _gdk_display_set_has_pointer_grab (data->display, data->window, data->native_window, data->owner_events, data->event_mask, data->serial, data->time, FALSE); g_free (data); } /* *-------------------------------------------------------------- * gdk_pointer_grab * * Grabs the pointer to a specific window * * Arguments: * "window" is the window which will receive the grab * "owner_events" specifies whether events will be reported as is, * or relative to "window" * "event_mask" masks only interesting events * "confine_to" limits the cursor movement to the specified window * "cursor" changes the cursor for the duration of the grab * "time" specifies the time * * Results: * * Side effects: * requires a corresponding call to gdk_pointer_ungrab * *-------------------------------------------------------------- */ GdkGrabStatus gdk_pointer_grab (GdkWindow * window, gboolean owner_events, GdkEventMask event_mask, GdkWindow * confine_to, GdkCursor * cursor, guint32 time) { gint return_val; GdkCursorPrivate *cursor_private; GdkWindow *native; GdkDisplayX11 *display_x11; guint xevent_mask; Window xwindow; Window xconfine_to; Cursor xcursor; unsigned long serial; int i; g_return_val_if_fail (window != NULL, 0); g_return_val_if_fail (GDK_IS_WINDOW (window), 0); g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0); native = gdk_window_get_toplevel (window); /* TODO: What do we do for offscreens and their children? We need to proxy the grab somehow */ if (!GDK_IS_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (native)->impl)) return GDK_GRAB_SUCCESS; if (confine_to) confine_to = _gdk_window_get_impl_window (confine_to); display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (native)); cursor_private = (GdkCursorPrivate*) cursor; xwindow = GDK_WINDOW_XID (native); serial = NextRequest (GDK_WINDOW_XDISPLAY (native)); if (!confine_to || GDK_WINDOW_DESTROYED (confine_to)) xconfine_to = None; else xconfine_to = GDK_WINDOW_XID (confine_to); if (!cursor) xcursor = None; else { _gdk_x11_cursor_update_theme (cursor); xcursor = cursor_private->xcursor; } xevent_mask = 0; for (i = 0; i < _gdk_nenvent_masks; i++) { if (event_mask & (1 << (i + 1))) xevent_mask |= _gdk_event_mask_table[i]; } /* We don't want to set a native motion hint mask, as we're emulating motion * hints. If we set a native one we just wouldn't get any events. */ xevent_mask &= ~PointerMotionHintMask; return_val = _gdk_input_grab_pointer (native, owner_events, event_mask, confine_to, time); if (return_val == GrabSuccess || G_UNLIKELY (!display_x11->trusted_client && return_val == AlreadyGrabbed)) { if (!GDK_WINDOW_DESTROYED (native)) { #ifdef G_ENABLE_DEBUG if (_gdk_debug_flags & GDK_DEBUG_NOGRABS) return_val = GrabSuccess; else #endif return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (native), xwindow, owner_events, xevent_mask, GrabModeAsync, GrabModeAsync, xconfine_to, xcursor, time); } else return_val = AlreadyGrabbed; } if (return_val == GrabSuccess) { struct XPointerGrabInfo *data; data = g_new (struct XPointerGrabInfo, 1); data->display = GDK_DISPLAY_OBJECT (display_x11); data->window = window; data->native_window = native; data->owner_events = owner_events; data->event_mask = event_mask; data->serial = serial; data->time = time; _gdk_x11_roundtrip_async (data->display, has_pointer_grab_callback, data); } return gdk_x11_convert_grab_status (return_val); } /* *-------------------------------------------------------------- * gdk_keyboard_grab * * Grabs the keyboard to a specific window * * Arguments: * "window" is the window which will receive the grab * "owner_events" specifies whether events will be reported as is, * or relative to "window" * "time" specifies the time * * Results: * * Side effects: * requires a corresponding call to gdk_keyboard_ungrab * *-------------------------------------------------------------- */ GdkGrabStatus gdk_keyboard_grab (GdkWindow * window, gboolean owner_events, guint32 time) { gint return_val; unsigned long serial; GdkDisplay *display; GdkDisplayX11 *display_x11; GdkWindow *native; g_return_val_if_fail (window != NULL, 0); g_return_val_if_fail (GDK_IS_WINDOW (window), 0); native = gdk_window_get_toplevel (window); /* TODO: What do we do for offscreens and children? We need to proxy the grab somehow */ if (!GDK_IS_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (native)->impl)) return GDK_GRAB_SUCCESS; display = GDK_WINDOW_DISPLAY (native); display_x11 = GDK_DISPLAY_X11 (display); serial = NextRequest (GDK_WINDOW_XDISPLAY (native)); if (!GDK_WINDOW_DESTROYED (native)) { #ifdef G_ENABLE_DEBUG if (_gdk_debug_flags & GDK_DEBUG_NOGRABS) return_val = GrabSuccess; else #endif return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (native), GDK_WINDOW_XID (native), owner_events, GrabModeAsync, GrabModeAsync, time); if (G_UNLIKELY (!display_x11->trusted_client && return_val == AlreadyGrabbed)) /* we can't grab the keyboard, but we can do a GTK-local grab */ return_val = GrabSuccess; } else return_val = AlreadyGrabbed; if (return_val == GrabSuccess) _gdk_display_set_has_keyboard_grab (display, window, native, owner_events, serial, time); return gdk_x11_convert_grab_status (return_val); } /** * _gdk_xgrab_check_unmap: * @window: a #GdkWindow * @serial: serial from Unmap event (or from NextRequest(display) * if the unmap is being done by this client.) * * Checks to see if an unmap request or event causes the current * grab window to become not viewable, and if so, clear the * the pointer we keep to it. **/ void _gdk_xgrab_check_unmap (GdkWindow *window, gulong serial) { GdkDisplay *display = gdk_drawable_get_display (window); if (display->pointer_grab.window && serial >= display->pointer_grab.serial) { GdkWindowObject *private = GDK_WINDOW_OBJECT (window); GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->pointer_grab.native_window); while (tmp && tmp != private) tmp = tmp->parent; if (tmp) _gdk_display_unset_has_pointer_grab (display, TRUE, FALSE, GDK_CURRENT_TIME); } if (display->keyboard_grab.window && serial >= display->keyboard_grab.serial) { GdkWindowObject *private = GDK_WINDOW_OBJECT (window); GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->keyboard_grab.window); while (tmp && tmp != private) tmp = tmp->parent; if (tmp) _gdk_display_unset_has_keyboard_grab (display, TRUE); } } /** * _gdk_xgrab_check_destroy: * @window: a #GdkWindow * * Checks to see if window is the current grab window, and if * so, clear the current grab window. **/ void _gdk_xgrab_check_destroy (GdkWindow *window) { GdkDisplay *display = gdk_drawable_get_display (window); GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); if (window == display->pointer_grab.native_window && display->pointer_grab.window != NULL) _gdk_display_unset_has_pointer_grab (display, TRUE, FALSE, GDK_CURRENT_TIME); if (window == display->keyboard_grab.native_window && display->keyboard_grab.window != NULL) _gdk_display_unset_has_keyboard_grab (display, TRUE); } #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ GDK_BUTTON2_MASK | \ GDK_BUTTON3_MASK | \ GDK_BUTTON4_MASK | \ GDK_BUTTON5_MASK) /** * _gdk_xgrab_check_button_event: * @window: a #GdkWindow * @event: an XEvent of type ButtonPress or ButtonRelease * * Checks to see if a button event starts or ends an implicit grab. **/ void _gdk_xgrab_check_button_event (GdkWindow *window, XEvent *xevent) { GdkDisplay *display = gdk_drawable_get_display (window); /* track implicit grabs for button presses */ switch (xevent->type) { case ButtonPress: if (!display->pointer_grab.window) { _gdk_display_set_has_pointer_grab (display, window, window, FALSE, gdk_window_get_events (window), xevent->xany.serial, xevent->xbutton.time, TRUE); } break; case ButtonRelease: if (display->pointer_grab.window && display->pointer_grab.implicit && (xevent->xbutton.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (xevent->xbutton.button - 1))) == 0) { _gdk_display_unset_has_pointer_grab (display, TRUE, TRUE, xevent->xbutton.time); } break; default: g_assert_not_reached (); } } void _gdk_windowing_display_set_sm_client_id (GdkDisplay *display, const gchar *sm_client_id) { GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); if (display->closed) return; if (sm_client_id && strcmp (sm_client_id, "")) { XChangeProperty (display_x11->xdisplay, display_x11->leader_window, gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"), XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id, strlen (sm_client_id)); } else XDeleteProperty (display_x11->xdisplay, display_x11->leader_window, gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID")); } /* Close all open displays */ void _gdk_windowing_exit (void) { GSList *tmp_list = _gdk_displays; while (tmp_list) { XCloseDisplay (GDK_DISPLAY_XDISPLAY (tmp_list->data)); tmp_list = tmp_list->next; } } /* *-------------------------------------------------------------- * gdk_x_error * * The X error handling routine. * * Arguments: * "display" is the X display the error orignated from. * "error" is the XErrorEvent that we are handling. * * Results: * Either we were expecting some sort of error to occur, * in which case we set the "_gdk_error_code" flag, or this * error was unexpected, in which case we will print an * error message and exit. (Since trying to continue will * most likely simply lead to more errors). * * Side effects: * *-------------------------------------------------------------- */ static int gdk_x_error (Display *display, XErrorEvent *error) { if (error->error_code) { if (_gdk_error_warnings) { gchar buf[64]; gchar *msg; XGetErrorText (display, error->error_code, buf, 63); msg = g_strdup_printf ("The program '%s' received an X Window System error.\n" "This probably reflects a bug in the program.\n" "The error was '%s'.\n" " (Details: serial %ld error_code %d request_code %d minor_code %d)\n" " (Note to programmers: normally, X errors are reported asynchronously;\n" " that is, you will receive the error a while after causing it.\n" " To debug your program, run it with the --sync command line\n" " option to change this behavior. You can then get a meaningful\n" " backtrace from your debugger if you break on the gdk_x_error() function.)", g_get_prgname (), buf, error->serial, error->error_code, error->request_code, error->minor_code); #ifdef G_ENABLE_DEBUG g_error ("%s", msg); #else /* !G_ENABLE_DEBUG */ g_fprintf (stderr, "%s\n", msg); exit (1); #endif /* G_ENABLE_DEBUG */ } _gdk_error_code = error->error_code; } return 0; } /* *-------------------------------------------------------------- * gdk_x_io_error * * The X I/O error handling routine. * * Arguments: * "display" is the X display the error orignated from. * * Results: * An X I/O error basically means we lost our connection * to the X server. There is not much we can do to * continue, so simply print an error message and exit. * * Side effects: * *-------------------------------------------------------------- */ static int gdk_x_io_error (Display *display) { /* This is basically modelled after the code in XLib. We need * an explicit error handler here, so we can disable our atexit() * which would otherwise cause a nice segfault. * We fprintf(stderr, instead of g_warning() because g_warning() * could possibly be redirected to a dialog */ if (errno == EPIPE) { g_fprintf (stderr, "The application '%s' lost its connection to the display %s;\n" "most likely the X server was shut down or you killed/destroyed\n" "the application.\n", g_get_prgname (), display ? DisplayString (display) : gdk_get_display_arg_name ()); } else { g_fprintf (stderr, "%s: Fatal IO error %d (%s) on X server %s.\n", g_get_prgname (), errno, g_strerror (errno), display ? DisplayString (display) : gdk_get_display_arg_name ()); } exit(1); } /************************************************************* * gdk_error_trap_push: * Push an error trap. X errors will be trapped until * the corresponding gdk_error_pop(), which will return * the error code, if any. * arguments: * * results: *************************************************************/ void gdk_error_trap_push (void) { GSList *node; GdkErrorTrap *trap; if (gdk_error_trap_free_list) { node = gdk_error_trap_free_list; gdk_error_trap_free_list = gdk_error_trap_free_list->next; } else { node = g_slist_alloc (); node->data = g_new (GdkErrorTrap, 1); } node->next = gdk_error_traps; gdk_error_traps = node; trap = node->data; trap->old_handler = XSetErrorHandler (gdk_x_error); trap->error_code = _gdk_error_code; trap->error_warnings = _gdk_error_warnings; _gdk_error_code = 0; _gdk_error_warnings = 0; } /************************************************************* * gdk_error_trap_pop: * Pop an error trap added with gdk_error_push() * arguments: * * results: * 0, if no error occured, otherwise the error code. *************************************************************/ gint gdk_error_trap_pop (void) { GSList *node; GdkErrorTrap *trap; gint result; g_return_val_if_fail (gdk_error_traps != NULL, 0); node = gdk_error_traps; gdk_error_traps = gdk_error_traps->next; node->next = gdk_error_trap_free_list; gdk_error_trap_free_list = node; result = _gdk_error_code; trap = node->data; _gdk_error_code = trap->error_code; _gdk_error_warnings = trap->error_warnings; XSetErrorHandler (trap->old_handler); return result; } gchar * gdk_get_display (void) { return g_strdup (gdk_display_get_name (gdk_display_get_default ())); } /** * _gdk_send_xevent: * @display: #GdkDisplay which @window is on * @window: window ID to which to send the event * @propagate: %TRUE if the event should be propagated if the target window * doesn't handle it. * @event_mask: event mask to match against, or 0 to send it to @window * without regard to event masks. * @event_send: #XEvent to send * * Send an event, like XSendEvent(), but trap errors and check * the result. * * Return value: %TRUE if sending the event succeeded. **/ gint _gdk_send_xevent (GdkDisplay *display, Window window, gboolean propagate, glong event_mask, XEvent *event_send) { gboolean result; if (display->closed) return FALSE; gdk_error_trap_push (); result = XSendEvent (GDK_DISPLAY_XDISPLAY (display), window, propagate, event_mask, event_send); XSync (GDK_DISPLAY_XDISPLAY (display), False); if (gdk_error_trap_pop ()) return FALSE; return result; } void _gdk_region_get_xrectangles (const GdkRegion *region, gint x_offset, gint y_offset, XRectangle **rects, gint *n_rects) { XRectangle *rectangles = g_new (XRectangle, region->numRects); GdkRegionBox *boxes = region->rects; gint i; for (i = 0; i < region->numRects; i++) { rectangles[i].x = CLAMP (boxes[i].x1 + x_offset, G_MINSHORT, G_MAXSHORT); rectangles[i].y = CLAMP (boxes[i].y1 + y_offset, G_MINSHORT, G_MAXSHORT); rectangles[i].width = CLAMP (boxes[i].x2 + x_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].x; rectangles[i].height = CLAMP (boxes[i].y2 + y_offset, G_MINSHORT, G_MAXSHORT) - rectangles[i].y; } *rects = rectangles; *n_rects = region->numRects; } /** * gdk_x11_grab_server: * * Call gdk_x11_display_grab() on the default display. * To ungrab the server again, use gdk_x11_ungrab_server(). * * gdk_x11_grab_server()/gdk_x11_ungrab_server() calls can be nested. **/ void gdk_x11_grab_server (void) { gdk_x11_display_grab (gdk_display_get_default ()); } /** * gdk_x11_ungrab_server: * * Ungrab the default display after it has been grabbed with * gdk_x11_grab_server(). **/ void gdk_x11_ungrab_server (void) { gdk_x11_display_ungrab (gdk_display_get_default ()); } /** * gdk_x11_get_default_screen: * * Gets the default GTK+ screen number. * * Return value: returns the screen number specified by * the --display command line option or the DISPLAY environment * variable when gdk_init() calls XOpenDisplay(). **/ gint gdk_x11_get_default_screen (void) { return gdk_screen_get_number (gdk_screen_get_default ()); } /** * gdk_x11_get_default_root_xwindow: * * Gets the root window of the default screen * (see gdk_x11_get_default_screen()). * * Return value: an Xlib Window. **/ Window gdk_x11_get_default_root_xwindow (void) { return GDK_SCREEN_XROOTWIN (gdk_screen_get_default ()); } /** * gdk_x11_get_default_xdisplay: * * Gets the default GTK+ display. * * Return value: the Xlib Display* for the display * specified in the command line option * or the DISPLAY environment variable. **/ Display * gdk_x11_get_default_xdisplay (void) { return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); } #define __GDK_MAIN_X11_C__ #include "gdkaliasdef.c"