Logo Search packages:      
Sourcecode: pbbuttonsd version File versions  Download package

input_manager.c

/* ----------------------------------------------------------------------------
 * input_manager.c
 * code to distribute input events to modules
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * ----------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include "pbbinput.h"

#include <pbb.h>

#include "gettext_macros.h"
#include "systems.h"
#include "init.h"
#include "input_manager.h"

extern volatile sig_atomic_t prgexit;

/* --- private module data structure of the input manager --- */

struct moddata_inputmanager {
      struct {
            unsigned int shift:1;
            unsigned int ctrl:1;
            unsigned int alt:1;
            unsigned int :0;
      } flags;
      unsigned short lastkey;
      struct {
            int fd;
            int (*handler)(int fd);
      } input[MAXINPUTS+1];
      struct {
            short timer;
            short start;
            void (*handler)();
      } timer[TIMERCOUNT];
      struct evdevice evdevs[EVDEVCOUNT];
      inputqueue_t *iqueues[QUEUECOUNT];
      inputqueue_t kbdqueue[MODULECOUNT];
      inputqueue_t mousequeue[MODULECOUNT];
      inputqueue_t t10queue[MODULECOUNT];
      inputqueue_t t100queue[MODULECOUNT];
      inputqueue_t t1000queue[MODULECOUNT];
      inputqueue_t queryqueue[MODULECOUNT];
      inputqueue_t configqueue[MODULECOUNT];
      inputqueue_t securequeue[MODULECOUNT];
} modbase_inputmanager;

/* --- interface functions of the inputmanager --- */

/* Init function called by the program init procedure. Function is known by
   the prginitab. It does module data initialisation. */

int
inputmanager_init ()
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int x, y, n;

      for (n=0; n <= MAXINPUTS; n++) {
      base->input[n].fd = -1;
      base->input[n].handler = NULL;
      }
      for (n=0; n < EVDEVCOUNT; n++) {
            base->evdevs[n].product = 0;
            base->evdevs[n].vendor  = 0;
            base->evdevs[n].inputid = -1;
      }

      scanEventDevices();
      
    /* each of TIMERCOUNT (=3) timers have to be initialised here.
         there is no mechanism to check if a timer slot is empty */
      base->timer[0].handler = timer10_handler;  /* timer for 10ms queue */
      base->timer[1].start = 10;
      base->timer[1].handler = timer100_handler;  /* timer for 100ms queue */
      base->timer[2].start = 100;
      base->timer[2].handler = timer1000_handler;  /* timer for 1s queue */

      base->flags.shift = 0;
      base->flags.ctrl = 0;
      base->flags.alt = 0;
      base->lastkey = 0;

      base->iqueues[KBDQUEUE] = base->kbdqueue;
      base->iqueues[MOUSEQUEUE] = base->mousequeue;
      base->iqueues[T10QUEUE] = base->t10queue;
      base->iqueues[T100QUEUE] = base->t100queue;
      base->iqueues[T1000QUEUE] = base->t1000queue;
      base->iqueues[QUERYQUEUE] = base->queryqueue;
      base->iqueues[CONFIGQUEUE] = base->configqueue;
      base->iqueues[SECUREQUEUE] = base->securequeue;

      for (y=0; y < QUEUECOUNT; y++)
            for (x=0; x < MODULECOUNT; x++)
                  base->iqueues[y][x] = NULL;

      return 0;
}

int
inputmanager_exit ()
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n;

      for (n=0; n < MAXINPUTS; n++)      /* close all input files */
            if (base->input[n].fd != -1)
                  close (base->input[n].fd);
      return 0;
}

/* ----------- */

int
track_modifier(unsigned short code, unsigned int value)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;

      if (value > 1) value = 1;   /* key repeated = pressed */
      switch (code)
      {
            case KEY_RIGHTSHIFT:
            case KEY_LEFTSHIFT:
                  base->flags.shift = value;
                  break;
            case KEY_RIGHTCTRL:
            case KEY_LEFTCTRL:
                  base->flags.ctrl = value;
                  break;
            case KEY_RIGHTALT:
            case KEY_LEFTALT:
                  base->flags.alt = value;
                  break;
      }
      return (base->flags.ctrl << 2) | (base->flags.alt << 1) | (base->flags.shift << 0);
}

int
get_modifier()
{
  struct moddata_inputmanager *base = &modbase_inputmanager;
  return (base->flags.ctrl << 2) | (base->flags.alt << 1) | (base->flags.shift << 0);
}

/* --- timer function --- */

void
call_timertick ()
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n;

      for (n=0; n < TIMERCOUNT; n++) {
            if ((--(base->timer[n].timer)) <= 0) {
                  base->timer[n].timer = base->timer[n].start;
                  base->timer[n].handler();
            }
      }
}

/* --- event handler management --- */

struct evdevice*
findEventDevice (int fd)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n;

      for (n = 0; n < EVDEVCOUNT; n++)
            if (fd == base->evdevs[n].inputid)
                  return &base->evdevs[n];
            
      return NULL;
}

void
clearEventDevice (struct evdevice *evdev)
{
      if (evdev->inputid >= 0) {
            unregister_inputhandler(evdev->inputid);
            close(evdev->inputid);
#ifdef DEBUG
            printf("Inputhandler unregistered:\t%04x %04x         \n",
                evdev->product, evdev->vendor);
#endif
      }
      evdev->product = 0;
      evdev->vendor  = 0;
      evdev->inputid = -1;
}

void
scanEventDevices ()
{
      struct moddata_inputmanager *base = &modbase_inputmanager;

      int n, m, fd, fcnt = 0;
      char filename[20];
      unsigned short id[4] = { 0,0,0,0 };

      for (n = 0, m = 0; n < EVDEVCOUNT; n++) {
            sprintf(filename, "/dev/input/event%d", n);
            if ((fd = open(filename, O_RDONLY)) >= 0) {
                  ioctl(fd, EVIOCGID, id);
                  if (id[ID_PRODUCT] != base->evdevs[m].product ||
                        id[ID_VENDOR]  != base->evdevs[m].vendor) {
                        clearEventDevice (&base->evdevs[m]);
                        base->evdevs[m].inputid = fd;
                        base->evdevs[m].product = id[ID_PRODUCT];
                        base->evdevs[m].vendor = id[ID_VENDOR];
                        register_inputhandler(fd, event_handler);
#ifdef DEBUG
                        printf("Inputhandler registered:\t%04x %04x         \n",id[ID_PRODUCT],id[ID_VENDOR]);
#endif
                  } else
                        close(fd);
                  m++;
            } else
                  fcnt++;  /* count number of event devices that could not be opened */
      }

      for (; m < EVDEVCOUNT; m++)    /* cleanup rest of list */
            clearEventDevice (&base->evdevs[m]);

      if (fcnt == EVDEVCOUNT)  /* none of the 32 devices can be opened */
            print_msg (PBB_WARN, _("No event devices available. Please check your configuration.\n"));
}


/* --- input handler --- */

int
register_inputhandler (int fd, void *func)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n;

      for (n=0; n < MAXINPUTS; n++)
            if (base->input[n].fd == -1) {
                  base->input[n].fd = fd;
                  base->input[n].handler = func;
                  return 0;
      }
      return -1;
}

void
unregister_inputhandler (int fd)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n, found = 0;

      for (n=0; n <= MAXINPUTS; n++)
            if (found) {
                  base->input[n-1].fd = base->input[n].fd;
                  base->input[n-1].handler = base->input[n].handler;
            } else if (base->input[n].fd == fd)
                  found = 1;
      if (found)
            base->input[n].fd = -1;
}     

int
create_fdset (fd_set *watchset)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      int n, maxfd;

      FD_ZERO(watchset);
      for (maxfd=n=0; n < MAXINPUTS; n++) {
            if (base->input[n].fd == -1) break;
            FD_SET(base->input[n].fd, watchset);
            if (base->input[n].fd > maxfd)
                  maxfd = base->input[n].fd;
      }
      return maxfd;
}

void
call_inputhandler(fd_set *inset)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      struct evdevice *evdev;
      int n = 0, rescan = 0;

      while (n < MAXINPUTS && base->input[n].fd != -1) {
            if (FD_ISSET(base->input[n].fd, inset)) {
                  if ((base->input[n].handler (base->input[n].fd)) == -1) {
                  if ((evdev = findEventDevice (base->input[n].fd))) {
                              clearEventDevice (evdev);
                              rescan = 1;
                        } else {
                              print_msg (PBB_ERR,
                                          _("Can't read filehandle %d anymore ... exiting!\n"),
                                          base->input[n].fd);
                              prgexit = 1;
                              return;
                        }
                  } else
                        n++;
            } else
                  n++;
      }
      
      if (rescan)     /* one or more filehandles have failed and must be closed */
            scanEventDevices ();  /* so rescan the event devices to fill the gap */
}

/* --- queue managers --- */

int
register_function (int queueid, void *func )
{
  struct moddata_inputmanager *base = &modbase_inputmanager;
  inputqueue_t *queue = base->iqueues[queueid];
  int n;

  for (n=0; n < MODULECOUNT; n++)
    if (queue[n] == NULL) {
      queue[n] = (inputqueue_t) func;
      return 0;
    }
  return -1;
}

long
process_queue_single (int queueid, long tag, long data)
{
  struct tagitem taglist[2];

  taglist_init (taglist);
  taglist_add (taglist, tag, data);
  process_queue (queueid, taglist);
  return taglist->data;
}

void
process_queue (int queueid, struct tagitem *taglist)
{
      struct moddata_inputmanager *base = &modbase_inputmanager;
      inputqueue_t *queue = base->iqueues[queueid];
      int n=0;

      if (queueid == KBDQUEUE) {
            if ((tagfind (taglist, TAG_KEYREPEAT, 0))) {
                  base->lastkey = tagfind (taglist, TAG_KEYCODE, 0);
            } else
                  base->lastkey = 0;
      }
      while ((queue[n] != NULL) && (n < MODULECOUNT)) {
            queue[n++] (taglist);
      }
}

/* --- input handlers --- */

void
ipc_handler ()
{
      struct pbbmessage *msg;
      struct tagitem *source, *dest;
      char msgbuffer[8192];

      if ((ipc_receive (msgbuffer, sizeof(msgbuffer))) == 0) {
            msg = (struct pbbmessage *) msgbuffer;
            switch (msg->action) {
            case READVALUE:
                  process_queue (QUERYQUEUE, msg->taglist);
                  ipc_send (msg->returnport, CHANGEVALUE, msg->taglist);
                  break;
            case CHANGEVALUE:
                  process_queue (CONFIGQUEUE, msg->taglist);
                  source = dest = msg->taglist;
                  while (source->tag != TAG_END) {
                        if ((source->tag & FLG_ERROR)) {
                              dest->tag = source->tag;
                              dest->data = source->data;
                              dest++;
                        }
                        source++;
                  }
                  dest->tag = TAG_END;
                  dest->data = 0;
                  ipc_send (msg->returnport, CHANGEERROR, msg->taglist);
                  break;
            }
      }
}

int
event_handler (int fd)
{
      struct input_event inp;
      struct tagitem taglist[10];

      taglist_init(taglist);
      
      if (read(fd, &inp, sizeof(inp)) == sizeof(inp)) {
            switch (inp.type) {
            case EV_KEY:            
                  taglist_add (taglist, TAG_KEYCODE,  (long) inp.code);
                  taglist_add (taglist, TAG_KEYREPEAT,(long) inp.value);
                  taglist_add (taglist, TAG_MODIFIER, (long) track_modifier (inp.code, inp.value));
                  process_queue (KBDQUEUE, taglist);
                  break;
            case EV_REL:
                  /* TAG_MOUSERELx are currently not used. Only the occourence
                   * of this events will currently be used */
                  if (inp.code)
                        taglist_add (taglist, TAG_MOUSERELY, (long) inp.value);
                  else
                        taglist_add (taglist, TAG_MOUSERELX, (long) inp.value);
            process_queue (MOUSEQUEUE, taglist);
                  break;
            case EV_ABS:
                  /* Mouse events are not used. Only the occourence of this events
                   * will currently be used */
            process_queue (MOUSEQUEUE, taglist);
                  break;
            default:
                  break; /* catch unknown event types */
            }
            return 0;
      } else
            return -1; /* read has failed */
}

void
timer10_handler ()
{
  struct moddata_inputmanager *base = &modbase_inputmanager;
  int mod;

  struct tagitem taglist[] = { { TAG_END, 0 } };
  process_queue (T10QUEUE, taglist);

  mod = get_modifier();
  if ((mod == 0) && (base->lastkey == 0))
    process_queue (SECUREQUEUE, taglist);
}

void
timer100_handler ()
{
  struct tagitem taglist[] = { { TAG_END, 0 } };
  process_queue (T100QUEUE, taglist);
}

void
timer1000_handler ()
{
  struct tagitem taglist[] = { { TAG_END, 0 } };
  process_queue (T1000QUEUE, taglist);
}

Generated by  Doxygen 1.6.0   Back to index