summaryrefslogtreecommitdiff
path: root/gdk/x11/gxid.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdk/x11/gxid.c')
-rw-r--r--gdk/x11/gxid.c844
1 files changed, 844 insertions, 0 deletions
diff --git a/gdk/x11/gxid.c b/gdk/x11/gxid.c
new file mode 100644
index 000000000..219c08bfe
--- /dev/null
+++ b/gdk/x11/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);
+}