summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2022-03-06 02:53:06 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2022-03-06 17:00:27 -0500
commit19d95d7b56aa8a6a3a7cdcd2ee80161de9598225 (patch)
tree2f23d029af340ff24a75a327f5ed5804f5665bc4
parentbc3fa5393762549a16f9de3e238a986607015fe3 (diff)
downloadrockbox-19d95d7b56.tar.gz
rockbox-19d95d7b56.zip
keyremap add plain text import and export
allows users to edit and view keymaps in a text editor (parser requires one entry per line (LF or CRLF work as line endings) allow naming of files on save Change-Id: Id6627171eb96210b2deeef8d15bab3f8d1e0ab3f
-rw-r--r--apps/plugins/keyremap.c522
1 files changed, 507 insertions, 15 deletions
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
index 0a6b705e9d..a4ce1c48e6 100644
--- a/apps/plugins/keyremap.c
+++ b/apps/plugins/keyremap.c
@@ -21,12 +21,12 @@
#include "plugin.h"
#include "lang_enum.h"
-#include "../open_plugin.h"
#include "lib/action_helper.h"
#include "lib/button_helper.h"
#include "lib/pluginlib_actions.h"
#include "lib/printcell_helper.h"
+#include "lib/kbd_helper.h"
#ifdef ROCKBOX_HAS_LOGF
#define logf rb->logf
@@ -102,6 +102,8 @@ enum {
M_SETKEYS,
M_TESTKEYS,
M_RESETKEYS,
+ M_EXPORTKEYS,
+ M_IMPORTKEYS,
M_SAVEKEYS,
M_LOADKEYS,
M_DELKEYS,
@@ -125,8 +127,10 @@ MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
-MENU_ITEM(M_SAVEKEYS, "Save Keymap", 1),
-MENU_ITEM(M_LOADKEYS, "Load Keymaps", 1),
+MENU_ITEM(M_EXPORTKEYS, "Export Text Keymap", 1),
+MENU_ITEM(M_IMPORTKEYS, "Import Text Keymap", 1),
+MENU_ITEM(M_SAVEKEYS, "Save Native Keymap", 1),
+MENU_ITEM(M_LOADKEYS, "Load Native Keymaps", 1),
MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1),
MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
@@ -172,8 +176,24 @@ static struct mainmenu *mainitem(int selected_item)
else
return &empty;
}
-
+/* Forward Declarations */
+static const char *edit_keymap_name_cb(int selected_item, void* data,char* buf, size_t buf_len);
+static int keyremap_import_file(char *filenamebuf, size_t bufsz);
static void synclist_set(int id, int selected_item, int items, int sel_size);
+
+static int prompt_filename(char *buf, size_t bufsz)
+{
+#define KBD_LAYOUT "abcdefghijklmnop\nqrstuvwxyz |()[]\n1234567890 /._-+\n\n" \
+ "\nABCDEFGHIJKLMNOP\nQRSTUVWXYZ |()[]\n1234567890 /._-+"
+ unsigned short kbd[sizeof(KBD_LAYOUT) + 10];
+ unsigned short *kbd_p = kbd;
+ if (!kbd_create_layout(KBD_LAYOUT, kbd, sizeof(kbd)))
+ kbd_p = NULL;
+
+#undef KBD_LAYOUT
+ return rb->kbd_input(buf, bufsz, kbd_p) + 1;
+}
+
static void synclist_set_update(int id, int selected_item, int items, int sel_size)
{
SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
@@ -471,13 +491,18 @@ static int keyremap_save_current(const char *filename)
static void keyremap_save_user_keys(bool notify)
{
char buf[MAX_PATH];
- int i = 1;
+ int i = 0;
do
{
- rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
} while (i < 100 && rb->file_exists(buf));
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
if (keyremap_save_current(buf) == 0)
{
if(notify)
@@ -487,6 +512,128 @@ static void keyremap_save_user_keys(bool notify)
rb->splashf(HZ *2, "Saved %s", buf);
}
+static int keyremap_export_current(char *filenamebuf, size_t bufsz)
+{
+ filenamebuf[bufsz - 1] = '\0';
+ int i, j;
+ int ctx_count = 0;
+ size_t entrylen;
+
+ int entry_count = ctx_data.ctx_count + ctx_data.act_count + 1;;/* (ctx_count + ctx_count + act_count + 1) */
+
+ if (entry_count <= 3)
+ return 0;
+
+ int fd = rb->open(filenamebuf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ if (fd < 0)
+ return -1;
+ rb->fdprintf(fd, "# Key Remap\n# Device: %s\n" \
+ "# Entries: %d\n\n", MODEL_NAME, entry_count - 1);
+ rb->fdprintf(fd, "# Each entry should be PROPER_CASE and on its own line\n" \
+ "# Comments run to end of line \n");
+
+ for (i = 0; i <= entry_count; i++)
+ {
+ entrylen = 0;
+ rb->memset(filenamebuf, 0, bufsz);
+ edit_keymap_name_cb(i, MENU_ID(M_EXPORTKEYS), filenamebuf, bufsz);
+ if (i == 0)
+ {
+ ctx_menu_data.act_fmt = " {%s%s, %s, %s},\n\n";
+ continue;
+ }
+ else if (i == entry_count)
+ {
+ rb->strlcpy(filenamebuf, "}\n\n", bufsz);
+ }
+ char last = '\0';
+ for (j = 0; j < (int)bufsz ;j++)
+ {
+ char ch = filenamebuf[j];
+ if (ch == '$' && last == '\0') /*CONTEXT*/
+ {
+ if (ctx_count > 0)
+ rb->fdprintf(fd, "}\n");
+ ctx_count++;
+ filenamebuf[j] = '\n';
+ }
+ if (ch == '\n' && last == '\n')
+ {
+ entrylen = j;
+ break;
+ }
+ else if (ch == '\0')
+ filenamebuf[j] = ',';
+ last = ch;
+ }
+
+ size_t bytes = rb->write(fd, filenamebuf, entrylen);
+ if (bytes != entrylen)
+ {
+ entry_count = -2;
+ goto fail;
+ }
+ }
+
+fail:
+ rb->close(fd);
+
+ return entry_count;
+}
+
+static void keyremap_export_user_keys(void)
+{
+ const bool notify = true;
+ char buf[MAX_PATH];
+ int i = 0;
+ do
+ {
+ i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ } while (i < 100 && rb->file_exists(buf));
+
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
+ if (keyremap_export_current(buf, sizeof(buf)) <= 0)
+ {
+ if(notify)
+ rb->splash(HZ *2, "Error Saving");
+ }
+ else if (notify)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ rb->splashf(HZ *2, "Saved %s", buf);
+ }
+}
+
+static void keyremap_import_user_keys(void)
+{
+ char buf[MAX_PATH];
+ struct browse_context browse;
+
+ rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "Select Keymap",
+ Icon_Plugin, "/", NULL);
+
+ browse.buf = buf;
+ browse.bufsize = sizeof(buf);
+
+ if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
+ {
+ int ret = keyremap_import_file(buf, sizeof(buf));
+ if (ret <= 0)
+ {
+ keyremap_reset_buffer();
+ rb->splash(HZ *2, "Error Opening");
+ }
+ else
+ rb->splashf(HZ * 2, "Loaded Text Keymap ");
+ }
+}
+
static int keymap_add_context_entry(int context)
{
int remap_context = CORE_CONTEXT_REMAP(context);
@@ -538,6 +685,328 @@ fail:
return 0;
}
+static int get_action_from_str(char *pfield, size_t bufsz)
+{
+ int act = -1;
+ for (int i=0;i < LAST_ACTION_PLACEHOLDER; i++)
+ {
+ if (rb->strncasecmp(pfield, action_name(i), bufsz) == 0)
+ {
+ logf("Action Found: %s (%d)", pfield, i);
+ act = i;
+ break;
+ }
+ }
+ return act;
+}
+
+static int get_button_from_str(char *pfield, size_t bufsz)
+{
+ int btn = -1;
+ for (int i=0;i < available_button_count; i++)
+ {
+ const struct available_button* abtn = &available_buttons[i];
+ if (rb->strncasecmp(pfield, abtn->name, bufsz) == 0)
+ {
+ logf("Button Found: %s (%lx)", abtn->name, abtn->value);
+ btn = abtn->value;
+ break;
+ }
+ }
+ if (btn < 0) /* Not Fatal */
+ {
+ logf("Invalid Button %s", pfield);
+ rb->splashf(HZ, "Invalid Button %s", pfield);
+ }
+ return btn;
+}
+
+static int parse_action_import_entry(int context, char * pbuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ char ch;
+ char *pfirst = NULL;
+ char *pfield;
+ int field = -1;
+ int act = -1;
+ int button = BUTTON_NONE;
+ int prebtn = BUTTON_NONE;
+ while ((ch = *(pbuf)) != '\0')
+ {
+ pfield = NULL; /* Valid names */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ',')
+ {
+ if (pfirst != NULL)
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ *pbuf = '\0';
+ }
+ }
+ else if (ch == ' ' || ch == '|'){;}
+ else
+ pfirst = NULL;
+
+ if (field == 1 && pfirst != NULL && pbuf[1] == '\0')
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ }
+
+ if (pfield != NULL)
+ {
+ char *pf;
+
+ if (field == 0) /* action */
+ {
+ char *pact = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if(ch == ' ')
+ *pf = '\0';
+ else
+ pf++;
+ }
+ bufleft = bufsz - (pact - pbuf);
+ act = get_action_from_str(pact, bufleft);
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pact);
+ return -1;
+ }
+ }
+ else if (field == 1 || field == 2) /* button / pre_btn */
+ {
+ char *pbtn = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (pf[1] == '\0') /* last item? */
+ {
+ pf++;
+ ch = '|';
+ pfield = NULL;
+ }
+ else if (ch == ' ' || ch == '|')
+ {
+ *pf = '\0';
+ }
+
+ if(ch == '|')
+ {
+ bufleft = bufsz - (pbtn - pbuf);
+ int btn = get_button_from_str(pbtn, bufleft);
+
+ if (btn > BUTTON_NONE)
+ {
+ if (field == 1)
+ button |= btn;
+ else if (field == 2)
+ prebtn |= btn;
+ }
+
+ if (pfield != NULL)
+ {
+ pf++;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (*pf == ' ')
+ pf++;
+ else
+ break;
+ }
+ pbtn = pf;
+ }
+ }
+ else
+ pf++;
+ }
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pfield);
+ return -1;
+ }
+ }
+
+ pfield = NULL;
+ }
+
+ pbuf++;
+ }
+ if (field == 2)
+ {
+ count = keymap_add_button_entry(context, act, button, prebtn);
+ if (count > 0)
+ {
+ logf("Added: Ctx: %d, Act: %d, Btn: %d PBtn: %d",
+ context, act, button, prebtn);
+ }
+ }
+ return count;
+}
+
+static int keyremap_import_file(char *filenamebuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ int line = 0;
+ filenamebuf[bufsz - 1] = '\0';
+ logf("keyremap: import %s", filenamebuf);
+ int fd = rb->open(filenamebuf, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ char ch;
+ char *pbuf;
+ char *pfirst;
+
+ char *pctx = NULL;
+ char *pact;
+ int ctx = -1;
+
+ keyremap_reset_buffer();
+next_line:
+ while (rb->read_line(fd, filenamebuf, (int) bufsz) > 0)
+ {
+ line++;
+
+
+ pbuf = filenamebuf;
+ pfirst = NULL;
+ pact = NULL;
+ char *pcomment = rb->strchr(pbuf, '#');
+ if (pcomment != NULL)
+ {
+ logf("ln: %d: Skipped Comment: %s", line, pcomment);
+ *pcomment = '\0';
+ }
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ /* PARSE CONTEXT = { */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ' ')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ }
+ else if (ch == '=')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pbuf++;
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '{')
+ break;
+ pbuf++;
+ }
+ if (ch == '{' && pctx != NULL)
+ {
+ bufleft = bufsz - (pctx - filenamebuf);
+ ctx = -1;
+ for (int i=0;i < LAST_CONTEXT_PLACEHOLDER;i++)
+ {
+ if (rb->strncasecmp(pctx, context_name(i), bufleft) == 0)
+ {
+ logf("ln: %d: Context Found: %s (%d)", line, pctx, i);
+ if (keymap_add_context_entry(i) <= 0)
+ logf("ln: %d: Context Exists: %s (%d)", line, pctx, i);
+ ctx = i;
+ goto next_line;
+
+ }
+ }
+ logf("ln: %d: ERROR { Context Expected got: %s", line, pctx);
+ goto fail;
+ }
+ }
+ else if (ch == '}')
+ {
+ if (ctx >= 0)
+ ctx = -1;
+ else
+ {
+ logf("ln: %d: ERROR no context, unexpected close {", line);
+ goto fail;
+ }
+ }
+ else if (ch == '{') /* PARSE FIELDS { ACTION, BUTTON, PREBTN } */
+ {
+ int res = 0;
+ if (ctx >= 0)
+ {
+ pfirst = pbuf;
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '}')
+ {
+ pact = pfirst + 1;
+ pfirst = NULL;
+ *pbuf = '\0';
+ pbuf = "";
+ continue;
+ }
+ pbuf++;
+ }
+ if (pact != NULL)
+ {
+ bufleft = bufsz - (pact - filenamebuf);
+ logf("ln: %d: Entry Found: {%s} (%d)", line, pact, 0);
+ res = parse_action_import_entry(ctx, pact, bufleft);
+ }
+ }
+ if (res <= 0)
+ {
+ logf("ln: %d: ERROR action entry expected", line);
+ goto fail;
+ }
+ else
+ {
+ pbuf = "";
+ continue;
+ }
+ }
+ else
+ pfirst = NULL;
+ pbuf++;
+ }
+
+ }
+ rb->close(fd);
+ count = ctx_data.ctx_count + ctx_data.act_count;
+ return count;
+
+fail:
+ rb->close(fd);
+ rb->splashf(HZ * 2, "Error @ line %d", line);
+ return 0;
+}
+
static int keyremap_load_file(const char *filename)
{
logf("keyremap: load %s", filename);
@@ -709,7 +1178,18 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
int i;
- (void)data;
+ int szbtn = sizeof("BUTTON");
+ int szctx = sizeof("CONTEXT");
+ int szact = sizeof("ACTION");
+ const char* ctxfmt = "%s";
+
+ if (data == MENU_ID(M_EXPORTKEYS))
+ {
+ szbtn = 0;
+ szctx = 0;
+ szact = 0;
+ ctxfmt = "$%s = {\n\n";
+ }
buf[0] = '\0';
for(i = 0; i < ctx_data.ctx_count; i ++)
{
@@ -720,7 +1200,7 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
context_name(ctx_data.ctx_map[i].context),
"Select$to add$actions");
else
- rb->snprintf(buf, buf_len, "%s", context_name(ctx_data.ctx_map[i].context));
+ rb->snprintf(buf, buf_len, ctxfmt, context_name(ctx_data.ctx_map[i].context));
return buf;
}
}
@@ -730,19 +1210,23 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
{
int context = ctx_data.act_map[i].context;
char ctxbuf[action_helper_maxbuffer];
- char *pctxbuf = ctxbuf;
+ char *pctxbuf = "\0";
char *pactname;
- rb->snprintf(ctxbuf, sizeof(ctxbuf), "%s", context_name(context));
- pctxbuf += sizeof("CONTEXT");
+ if (data != MENU_ID(M_EXPORTKEYS))
+ {
+ pctxbuf = ctxbuf;
+ rb->snprintf(ctxbuf, sizeof(ctxbuf), ctxfmt, context_name(context));
+ pctxbuf += szctx;//sizeof("CONTEXT")
+ }
struct button_mapping * bm = &ctx_data.act_map[i].map;
pactname = action_name(bm->action_code);
- pactname += sizeof("ACTION");
+ pactname += szact;//sizeof("ACTION")
/* BUTTON & PRE_BUTTON */
btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
- pactname, buf_button + sizeof("BUTTON"), buf_prebtn + sizeof("BUTTON"));
+ pactname, buf_button + szbtn, buf_prebtn + szbtn);
return buf;
}
}
@@ -752,7 +1236,6 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
static const char *edit_keymap_name_cb(int selected_item, void* data,
char* buf, size_t buf_len)
{
- (void)data;
buf[0] = '\0';
if (selected_item == 0)
{
@@ -930,6 +1413,16 @@ int menu_action_root(int *action, int selected_item, bool* exit, struct gui_sync
keyremap_save_user_keys(true);
goto default_handler;
}
+ else if (cur->menuid == MENU_ID(M_EXPORTKEYS))
+ {
+ keyremap_export_user_keys();
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_IMPORTKEYS))
+ {
+ keyremap_import_user_keys();
+ goto default_handler;
+ }
else if (cur->menuid == MENU_ID(M_DELKEYS) ||
cur->menuid == MENU_ID(M_LOADKEYS))
{
@@ -1118,7 +1611,6 @@ int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_
goto default_handler;
}
-
if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
{
keytest.index -= 2;