4 * Copyright 1997 Owen Taylor <owt1@cornell.edu>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
16 #include <X11/extensions/XInput.h>
18 #include "gxid_proto.h"
20 /* #define DEBUG_CLIENTS */
21 /* #define DEBUG_EVENTS */
25 Window root_window; /* default root window of dpy */
26 int port = 0; /* port to listen on */
27 int socket_fd = 0; /* file descriptor of socket */
28 typedef struct GxidWindow_ GxidWindow;
30 typedef struct GxidDevice_ GxidDevice;
37 int motionnotify_type;
38 int changenotify_type;
43 /* Immediate child of root that is ancestor of window */
49 GxidDevice **devices = NULL;
51 GxidWindow **windows = NULL;
57 fprintf(stderr,"%s: dying on signal %d\n",program_name,signal);
66 struct sockaddr_in sin;
68 socket_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
71 fprintf (stderr, "%s: error getting socket\n",
76 sin.sin_family = AF_INET;
77 sin.sin_port = htons(port);
78 sin.sin_addr.s_addr = INADDR_ANY;
80 if (bind(socket_fd,(struct sockaddr *)(&sin),
81 sizeof(struct sockaddr_in)) < 0)
83 fprintf (stderr,"%s: cannot bind to port %d\n",
88 if (listen(socket_fd,5) < 0)
90 fprintf (stderr,"%s: error listening on socket\n",
98 enable_device(GxidDevice *dev)
100 XEventClass xevc[NUM_EVENTC];
101 int num_eventc = NUM_EVENTC;
106 if (dev->ispointer) return;
108 dev->xdevice = XOpenDevice(dpy, dev->id);
109 if (!dev->xdevice) return;
111 DeviceMotionNotify (dev->xdevice, dev->motionnotify_type,
113 ChangeDeviceNotify (dev->xdevice, dev->changenotify_type,
116 /* compress out zero event classes */
117 for (i=0,j=0;i<NUM_EVENTC;i++)
126 XSelectExtensionEvent (dpy, root_window, xevc, num_eventc);
130 /* switch the core pointer from whatever it is now to something else,
131 return true on success, false otherwise */
133 switch_core_pointer()
135 GxidDevice *old_pointer = 0;
136 GxidDevice *new_pointer = 0;
140 for (i=0;i<num_devices;i++)
142 if (devices[i]->ispointer)
143 old_pointer = devices[i];
145 if (!new_pointer && !devices[i]->exclusive)
146 new_pointer = devices[i];
149 if (!old_pointer || !new_pointer)
153 fprintf(stderr,"gxid: Switching core from %ld to %ld\n",
154 old_pointer->id,new_pointer->id);
156 result = XChangePointerDevice(dpy,new_pointer->xdevice, 0, 1);
157 if (result != Success)
159 fprintf(stderr,"gxid: Error %d switching core from %ld to %ld\n",
160 result, old_pointer->id, new_pointer->id);
164 new_pointer->ispointer = 1;
165 old_pointer->ispointer = 0;
166 if (!old_pointer->xdevice)
167 enable_device(old_pointer);
174 disable_device(GxidDevice *dev)
180 XCloseDevice(dpy,dev->xdevice);
186 init_device(XDeviceInfo *xdevice)
188 GxidDevice *dev = (GxidDevice *)malloc(sizeof(GxidDevice));
192 dev->id = xdevice->id;
196 dev->ispointer = (xdevice->use == IsXPointer);
198 /* step through the classes */
201 class = xdevice->inputclassinfo;
202 for (i=0;i<xdevice->num_classes;i++)
204 if (class->class == ValuatorClass)
206 XValuatorInfo *xvi = (XValuatorInfo *)class;
207 num_axes = xvi->num_axes;
209 class = (XAnyClassPtr)(((char *)class) + class->length);
212 /* return NULL if insufficient axes */
228 XDeviceInfo *xdevices;
233 extensions = XListExtensions(dpy, &num_extensions);
234 for (i = 0; i < num_extensions &&
235 (strcmp(extensions[i], "XInputExtension") != 0); i++);
236 XFreeExtensionList(extensions);
237 if (i == num_extensions) /* XInput extension not found */
239 fprintf(stderr,"XInput extension not found\n");
243 xdevices = XListInputDevices(dpy, &num_xdevices);
244 devices = (GxidDevice **)malloc(num_xdevices * sizeof(GxidDevice *));
247 for(i=0; i<num_xdevices; i++)
249 GxidDevice *dev = init_device(&xdevices[i]);
251 devices[num_devices++] = dev;
253 XFreeDeviceList(xdevices);
256 /* If this routine needs fixing, the corresponding routine
257 in gdkinputgxi.h will need it too. */
260 gxi_find_root_child(Display *dpy, Window w)
270 XQueryTree(dpy,w,&root,&parent,&children,&nchildren);
271 if (children) XFree(children);
273 while (parent != root);
279 handle_claim_device(GxidClaimDevice *msg)
282 XID devid = ntohl(msg->device);
283 XID winid = ntohl(msg->window);
284 int exclusive = ntohl(msg->exclusive);
285 GxidDevice *device = NULL;
286 GxidWindow *window = NULL;
289 fprintf(stderr,"device %ld claimed (window 0x%lx)\n",devid,winid);
292 for (i=0;i<num_devices;i++)
294 if (devices[i]->id == devid)
302 fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
303 return GXID_RETURN_ERROR;
306 if (device->exclusive)
310 "%s: Device %ld already claimed in exclusive mode\n",
312 return GXID_RETURN_ERROR;
317 for (i=0;i<num_windows;i++)
319 for (j=0;j<windows[i]->num_devices;j++)
320 if (windows[i]->devices[j]->id == devid)
324 "%s: Can't establish exclusive use of device %ld\n",
326 return GXID_RETURN_ERROR;
329 if (device->ispointer)
330 if (!switch_core_pointer())
333 "%s: Can't free up core pointer %ld\n",
335 return GXID_RETURN_ERROR;
338 device->exclusive = 1;
339 disable_device(device);
340 XSelectInput(dpy,winid,StructureNotifyMask);
342 else /* !exclusive */
344 /* FIXME: this is a bit improper. We probably should do this only
345 when a window is first claimed. But we might be fooled if
346 an old client died without releasing it's windows. So until
347 we look for client-window closings, do it here
349 (We do look for closings now...)
352 XSelectInput(dpy,winid,EnterWindowMask|StructureNotifyMask);
355 for (i=0;i<num_windows;i++)
357 if (windows[i]->xwindow == winid)
364 /* Create window structure if no devices have been previously
369 windows = (GxidWindow **)realloc(windows,
370 sizeof(GxidWindow*)*num_windows);
371 window = (GxidWindow *)malloc(sizeof(GxidWindow));
372 windows[num_windows-1] = window;
374 window->xwindow = winid;
375 window->root_child = gxi_find_root_child(dpy,winid);
376 window->num_devices = 0;
381 for (i=0;i<window->num_devices;i++)
383 if (window->devices[i] == device)
384 return GXID_RETURN_OK;
387 window->num_devices++;
388 window->devices = (GxidDevice **)realloc(window->devices,
389 sizeof(GxidDevice*)*num_devices);
390 /* we need add the device to the window */
391 window->devices[i] = device;
393 return GXID_RETURN_OK;
397 handle_release_device(GxidReleaseDevice *msg)
400 XID devid = ntohl(msg->device);
401 XID winid = ntohl(msg->window);
403 GxidDevice *device = NULL;
406 fprintf(stderr,"device %ld released (window 0x%lx)\n",devid,winid);
409 for (i=0;i<num_devices;i++)
411 if (devices[i]->id == devid)
419 fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
420 return GXID_RETURN_ERROR;
423 for (i=0;i<num_windows;i++)
425 GxidWindow *w = windows[i];
427 if (w->xwindow == winid)
428 for (j=0;j<w->num_devices;j++)
429 if (w->devices[j]->id == devid)
431 if (j<w->num_devices-1)
432 w->devices[j] = w->devices[w->num_devices-1];
435 if (w->num_devices == 0)
438 windows[i] = windows[num_windows-1];
442 /* FIXME: should we deselect input? But what
443 what if window is already destroyed */
446 if (device->exclusive)
448 device->exclusive = 0;
449 enable_device(device);
451 return GXID_RETURN_OK;
455 /* device/window combination not found */
457 "%s: Device %ld not claimed for window 0x%lx\n",
458 program_name,devid,winid);
459 return GXID_RETURN_ERROR;
471 struct sockaddr_in sin;
475 sin_length = sizeof(struct sockaddr_in);
476 conn_fd = accept(socket_fd,(struct sockaddr *)&sin,&sin_length);
479 fprintf(stderr,"%s: Error accepting connection\n",
484 /* read type and length of message */
486 count = read(conn_fd,(char *)&msg,2*sizeof(GxidU32));
487 if (count != 2*sizeof(GxidU32))
489 fprintf(stderr,"%s: Error reading message header\n",
494 type = ntohl(msg.any.type);
495 length = ntohl(msg.any.length);
497 /* read rest of message */
499 if (length > sizeof(GxidMessage))
501 fprintf(stderr,"%s: Bad message length\n",
507 count = read(conn_fd,2*sizeof(GxidU32) + (char *)&msg,
508 length - 2*sizeof(GxidU32));
509 if (count != length - 2*sizeof(GxidU32))
511 fprintf(stderr,"%s: Error reading message body\n",
519 case GXID_CLAIM_DEVICE:
520 retval = handle_claim_device((GxidClaimDevice *)&msg);
522 case GXID_RELEASE_DEVICE:
523 retval = handle_release_device((GxidReleaseDevice *)&msg);
526 fprintf(stderr,"%s: Unknown message type: %ld (ignoring)\n",
532 count = write(conn_fd,&retval,sizeof(GxidI32));
533 if (count != sizeof(GxidI32))
535 fprintf(stderr,"%s: Error writing return code\n",
543 handle_motion_notify(XDeviceMotionEvent *event)
546 GxidDevice *old_device = NULL;
547 GxidDevice *new_device = NULL;
548 Window w, root, child;
549 int root_x, root_y, x, y, mask;
551 for (j=0;j<num_devices;j++)
553 if (devices[j]->ispointer)
554 old_device = devices[j];
555 if (devices[j]->id == event->deviceid)
556 new_device = devices[j];
559 if (new_device && !new_device->exclusive && !new_device->ispointer)
561 /* make sure we aren't stealing the pointer back from a slow
567 /* FIXME: this fails disasterously if child vanishes between
568 calls. (Which is prone to happening since we get events
569 on root just as the client exits) */
571 XQueryPointer(dpy,w,&root,&child,&root_x,&root_y,
574 while (child != None);
576 for (i=0;i<num_windows;i++)
577 if (windows[i]->xwindow == w)
578 for (j=0;j<windows[i]->num_devices;j++)
579 if (windows[i]->devices[j] == new_device)
582 /* FIXME: do something smarter with axes */
583 XChangePointerDevice(dpy,new_device->xdevice, 0, 1);
584 new_device->ispointer = 1;
586 old_device->ispointer = 0;
587 if (!old_device->xdevice)
588 enable_device(old_device);
593 handle_change_notify(XChangeDeviceNotifyEvent *event)
596 GxidDevice *old_device = NULL;
597 GxidDevice *new_device = NULL;
600 for (j=0;j<num_devices;j++)
602 if (devices[j]->ispointer)
603 old_device = devices[j];
604 if (devices[j]->id == event->deviceid)
605 new_device = devices[j];
609 fprintf(stderr,"gxid: ChangeNotify event; old = %ld; new = %ld\n",
610 old_device->id, new_device->id);
613 if (old_device != new_device)
615 new_device->ispointer = 1;
617 old_device->ispointer = 0;
618 if (!old_device->xdevice)
619 enable_device(old_device);
624 handle_enter_notify(XEnterWindowEvent *event, GxidWindow *window)
627 GxidDevice *old_pointer = NULL;
628 for (i=0;i<num_devices;i++)
630 if (devices[i]->ispointer)
632 old_pointer = devices[i];
638 fprintf(stderr,"gxid: Enter event; oldpointer = %ld\n",
643 for (i=0;i<window->num_devices;i++)
645 if (window->devices[i] == old_pointer)
647 switch_core_pointer();
654 handle_destroy_notify(XDestroyWindowEvent *event)
658 for (i=0;i<num_windows;i++)
659 if (windows[i]->xwindow == event->window)
661 GxidWindow *w = windows[i];
663 for (j=0;j<w->num_devices;j++)
666 fprintf(stderr,"device %ld released on destruction of window 0x%lx.\n",
667 w->devices[j]->id,w->xwindow);
670 if (w->devices[j]->exclusive)
672 w->devices[j]->exclusive = 0;
673 enable_device(devices[j]);
678 windows[i] = windows[num_windows-1];
682 free((void *)w->devices);
684 /* FIXME: should we deselect input? But what
685 what if window is already destroyed */
697 XNextEvent (dpy, &event);
700 fprintf(stderr,"Event - type = %d; window = 0x%lx\n",
701 event.type,event.xany.window);
704 if (event.type == ConfigureNotify)
707 XConfigureEvent *xce = (XConfigureEvent *)&event;
708 fprintf(stderr," configureNotify: window = 0x%lx\n",xce->window);
711 else if (event.type == EnterNotify)
713 /* pointer entered a claimed window */
714 for (i=0;i<num_windows;i++)
716 if (event.xany.window == windows[i]->xwindow)
717 handle_enter_notify((XEnterWindowEvent *)&event,windows[i]);
720 else if (event.type == DestroyNotify)
722 /* A claimed window was destroyed */
723 for (i=0;i<num_windows;i++)
725 if (event.xany.window == windows[i]->xwindow)
726 handle_destroy_notify((XDestroyWindowEvent *)&event);
730 for (i=0;i<num_devices;i++)
732 if (event.type == devices[i]->motionnotify_type)
734 handle_motion_notify((XDeviceMotionEvent *)&event);
737 else if (event.type == devices[i]->changenotify_type)
739 handle_change_notify((XChangeDeviceNotifyEvent *)&event);
748 fprintf(stderr,"Usage: %s [-d display] [-p --gxid-port port]\n",
754 main(int argc, char **argv)
757 char *display_name = NULL;
760 program_name = argv[0];
764 if (!strcmp(argv[i],"-d"))
766 if (++i >= argc) usage();
767 display_name = argv[i];
769 else if (!strcmp(argv[i],"--gxid-port") ||
770 !strcmp(argv[i],"-p"))
772 if (++i >= argc) usage();
773 port = atoi(argv[i]);
782 char *t = getenv("GXID_PORT");
788 /* set up a signal handler so we can clean up if killed */
790 signal(SIGTERM,handler);
791 signal(SIGINT,handler);
793 /* initialize the X connection */
795 dpy = XOpenDisplay (display_name);
798 fprintf (stderr, "%s: unable to open display '%s'\n",
799 program_name, XDisplayName (display_name));
803 root_window = DefaultRootWindow(dpy);
805 /* We'll want to do this in the future if we are to support
806 gxid monitoring visibility information for clients */
808 XSelectInput(dpy,root_window,SubstructureNotifyMask);
812 /* set up our server connection */
818 if (XPending(dpy)) /* this seems necessary to get things
825 FD_SET(ConnectionNumber(dpy),&readfds);
826 FD_SET(socket_fd,&readfds);
828 if (select(8*sizeof(readfds),&readfds,
829 (fd_set *)0,(fd_set *)0, (struct timeval *)0) < 0)
831 fprintf(stderr,"Error in select\n");
835 if (FD_ISSET(socket_fd,&readfds))
836 handle_connection(socket_fd);
838 while (XPending(dpy))