4 * Copyright 1997 Owen Taylor <owt1@cornell.edu>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
17 #include <X11/extensions/XInput.h>
19 #include "gxid_proto.h"
21 /* #define DEBUG_CLIENTS */
22 /* #define DEBUG_EVENTS */
26 Window root_window; /* default root window of dpy */
27 int port = 0; /* port to listen on */
28 int socket_fd = 0; /* file descriptor of socket */
29 typedef struct GxidWindow_ GxidWindow;
31 typedef struct GxidDevice_ GxidDevice;
38 int motionnotify_type;
39 int changenotify_type;
44 /* Immediate child of root that is ancestor of window */
50 GxidDevice **devices = NULL;
52 GxidWindow **windows = NULL;
58 fprintf(stderr,"%s: dying on signal %d\n",program_name,signal);
67 struct sockaddr_in sin;
69 socket_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
72 fprintf (stderr, "%s: error getting socket\n",
77 sin.sin_family = AF_INET;
78 sin.sin_port = htons(port);
79 sin.sin_addr.s_addr = INADDR_ANY;
81 if (bind(socket_fd,(struct sockaddr *)(&sin),
82 sizeof(struct sockaddr_in)) < 0)
84 fprintf (stderr,"%s: cannot bind to port %d\n",
89 if (listen(socket_fd,5) < 0)
91 fprintf (stderr,"%s: error listening on socket\n",
99 enable_device(GxidDevice *dev)
101 XEventClass xevc[NUM_EVENTC];
102 int num_eventc = NUM_EVENTC;
107 if (dev->ispointer) return;
109 dev->xdevice = XOpenDevice(dpy, dev->id);
110 if (!dev->xdevice) return;
112 DeviceMotionNotify (dev->xdevice, dev->motionnotify_type,
114 ChangeDeviceNotify (dev->xdevice, dev->changenotify_type,
117 /* compress out zero event classes */
118 for (i=0,j=0;i<NUM_EVENTC;i++)
127 XSelectExtensionEvent (dpy, root_window, xevc, num_eventc);
131 /* switch the core pointer from whatever it is now to something else,
132 return true on success, false otherwise */
134 switch_core_pointer(void)
136 GxidDevice *old_pointer = 0;
137 GxidDevice *new_pointer = 0;
141 for (i=0;i<num_devices;i++)
143 if (devices[i]->ispointer)
144 old_pointer = devices[i];
146 if (!new_pointer && !devices[i]->exclusive)
147 new_pointer = devices[i];
150 if (!old_pointer || !new_pointer)
154 fprintf(stderr,"gxid: Switching core from %ld to %ld\n",
155 old_pointer->id,new_pointer->id);
157 result = XChangePointerDevice(dpy,new_pointer->xdevice, 0, 1);
158 if (result != Success)
160 fprintf(stderr,"gxid: Error %d switching core from %ld to %ld\n",
161 result, old_pointer->id, new_pointer->id);
165 new_pointer->ispointer = 1;
166 old_pointer->ispointer = 0;
167 if (!old_pointer->xdevice)
168 enable_device(old_pointer);
175 disable_device(GxidDevice *dev)
181 XCloseDevice(dpy,dev->xdevice);
187 init_device(XDeviceInfo *xdevice)
189 GxidDevice *dev = (GxidDevice *)malloc(sizeof(GxidDevice));
193 dev->id = xdevice->id;
197 dev->ispointer = (xdevice->use == IsXPointer);
199 /* step through the classes */
202 class = xdevice->inputclassinfo;
203 for (i=0;i<xdevice->num_classes;i++)
205 if (class->class == ValuatorClass)
207 XValuatorInfo *xvi = (XValuatorInfo *)class;
208 num_axes = xvi->num_axes;
210 class = (XAnyClassPtr)(((char *)class) + class->length);
213 /* return NULL if insufficient axes */
229 XDeviceInfo *xdevices;
234 extensions = XListExtensions(dpy, &num_extensions);
235 for (i = 0; i < num_extensions &&
236 (strcmp(extensions[i], "XInputExtension") != 0); i++);
237 XFreeExtensionList(extensions);
238 if (i == num_extensions) /* XInput extension not found */
240 fprintf(stderr,"XInput extension not found\n");
244 xdevices = XListInputDevices(dpy, &num_xdevices);
245 devices = (GxidDevice **)malloc(num_xdevices * sizeof(GxidDevice *));
248 for(i=0; i<num_xdevices; i++)
250 GxidDevice *dev = init_device(&xdevices[i]);
252 devices[num_devices++] = dev;
254 XFreeDeviceList(xdevices);
257 /* If this routine needs fixing, the corresponding routine
258 in gdkinputgxi.h will need it too. */
261 gxi_find_root_child(Display *dpy, Window w)
271 XQueryTree (dpy, w, &root, &parent, &children, &nchildren);
275 while (parent != root);
281 handle_claim_device(GxidClaimDevice *msg)
287 GxidDevice *device = NULL;
288 GxidWindow *window = NULL;
290 if (msg->length != sizeof(GxidClaimDevice))
292 fprintf(stderr,"Bad length for ClaimDevice message\n");
293 return GXID_RETURN_ERROR;
296 devid = ntohl(msg->device);
297 winid = ntohl(msg->window);
298 exclusive = ntohl(msg->exclusive);
301 fprintf(stderr,"device %ld claimed (window 0x%lx)\n",devid,winid);
304 for (i=0;i<num_devices;i++)
306 if (devices[i]->id == devid)
314 fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
315 return GXID_RETURN_ERROR;
318 if (device->exclusive)
322 "%s: Device %ld already claimed in exclusive mode\n",
324 return GXID_RETURN_ERROR;
329 for (i=0;i<num_windows;i++)
331 for (j=0;j<windows[i]->num_devices;j++)
332 if (windows[i]->devices[j]->id == devid)
336 "%s: Can't establish exclusive use of device %ld\n",
338 return GXID_RETURN_ERROR;
341 if (device->ispointer)
342 if (!switch_core_pointer())
345 "%s: Can't free up core pointer %ld\n",
347 return GXID_RETURN_ERROR;
350 device->exclusive = 1;
351 disable_device(device);
352 XSelectInput(dpy,winid,StructureNotifyMask);
354 else /* !exclusive */
356 /* FIXME: this is a bit improper. We probably should do this only
357 when a window is first claimed. But we might be fooled if
358 an old client died without releasing it's windows. So until
359 we look for client-window closings, do it here
361 (We do look for closings now...)
364 XSelectInput(dpy,winid,EnterWindowMask|StructureNotifyMask);
367 for (i=0;i<num_windows;i++)
369 if (windows[i]->xwindow == winid)
376 /* Create window structure if no devices have been previously
381 windows = (GxidWindow **)realloc(windows,
382 sizeof(GxidWindow*)*num_windows);
383 window = (GxidWindow *)malloc(sizeof(GxidWindow));
384 windows[num_windows-1] = window;
386 window->xwindow = winid;
387 window->root_child = gxi_find_root_child(dpy,winid);
388 window->num_devices = 0;
393 for (i=0;i<window->num_devices;i++)
395 if (window->devices[i] == device)
396 return GXID_RETURN_OK;
399 window->num_devices++;
400 window->devices = (GxidDevice **)realloc(window->devices,
401 sizeof(GxidDevice*)*num_devices);
402 /* we need add the device to the window */
403 window->devices[i] = device;
405 return GXID_RETURN_OK;
409 handle_release_device(GxidReleaseDevice *msg)
415 GxidDevice *device = NULL;
417 if (msg->length != sizeof(GxidReleaseDevice))
419 fprintf(stderr,"Bad length for ReleaseDevice message\n");
420 return GXID_RETURN_ERROR;
423 devid = ntohl(msg->device);
424 winid = ntohl(msg->window);
427 fprintf(stderr,"device %ld released (window 0x%lx)\n",devid,winid);
430 for (i=0;i<num_devices;i++)
432 if (devices[i]->id == devid)
440 fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
441 return GXID_RETURN_ERROR;
444 for (i=0;i<num_windows;i++)
446 GxidWindow *w = windows[i];
448 if (w->xwindow == winid)
449 for (j=0;j<w->num_devices;j++)
450 if (w->devices[j]->id == devid)
452 if (j<w->num_devices-1)
453 w->devices[j] = w->devices[w->num_devices-1];
456 if (w->num_devices == 0)
459 windows[i] = windows[num_windows-1];
463 /* FIXME: should we deselect input? But what
464 what if window is already destroyed */
467 if (device->exclusive)
469 device->exclusive = 0;
470 enable_device(device);
472 return GXID_RETURN_OK;
476 /* device/window combination not found */
478 "%s: Device %ld not claimed for window 0x%lx\n",
479 program_name,devid,winid);
480 return GXID_RETURN_ERROR;
484 handle_connection (void)
492 struct sockaddr_in sin;
496 sin_length = sizeof(struct sockaddr_in);
497 conn_fd = accept(socket_fd,(struct sockaddr *)&sin,&sin_length);
500 fprintf(stderr,"%s: Error accepting connection\n",
505 /* read type and length of message */
507 count = read(conn_fd,(char *)&msg,2*sizeof(GxidU32));
508 if (count != 2*sizeof(GxidU32))
510 fprintf(stderr,"%s: Error reading message header\n",
515 type = ntohl(msg.any.type);
516 length = ntohl(msg.any.length);
518 /* read rest of message */
520 if ((length > sizeof(GxidMessage)) || (length < 2*sizeof(GxidU32)))
522 fprintf(stderr,"%s: Bad message length\n",
528 count = read(conn_fd,2*sizeof(GxidU32) + (char *)&msg,
529 length - 2*sizeof(GxidU32));
530 if (count != length - 2*sizeof(GxidU32))
532 fprintf(stderr,"%s: Error reading message body\n",
540 case GXID_CLAIM_DEVICE:
541 retval = handle_claim_device((GxidClaimDevice *)&msg);
543 case GXID_RELEASE_DEVICE:
544 retval = handle_release_device((GxidReleaseDevice *)&msg);
547 fprintf(stderr,"%s: Unknown message type: %ld (ignoring)\n",
553 count = write(conn_fd,&retval,sizeof(GxidI32));
554 if (count != sizeof(GxidI32))
556 fprintf(stderr,"%s: Error writing return code\n",
564 handle_motion_notify(XDeviceMotionEvent *event)
567 GxidDevice *old_device = NULL;
568 GxidDevice *new_device = NULL;
569 Window w, root, child;
570 int root_x, root_y, x, y, mask;
572 for (j=0;j<num_devices;j++)
574 if (devices[j]->ispointer)
575 old_device = devices[j];
576 if (devices[j]->id == event->deviceid)
577 new_device = devices[j];
580 if (new_device && !new_device->exclusive && !new_device->ispointer)
582 /* make sure we aren't stealing the pointer back from a slow
588 /* FIXME: this fails disasterously if child vanishes between
589 calls. (Which is prone to happening since we get events
590 on root just as the client exits) */
592 XQueryPointer(dpy,w,&root,&child,&root_x,&root_y,
595 while (child != None);
597 for (i=0;i<num_windows;i++)
598 if (windows[i]->xwindow == w)
599 for (j=0;j<windows[i]->num_devices;j++)
600 if (windows[i]->devices[j] == new_device)
603 /* FIXME: do something smarter with axes */
604 XChangePointerDevice(dpy,new_device->xdevice, 0, 1);
605 new_device->ispointer = 1;
607 old_device->ispointer = 0;
608 if (!old_device->xdevice)
609 enable_device(old_device);
614 handle_change_notify(XChangeDeviceNotifyEvent *event)
617 GxidDevice *old_device = NULL;
618 GxidDevice *new_device = NULL;
621 for (j=0;j<num_devices;j++)
623 if (devices[j]->ispointer)
624 old_device = devices[j];
625 if (devices[j]->id == event->deviceid)
626 new_device = devices[j];
630 fprintf(stderr,"gxid: ChangeNotify event; old = %ld; new = %ld\n",
631 old_device->id, new_device->id);
634 if (old_device != new_device)
636 new_device->ispointer = 1;
638 old_device->ispointer = 0;
639 if (!old_device->xdevice)
640 enable_device(old_device);
645 handle_enter_notify(XEnterWindowEvent *event, GxidWindow *window)
648 GxidDevice *old_pointer = NULL;
649 for (i=0;i<num_devices;i++)
651 if (devices[i]->ispointer)
653 old_pointer = devices[i];
659 fprintf(stderr,"gxid: Enter event; oldpointer = %ld\n",
664 for (i=0;i<window->num_devices;i++)
666 if (window->devices[i] == old_pointer)
668 switch_core_pointer();
675 handle_destroy_notify(XDestroyWindowEvent *event)
679 for (i=0;i<num_windows;i++)
680 if (windows[i]->xwindow == event->window)
682 GxidWindow *w = windows[i];
684 for (j=0;j<w->num_devices;j++)
687 fprintf(stderr,"device %ld released on destruction of window 0x%lx.\n",
688 w->devices[j]->id,w->xwindow);
691 if (w->devices[j]->exclusive)
693 w->devices[j]->exclusive = 0;
694 enable_device(devices[j]);
699 windows[i] = windows[num_windows-1];
703 free((void *)w->devices);
705 /* FIXME: should we deselect input? But what
706 what if window is already destroyed */
718 XNextEvent (dpy, &event);
721 fprintf(stderr,"Event - type = %d; window = 0x%lx\n",
722 event.type,event.xany.window);
725 if (event.type == ConfigureNotify)
728 XConfigureEvent *xce = (XConfigureEvent *)&event;
729 fprintf(stderr," configureNotify: window = 0x%lx\n",xce->window);
732 else if (event.type == EnterNotify)
734 /* pointer entered a claimed window */
735 for (i=0;i<num_windows;i++)
737 if (event.xany.window == windows[i]->xwindow)
738 handle_enter_notify((XEnterWindowEvent *)&event,windows[i]);
741 else if (event.type == DestroyNotify)
743 /* A claimed window was destroyed */
744 for (i=0;i<num_windows;i++)
746 if (event.xany.window == windows[i]->xwindow)
747 handle_destroy_notify((XDestroyWindowEvent *)&event);
751 for (i=0;i<num_devices;i++)
753 if (event.type == devices[i]->motionnotify_type)
755 handle_motion_notify((XDeviceMotionEvent *)&event);
758 else if (event.type == devices[i]->changenotify_type)
760 handle_change_notify((XChangeDeviceNotifyEvent *)&event);
769 fprintf(stderr,"Usage: %s [-d display] [-p --gxid-port port]\n",
775 main(int argc, char **argv)
778 char *display_name = NULL;
781 program_name = argv[0];
785 if (!strcmp(argv[i],"-d"))
787 if (++i >= argc) usage();
788 display_name = argv[i];
790 else if (!strcmp(argv[i],"--gxid-port") ||
791 !strcmp(argv[i],"-p"))
793 if (++i >= argc) usage();
794 port = atoi(argv[i]);
803 char *t = getenv("GXID_PORT");
809 /* set up a signal handler so we can clean up if killed */
811 signal(SIGTERM,handler);
812 signal(SIGINT,handler);
814 /* initialize the X connection */
816 dpy = XOpenDisplay (display_name);
819 fprintf (stderr, "%s: unable to open display '%s'\n",
820 program_name, XDisplayName (display_name));
824 root_window = DefaultRootWindow(dpy);
826 /* We'll want to do this in the future if we are to support
827 gxid monitoring visibility information for clients */
829 XSelectInput(dpy,root_window,SubstructureNotifyMask);
833 /* set up our server connection */
839 if (XPending(dpy)) /* this seems necessary to get things
846 FD_SET(ConnectionNumber(dpy),&readfds);
847 FD_SET(socket_fd,&readfds);
849 if (select(8*sizeof(readfds),&readfds,
850 (fd_set *)0,(fd_set *)0, (struct timeval *)0) < 0)
852 fprintf(stderr,"Error in select\n");
856 if (FD_ISSET(socket_fd,&readfds))
859 while (XPending(dpy))