Added my own evdev handler for dpad/dpadbuttons/nubs, works fantastic
authorskeezix <skeezix@flotsam-vm.(none)>
Mon, 15 Mar 2010 02:14:02 +0000 (22:14 -0400)
committerskeezix <skeezix@flotsam-vm.(none)>
Mon, 15 Mar 2010 02:14:02 +0000 (22:14 -0400)
Makefile
include/pnd_device.h
include/pnd_io_evdev.h [new file with mode: 0644]
include/pnd_io_ioctl.h [new file with mode: 0644]
lib/pnd_io_evdev.c [new file with mode: 0644]
lib/pnd_io_gpio.c
lib/pnd_io_ioctl.c [new file with mode: 0644]
test/evdevtest.c [new file with mode: 0644]

index bd8e912..eb73f4d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -21,12 +21,12 @@ LIB = libpnd.a
 SOLIB = libpnd.so.1         # canonicle name
 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 pnd_dbusnotify.o pnd_device.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_io_ioctl.o pnd_io_evdev.o pnd_logger.o pnd_dbusnotify.o pnd_device.o
 
-all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper
+all: ${SOLIB} ${LIB} conftest discotest evdevtest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper
 
 clean:
-       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info 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/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o
+       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o evdevtest.o bin/evdevtest bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info 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/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o
        ${RM} -rf deployment/media deployment/etc/pandora/mmenu
        find . -name "*~*" -exec rm {} \; -print
 
@@ -114,6 +114,9 @@ conftest:   conftest.o ${LIB}
 discotest:     discotest.o ${LIB}
        ${CC} -lstdc++ -o bin/discotest discotest.o libpnd.a
 
+evdevtest:     evdevtest.o ${LIB}
+       ${CC} -lstdc++ -o bin/evdevtest evdevtest.o -static libpnd.a
+
 notifytest:    notifytest.o ${LIB}
        ${CC} -lstdc++ -o bin/notifytest notifytest.o libpnd.a
 
index d0b32f5..acd1298 100644 (file)
@@ -31,6 +31,14 @@ extern "C" {
 #define PND_DEVICE_LED_BT      "/sys/class/leds/pandora::bluetooth"
 #define PND_DEVICE_LED_SUFFIX_BRIGHTNESS "/brightness"
 
+// device names
+#define PND_EVDEV_NUB1    "vsense66"
+#define PND_EVDEV_NUB2    "vsense67"
+#define PND_EVDEV_KEYPAD  "omap_twl4030keypad"
+#define PND_EVDEV_GPIO    "gpio-keys"
+#define PND_EVDEV_TS      "ADS784x Touchscreen"
+#define PND_EVDEV_POWER   "triton2-pwrbutton"
+
 /* utility
  */
 unsigned char pnd_device_open_write_close ( char *name, char *v );
diff --git a/include/pnd_io_evdev.h b/include/pnd_io_evdev.h
new file mode 100644 (file)
index 0000000..3503a9d
--- /dev/null
@@ -0,0 +1,69 @@
+
+#ifndef h_pnd_io_evdev_h
+#define h_pnd_io_evdev_h
+
+// some handy routines for open/close/monitor of the evdev controller devices ..
+// ie: so you can watch the analogs and d-pads pretty easily, for when you don't
+// want to rely on SDL or whatever
+//
+// this technique works fine for nubs, d-pads, even keyboard and various buttons
+// this API may only handle some parts for now, but you coudl easily duplicate some of the
+// code to extend for adding keyboard.
+//
+// dpad, nubs, start/select/pandora, triggers all work fine as of Mar 2010.
+//
+
+// can get analog nubs, d-pads, or even keyboard A-Z type keys here
+// some special ones like Power are on other devices than you'd expect, but all good
+
+typedef enum {
+  pnd_evdev_dpads = 0,   // for d-pad and d-pad-buttons
+  pnd_evdev_nub1,
+  pnd_evdev_nub2,
+  pnd_evdev_power,
+  pnd_evdev_max
+} pnd_evdev_e;
+
+unsigned char pnd_evdev_open ( pnd_evdev_e device ); // returns 0 on error, >0 success
+void pnd_evdev_close ( pnd_evdev_e device );
+void pnd_evdev_closeall ( void );
+int pnd_evdev_get_fd ( unsigned char handle ); // obtain actual fd from handle
+int pnd_evdev_open_by_name ( char *devname ); // internal but handy; see device names in pnd_device.h
+
+typedef enum {
+  pnd_evdev_left = (1<<0),     // these are bitmask; ex: (pnd_evdev_left | pnd_evdev_up)
+  pnd_evdev_right = 1<<1,
+  pnd_evdev_up = 1<<2,
+  pnd_evdev_down = 1<<3,
+  pnd_evdev_x = 1<<4,
+  pnd_evdev_y = 1<<5,
+  pnd_evdev_a = 1<<6,
+  pnd_evdev_b = 1<<7,
+  pnd_evdev_ltrigger = 1<<8,
+  pnd_evdev_rtrigger = 1<<9,
+  pnd_evdev_start = 1<<10,
+  pnd_evdev_select = 1<<11,
+  pnd_evdev_pandora = 1<<12
+} pnd_evdev_dpad_e;
+
+typedef struct {
+  int x;
+  int y;
+} pnd_nubstate_t;
+
+// catchup() - catch up any pending events
+// return 0 if something weird happened, like device error; in that case, closeall and reopen?
+// return 1 if looks like state is all up to date now (ie: no more events)
+unsigned char pnd_evdev_catchup ( unsigned char blockp ); // will do all open devices
+
+// fetch dpad state -- a mask of what buttons are pressed currently
+// return -1 if device not open
+int pnd_evdev_dpad_state ( pnd_evdev_e device ); // returns bitmask of pnd_evdev_dpad_e
+
+// try to obtain X/Y axis for the requested nub
+// r_nubstate best not be null or the behaviour is undefined. (Well, it is defined .. *catch fire*)
+// return 1 when state is copied over
+// return -1 when device not opened
+int pnd_evdev_nub_state ( pnd_evdev_e nubdevice, pnd_nubstate_t *r_nubstate );
+
+#endif
diff --git a/include/pnd_io_ioctl.h b/include/pnd_io_ioctl.h
new file mode 100644 (file)
index 0000000..5ed7b1d
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef h_pnd_io_ioctl_h
+#define h_pnd_io_ioctl_h
+
+// this is a simple 'is key pressed?' type routine, using standard ioctl; if you don't want to
+// monitor the events and just ewant to see whats up, this can be handy.
+// returns -1 on error, 0 for 'not down', and >0 for 'is down'
+int pnd_is_key_down ( int fd, int key );
+
+#endif
diff --git a/lib/pnd_io_evdev.c b/lib/pnd_io_evdev.c
new file mode 100644 (file)
index 0000000..c57392e
--- /dev/null
@@ -0,0 +1,300 @@
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h> /* abs() */
+#include <linux/input.h> /* struct input_event */
+#include <sys/select.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "pnd_device.h"
+#include "pnd_io_evdev.h"
+
+typedef struct {
+  int fd;
+  // state info
+  union {
+    pnd_nubstate_t nub;
+    unsigned int buttons;
+  } state;
+} pnd_evmap_t;
+static pnd_evmap_t evmap [ pnd_evdev_max ];
+static unsigned char evmap_first = 1;
+static pnd_evdev_e evmapback [ 10 ]; // map fd back to which device
+
+unsigned char pnd_evdev_open ( pnd_evdev_e device ) {
+
+  // if first, reset global struct
+  if ( evmap_first ) {
+    evmap_first = 0; // don't repeat
+    unsigned char i;
+    for ( i = 0; i < pnd_evdev_max; i++ ) {
+      evmap [ i ].fd = -1; // not opened
+    }
+  } // if first
+
+  // do it
+  switch ( device ) {
+
+  case pnd_evdev_dpads:
+    if ( ( evmap [ pnd_evdev_dpads ].fd = pnd_evdev_open_by_name ( PND_EVDEV_GPIO ) ) >= 0 ) {
+      evmapback [ evmap [ pnd_evdev_dpads ].fd ] = pnd_evdev_dpads;
+      return ( 1 );
+    }
+    break;
+
+  case pnd_evdev_nub1:
+    if ( ( evmap [ pnd_evdev_nub1 ].fd = pnd_evdev_open_by_name ( PND_EVDEV_NUB1 ) ) >= 0 ) {
+      evmapback [ evmap [ pnd_evdev_nub1 ].fd ] = pnd_evdev_nub1;
+      return ( 1 );
+    }
+    break;
+
+  case pnd_evdev_nub2:
+    if ( ( evmap [ pnd_evdev_nub2 ].fd = pnd_evdev_open_by_name ( PND_EVDEV_NUB2 ) ) >= 0 ) {
+      evmapback [ evmap [ pnd_evdev_nub2 ].fd ] = pnd_evdev_nub2;
+      return ( 1 );
+    }
+    break;
+
+  case pnd_evdev_power:
+    if ( ( evmap [ pnd_evdev_power ].fd = pnd_evdev_open_by_name ( PND_EVDEV_POWER ) ) >= 0 ) {
+      evmapback [ evmap [ pnd_evdev_power ].fd ] = pnd_evdev_power;
+      return ( 1 );
+    }
+    break;
+
+  case pnd_evdev_max:
+    return ( 0 ); // shutup gcc
+  } // switch
+
+  // error opening, or not supported in this bit of code, or wtf
+  return ( 0 );
+}
+
+void pnd_evdev_close ( pnd_evdev_e device ) {
+  // if open
+  if ( evmap [ device ].fd >= 0 ) {
+    close ( evmap [ device ].fd ); // close it
+    evmap [ device ].fd = -1; // flag as closed
+  }
+  return;
+}
+
+void pnd_evdev_closeall ( void ) {
+  unsigned char i;
+
+  for ( i = 0; i < pnd_evdev_max; i++ ) {
+    pnd_evdev_close ( i );
+  } // for
+
+  return;
+}
+
+int pnd_evdev_get_fd ( unsigned char device ) {
+  return ( evmap [ device ].fd );
+}
+
+int pnd_evdev_open_by_name ( char *devname ) {
+  int fd;
+  char fname [ 64 ];
+  char name[256] = { 0, };
+  unsigned char id;
+
+  // keep opening event# devices until we run out
+  for ( id = 0; ; id++ ) {
+
+    // set temp filename
+    snprintf ( fname, sizeof ( fname ), "/dev/input/event%i", id );
+
+    // try open, or bail out if no more
+    if ( ( fd = open ( fname, O_RDONLY | O_NDELAY ) ) < 0 ) {
+      return ( -1 );
+    }
+
+    // fetch the devices name
+    if ( ioctl (fd, EVIOCGNAME(sizeof(name)), name ) < 0 ) {
+      close ( fd ); // couldn't get the name?!
+      continue; // pretty odd to not have a name, but maybe the next device does..
+    }
+
+    // are we done?
+    if ( strcmp ( name, devname ) == 0 ) {
+      return ( fd );
+    }
+
+    // next!
+    close ( fd );
+  }
+
+  return ( -1 ); // couldn't find it
+}
+
+unsigned char pnd_evdev_catchup ( unsigned char blockp ) {
+  fd_set fdset;
+  int maxfd = -99;
+  int i;
+
+  // if not blocking, we want a zero duration timeout
+  struct timeval tv;
+  struct timeval *ptv;
+  bzero ( &tv, sizeof(struct timeval) ); // set to 0s, ie: return immediately
+
+  if ( blockp ) {
+    ptv = NULL;
+  } else {
+    ptv = &tv;
+  }
+
+  // clear watch fd's
+  FD_ZERO ( &fdset );
+
+  // for all our open evdev's, flag them in the watch fd list
+  for ( i = 0; i < pnd_evdev_max; i++ ) {
+    if ( evmap [ i ].fd >= 0 ) {
+      // set the fd into the select set
+      FD_SET( evmap [ i ].fd, &fdset );
+      // select(2) needs to know the highest fd
+      if ( evmap [ i ].fd > maxfd ) {
+       maxfd = evmap [ i ].fd;
+      } // if
+    } // if
+  } // for
+
+  if ( maxfd < 0 ) {
+    return ( 0 ); // nothing to do
+  }
+
+  int ret;
+
+  ret = select ( maxfd + 1, &fdset, NULL, NULL, ptv );
+
+  if ( ret < 0 ) { 
+    return ( 0 ); // something bad
+  } else if ( ret == 0 ) {
+    return ( 1 ); // all good, nothing here
+  }
+
+  // all good, and something in the queue..
+  for ( i = 0; i < pnd_evdev_max; i++ ) {
+    // if open..
+    if ( evmap [ i ].fd >= 0 ) {
+      // if pending in the queue
+      if ( FD_ISSET ( evmap [ i ].fd, &fdset ) ) {
+
+       int rd, fd;
+       int j;
+       struct input_event ev[64];
+
+       fd = evmap [ i ].fd;
+
+       // pull events from the queue; max 64 events
+       rd = read ( fd, ev, sizeof(struct input_event) * 64 );
+       if ( rd < (int) sizeof(struct input_event) ) {
+         // less than a whole event-struct? bad!
+         break;
+       }
+
+       // for each event in the pulled events, parse it
+       for (j = 0; j < rd / sizeof(struct input_event); j++ ) {
+
+         // SYN, ignore
+         if ( ev[j].type == EV_SYN ) {
+           continue;
+
+         } else if ( ev[j].type == EV_KEY ) {
+           // KEY event -- including dpads
+
+           // store val for key
+           // keycode: ev[j].code -> keycode, see linux/input.h
+           // state val: ev[j].value -> 0keyup, 1keydown
+
+#if 0 // as of mid-March; notaz did a recent keycode change so had to refigure it out
+           printf ( "evdev\tkey %d\tvalue %d\n", ev[j].code, ev[j].value );
+           // a 102    b 107    x 109     y 104
+           // ltrigger 54           rtrigger 97
+           // start  56      select 29    pandora 139
+#endif
+
+           unsigned int state = evmap [ pnd_evdev_dpads ].state.buttons;
+
+           switch ( ev[j].code ) {
+
+           case KEY_UP:     state &= ~pnd_evdev_up; if ( ev[j].value ) state |= pnd_evdev_up; break;
+           case KEY_DOWN:   state &= ~pnd_evdev_down; if ( ev[j].value ) state |= pnd_evdev_down; break;
+           case KEY_LEFT:   state &= ~pnd_evdev_left; if ( ev[j].value ) state |= pnd_evdev_left; break;
+           case KEY_RIGHT:  state &= ~pnd_evdev_right; if ( ev[j].value ) state |= pnd_evdev_right; break;
+           case KEY_PAGEDOWN: /*KEY_X*/     state &= ~pnd_evdev_x; if ( ev[j].value ) state |= pnd_evdev_x; break;
+           case KEY_PAGEUP: /*KEY_Y*/       state &= ~pnd_evdev_y; if ( ev[j].value ) state |= pnd_evdev_y; break;
+           case KEY_HOME: /*KEY_A*/         state &= ~pnd_evdev_a; if ( ev[j].value ) state |= pnd_evdev_a; break;
+           case KEY_END: /*KEY_B*/          state &= ~pnd_evdev_b; if ( ev[j].value ) state |= pnd_evdev_b; break;
+
+           case KEY_RIGHTSHIFT: // ltrigger
+             state &= ~pnd_evdev_ltrigger; if ( ev[j].value ) state |= pnd_evdev_ltrigger; break;
+           case KEY_RIGHTCTRL: // rtrigger
+             state &= ~pnd_evdev_rtrigger; if ( ev[j].value ) state |= pnd_evdev_rtrigger; break;
+
+             // start
+             // select
+             // pandora
+           case KEY_LEFTALT:     state &= ~pnd_evdev_start; if ( ev[j].value ) state |= pnd_evdev_start; break;
+           case KEY_LEFTCTRL:     state &= ~pnd_evdev_select; if ( ev[j].value ) state |= pnd_evdev_select; break;
+           case KEY_MENU:     state &= ~pnd_evdev_pandora; if ( ev[j].value ) state |= pnd_evdev_pandora; break;
+
+           } // switch
+
+           evmap [ pnd_evdev_dpads ].state.buttons = state;
+
+         } else if ( ev[j].type == EV_SW ) {
+           // SWITCH event
+
+         } else if ( ev[j].type == EV_ABS ) {
+           // ABS .. ie: nub
+
+           // vsense66 -> nub1 (left)
+           // vsense67 -> nub2 (right)
+           pnd_nubstate_t *pn = NULL;
+           if ( evmapback [ fd ] == pnd_evdev_nub1 ) {
+             pn = &( evmap [ pnd_evdev_nub1 ].state.nub );
+           } else {
+             pn = &( evmap [ pnd_evdev_nub2 ].state.nub );
+           }
+
+           if ( ev[j].code == ABS_X ) {
+             pn -> x = ev[j].value;
+           } else if ( ev[j].code == ABS_Y ) {
+             pn -> y = ev[j].value;
+           } else {
+             //printf("unexpected EV_ABS code: %i\n", ev[i].code);
+           }
+
+         } else {
+           // unknown?
+           continue;
+         } // event type?
+
+       } // for each pulled event
+
+      } // if pending
+    } // if open
+  } // for all device..
+
+  return ( 1 );
+}
+
+int pnd_evdev_dpad_state ( pnd_evdev_e device ) {
+  if ( evmap [ device ].fd < 0 ) {
+    return ( -1 );
+  }
+  return ( evmap [ device ].state.buttons );
+}
+
+int pnd_evdev_nub_state ( pnd_evdev_e nubdevice, pnd_nubstate_t *r_nubstate ) {
+  if ( evmap [ nubdevice ].fd < 0 ) {
+    return ( -1 );
+  }
+  memcpy ( r_nubstate, &(evmap [ nubdevice ].state.nub), sizeof(pnd_nubstate_t) );
+  return ( 1 );
+}
index 23105e5..d36c2ab 100755 (executable)
@@ -1,4 +1,6 @@
 
+// this is pulled from cpasjuste and/or pickle
+
 #if defined (_PANDORA) || !defined (EMULATOR)
 
 /* cribbed from pnd_keytypes.h so as to make it unnecessary */
diff --git a/lib/pnd_io_ioctl.c b/lib/pnd_io_ioctl.c
new file mode 100644 (file)
index 0000000..c501842
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <linux/input.h>
+#include <sys/ioctl.h>
+#include <strings.h>
+#include "pnd_io_ioctl.h"
+
+int pnd_is_key_down ( int fd, int key ) {
+  unsigned int size = KEY_MAX / 8 + 1;
+  unsigned char buf [ size ];
+  bzero ( buf, size );
+
+  if ( ioctl ( fd, EVIOCGKEY(size), buf ) < 0 ) {
+    return ( -1 ); // error
+  }
+
+  if ( buf [ key / 8 ] & ( 1<<(key%8) ) ) {
+    return ( 1 ); // down
+  }
+
+  return ( 0 ); // not down
+}
diff --git a/test/evdevtest.c b/test/evdevtest.c
new file mode 100644 (file)
index 0000000..1772679
--- /dev/null
@@ -0,0 +1,75 @@
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "pnd_device.h"
+#include "pnd_io_evdev.h"
+
+int main ( void ) {
+
+  if ( ! pnd_evdev_open ( pnd_evdev_dpads ) ) {
+    printf ( "Couldn't open dpads\n" );
+    exit ( 0 );
+  }
+
+  if ( ! pnd_evdev_open ( pnd_evdev_nub1 ) ) {
+    printf ( "Couldn't open nub1\n" );
+    exit ( 0 );
+  }
+
+  if ( ! pnd_evdev_open ( pnd_evdev_nub2 ) ) {
+    printf ( "Couldn't open nub2\n" );
+    exit ( 0 );
+  }
+
+  while ( 1 ) {
+
+    // poll and process
+    if ( ! pnd_evdev_catchup ( 1 /* block */ ) ) {
+      printf ( "Couldn't catch up events\n" );
+      exit ( 0 );
+    }
+
+    // check state
+    int s;
+
+    s = pnd_evdev_dpad_state ( pnd_evdev_dpads );
+
+    if ( s & pnd_evdev_up ) { printf ( "d-pad up\n" ); }
+    if ( s & pnd_evdev_down ) { printf ( "d-pad down\n" ); }
+    if ( s & pnd_evdev_left ) { printf ( "d-pad left\n" ); }
+    if ( s & pnd_evdev_right ) { printf ( "d-pad right\n" ); }
+
+    if ( s & pnd_evdev_x ) { printf ( "d-pad x\n" ); }
+    if ( s & pnd_evdev_y ) { printf ( "d-pad y\n" ); }
+    if ( s & pnd_evdev_a ) { printf ( "d-pad a\n" ); }
+    if ( s & pnd_evdev_b ) { printf ( "d-pad b\n" ); }
+    if ( s & pnd_evdev_ltrigger ) { printf ( "d-pad ltrigger\n" ); }
+    if ( s & pnd_evdev_rtrigger ) { printf ( "d-pad rtrigger\n" ); }
+
+    if ( s & pnd_evdev_start ) { printf ( "d-pad start\n" ); }
+    if ( s & pnd_evdev_select ) { printf ( "d-pad select\n" ); }
+    if ( s & pnd_evdev_pandora ) { printf ( "d-pad pandora\n" ); }
+
+    pnd_nubstate_t ns;
+
+    pnd_evdev_nub_state ( pnd_evdev_nub1, &ns );
+    if ( ns.x || ns.y ) {
+      printf ( "ns1 x / y: %d / %d\n", ns.x, ns.y );
+    }
+
+    pnd_evdev_nub_state ( pnd_evdev_nub2, &ns );
+    if ( ns.x || ns.y ) {
+      printf ( "ns2 x / y: %d / %d\n", ns.x, ns.y );
+    }
+
+    // sleep
+    //usleep ( 500000 );
+
+  } // while
+
+  pnd_evdev_closeall();
+
+  return ( 0 );
+}