Start building in GUI stuff.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 22 Dec 2013 03:01:08 -0500
changeset 11 b52e0f1798b8
parent 10 85496558f3a4
child 12 6a2d5b34d5ca
Start building in GUI stuff.
1pass.c
1pass.lua
--- a/1pass.c	Thu Dec 19 23:04:04 2013 -0500
+++ b/1pass.c	Sun Dec 22 03:01:08 2013 -0500
@@ -9,6 +9,7 @@
 #include "aes.h"
 #include "base64.h"
 #include "md5.h"
+#include <gtk/gtk.h>
 
 #define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
 
@@ -25,6 +26,15 @@
     return 1;
 } // retvalStringBytes
 
+static inline int retvalPointer(lua_State *L, void *ptr)
+{
+    if (ptr != NULL)
+        lua_pushlightuserdata(L, ptr);
+    else
+        lua_pushnil(L);
+    return 1;
+} // retvalPointer
+
 static inline void xorBlock(uint8_t *dst, const uint8_t *src)
 {
     int i;
@@ -168,6 +178,66 @@
 } // decryptBase64UsingKey
 
 
+static int makeGuiMenu(lua_State *L)
+{
+    return retvalPointer(L, gtk_menu_new());
+} // makeGuiMenu
+
+
+static void clickedMenuItem(void *arg)
+{
+    // This is the callback from GTK+; now call into our actual Lua callback!
+    const int callback = (int) ((size_t)arg);
+    lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback);
+    lua_call(luaState, 0, 0);
+} // clickedMenuItem
+
+
+// !!! FIXME: on destruction of a menu item, we need to luaL_unref(L, LUA_REGISTRYINDEX, callback)...
+static int appendGuiMenuItem(lua_State *L)
+{
+    const int argc = lua_gettop(L);
+    GtkWidget *menu = (GtkWidget *) lua_touserdata(L, 1);
+    const char *label = luaL_checkstring(L, 2);
+    GtkWidget *item = gtk_menu_item_new_with_label(label);
+
+    if ((argc >= 3) && (!lua_isnil(L, 3)))
+    {
+        assert(lua_isfunction(L, 3));
+        lua_pushvalue(L, 3);  // copy the Lua callback (luaL_ref() pops it).
+        const int callback = luaL_ref(L, LUA_REGISTRYINDEX);
+        gtk_signal_connect_object(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(clickedMenuItem), (gpointer) ((size_t)callback));
+    } // if
+
+    gtk_widget_show(item);
+    gtk_menu_append(menu, item);
+    return retvalPointer(L, item);
+} // appendGuiMenuItem
+
+
+static int setGuiMenuItemSubmenu(lua_State *L)
+{
+    GtkMenuItem *item = (GtkMenuItem *) lua_touserdata(L, 1);
+    GtkWidget *submenu = (GtkWidget *) lua_touserdata(L, 2);
+    gtk_menu_item_set_submenu(item, submenu);
+    return 0;
+} // setGuiMenuItemSubmenu
+
+
+static int popupGuiMenu(lua_State *L)
+{
+    GtkMenu *menu = (GtkMenu *) lua_touserdata(L, 1);
+    gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
+    return 0;
+} // popupGuiMenu
+
+
+static int giveControlToGui(lua_State *L)
+{
+    gtk_main();
+    return 0;
+} // giveControlToGui
+
 
 static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
 {
@@ -196,8 +266,20 @@
 } // luaFatal
 
 
+static void deinitLua(void)
+{
+    if (luaState != NULL)
+    {
+        lua_close(luaState);
+        luaState = NULL;
+    } // if
+} // deinitLua
+
+
 static int initLua(const int argc, char **argv)
 {
+    atexit(deinitLua);
+
     assert(luaState == NULL);
     luaState = lua_newstate(luaAlloc, NULL);
 
@@ -208,6 +290,11 @@
     // Set up initial C functions, etc we want to expose to Lua code...
     luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
     luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
+    luaSetCFunc(luaState, makeGuiMenu, "makeGuiMenu");
+    luaSetCFunc(luaState, appendGuiMenuItem, "appendGuiMenuItem");
+    luaSetCFunc(luaState, setGuiMenuItemSubmenu, "setGuiMenuItemSubmenu");
+    luaSetCFunc(luaState, popupGuiMenu, "popupGuiMenu");
+    luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
 
     // Set up argv table...
     lua_newtable(luaState);
@@ -233,19 +320,9 @@
 } // initLua
 
 
-static void deinitLua(void)
-{
-    if (luaState != NULL)
-    {
-        lua_close(luaState);
-        luaState = NULL;
-    } // if
-} // deinitLua
-
-
 int main(int argc, char **argv)
 {
-    atexit(deinitLua);
+    gtk_init(&argc, &argv);
 
     if (!initLua(argc, argv))  // this will move control to 1pass.lua
         return 1;
--- a/1pass.lua	Thu Dec 19 23:04:04 2013 -0500
+++ b/1pass.lua	Sun Dec 22 03:01:08 2013 -0500
@@ -94,6 +94,52 @@
 
 local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
 
+local passwordTypeNameMap = {
+    ["wallet.financial.BankAccountUS"] = "Bank accounts",
+    ["wallet.financial.CreditCard"] = "Credit cards",
+    ["webforms.WebForm"] = "Logins",
+    ["system.Tombstone"] = "Dead items",
+    ["wallet.membership.Membership"] = "Memberships",
+    ["wallet.government.DriversLicense"] = "Drivers licenses",
+    ["passwords.Password"] = "Passwords",
+    -- !!! FIXME: more!
+}
+
+local contents = loadContents(basedir)
+local items = {}
+for i,v in ipairs(contents) do
+    local t = v[2]
+    if t ~= "system.Tombstone" then
+        if items[t] == nil then
+            items[t] = {}
+        end
+        local bucket = items[t]
+        bucket[#bucket+1] = { uuid=v[1], type=t, name=v[3], url=v[4] }  -- !!! FIXME: there are more fields, don't know what they mean yet.
+    end
+end
+contents = nil
+
+local topmenu = makeGuiMenu()
+for type,bucket in pairs(items) do
+print(type)
+    local realname = passwordTypeNameMap[type]
+    if realname == nil then
+        realname = type
+    end
+    local menuitem = appendGuiMenuItem(topmenu, realname)
+    local submenu = makeGuiMenu()
+    for i,v in pairs(bucket) do
+        local submenuitem = appendGuiMenuItem(submenu, v.name, function() print("Clicked on " .. v.name .. ", uuid is '" .. v.uuid .. "'") end)
+    end
+    setGuiMenuItemSubmenu(menuitem, submenu)
+end
+
+popupGuiMenu(topmenu)
+giveControlToGui()
+
+os.exit(1)
+
+
 local password = argv[3]
 if password == nil then
     showHint(basedir)