diff options
Diffstat (limited to 'gdk/gxid.c')
-rw-r--r-- | gdk/gxid.c | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/gdk/gxid.c b/gdk/gxid.c new file mode 100644 index 0000000000..219c08bfe0 --- /dev/null +++ b/gdk/gxid.c @@ -0,0 +1,844 @@ +/* + * gxid version 0.3 + * + * Copyright 1997 Owen Taylor <owt1@cornell.edu> +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <X11/Xlib.h> +#include <X11/extensions/XInput.h> + +#include "gxid_proto.h" + +/* #define DEBUG_CLIENTS */ +/* #define DEBUG_EVENTS */ + +char *program_name; +Display *dpy; +Window root_window; /* default root window of dpy */ +int port = 0; /* port to listen on */ +int socket_fd = 0; /* file descriptor of socket */ +typedef struct GxidWindow_ GxidWindow; + +typedef struct GxidDevice_ GxidDevice; +struct GxidDevice_ { + XID id; + int exclusive; + int ispointer; + + XDevice *xdevice; + int motionnotify_type; + int changenotify_type; +}; + +struct GxidWindow_ { + Window xwindow; + /* Immediate child of root that is ancestor of window */ + Window root_child; + int num_devices; + GxidDevice **devices; +}; + +GxidDevice **devices = NULL; +int num_devices = 0; +GxidWindow **windows = NULL; +int num_windows = 0; + +void +handler(int signal) +{ + fprintf(stderr,"%s: dying on signal %d\n",program_name,signal); + if (socket_fd) + close(socket_fd); + exit(1); +} + +void +init_socket() +{ + struct sockaddr_in sin; + + socket_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if (socket_fd < 0) + { + fprintf (stderr, "%s: error getting socket\n", + program_name); + exit(1); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = INADDR_ANY; + + if (bind(socket_fd,(struct sockaddr *)(&sin), + sizeof(struct sockaddr_in)) < 0) + { + fprintf (stderr,"%s: cannot bind to port %d\n", + program_name,port); + exit(1); + } + + if (listen(socket_fd,5) < 0) + { + fprintf (stderr,"%s: error listening on socket\n", + program_name); + exit(1); + }; +} + +#define NUM_EVENTC 2 +static void +enable_device(GxidDevice *dev) +{ + XEventClass xevc[NUM_EVENTC]; + int num_eventc = NUM_EVENTC; + int i,j; + + if (!dev->xdevice) + { + if (dev->ispointer) return; + + dev->xdevice = XOpenDevice(dpy, dev->id); + if (!dev->xdevice) return; + + DeviceMotionNotify (dev->xdevice, dev->motionnotify_type, + xevc[0]); + ChangeDeviceNotify (dev->xdevice, dev->changenotify_type, + xevc[1]); + + /* compress out zero event classes */ + for (i=0,j=0;i<NUM_EVENTC;i++) + { + if (xevc[i]) { + xevc[j] = xevc[i]; + j++; + } + } + num_eventc = j; + + XSelectExtensionEvent (dpy, root_window, xevc, num_eventc); + } +} + +/* switch the core pointer from whatever it is now to something else, + return true on success, false otherwise */ +static int +switch_core_pointer() +{ + GxidDevice *old_pointer = 0; + GxidDevice *new_pointer = 0; + int result; + int i; + + for (i=0;i<num_devices;i++) + { + if (devices[i]->ispointer) + old_pointer = devices[i]; + else + if (!new_pointer && !devices[i]->exclusive) + new_pointer = devices[i]; + } + + if (!old_pointer || !new_pointer) + return 0; + +#ifdef DEBUG_EVENTS + fprintf(stderr,"gxid: Switching core from %ld to %ld\n", + old_pointer->id,new_pointer->id); +#endif + result = XChangePointerDevice(dpy,new_pointer->xdevice, 0, 1); + if (result != Success) + { + fprintf(stderr,"gxid: Error %d switching core from %ld to %ld\n", + result, old_pointer->id, new_pointer->id); + } + else + { + new_pointer->ispointer = 1; + old_pointer->ispointer = 0; + if (!old_pointer->xdevice) + enable_device(old_pointer); + } + + return 1; +} + +void +disable_device(GxidDevice *dev) +{ + if (dev->xdevice) + { + if (dev->ispointer) + return; + XCloseDevice(dpy,dev->xdevice); + dev->xdevice = 0; + } +} + +GxidDevice * +init_device(XDeviceInfo *xdevice) +{ + GxidDevice *dev = (GxidDevice *)malloc(sizeof(GxidDevice)); + XAnyClassPtr class; + int num_axes, i; + + dev->id = xdevice->id; + dev->exclusive = 0; + dev->xdevice = NULL; + + dev->ispointer = (xdevice->use == IsXPointer); + + /* step through the classes */ + + num_axes = 0; + class = xdevice->inputclassinfo; + for (i=0;i<xdevice->num_classes;i++) + { + if (class->class == ValuatorClass) + { + XValuatorInfo *xvi = (XValuatorInfo *)class; + num_axes = xvi->num_axes; + } + class = (XAnyClassPtr)(((char *)class) + class->length); + } + + /* return NULL if insufficient axes */ + if (num_axes < 2) + { + free((void *)dev); + return NULL; + } + + if (!dev->ispointer) + enable_device(dev); + return dev; +} + +void +init_xinput() +{ + char **extensions; + XDeviceInfo *xdevices; + int num_xdevices; + int num_extensions; + int i; + + extensions = XListExtensions(dpy, &num_extensions); + for (i = 0; i < num_extensions && + (strcmp(extensions[i], "XInputExtension") != 0); i++); + XFreeExtensionList(extensions); + if (i == num_extensions) /* XInput extension not found */ + { + fprintf(stderr,"XInput extension not found\n"); + exit(1); + } + + xdevices = XListInputDevices(dpy, &num_xdevices); + devices = (GxidDevice **)malloc(num_xdevices * sizeof(GxidDevice *)); + + num_devices = 0; + for(i=0; i<num_xdevices; i++) + { + GxidDevice *dev = init_device(&xdevices[i]); + if (dev) + devices[num_devices++] = dev; + } + XFreeDeviceList(xdevices); +} + +/* If this routine needs fixing, the corresponding routine + in gdkinputgxi.h will need it too. */ + +Window +gxi_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; +} + +int +handle_claim_device(GxidClaimDevice *msg) +{ + int i,j; + XID devid = ntohl(msg->device); + XID winid = ntohl(msg->window); + int exclusive = ntohl(msg->exclusive); + GxidDevice *device = NULL; + GxidWindow *window = NULL; + +#ifdef DEBUG_CLIENTS + fprintf(stderr,"device %ld claimed (window 0x%lx)\n",devid,winid); +#endif + + for (i=0;i<num_devices;i++) + { + if (devices[i]->id == devid) + { + device = devices[i]; + break; + } + } + if (!device) + { + fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid); + return GXID_RETURN_ERROR; + } + + if (device->exclusive) + { + /* already in use */ + fprintf(stderr, + "%s: Device %ld already claimed in exclusive mode\n", + program_name,devid); + return GXID_RETURN_ERROR; + } + + if (exclusive) + { + for (i=0;i<num_windows;i++) + { + for (j=0;j<windows[i]->num_devices;j++) + if (windows[i]->devices[j]->id == devid) + { + /* already in use */ + fprintf(stderr, + "%s: Can't establish exclusive use of device %ld\n", + program_name,devid); + return GXID_RETURN_ERROR; + } + } + if (device->ispointer) + if (!switch_core_pointer()) + { + fprintf(stderr, + "%s: Can't free up core pointer %ld\n", + program_name,devid); + return GXID_RETURN_ERROR; + } + + device->exclusive = 1; + disable_device(device); + XSelectInput(dpy,winid,StructureNotifyMask); + } + else /* !exclusive */ + { + /* FIXME: this is a bit improper. We probably should do this only + when a window is first claimed. But we might be fooled if + an old client died without releasing it's windows. So until + we look for client-window closings, do it here + + (We do look for closings now...) + */ + + XSelectInput(dpy,winid,EnterWindowMask|StructureNotifyMask); + } + + for (i=0;i<num_windows;i++) + { + if (windows[i]->xwindow == winid) + { + window = windows[i]; + break; + } + } + + /* Create window structure if no devices have been previously + claimed on it */ + if (!window) + { + num_windows++; + windows = (GxidWindow **)realloc(windows, + sizeof(GxidWindow*)*num_windows); + window = (GxidWindow *)malloc(sizeof(GxidWindow)); + windows[num_windows-1] = window; + + window->xwindow = winid; + window->root_child = gxi_find_root_child(dpy,winid); + window->num_devices = 0; + window->devices = 0; + } + + + for (i=0;i<window->num_devices;i++) + { + if (window->devices[i] == device) + return GXID_RETURN_OK; + } + + window->num_devices++; + window->devices = (GxidDevice **)realloc(window->devices, + sizeof(GxidDevice*)*num_devices); + /* we need add the device to the window */ + window->devices[i] = device; + + return GXID_RETURN_OK; +} + +int +handle_release_device(GxidReleaseDevice *msg) +{ + int i,j; + XID devid = ntohl(msg->device); + XID winid = ntohl(msg->window); + + GxidDevice *device = NULL; + +#ifdef DEBUG_CLIENTS + fprintf(stderr,"device %ld released (window 0x%lx)\n",devid,winid); +#endif + + for (i=0;i<num_devices;i++) + { + if (devices[i]->id == devid) + { + device = devices[i]; + break; + } + } + if (!device) + { + fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid); + return GXID_RETURN_ERROR; + } + + for (i=0;i<num_windows;i++) + { + GxidWindow *w = windows[i]; + + if (w->xwindow == winid) + for (j=0;j<w->num_devices;j++) + if (w->devices[j]->id == devid) + { + if (j<w->num_devices-1) + w->devices[j] = w->devices[w->num_devices-1]; + w->num_devices--; + + if (w->num_devices == 0) + { + if (i<num_windows-1) + windows[i] = windows[num_windows-1]; + num_windows--; + + free((void *)w); + /* FIXME: should we deselect input? But what + what if window is already destroyed */ + } + + if (device->exclusive) + { + device->exclusive = 0; + enable_device(device); + } + return GXID_RETURN_OK; + } + } + + /* device/window combination not found */ + fprintf(stderr, + "%s: Device %ld not claimed for window 0x%lx\n", + program_name,devid,winid); + return GXID_RETURN_ERROR; +} + +void +handle_connection() +{ + GxidMessage msg; + GxidU32 type; + int length; + GxidI32 retval; + + int conn_fd; + struct sockaddr_in sin; + int sin_length; + int count; + + sin_length = sizeof(struct sockaddr_in); + conn_fd = accept(socket_fd,(struct sockaddr *)&sin,&sin_length); + if (conn_fd < 0) + { + fprintf(stderr,"%s: Error accepting connection\n", + program_name); + exit(1); + } + + /* read type and length of message */ + + count = read(conn_fd,(char *)&msg,2*sizeof(GxidU32)); + if (count != 2*sizeof(GxidU32)) + { + fprintf(stderr,"%s: Error reading message header\n", + program_name); + close(conn_fd); + return; + } + type = ntohl(msg.any.type); + length = ntohl(msg.any.length); + + /* read rest of message */ + + if (length > sizeof(GxidMessage)) + { + fprintf(stderr,"%s: Bad message length\n", + program_name); + close(conn_fd); + return; + } + + count = read(conn_fd,2*sizeof(GxidU32) + (char *)&msg, + length - 2*sizeof(GxidU32)); + if (count != length - 2*sizeof(GxidU32)) + { + fprintf(stderr,"%s: Error reading message body\n", + program_name); + close(conn_fd); + return; + } + + switch (type) + { + case GXID_CLAIM_DEVICE: + retval = handle_claim_device((GxidClaimDevice *)&msg); + break; + case GXID_RELEASE_DEVICE: + retval = handle_release_device((GxidReleaseDevice *)&msg); + break; + default: + fprintf(stderr,"%s: Unknown message type: %ld (ignoring)\n", + program_name,type); + close(conn_fd); + return; + } + + count = write(conn_fd,&retval,sizeof(GxidI32)); + if (count != sizeof(GxidI32)) + { + fprintf(stderr,"%s: Error writing return code\n", + program_name); + } + + close(conn_fd); +} + +void +handle_motion_notify(XDeviceMotionEvent *event) +{ + int i,j; + GxidDevice *old_device = NULL; + GxidDevice *new_device = NULL; + Window w, root, child; + int root_x, root_y, x, y, mask; + + for (j=0;j<num_devices;j++) + { + if (devices[j]->ispointer) + old_device = devices[j]; + if (devices[j]->id == event->deviceid) + new_device = devices[j]; + } + + if (new_device && !new_device->exclusive && !new_device->ispointer) + { + /* make sure we aren't stealing the pointer back from a slow + client */ + child = root_window; + do + { + w = child; + /* FIXME: this fails disasterously if child vanishes between + calls. (Which is prone to happening since we get events + on root just as the client exits) */ + + XQueryPointer(dpy,w,&root,&child,&root_x,&root_y, + &x,&y,&mask); + } + while (child != None); + + for (i=0;i<num_windows;i++) + if (windows[i]->xwindow == w) + for (j=0;j<windows[i]->num_devices;j++) + if (windows[i]->devices[j] == new_device) + return; + + /* FIXME: do something smarter with axes */ + XChangePointerDevice(dpy,new_device->xdevice, 0, 1); + new_device->ispointer = 1; + + old_device->ispointer = 0; + if (!old_device->xdevice) + enable_device(old_device); + } +} + +void +handle_change_notify(XChangeDeviceNotifyEvent *event) +{ + int j; + GxidDevice *old_device = NULL; + GxidDevice *new_device = NULL; + + + for (j=0;j<num_devices;j++) + { + if (devices[j]->ispointer) + old_device = devices[j]; + if (devices[j]->id == event->deviceid) + new_device = devices[j]; + } + +#ifdef DEBUG_EVENTS + fprintf(stderr,"gxid: ChangeNotify event; old = %ld; new = %ld\n", + old_device->id, new_device->id); +#endif + + if (old_device != new_device) + { + new_device->ispointer = 1; + + old_device->ispointer = 0; + if (!old_device->xdevice) + enable_device(old_device); + } +} + +void +handle_enter_notify(XEnterWindowEvent *event, GxidWindow *window) +{ + int i; + GxidDevice *old_pointer = NULL; + for (i=0;i<num_devices;i++) + { + if (devices[i]->ispointer) + { + old_pointer = devices[i]; + break; + } + } + +#ifdef DEBUG_EVENTS + fprintf(stderr,"gxid: Enter event; oldpointer = %ld\n", + old_pointer->id); +#endif + + if (old_pointer) + for (i=0;i<window->num_devices;i++) + { + if (window->devices[i] == old_pointer) + { + switch_core_pointer(); + break; + } + } +} + +void +handle_destroy_notify(XDestroyWindowEvent *event) +{ + int i,j; + + for (i=0;i<num_windows;i++) + if (windows[i]->xwindow == event->window) + { + GxidWindow *w = windows[i]; + + for (j=0;j<w->num_devices;j++) + { +#ifdef DEBUG_CLIENTS + fprintf(stderr,"device %ld released on destruction of window 0x%lx.\n", + w->devices[j]->id,w->xwindow); +#endif + + if (w->devices[j]->exclusive) + { + w->devices[j]->exclusive = 0; + enable_device(devices[j]); + } + } + + if (i<num_windows-1) + windows[i] = windows[num_windows-1]; + num_windows--; + + if (w->devices) + free((void *)w->devices); + free((void *)w); + /* FIXME: should we deselect input? But what + what if window is already destroyed */ + + return; + } +} + +void +handle_xevent() +{ + int i; + XEvent event; + + XNextEvent (dpy, &event); + +#ifdef DEBUG_EVENTS + fprintf(stderr,"Event - type = %d; window = 0x%lx\n", + event.type,event.xany.window); +#endif + + if (event.type == ConfigureNotify) + { +#ifdef DEBUG_EVENTS + XConfigureEvent *xce = (XConfigureEvent *)&event; + fprintf(stderr," configureNotify: window = 0x%lx\n",xce->window); +#endif + } + else if (event.type == EnterNotify) + { + /* pointer entered a claimed window */ + for (i=0;i<num_windows;i++) + { + if (event.xany.window == windows[i]->xwindow) + handle_enter_notify((XEnterWindowEvent *)&event,windows[i]); + } + } + else if (event.type == DestroyNotify) + { + /* A claimed window was destroyed */ + for (i=0;i<num_windows;i++) + { + if (event.xany.window == windows[i]->xwindow) + handle_destroy_notify((XDestroyWindowEvent *)&event); + } + } + else + for (i=0;i<num_devices;i++) + { + if (event.type == devices[i]->motionnotify_type) + { + handle_motion_notify((XDeviceMotionEvent *)&event); + break; + } + else if (event.type == devices[i]->changenotify_type) + { + handle_change_notify((XChangeDeviceNotifyEvent *)&event); + break; + } + } +} + +void +usage() +{ + fprintf(stderr,"Usage: %s [-d display] [-p --gxid-port port]\n", + program_name); + exit(1); +} + +int +main(int argc, char **argv) +{ + int i; + char *display_name = NULL; + fd_set readfds; + + program_name = argv[0]; + + for (i=1;i<argc;i++) + { + if (!strcmp(argv[i],"-d")) + { + if (++i >= argc) usage(); + display_name = argv[i]; + } + else if (!strcmp(argv[i],"--gxid-port") || + !strcmp(argv[i],"-p")) + { + if (++i >= argc) usage(); + port = atoi(argv[i]); + break; + } + else + usage(); + } + + if (!port) + { + char *t = getenv("GXID_PORT"); + if (t) + port = atoi(t); + else + port = 6951; + } + /* set up a signal handler so we can clean up if killed */ + + signal(SIGTERM,handler); + signal(SIGINT,handler); + + /* initialize the X connection */ + + dpy = XOpenDisplay (display_name); + if (!dpy) + { + fprintf (stderr, "%s: unable to open display '%s'\n", + program_name, XDisplayName (display_name)); + exit (1); + } + + root_window = DefaultRootWindow(dpy); + + /* We'll want to do this in the future if we are to support + gxid monitoring visibility information for clients */ +#if 0 + XSelectInput(dpy,root_window,SubstructureNotifyMask); +#endif + init_xinput(); + + /* set up our server connection */ + + init_socket(); + + /* main loop */ + + if (XPending(dpy)) /* this seems necessary to get things + in sync */ + handle_xevent(); + while (1) + { + + FD_ZERO(&readfds); + FD_SET(ConnectionNumber(dpy),&readfds); + FD_SET(socket_fd,&readfds); + + if (select(8*sizeof(readfds),&readfds, + (fd_set *)0,(fd_set *)0, (struct timeval *)0) < 0) + { + fprintf(stderr,"Error in select\n"); + exit(1); + } + + if (FD_ISSET(socket_fd,&readfds)) + handle_connection(socket_fd); + + while (XPending(dpy)) + handle_xevent(); + } + + XCloseDisplay (dpy); + exit (0); +} |