Hooked up support for a Griffin Powermate.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 15 Apr 2014 10:06:56 -0400
changeset 28 60136c984e3b
parent 27 d4d54dc1ee5f
child 29 769393898e93
Hooked up support for a Griffin Powermate.

If you have access to one and run the program with --powermate=/dev/whatever,
1pass will accept button presses on the Powermate as if you hit the magic
key combo. Also, it will toggle the light on the device to pulse when the
keychain is unlocked.
1pass.c
1pass.lua
--- a/1pass.c	Mon Feb 10 20:12:41 2014 -0500
+++ b/1pass.c	Tue Apr 15 10:06:56 2014 -0400
@@ -1,7 +1,12 @@
+#include <linux/input.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+
 #include "lua.h"
 #include "lauxlib.h"
 #include "lualib.h"
@@ -14,6 +19,87 @@
 
 #define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
 
+// plug in a Griffin Powermate, make sure you have access to it, and run with
+//  --powermate=/dev/input/eventX
+static int powermate_fd = -1;
+static int pumpPowermate(void)
+{
+    struct input_event buf[32];
+    int pressed = 0;
+    ssize_t br;
+
+    if (powermate_fd == -1)
+        return 0;  // nothing to do.
+
+    while ((br = read(powermate_fd, buf, sizeof (buf))) > 0)
+    {
+        ssize_t i;
+        br /= sizeof (buf[0]);
+        for (i = 0; i < br; i++)
+        {
+            struct input_event *ev = &buf[i];
+            if ((ev->type == EV_KEY) && (ev->code == BTN_0) && (ev->value))
+                pressed = 1;
+        } // for
+    } // while
+
+    return pressed;
+}
+
+static void setPowermateLED(const int enable)
+{
+    struct input_event ev;
+    const int brightness = enable ? 255 : 0;
+    const int pulse_speed = 255;
+    const int pulse_table = 0;
+    const int pulse_awake = enable ? 1 : 0;
+    const int pulse_asleep = 0;
+
+    if (powermate_fd == -1)
+        return;
+
+    memset(&ev, '\0', sizeof (ev));
+    ev.type = EV_MSC;
+    ev.code = MSC_PULSELED;
+    ev.value = brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20);
+
+    if (write(powermate_fd, &ev, sizeof (ev)) != sizeof (ev))
+        fprintf(stderr, "WARNING: tried to set Powermate LED and failed: %s\n", strerror(errno));
+} // setPowermateLED
+
+
+static void initPowermate(int *_argc, char **argv)
+{
+    const char *arg = "--powermate=";
+    const size_t arglen = strlen(arg);
+    int argc = *_argc;
+    int i;
+
+    for (i = 1; i < argc; i++)
+    {
+        const char *thisarg = argv[i];
+        if (strncmp(thisarg, arg, arglen) != 0)
+            continue;
+
+        thisarg += arglen;
+        powermate_fd = open(thisarg, O_RDWR);
+        if (powermate_fd == -1)
+            fprintf(stderr, "WARNING: couldn't open Powermate at %s: %s\n", thisarg, strerror(errno));
+        else
+        {
+            const int flags = fcntl(powermate_fd, F_GETFL, 0) | O_NONBLOCK;
+            fcntl(powermate_fd, F_SETFL, flags);
+        } // else
+
+        // eliminate this command line.
+        memmove(&argv[i], &argv[i+1], (argc-i) * sizeof (char *));
+        argc--;
+    } // for
+
+    *_argc = argc;
+} // initPowermate
+
+
 static lua_State *luaState = NULL;
 static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
 static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
@@ -289,6 +375,14 @@
 } // popupGuiMenu
 
 
+static int setPowermateLED_Lua(lua_State *L)
+{
+    const int enable = lua_toboolean(L, 1);
+    setPowermateLED(enable);
+    return 0;
+} // setPowermateLED_Lua
+
+
 static void keyhookPressed(void)
 {
     lua_getglobal(luaState, "keyhookPressed");
@@ -300,6 +394,9 @@
 {
     if (pumpKeyHook())
         keyhookPressed();
+    else if (pumpPowermate())
+        keyhookPressed();
+
     return TRUE;  // keep firing timer
 } // keyhookPumper
 
@@ -375,15 +472,21 @@
     luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
     luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt");
     luaSetCFunc(luaState, copyToClipboard, "copyToClipboard");
+    luaSetCFunc(luaState, setPowermateLED_Lua, "setPowermateLED");
 
     // Set up argv table...
     lua_newtable(luaState);
     int i;
+    int luai = 1;
     for (i = 0; i < argc; i++)
     {
-        lua_pushinteger(luaState, i+1);
-        lua_pushstring(luaState, argv[i]);
-        lua_settable(luaState, -3);
+        if (argv[i])
+        {
+            lua_pushinteger(luaState, luai);
+            lua_pushstring(luaState, argv[i]);
+            lua_settable(luaState, -3);
+            luai++;
+        } // if
     } // for
     lua_setglobal(luaState, "argv");
 
@@ -397,6 +500,7 @@
 
 int main(int argc, char **argv)
 {
+    initPowermate(&argc, argv);
     gtk_init(&argc, &argv);
 
     if (!initLua(argc, argv))  // this will move control to 1pass.lua
--- a/1pass.lua	Mon Feb 10 20:12:41 2014 -0500
+++ b/1pass.lua	Tue Apr 15 10:06:56 2014 -0400
@@ -289,11 +289,15 @@
 
     keyhookRunning = true
 
+    -- !!! FIXME: this should lose the key in RAM and turn off the Powermate
+    -- !!! FIXME:  LED when the time expires instead of if the time has
+    -- !!! FIXME:  expired when the user is trying to get at the keychain.
     if passwordUnlockTime ~= nil then
         local now = os.time()
         local maxTime = (15 * 60)  -- !!! FIXME: don't hardcode.
         if os.difftime(now, passwordUnlockTime) > maxTime then
             -- lose the existing password and key, prompt user again.
+            setPowermateLED(false)
             password = argv[2]  -- might be nil, don't reset if on command line.
             keys["SL5"] = nil
         end
@@ -314,6 +318,7 @@
             end
         else
             passwordUnlockTime = os.time()
+            setPowermateLED(true)
         end
     end
 
@@ -326,6 +331,7 @@
         keys["SL5"] = nil
         passwordUnlockTime = nil
         keyhookRunning = false
+        setPowermateLED(false)
     end
     appendGuiMenuItem(topmenu, "Lock keychain", lock_callback)
 
@@ -360,6 +366,7 @@
 
 -- !!! FIXME: message box, exit if basedir is wack.
 -- !!! FIXME: this can probably happen in C now (the Lua mainline is basically gone now).
+setPowermateLED(false)  -- off by default
 print("Now waiting for the magic key combo (probably Alt-Meta-\\) ...")
 giveControlToGui()