Added basic and UNTESTED pndevmapperd to catch key events and invoke scripts
authorskeezix <skeezix@flotsam-vm.(none)>
Thu, 17 Dec 2009 18:54:48 +0000 (13:54 -0500)
committerskeezix <skeezix@flotsam-vm.(none)>
Thu, 17 Dec 2009 18:54:48 +0000 (13:54 -0500)
TODO:
  - handle lid open/close events (toggle screen)
  - get it added to init scripts and image
  - handle keys .. menu -> popmenu script, key lock (?) -> toggle power mode
  - does it work at all?

Makefile
apps/pndevmapperd.c [new file with mode: 0644]
include/pnd_conf.h
lib/pnd_conf.c

index fa52006..e923a06 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -23,10 +23,10 @@ SOLIB1 = libpnd.so.1.0.1    # versioned name
 XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o
 ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_logger.o
 
-all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest pnd_run
+all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest pnd_run pndevmapperd
 
 clean:
-       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o
+       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd
        ${RM} -rf deployment/media
        find . -name "*~*" -exec rm {} \; -print
 
@@ -54,6 +54,9 @@ pndvalidator: pndvalidator.o ${SOLIB1}
 pnd_run:       pnd_run.o ${SOLIB1}
        ${CC} -lstdc++ -o bin/pnd_run pnd_run.o ${SOLIB1}
 
+pndevmapperd:  pndevmapperd.o ${SOLIB1}
+       ${CC} -lstdc++ -o bin/pndevmapperd pndevmapperd.o ${SOLIB1}
+
 # deployment and assembly components
 #
 
@@ -80,6 +83,7 @@ deploy:
        cp bin/pndnotifyd deployment/usr/bin
        cp bin/pnd_run deployment/usr/bin
        cp testdata/scripts/* deployment/usr/pandora/scripts
+       cp bin/pndevmapperd deployment/usr/bin
        # copy in freebee .pnd apps to /usr/pandora/apps
        # add pndnotify to etc/rc/startup-whatever
        cp testdata/sh/pndnotifyd deployment/etc/init.d/pndnotifyd
diff --git a/apps/pndevmapperd.c b/apps/pndevmapperd.c
new file mode 100644 (file)
index 0000000..7475903
--- /dev/null
@@ -0,0 +1,356 @@
+
+/* pndevmapperd exists to watch for a few interesting events and to launch scripts when they occur
+ * ie: when the lid closes, should invoke power-mode toggle sh-script
+ */
+
+// woot for writing code while sick.
+
+#include <stdio.h> /* for printf, NULL */
+#include <stdlib.h> /* for free */
+#include <string.h> /* for strdup */
+#include <unistd.h>    // for exit()
+#include <sys/types.h> // for umask
+#include <sys/stat.h>  // for umask
+#include <fcntl.h> // for open(2)
+#include <errno.h> // for errno
+
+#include <linux/input.h> // for keys
+//#include "../../kernel-rip/input.h" // for keys
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+#include "pnd_discovery.h"
+#include "pnd_locate.h"
+#include "pnd_pndfiles.h"
+#include "pnd_pxml.h"
+#include "pnd_logger.h"
+
+// daemon and logging
+//
+unsigned char g_daemon_mode = 0;
+
+typedef enum {
+  pndn_debug = 0,
+  pndn_rem,          // will set default log level to here, so 'debug' is omitted
+  pndn_warning,
+  pndn_error,
+  pndn_none
+} pndnotify_loglevels_e;
+
+// event-to-sh mapping
+//
+typedef struct {
+  unsigned char key_p; // 1 if its a key, otherwise an event
+  int keycode;         // scancode for the key in question
+  char *script;        // script to invoke
+  //unsigned int hold_min; // minimum hold-time to trigger
+} evmap_t;
+
+#define MAXEVENTS 255
+evmap_t g_evmap [ MAXEVENTS ];
+unsigned int g_evmap_max = 0;
+
+// key definition
+//
+typedef struct {
+  int keycode;
+  char *keyname;
+} keycode_t;
+
+keycode_t keycodes[] = {
+  { KEY_A, "a" },
+  { KEY_MENU, "pandora" },
+  { -1, NULL }
+};
+
+/* get to it
+ */
+void dispatch_key ( int keycode, int val );
+
+static void usage ( char *argv[] ) {
+  printf ( "%s [-d]\n", argv [ 0 ] );
+  printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" );
+  printf ( "Signal: HUP the process to force reload of configuration and reset the notifier watch paths\n" );
+  return;
+}
+
+int main ( int argc, char *argv[] ) {
+  int i;
+
+  for ( i = 1; i < argc; i++ ) {
+
+    if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'd' ) {
+      //printf ( "Going daemon mode. Silent running.\n" );
+      g_daemon_mode = 1;
+    } else {
+      usage ( argv );
+      exit ( 0 );
+    }
+
+  } // for
+
+  /* enable logging?
+   */
+  if ( g_daemon_mode ) {
+    // nada
+  } else {
+    pnd_log_set_filter ( pndn_rem );
+    pnd_log_set_pretext ( "pndevmapperd" );
+    pnd_log_to_stdout();
+    pnd_log ( pndn_rem, "log level starting as %u", pnd_log_get_filter() );
+  }
+
+  // basic daemon set up
+  if ( g_daemon_mode ) {
+
+    // set a CWD somewhere else
+    chdir ( "/tmp" );
+
+    // detach from terminal
+    if ( ( i = fork() ) < 0 ) {
+      pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
+      exit ( i );
+    }
+    if ( i ) {
+      exit ( 0 ); // exit parent
+    }
+    setsid();
+
+    // umask
+    umask ( 022 ); // emitted files can be rwxr-xr-x
+    
+  } // set up daemon
+
+  /* inhale config or die trying
+   */
+  char *configpath;
+
+  // attempt to fetch a sensible default searchpath for configs
+  configpath = pnd_conf_query_searchpath();
+
+  // attempt to fetch the apps config. since it finds us the runscript
+  pnd_conf_handle evmaph;
+
+  evmaph = pnd_conf_fetch_by_id ( pnd_conf_evmap, configpath );
+
+  if ( ! evmaph ) {
+    // couldn't locate conf, just bail
+    pnd_log ( pndn_error, "ERROR: Couldn't locate conf file\n" );
+    exit ( -1 );
+  }
+
+  /* iterate across conf, stocking the event map
+   */
+  void *n = pnd_box_get_head ( evmaph );
+
+  while ( n ) {
+    char *k = pnd_box_get_key ( n );
+    //printf ( "key %s\n", k );
+
+    if ( strncmp ( k, "keys.", 5 ) == 0 ) {
+      k += 5;
+
+      // figure out which keycode we're talking about
+      keycode_t *p = keycodes;
+      while ( p -> keycode != -1 ) {
+       if ( strcasecmp ( p -> keyname, k ) == 0 ) {
+         break;
+       }
+       p++;
+      }
+
+      if ( p -> keycode != -1 ) {
+       g_evmap [ g_evmap_max ].key_p = 1;
+       g_evmap [ g_evmap_max ].keycode = p -> keycode;
+       g_evmap [ g_evmap_max ].script = n;
+       pnd_log ( pndn_debug, "Registered key %s [%d] to script %s\n", p -> keyname, p -> keycode, (char*) n );
+       g_evmap_max++;
+      } else {
+       pnd_log ( pndn_warning, "WARNING! Key '%s' is not handled by pndevmapperd yet! Skipping.", k );
+      }
+
+    } else if ( strncmp ( k, "events.", 7 ) == 0 ) {
+      k += 7;
+
+    } else {
+      // uhhh
+      pnd_log ( pndn_warning, "Unknown config key '%s'; skipping.\n", k );
+    }
+
+    n = pnd_box_get_next ( n );
+  } // while
+
+  if ( pnd_conf_get_as_int ( evmaph, "pndevmapperd.loglevel" ) != PND_CONF_BADNUM ) {
+    pnd_log_set_filter ( pnd_conf_get_as_int ( evmaph, "pndevmapperd.loglevel" ) );
+    pnd_log ( pndn_rem, "config file causes loglevel to change to %u", pnd_log_get_filter() );
+  }
+
+  /* do we have anything to do?
+   */
+  if ( ! g_evmap_max ) {
+    // uuuh, nothing to do?
+    pnd_log ( pndn_warning, "WARNING! No events configured to watch, so just spinning wheels...\n" );
+    exit ( -1 );
+  } // spin
+
+  /* actually try to do something useful
+   */
+
+  // stolen in part from notaz :)
+
+  // try to locate the appropriate devices
+  int id;
+  int fds [ 5 ] = { -1, -1, -1, -1, -1 }; // 0 = keypad, 1 = gpio keys
+  int imaxfd = 0;
+
+  for ( id = 0; ; id++ ) {
+    char fname[64];
+    char name[256] = { 0, };
+    int fd;
+
+    snprintf ( fname, sizeof ( fname ), "/dev/input/event%i", id );
+    fd = open ( fname, O_RDONLY );
+
+    if ( fd == -1 ) {
+      break;
+    }
+
+    ioctl (fd, EVIOCGNAME(sizeof(name)), name );
+
+    if ( strcmp ( name, "omap_twl4030keypad" ) == 0 ) {
+      fds [ 0 ] = fd;
+    } else if (strcmp(name, "gpio-keys") == 0) {
+      fds [ 1 ] = fd;
+    } else {
+      close ( fd );
+      continue;
+    }
+
+    if (imaxfd < fd) imaxfd = fd;
+  } // for
+
+  if ( fds [ 0 ] == -1 ) {
+    pnd_log ( pndn_warning, "WARNING! Couldn't find keypad device\n" );
+  }
+
+  if ( fds [ 1 ] == -1 ) {
+    pnd_log ( pndn_warning, "WARNING! couldn't find button device\n" );
+  }
+
+  if ( fds [ 0 ] == -1 && fds [ 1 ] == -1 ) {
+    pnd_log ( pndn_error, "ERROR! Couldn't find either device; exiting!\n" );
+    exit ( -2 );
+  }
+
+  /* loop forever, watching for events
+   */
+
+  while ( 1 ) {
+    struct input_event ev[64];
+
+    int fd = -1, rd, ret;
+    fd_set fdset;
+
+    FD_ZERO ( &fdset );
+
+    for (i = 0; i < 2; i++) {
+      if ( fds [ i ] != -1 ) {
+       FD_SET( fds [ i ], &fdset );
+      }
+    }
+
+    ret = select ( imaxfd + 1, &fdset, NULL, NULL, NULL );
+
+    if ( ret == -1 ) {
+      pnd_log ( pndn_error, "ERROR! select(2) failed with: %s\n", strerror ( errno ) );
+      break;
+    }
+
+    for ( i = 0; i < 2; i++ ) {
+      if ( fds [ i ] != -1 && FD_ISSET ( fds [ i ], &fdset ) ) {
+       fd = fds [ i ];
+      }
+    }
+
+    /* buttons or keypad */
+    rd = read ( fd, ev, sizeof(struct input_event) * 64 );
+    if ( rd < (int) sizeof(struct input_event) ) {
+      pnd_log ( pndn_error, "ERROR! read(2) input_event failed with: %s\n", strerror ( errno ) );
+      break;
+    }
+
+    for (i = 0; i < rd / sizeof(struct input_event); i++ ) {
+
+      if ( ev[i].type == EV_SYN ) {
+       continue;
+      } else if ( ev[i].type == EV_KEY ) {
+
+       keycode_t *p = keycodes;
+       while ( p -> keycode != -1 ) {
+         if ( p -> keycode == ev [ i ].code ) {
+           break;
+         }
+         p++;
+       }
+
+       if ( p -> keycode != -1 ) {
+         pnd_log ( pndn_debug, "Key Event: key %s [%d] value %d\n", p -> keyname, p -> keycode, ev [ i ].value );
+         dispatch_key ( p -> keycode, ev [ i ].value );
+       } else {
+         pnd_log ( pndn_warning, "Unknown Key Event: keycode %d value %d\n",  ev [ i ].code, ev [ i ].value );
+       }
+
+      } else {
+       pnd_log ( pndn_warning, "WARNING: Unexpected event type %i received\n", ev[i].type );
+       continue;
+      }
+
+    } // for
+
+  } // while
+
+  for (i = 0; i < 2; i++) {
+    if ( i != 2 && fds [ i ] != -1 ) {
+      close (fds [ i ] );
+    }
+  }
+
+  return ( 0 );
+} // main
+
+// this should really register the keystate and time, and then another func to monitor
+// time-passage and check the registered list and act on the event..
+void dispatch_key ( int keycode, int val ) {
+  unsigned int i;
+
+  while ( i < g_evmap_max ) {
+
+    if ( ( g_evmap [ i ].key_p ) &&
+        ( g_evmap [ i ].keycode == keycode ) )
+    {
+
+      if ( g_evmap [ i ].script ) {
+       int x;
+
+       if ( ( x = fork() ) < 0 ) {
+         pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
+         exit ( -3 );
+       }
+
+       if ( x == 0 ) {
+         pnd_log ( pndn_debug, "REM: Invoking %s\n", g_evmap [ i ].script );
+         execl ( g_evmap [ i ].script, g_evmap [ i ].script, (char*)NULL );
+         pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", g_evmap [ i ].script );
+         exit ( -4 );
+       }
+
+      }
+
+      return;
+    }
+
+  } // while
+
+  return;
+}
index 9af6eee..8779f17 100644 (file)
@@ -68,6 +68,7 @@ typedef enum {
   pnd_conf_startup,        // provides list of startup applications, basic shell application, etc.
   pnd_conf_desktop,        // provides settings for the launchers
   pnd_conf_categories,     // provides mapping from PXML category to dot-desktop category
+  pnd_conf_evmap,          // provides mapping from event to sh-script
 } pnd_conf_filename_e;
 
 typedef struct {
index 636625e..64c9ade 100644 (file)
@@ -14,6 +14,7 @@ pnd_conf_filename_t pnd_conf_filenames[] = {
   { pnd_conf_startup,      "startup" },
   { pnd_conf_desktop,      "desktop" },
   { pnd_conf_categories,   "categories" },
+  { pnd_conf_evmap,        "eventmap" },
   { pnd_conf_nil,          NULL },
 };