]> Pileus Git - ~andy/gtk/blob - gdk/gxid.c
create x-specific subdirectory.
[~andy/gtk] / gdk / gxid.c
1 /* 
2  * gxid version 0.3
3  *
4  * Copyright 1997 Owen Taylor <owt1@cornell.edu>
5 */
6 #undef  G_LOG_DOMAIN
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <signal.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <X11/Xlib.h>
17 #include <X11/extensions/XInput.h>
18
19 #include "gxid_proto.h"
20
21 /* #define DEBUG_CLIENTS  */
22 /* #define DEBUG_EVENTS */
23
24 char *program_name;
25 Display *dpy;
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;
30
31 typedef struct GxidDevice_ GxidDevice;
32 struct GxidDevice_ {
33   XID id;
34   int exclusive;
35   int ispointer;
36   
37   XDevice *xdevice;
38   int motionnotify_type;
39   int changenotify_type;
40 };
41
42 struct GxidWindow_ {
43   Window xwindow;
44   /* Immediate child of root that is ancestor of window */
45   Window root_child;
46   int num_devices;
47   GxidDevice **devices;
48 };
49
50 GxidDevice **devices = NULL;
51 int num_devices = 0;
52 GxidWindow **windows = NULL;
53 int num_windows = 0;
54
55 void
56 handler(int signal)
57 {
58   fprintf(stderr,"%s: dying on signal %d\n",program_name,signal);
59   if (socket_fd)
60     close(socket_fd);
61   exit(1);
62 }
63
64 void
65 init_socket(void)
66 {
67   struct sockaddr_in sin;
68
69   socket_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
70   if (socket_fd < 0)
71     {
72       fprintf (stderr, "%s: error getting socket\n",
73                program_name);
74       exit(1);
75     }
76   
77   sin.sin_family = AF_INET;
78   sin.sin_port = htons(port);
79   sin.sin_addr.s_addr = INADDR_ANY;
80   
81   if (bind(socket_fd,(struct sockaddr *)(&sin),
82            sizeof(struct sockaddr_in)) < 0)
83     {
84       fprintf (stderr,"%s: cannot bind to port %d\n",
85                program_name,port);
86       exit(1);
87     }
88
89   if (listen(socket_fd,5) < 0)
90     {
91       fprintf (stderr,"%s: error listening on socket\n",
92                program_name);
93       exit(1);
94     };
95 }
96
97 #define NUM_EVENTC 2
98 static void
99 enable_device(GxidDevice *dev)
100 {
101   XEventClass xevc[NUM_EVENTC];
102   int num_eventc = NUM_EVENTC;
103   int i,j;
104
105   if (!dev->xdevice) 
106     {
107       if (dev->ispointer) return;
108
109       dev->xdevice = XOpenDevice(dpy, dev->id);
110       if (!dev->xdevice) return;
111       
112       DeviceMotionNotify (dev->xdevice, dev->motionnotify_type,
113                           xevc[0]);
114       ChangeDeviceNotify (dev->xdevice, dev->changenotify_type,
115                           xevc[1]);
116
117       /* compress out zero event classes */
118       for (i=0,j=0;i<NUM_EVENTC;i++)
119         {
120           if (xevc[i]) {
121             xevc[j] = xevc[i];
122             j++;
123           }
124       }
125       num_eventc = j;
126       
127       XSelectExtensionEvent (dpy, root_window, xevc, num_eventc);
128     }
129 }
130
131 /* switch the core pointer from whatever it is now to something else,
132    return true on success, false otherwise */
133 static int
134 switch_core_pointer(void)
135 {
136   GxidDevice *old_pointer = 0;
137   GxidDevice *new_pointer = 0;
138   int result;
139   int i;
140
141   for (i=0;i<num_devices;i++)
142     {
143       if (devices[i]->ispointer)
144         old_pointer = devices[i];
145       else
146         if (!new_pointer && !devices[i]->exclusive)
147           new_pointer = devices[i];
148     }
149
150   if (!old_pointer || !new_pointer)
151     return 0;
152
153 #ifdef DEBUG_EVENTS
154   fprintf(stderr,"gxid: Switching core from %ld to %ld\n",
155          old_pointer->id,new_pointer->id);
156 #endif
157   result = XChangePointerDevice(dpy,new_pointer->xdevice, 0, 1);
158   if (result != Success)
159     {
160       fprintf(stderr,"gxid: Error %d switching core from %ld to %ld\n",
161               result, old_pointer->id, new_pointer->id);
162     }
163   else
164     {
165       new_pointer->ispointer = 1;
166       old_pointer->ispointer = 0;
167       if (!old_pointer->xdevice)
168         enable_device(old_pointer);
169     }
170
171   return 1;
172 }
173
174 void
175 disable_device(GxidDevice *dev)
176 {
177   if (dev->xdevice)
178     {
179       if (dev->ispointer)
180         return;
181       XCloseDevice(dpy,dev->xdevice);
182       dev->xdevice = 0;
183     }
184 }
185
186 GxidDevice *
187 init_device(XDeviceInfo *xdevice)
188 {
189   GxidDevice *dev = (GxidDevice *)malloc(sizeof(GxidDevice));
190   XAnyClassPtr class;
191   int num_axes, i;
192
193   dev->id = xdevice->id;
194   dev->exclusive = 0;
195   dev->xdevice = NULL;
196
197   dev->ispointer = (xdevice->use == IsXPointer);
198
199   /* step through the classes */
200
201   num_axes = 0;
202   class = xdevice->inputclassinfo;
203   for (i=0;i<xdevice->num_classes;i++) 
204     {
205       if (class->class == ValuatorClass) 
206         {
207           XValuatorInfo *xvi = (XValuatorInfo *)class;
208           num_axes = xvi->num_axes;
209         }
210       class = (XAnyClassPtr)(((char *)class) + class->length);
211     }
212
213   /* return NULL if insufficient axes */
214   if (num_axes < 2)
215     {
216       free((void *)dev);
217       return NULL;
218     }
219
220   if (!dev->ispointer)
221       enable_device(dev);
222   return dev;
223 }
224
225 void
226 init_xinput(void)
227 {
228   char **extensions;
229   XDeviceInfo   *xdevices;
230   int num_xdevices;
231   int num_extensions;
232   int i;
233
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 */
239     {
240       fprintf(stderr,"XInput extension not found\n");
241       exit(1);
242     }
243
244   xdevices = XListInputDevices(dpy, &num_xdevices);
245   devices = (GxidDevice **)malloc(num_xdevices * sizeof(GxidDevice *));
246
247   num_devices = 0;
248   for(i=0; i<num_xdevices; i++)
249     {
250       GxidDevice *dev = init_device(&xdevices[i]);
251       if (dev)
252           devices[num_devices++] = dev;
253     }
254   XFreeDeviceList(xdevices);
255 }
256
257 /* If this routine needs fixing, the corresponding routine
258    in gdkinputgxi.h will need it too. */
259
260 Window
261 gxi_find_root_child(Display *dpy, Window w)
262 {
263   Window root,parent;
264   Window *children;
265   int nchildren;
266
267   parent = w;
268   do 
269     {
270       w = parent;
271       XQueryTree (dpy, w, &root, &parent, &children, &nchildren);
272       if (children)
273         XFree (children);
274     } 
275   while (parent != root);
276   
277   return w;
278 }
279
280 int
281 handle_claim_device(GxidClaimDevice *msg)
282 {
283   int i,j;
284   XID devid;
285   XID winid;
286   int exclusive;
287   GxidDevice *device = NULL;
288   GxidWindow *window = NULL;
289
290   if (msg->length != sizeof(GxidClaimDevice))
291     {
292       fprintf(stderr,"Bad length for ClaimDevice message\n");
293       return GXID_RETURN_ERROR;
294     }
295
296   devid = ntohl(msg->device);
297   winid = ntohl(msg->window);
298   exclusive = ntohl(msg->exclusive);
299
300 #ifdef DEBUG_CLIENTS
301   fprintf(stderr,"device %ld claimed (window 0x%lx)\n",devid,winid);
302 #endif  
303
304   for (i=0;i<num_devices;i++)
305     {
306       if (devices[i]->id == devid)
307         {
308           device = devices[i];
309           break;
310         }
311     }
312   if (!device)
313     {
314       fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
315       return GXID_RETURN_ERROR;
316     }
317
318   if (device->exclusive)
319     {
320       /* already in use */
321       fprintf(stderr,
322               "%s: Device %ld already claimed in exclusive mode\n",
323               program_name,devid);
324       return GXID_RETURN_ERROR;
325     }
326
327   if (exclusive)
328     {
329       for (i=0;i<num_windows;i++)
330         {
331           for (j=0;j<windows[i]->num_devices;j++)
332             if (windows[i]->devices[j]->id == devid)
333               {
334                 /* already in use */
335                 fprintf(stderr,
336                         "%s: Can't establish exclusive use of device %ld\n",
337                         program_name,devid);
338                 return GXID_RETURN_ERROR;
339               }
340         }
341       if (device->ispointer)
342         if (!switch_core_pointer())
343           {
344             fprintf(stderr,
345                     "%s: Can't free up core pointer %ld\n",
346                     program_name,devid);
347             return GXID_RETURN_ERROR;
348           }
349
350       device->exclusive = 1;
351       disable_device(device);
352       XSelectInput(dpy,winid,StructureNotifyMask);
353     }
354   else                          /* !exclusive */
355     {
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 
360          
361          (We do look for closings now...)
362          */
363       
364       XSelectInput(dpy,winid,EnterWindowMask|StructureNotifyMask);
365     }
366
367   for (i=0;i<num_windows;i++)
368     {
369       if (windows[i]->xwindow == winid)
370         {
371           window = windows[i];
372           break;
373         }
374     }
375
376   /* Create window structure if no devices have been previously
377      claimed on it */
378   if (!window)
379     {
380       num_windows++;
381       windows = (GxidWindow **)realloc(windows,
382                                        sizeof(GxidWindow*)*num_windows);
383       window = (GxidWindow *)malloc(sizeof(GxidWindow));
384       windows[num_windows-1] = window;
385
386       window->xwindow = winid;
387       window->root_child = gxi_find_root_child(dpy,winid);
388       window->num_devices = 0;
389       window->devices = 0;
390     }
391
392   
393   for (i=0;i<window->num_devices;i++)
394     {
395       if (window->devices[i] == device)
396         return GXID_RETURN_OK;
397     }
398   
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;
404
405   return GXID_RETURN_OK;
406 }
407
408 int
409 handle_release_device(GxidReleaseDevice *msg)
410 {
411   int i,j;
412   XID devid;
413   XID winid;
414
415   GxidDevice *device = NULL;
416
417   if (msg->length != sizeof(GxidReleaseDevice))
418     {
419       fprintf(stderr,"Bad length for ReleaseDevice message\n");
420       return GXID_RETURN_ERROR;
421     }
422
423   devid = ntohl(msg->device);
424   winid = ntohl(msg->window);
425
426 #ifdef DEBUG_CLIENTS
427   fprintf(stderr,"device %ld released (window 0x%lx)\n",devid,winid);
428 #endif  
429
430   for (i=0;i<num_devices;i++)
431     {
432       if (devices[i]->id == devid)
433         {
434           device = devices[i];
435           break;
436         }
437     }
438   if (!device)
439     {
440       fprintf(stderr,"%s: Unknown device id %ld\n",program_name,devid);
441       return GXID_RETURN_ERROR;
442     }
443
444   for (i=0;i<num_windows;i++)
445     {
446       GxidWindow *w = windows[i];
447       
448       if (w->xwindow == winid)
449         for (j=0;j<w->num_devices;j++)
450           if (w->devices[j]->id == devid)
451             {
452               if (j<w->num_devices-1)
453                 w->devices[j] = w->devices[w->num_devices-1];
454               w->num_devices--;
455
456               if (w->num_devices == 0)
457                 {
458                   if (i<num_windows-1)
459                     windows[i] = windows[num_windows-1];
460                   num_windows--;
461
462                   free((void *)w);
463                   /* FIXME: should we deselect input? But what
464                      what if window is already destroyed */
465                 }
466
467               if (device->exclusive)
468                 {
469                   device->exclusive = 0;
470                   enable_device(device);
471                 }
472               return GXID_RETURN_OK;
473             }
474     }
475   
476   /* device/window combination not found */
477   fprintf(stderr,
478           "%s: Device %ld not claimed for window 0x%lx\n",
479           program_name,devid,winid);
480   return GXID_RETURN_ERROR;
481 }
482
483 void
484 handle_connection (void)
485 {
486   GxidMessage msg;
487   GxidU32 type;
488   GxidU32 length;
489   GxidI32 retval;
490
491   int conn_fd;
492   struct sockaddr_in sin;
493   int sin_length;
494   int count;
495
496   sin_length = sizeof(struct sockaddr_in);
497   conn_fd = accept(socket_fd,(struct sockaddr *)&sin,&sin_length);
498   if (conn_fd < 0)
499     {
500       fprintf(stderr,"%s: Error accepting connection\n",
501               program_name);
502       exit(1);
503     }
504
505   /* read type and length of message */
506
507   count = read(conn_fd,(char *)&msg,2*sizeof(GxidU32));
508   if (count != 2*sizeof(GxidU32))
509     {
510       fprintf(stderr,"%s: Error reading message header\n",
511               program_name);
512       close(conn_fd);
513       return;
514     }
515   type = ntohl(msg.any.type);
516   length = ntohl(msg.any.length);
517
518   /* read rest of message */
519
520   if ((length > sizeof(GxidMessage)) || (length < 2*sizeof(GxidU32)))
521     {
522       fprintf(stderr,"%s: Bad message length\n",
523               program_name);
524       close(conn_fd);
525       return;
526     }
527
528   count = read(conn_fd,2*sizeof(GxidU32) + (char *)&msg,
529                     length - 2*sizeof(GxidU32));
530   if (count != length - 2*sizeof(GxidU32))
531     {
532       fprintf(stderr,"%s: Error reading message body\n",
533               program_name);
534       close(conn_fd);
535       return;
536     }
537
538   switch (type)
539     {
540     case GXID_CLAIM_DEVICE:
541       retval = handle_claim_device((GxidClaimDevice *)&msg);
542       break;
543     case GXID_RELEASE_DEVICE:
544       retval = handle_release_device((GxidReleaseDevice *)&msg);
545       break;
546     default:
547       fprintf(stderr,"%s: Unknown message type: %ld (ignoring)\n",
548               program_name,type);
549       close(conn_fd);
550       return;
551     }
552
553   count = write(conn_fd,&retval,sizeof(GxidI32));
554   if (count != sizeof(GxidI32))
555     {
556       fprintf(stderr,"%s: Error writing return code\n",
557               program_name);
558     }
559   
560   close(conn_fd);
561 }
562
563 void
564 handle_motion_notify(XDeviceMotionEvent *event)
565 {
566   int i,j;
567   GxidDevice *old_device = NULL;
568   GxidDevice *new_device = NULL;
569   Window w, root, child;
570   int root_x, root_y, x, y, mask;
571   
572   for (j=0;j<num_devices;j++)
573     {
574       if (devices[j]->ispointer)
575         old_device = devices[j];
576       if (devices[j]->id == event->deviceid)
577         new_device = devices[j];
578     }
579
580   if (new_device && !new_device->exclusive && !new_device->ispointer)
581     {
582       /* make sure we aren't stealing the pointer back from a slow
583          client */
584       child = root_window;
585       do
586         {
587           w = child;
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) */
591              
592           XQueryPointer(dpy,w,&root,&child,&root_x,&root_y,
593                         &x,&y,&mask);
594         }
595       while (child != None);
596
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)
601                 return;
602       
603       /* FIXME: do something smarter with axes */
604       XChangePointerDevice(dpy,new_device->xdevice, 0, 1);
605       new_device->ispointer = 1;
606       
607       old_device->ispointer = 0;
608       if (!old_device->xdevice)
609         enable_device(old_device);
610     }
611 }
612
613 void
614 handle_change_notify(XChangeDeviceNotifyEvent *event)
615 {
616   int j;
617   GxidDevice *old_device = NULL;
618   GxidDevice *new_device = NULL;
619
620
621   for (j=0;j<num_devices;j++)
622     {
623       if (devices[j]->ispointer)
624         old_device = devices[j];
625       if (devices[j]->id == event->deviceid)
626         new_device = devices[j];
627     }
628
629 #ifdef DEBUG_EVENTS
630   fprintf(stderr,"gxid: ChangeNotify event; old = %ld; new = %ld\n",
631           old_device->id, new_device->id);
632 #endif
633
634   if (old_device != new_device)
635     {
636       new_device->ispointer = 1;
637       
638       old_device->ispointer = 0;
639       if (!old_device->xdevice)
640         enable_device(old_device);
641     }
642 }
643
644 void
645 handle_enter_notify(XEnterWindowEvent *event, GxidWindow *window)
646 {
647   int i;
648   GxidDevice *old_pointer = NULL;
649   for (i=0;i<num_devices;i++)
650     {
651       if (devices[i]->ispointer)
652         {
653           old_pointer = devices[i];
654           break;
655         }
656     }
657
658 #ifdef DEBUG_EVENTS
659   fprintf(stderr,"gxid: Enter event; oldpointer = %ld\n",
660           old_pointer->id);
661 #endif
662
663   if (old_pointer)
664     for (i=0;i<window->num_devices;i++)
665       {
666         if (window->devices[i] == old_pointer)
667           {
668             switch_core_pointer();
669             break;
670           }
671       }
672 }
673
674 void
675 handle_destroy_notify(XDestroyWindowEvent *event)
676 {
677   int i,j;
678
679   for (i=0;i<num_windows;i++)
680     if (windows[i]->xwindow == event->window)
681       {
682         GxidWindow *w = windows[i];
683         
684         for (j=0;j<w->num_devices;j++)
685           {
686 #ifdef DEBUG_CLIENTS
687             fprintf(stderr,"device %ld released on destruction of window 0x%lx.\n",
688                     w->devices[j]->id,w->xwindow);
689 #endif  
690
691             if (w->devices[j]->exclusive)
692               {
693                 w->devices[j]->exclusive = 0;
694                 enable_device(devices[j]);
695               }
696           }
697         
698         if (i<num_windows-1)
699           windows[i] = windows[num_windows-1];
700         num_windows--;
701         
702         if (w->devices)
703           free((void *)w->devices);
704         free((void *)w);
705         /* FIXME: should we deselect input? But what
706            what if window is already destroyed */
707         
708         return;
709       }
710 }
711
712 void
713 handle_xevent(void)
714 {
715   int i;
716   XEvent event;
717         
718   XNextEvent (dpy, &event);
719
720 #ifdef DEBUG_EVENTS
721   fprintf(stderr,"Event - type = %d; window = 0x%lx\n",
722           event.type,event.xany.window);
723 #endif
724
725   if (event.type == ConfigureNotify)
726     {
727 #ifdef DEBUG_EVENTS
728       XConfigureEvent *xce = (XConfigureEvent *)&event;
729       fprintf(stderr," configureNotify: window = 0x%lx\n",xce->window);
730 #endif
731     }
732   else if (event.type == EnterNotify)
733     {
734       /* pointer entered a claimed window */
735       for (i=0;i<num_windows;i++)
736         {
737           if (event.xany.window == windows[i]->xwindow)
738             handle_enter_notify((XEnterWindowEvent *)&event,windows[i]);
739         }
740     }
741   else if (event.type == DestroyNotify)
742     {
743       /* A claimed window was destroyed */
744       for (i=0;i<num_windows;i++)
745         {
746           if (event.xany.window == windows[i]->xwindow)
747             handle_destroy_notify((XDestroyWindowEvent *)&event);
748         }
749     }
750   else
751     for (i=0;i<num_devices;i++)
752       {
753         if (event.type == devices[i]->motionnotify_type)
754           {
755             handle_motion_notify((XDeviceMotionEvent *)&event);
756             break;
757           }
758         else if (event.type == devices[i]->changenotify_type)
759           {
760             handle_change_notify((XChangeDeviceNotifyEvent *)&event);
761             break;
762           }
763       }
764 }
765
766 void 
767 usage(void)
768 {
769   fprintf(stderr,"Usage: %s [-d display] [-p --gxid-port port]\n",
770           program_name);
771   exit(1);
772 }
773
774 int
775 main(int argc, char **argv)
776 {
777   int i;
778   char *display_name = NULL;
779   fd_set readfds;
780
781   program_name = argv[0];
782
783   for (i=1;i<argc;i++)
784     {
785       if (!strcmp(argv[i],"-d"))
786         {
787             if (++i >= argc) usage();
788             display_name = argv[i];
789         }
790       else if (!strcmp(argv[i],"--gxid-port") ||
791                !strcmp(argv[i],"-p"))
792         {
793           if (++i >= argc) usage();
794           port = atoi(argv[i]);
795           break;
796         }
797       else
798         usage();
799     }
800
801   if (!port) 
802     {
803       char *t = getenv("GXID_PORT");
804       if (t)
805         port = atoi(t);
806       else
807         port = 6951;
808     }
809   /* set up a signal handler so we can clean up if killed */
810
811   signal(SIGTERM,handler);
812   signal(SIGINT,handler);
813   
814   /* initialize the X connection */
815   
816   dpy = XOpenDisplay (display_name);
817   if (!dpy)
818     {
819       fprintf (stderr, "%s:  unable to open display '%s'\n",
820                program_name, XDisplayName (display_name));
821       exit (1);
822     }
823   
824   root_window = DefaultRootWindow(dpy);
825
826   /* We'll want to do this in the future if we are to support
827      gxid monitoring visibility information for clients */
828 #if 0
829   XSelectInput(dpy,root_window,SubstructureNotifyMask);
830 #endif
831   init_xinput();
832   
833   /* set up our server connection */
834   
835   init_socket();
836   
837   /* main loop */
838
839   if (XPending(dpy))            /* this seems necessary to get things
840                                    in sync */
841     handle_xevent();
842   while (1) 
843     {
844
845       FD_ZERO(&readfds);
846       FD_SET(ConnectionNumber(dpy),&readfds);
847       FD_SET(socket_fd,&readfds);
848
849       if (select(8*sizeof(readfds),&readfds,
850                  (fd_set *)0,(fd_set *)0, (struct timeval *)0) < 0)
851         {
852           fprintf(stderr,"Error in select\n");
853           exit(1);
854         }
855
856       if (FD_ISSET(socket_fd,&readfds))
857         handle_connection();
858         
859       while (XPending(dpy))
860         handle_xevent();
861     }
862
863   XCloseDisplay (dpy);
864   exit (0);
865 }