/* 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 #include "gdkinputprivate.h" #include "gdkx.h" /* #define DEBUG_SWITCHING */ #include /* Forward declarations */ static void gdk_input_gxi_select_notify (GdkDevicePrivate *gdkdev); static gint gdk_input_is_extension_device (GdkDevicePrivate *device_private); static void gdk_input_gxi_update_device (GdkDevicePrivate *gdkdev); static Window gdk_input_find_root_child(Display *dpy, Window w); static void gdk_input_compute_obscuring(GdkInputWindow *input_window); static gint gdk_input_is_obscured(GdkInputWindow *input_window, gdouble x, gdouble y); /* Local variables */ static GdkDevicePrivate *gdk_input_current_device; static GdkDevicePrivate *gdk_input_core_pointer; void gdk_input_init(void) { GList *tmp_list; gdk_input_ignore_core = FALSE; gdk_input_core_pointer = NULL; if (!gdk_input_gxid_host) { gdk_input_gxid_host = getenv("GXID_HOST"); } if (!gdk_input_gxid_port) { char *t = getenv("GXID_PORT"); if (t) gdk_input_gxid_port = atoi(t); } gdk_input_common_init(TRUE); /* find initial core pointer */ for (tmp_list = gdk_input_devices; tmp_list; tmp_list = tmp_list->next) { GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)tmp_list->data; if (gdk_input_is_extension_device (gdkdev)) { gdk_input_gxi_select_notify (gdkdev); } else { if (!GDK_IS_CORE (gdkdev)) gdk_input_core_pointer = gdkdev; } } } static void gdk_input_gxi_select_notify (GdkDevicePrivate *gdkdev) { XEventClass class; ChangeDeviceNotify (gdkdev->xdevice, gdkdev->changenotify_type, class); XSelectExtensionEvent (gdk_display, gdk_root_window, &class, 1); } /* Set the core pointer. Device should already be enabled. */ static gint gdk_input_gxi_set_core_pointer(GdkDevicePrivate *gdkdev) { gint x_axis = -1; gint y_axis = -1; gint i; g_return_val_if_fail(gdkdev->xdevice,FALSE); for (i=0; iinfo.num_axes; i++) { if (gdkdev->info.axes[i].use == GDK_AXIS_X) x_axis = i; else if (gdkdev->info.axes[i].use == GDK_AXIS_Y) y_axis = i; } g_return_val_if_fail (x_axis != -1 && y_axis != -1,FALSE); /* core_pointer might not be up to date so we check with the server before change the pointer */ if (!gdk_input_is_extension_device (gdkdev)) { #if 0 if (gdkdev != gdk_input_core_pointer) g_warning("core pointer inconsistency"); #endif return TRUE; } if ( XChangePointerDevice(gdk_display,gdkdev->xdevice, x_axis, y_axis) != Success ) { return FALSE; } else { gdk_input_gxi_update_device (gdk_input_core_pointer); gdk_input_core_pointer = gdkdev; return TRUE; } } /* FIXME, merge with the XFree implementation */ gboolean gdk_device_set_mode (GdkDevice *device, GdkInputMode mode) { GList *tmp_list; GdkDevicePrivate *gdkdev; GdkInputMode old_mode; GdkInputWindow *input_window; if (GDK_IS_CORE (device)) return FALSE; gdkdev = (GdkDevicePrivate *)device; if (device->mode == mode) return TRUE; old_mode = device->mode; device->mode = mode; if (old_mode != GDK_MODE_DISABLED) { for (tmp_list = gdk_input_windows; tmp_list; tmp_list = tmp_list->next) { input_window = (GdkInputWindow *)tmp_list->data; if (input_window->mode != GDK_EXTENSION_EVENTS_CURSOR) _gdk_input_disable_window (input_window->window, gdkdev); } } if (mode != GDK_MODE_DISABLED) { for (tmp_list = gdk_input_windows; tmp_list; tmp_list = tmp_list->next) { input_window = (GdkInputWindow *)tmp_list->data; if (input_window->mode != GDK_EXTENSION_EVENTS_CURSOR) if (!_gdk_input_enable_window(input_window->window, gdkdev)) { gdk_device_set_mode (device, old_mode); return FALSE; } } } return TRUE; } gint gdk_input_is_extension_device (GdkDevicePrivate *private) { XDeviceInfo *devices; int num_devices, loop; if (GDK_IS_CORE (private)) return FALSE; devices = XListInputDevices(gdk_display, &num_devices); for(loop=0; loopdeviceid) && (devices[loop].use == IsXExtensionDevice)) { XFreeDeviceList(devices); return TRUE; } } XFreeDeviceList(devices); return FALSE; } void _gdk_input_configure_event (XConfigureEvent *xevent, GdkWindow *window) { GdkInputWindow *input_window; gint root_x, root_y; input_window = gdk_input_window_find(window); g_return_if_fail (input_window != NULL); gdk_input_get_root_relative_geometry(gdk_display,GDK_WINDOW_XWINDOW(window), &root_x, &root_y, NULL, NULL); input_window->root_x = root_x; input_window->root_y = root_y; gdk_input_compute_obscuring(input_window); } void _gdk_input_enter_event (XCrossingEvent *xevent, GdkWindow *window) { GdkInputWindow *input_window; input_window = gdk_input_window_find(window); g_return_if_fail (input_window != NULL); gdk_input_compute_obscuring(input_window); } gint _gdk_input_other_event (GdkEvent *event, XEvent *xevent, GdkWindow *window) { GdkInputWindow *input_window; GdkWindowImplX11 *impl; GdkDevicePrivate *gdkdev; gint return_val; input_window = gdk_input_window_find(window); g_return_val_if_fail (window != NULL, -1); impl = GDK_WINDOW_IMPL_X11 (((GdkWindowObject *) input_window->window)->impl); /* This is a sort of a hack, as there isn't any XDeviceAnyEvent - but it's potentially faster than scanning through the types of every device. If we were deceived, then it won't match any of the types for the device anyways */ gdkdev = gdk_input_find_device(((XDeviceButtonEvent *)xevent)->deviceid); if (!gdkdev) { return -1; /* we don't handle it - not an XInput event */ } if (gdkdev->info.mode == GDK_MODE_DISABLED || input_window->mode == GDK_EXTENSION_EVENTS_CURSOR) return FALSE; if (gdkdev != gdk_input_current_device && xevent->type != gdkdev->changenotify_type) { gdk_input_current_device = gdkdev; } return_val = gdk_input_common_other_event (event, xevent, input_window, gdkdev); if (return_val > 0 && event->type == GDK_MOTION_NOTIFY && (!gdkdev->button_state) && (!input_window->grabbed) && ((event->motion.x < 0) || (event->motion.y < 0) || (event->motion.x > impl->width) || (event->motion.y > impl->height) || gdk_input_is_obscured(input_window,event->motion.x,event->motion.y))) { #ifdef DEBUG_SWITCHING g_print("gdkinput: Setting core pointer to %d on motion at (%f,%f)\n", gdkdev->info.deviceid,event->motion.x,event->motion.y); g_print(" window geometry is: %dx%d\n", ((GdkWindowPrivate *)window)->width, ((GdkWindowPrivate *)window)->height); #endif gdk_input_gxi_set_core_pointer(gdkdev); return FALSE; } else return return_val; } static void gdk_input_gxi_update_device (GdkDevicePrivate *gdkdev) { GList *t; if (gdk_input_is_extension_device (gdkdev)) { if (!gdkdev->xdevice) { gdkdev->xdevice = XOpenDevice(gdk_display, gdkdev->deviceid); gdk_input_gxi_select_notify (gdkdev); gdkdev->needs_update = 1; } if (gdkdev->needs_update && gdkdev->xdevice) { for (t = gdk_input_windows; t; t = t->next) gdk_input_common_select_events (((GdkInputWindow *)t->data)->window, gdkdev); gdkdev->needs_update = 0; } } } gint _gdk_input_window_none_event (GdkEvent *event, XEvent *xevent) { GdkDevicePrivate *gdkdev = gdk_input_find_device(((XDeviceButtonEvent *)xevent)->deviceid); if (!gdkdev) { return -1; /* we don't handle it - not an XInput event */ } if (xevent->type == gdkdev->changenotify_type) { if (gdk_input_core_pointer != gdkdev) { #ifdef DEBUG_SWITCHING g_print("ChangeNotify from %d to %d:\n", gdk_input_core_pointer->info.deviceid, gdkdev->info.deviceid); #endif gdk_input_gxi_update_device (gdk_input_core_pointer); gdk_input_core_pointer = gdkdev; } } return FALSE; } gboolean _gdk_input_enable_window (GdkWindow *window, GdkDevicePrivate *gdkdev) { GdkInputWindow *input_window; input_window = gdk_input_window_find (window); g_return_val_if_fail (input_window != NULL, FALSE); if (!gdkdev->claimed) { if (gxid_claim_device(gdk_input_gxid_host, gdk_input_gxid_port, gdkdev->deviceid, GDK_WINDOW_XWINDOW(window), FALSE) != GXID_RETURN_OK) { g_warning("Could not get device (is gxid running?)\n"); return FALSE; } gdkdev->claimed = TRUE; } if (gdkdev->xdevice && gdkdev != gdk_input_core_pointer) gdk_input_common_select_events(window, gdkdev); else gdkdev->needs_update = TRUE; return TRUE; } gboolean _gdk_input_disable_window (GdkWindow *window, GdkDevicePrivate *gdkdev) { GdkInputWindow *input_window; input_window = gdk_input_window_find (window); g_return_val_if_fail (input_window != NULL, FALSE); if (gdkdev->claimed) { gxid_release_device(gdk_input_gxid_host, gdk_input_gxid_port, gdkdev->deviceid, GDK_WINDOW_XWINDOW(window)); gdkdev->claimed = FALSE; } if (gdkdev->xdevice && gdkdev != gdk_input_core_pointer) gdk_input_common_select_events(window, gdkdev); else gdkdev->needs_update = TRUE; return TRUE; } static gint gdk_input_is_obscured(GdkInputWindow *input_window, gdouble x, gdouble y) { int i; for (i=0;inum_obscuring;i++) { GdkRectangle *rect = &input_window->obscuring[i]; if ((x >= rect->x) && (y >= rect->y) && (x < rect->x + rect->width) && (y < rect->y + rect->height)) return TRUE; } return FALSE; } /* If this routine needs fixing, the corresponding routine in gxid.c will need it too. */ static Window gdk_input_find_root_child(Display *dpy, Window w) { Window root,parent; Window *children; int nchildren; parent = w; do { w = parent; XQueryTree(dpy,w,&root,&parent,&children,&nchildren); if (children) XFree(children); } while (parent != root); return w; } static void gdk_input_compute_obscuring(GdkInputWindow *input_window) { int i; int x,y,width,height; int xc,yc,widthc,heightc,border_widthc,depthc; Window root,parent; Window *children; int nchildren; Window w = GDK_WINDOW_XWINDOW(input_window->window); Window root_child = gdk_input_find_root_child(gdk_display,w); gdk_input_get_root_relative_geometry(gdk_display,w,&x,&y,&width,&height); input_window->root_x = x; input_window->root_y = y; XQueryTree(gdk_display,GDK_ROOT_WINDOW(), &root,&parent,&children,&nchildren); if (input_window->obscuring) g_free(input_window->obscuring); input_window->obscuring = 0; input_window->num_obscuring = 0; for (i=0;i=nchildren-1) { if (nchildren) XFree(children); return; } input_window->obscuring = g_new(GdkRectangle,(nchildren-i-1)); for (i=i+1;ix ? xc : x; xmax = (xc+widthc)<(x+width) ? xc+widthc : x+width; ymin = yc>y ? yc : y; ymax = (yc+heightc)<(y+height) ? yc+heightc : y+height; if ((xmin < xmax) && (ymin < ymax)) { XWindowAttributes attributes; XGetWindowAttributes(gdk_display,children[i],&attributes); if (attributes.map_state == IsViewable) { GdkRectangle *rect = &input_window->obscuring[input_window->num_obscuring]; /* we store the whole window, not just the obscuring part */ rect->x = xc - x; rect->y = yc - y; rect->width = widthc; rect->height = heightc; input_window->num_obscuring++; } } } if (nchildren) XFree(children); } gint _gdk_input_grab_pointer (GdkWindow * window, gint owner_events, GdkEventMask event_mask, GdkWindow * confine_to, guint32 time) { GList *tmp_list; GdkInputWindow *input_window; GdkDevicePrivate *gdkdev; tmp_list = gdk_input_windows; while (tmp_list) { input_window = (GdkInputWindow *)tmp_list->data; if (input_window->window == window) input_window->grabbed = TRUE; else if (input_window->grabbed) input_window->grabbed = FALSE; tmp_list = tmp_list->next; } tmp_list = gdk_input_devices; while (tmp_list) { gdkdev = (GdkDevicePrivate *)tmp_list->data; if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice && (gdkdev->button_state != 0)) gdkdev->button_state = 0; tmp_list = tmp_list->next; } return Success; } void _gdk_input_ungrab_pointer (guint32 time) { GdkInputWindow *input_window; GList *tmp_list; tmp_list = gdk_input_windows; while (tmp_list) { input_window = (GdkInputWindow *)tmp_list->data; if (input_window->grabbed) input_window->grabbed = FALSE; tmp_list = tmp_list->next; } }