source: trunk/third/gtk/gdk/gxid.c @ 14810

Revision 14810, 17.8 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14809, which included commits to RCS files with non-trunk default branches.
Line 
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
24char *program_name;
25Display *dpy;
26Window root_window;             /* default root window of dpy */
27int port = 0;                   /* port to listen on */
28int socket_fd = 0;              /* file descriptor of socket */
29typedef struct GxidWindow_ GxidWindow;
30
31typedef struct GxidDevice_ GxidDevice;
32struct GxidDevice_ {
33  XID id;
34  int exclusive;
35  int ispointer;
36 
37  XDevice *xdevice;
38  int motionnotify_type;
39  int changenotify_type;
40};
41
42struct 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
50GxidDevice **devices = NULL;
51int num_devices = 0;
52GxidWindow **windows = NULL;
53int num_windows = 0;
54
55void
56handler(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
64void
65init_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
98static void
99enable_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 */
133static int
134switch_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
174void
175disable_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
186GxidDevice *
187init_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
225void
226init_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
260Window
261gxi_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
280int
281handle_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 its 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
408int
409handle_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
483void
484handle_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
563void
564handle_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
613void
614handle_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
644void
645handle_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
674void
675handle_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
712void
713handle_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
766void
767usage(void)
768{
769  fprintf(stderr,"Usage: %s [-d display] [-p --gxid-port port]\n",
770          program_name);
771  exit(1);
772}
773
774int
775main(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}
Note: See TracBrowser for help on using the repository browser.