summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Fleischer <lfleischer@calcurse.org>2017-08-30 16:27:29 +0200
committerLukas Fleischer <lfleischer@calcurse.org>2017-08-30 16:39:23 +0200
commit578091f051541ffcd68fb755641fd7c45e28977c (patch)
tree8a02c2ec5c1ac9109076d5bee6c80d553d9c7f8b
parent8544e4a57016e370a25bb7038cc3ef11c81eb9c3 (diff)
downloadcalcurse-578091f051541ffcd68fb755641fd7c45e28977c.tar.gz
calcurse-578091f051541ffcd68fb755641fd7c45e28977c.zip
Add support for UTF-8 key bindings
A new function keys_wgetch() reads full UTF-8 characters instead of single ASCII characters only. Key bindings for regular ASCII characters are stored in a hash map while the actions of keys with higher code points are stored in a linked list for space efficiency. The key serialization methods are updated to handle UTF-8 characters as well; extended UTF-8 characters (characters not in the ASCII range) are serialized by using the hexadecimal representation of the corresponding code points (e.g. "U+00E4"). Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
-rw-r--r--src/calcurse.h4
-rw-r--r--src/custom.c4
-rw-r--r--src/keys.c134
3 files changed, 112 insertions, 30 deletions
diff --git a/src/calcurse.h b/src/calcurse.h
index ac1baba..f9439b7 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -230,6 +230,7 @@
((unsigned char)ch >= 0xF0 ? 4 : \
((unsigned char)ch >= 0xE0 ? 3 : \
((unsigned char)ch >= 0xC0 ? 2 : 1)))))
+#define UTF8_ISMULTI(ch) ((unsigned char)ch >= 0x80)
#define UTF8_ISCONT(ch) ((unsigned char)ch >= 0x80 && \
(unsigned char)ch <= 0xBF)
@@ -871,11 +872,12 @@ void keys_free(void);
void keys_dump_defaults(char *);
const char *keys_get_label(enum key);
enum key keys_get_action(int);
+int keys_wgetch(WINDOW *);
enum key keys_get(WINDOW *, int *, int *);
int keys_assign_binding(int, enum key);
void keys_remove_binding(int, enum key);
int keys_str2int(const char *);
-const char *keys_int2str(int);
+char *keys_int2str(int);
int keys_action_count_keys(enum key);
const char *keys_action_firstkey(enum key);
const char *keys_action_nkey(enum key, int);
diff --git a/src/custom.c b/src/custom.c
index ce219c1..f83a87a 100644
--- a/src/custom.c
+++ b/src/custom.c
@@ -972,10 +972,10 @@ void custom_keys_config(void)
(col - WINCOL) / 2,
_("Press the key you want to assign to:"),
keys_get_label(selrow), 0);
- ch = wgetch(grabwin);
+ ch = keys_wgetch(grabwin);
/* First check if this key would be recognized by calcurse. */
- if (keys_str2int(keys_int2str(ch)) == -1) {
+ if (ch < 0) {
not_recognized = 1;
WARN_MSG(_("This key is not yet recognized by calcurse, "
"please choose another one."));
diff --git a/src/keys.c b/src/keys.c
index e71a2c4..6dad43e 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -50,6 +50,13 @@ struct keydef_s {
static llist_t keys[NBKEYS];
static enum key actions[MAXKEYVAL];
+struct key_ext {
+ int ch;
+ enum key action;
+};
+
+llist_t actions_ext;
+
#define gettext_noop(s) s
static struct keydef_s keydef[NBKEYS] = {
{ "generic-cancel", "ESC", gettext_noop("Cancel") },
@@ -135,6 +142,7 @@ void keys_init(void)
for (i = 0; i < MAXKEYVAL; i++)
actions[i] = KEY_UNDEF;
+ LLIST_INIT(&actions_ext);
for (i = 0; i < NBKEYS; i++)
LLIST_INIT(&keys[i]);
}
@@ -179,12 +187,56 @@ const char *keys_get_label(enum key key)
return keydef[key].label;
}
+static int key_ext_hasch(struct key_ext *k, void *cbdata)
+{
+ return (k->ch == *((int *)cbdata));
+}
+
enum key keys_get_action(int pressed)
{
- if (pressed < 0 || pressed > MAXKEYVAL)
+ if (pressed < 0) {
return -1;
- else
+ } else if (pressed > MAXKEYVAL) {
+ llist_item_t *i = LLIST_FIND_FIRST(&actions_ext, &pressed,
+ key_ext_hasch);
+ if (!i)
+ return KEY_UNDEF;
+
+ struct key_ext *k = LLIST_GET_DATA(i);
+ return k->action;
+ } else {
return actions[pressed];
+ }
+}
+
+int keys_wgetch(WINDOW *win)
+{
+ int ch, i;
+ char buf[UTF8_MAXLEN];
+
+ ch = wgetch(win);
+
+ if (ch > 255) {
+ if (ch == KEY_UP || ch == KEY_DOWN || ch == KEY_LEFT ||
+ ch == KEY_RIGHT || ch == KEY_HOME || ch == KEY_END) {
+ return ch;
+ }
+ return -1;
+ }
+
+ if (UTF8_ISMULTI(ch) && !UTF8_ISCONT(ch)) {
+ buf[0] = ch;
+ for (i = 1; i < UTF8_LENGTH(ch); i++) {
+ ch = wgetch(win);
+ if (!UTF8_ISCONT(ch))
+ return -1;
+ buf[i] = ch;
+ }
+ return utf8_ord(buf);
+ } else {
+ return ch;
+ }
+
}
enum key keys_get(WINDOW *win, int *count, int *reg)
@@ -196,7 +248,7 @@ enum key keys_get(WINDOW *win, int *count, int *reg)
*reg = 0;
do {
*count = *count * 10 + ch - '0';
- ch = wgetch(win);
+ ch = keys_wgetch(win);
}
while ((ch == '0' && *count > 0)
|| (ch >= '1' && ch <= '9'));
@@ -205,7 +257,7 @@ enum key keys_get(WINDOW *win, int *count, int *reg)
*count = 1;
if (ch == '"') {
- ch = wgetch(win);
+ ch = keys_wgetch(win);
if (ch >= '1' && ch <= '9') {
*reg = ch - '1' + 1;
} else if (ch >= 'a' && ch <= 'z') {
@@ -213,10 +265,10 @@ enum key keys_get(WINDOW *win, int *count, int *reg)
} else if (ch == '_') {
*reg = REG_BLACK_HOLE;
}
- ch = wgetch(win);
+ ch = keys_wgetch(win);
}
} else {
- ch = wgetch(win);
+ ch = keys_wgetch(win);
}
switch (ch) {
@@ -232,45 +284,64 @@ static void add_key_str(enum key action, int key)
if (action > NBKEYS)
return;
- LLIST_ADD(&keys[action], mem_strdup(keys_int2str(key)));
+ LLIST_ADD(&keys[action], keys_int2str(key));
}
int keys_assign_binding(int key, enum key action)
{
- if (key < 0 || key > MAXKEYVAL || actions[key] != KEY_UNDEF) {
+ if (key < 0 || actions[key] != KEY_UNDEF) {
return 1;
+ } else if (key > MAXKEYVAL) {
+ struct key_ext *k = mem_malloc(sizeof(struct key_ext));
+ k->ch = key;
+ k->action = action;
+ LLIST_ADD(&actions_ext, k);
} else {
actions[key] = action;
- add_key_str(action, key);
}
+ add_key_str(action, key);
+
return 0;
}
static void del_key_str(enum key action, int key)
{
llist_item_t *i;
- char oldstr[BUFSIZ];
+ char *oldstr = keys_int2str(key);;
if (action > NBKEYS)
return;
- strncpy(oldstr, keys_int2str(key), BUFSIZ);
-
LLIST_FOREACH(&keys[action], i) {
if (strcmp(LLIST_GET_DATA(i), oldstr) == 0) {
LLIST_REMOVE(&keys[action], i);
- return;
+ goto cleanup;
}
}
+
+cleanup:
+ mem_free(oldstr);
}
void keys_remove_binding(int key, enum key action)
{
- if (key >= 0 && key <= MAXKEYVAL) {
+ if (key < 0)
+ return;
+
+ if (key <= MAXKEYVAL) {
actions[key] = KEY_UNDEF;
- del_key_str(action, key);
+ } else {
+ llist_item_t *i = LLIST_FIND_FIRST(&actions_ext, &key,
+ key_ext_hasch);
+ if (i) {
+ struct key_ext *k = LLIST_GET_DATA(i);
+ LLIST_REMOVE(&actions_ext, i);
+ mem_free(k);
+ }
}
+
+ del_key_str(action, key);
}
int keys_str2int(const char *key)
@@ -285,6 +356,8 @@ int keys_str2int(const char *key)
return CTRL((int)key[1]);
else if (starts_with(key, "C-"))
return CTRL((int)key[strlen("C-")]);
+ else if (starts_with(key, "U+"))
+ return strtol(&key[2], NULL, 16);
else if (!strcmp(key, "TAB"))
return TAB;
else if (!strcmp(key, "ESC"))
@@ -307,29 +380,36 @@ int keys_str2int(const char *key)
return -1;
}
-const char *keys_int2str(int key)
+char *keys_int2str(int key)
{
+ char *res;
+
switch (key) {
case TAB:
- return "TAB";
+ return strdup("TAB");
case SPACE:
- return "SPC";
+ return strdup("SPC");
case ESCAPE:
- return "ESC";
+ return strdup("ESC");
case KEY_UP:
- return "UP";
+ return strdup("UP");
case KEY_DOWN:
- return "DWN";
+ return strdup("DWN");
case KEY_LEFT:
- return "LFT";
+ return strdup("LFT");
case KEY_RIGHT:
- return "RGT";
+ return strdup("RGT");
case KEY_HOME:
- return "KEY_HOME";
+ return strdup("KEY_HOME");
case KEY_END:
- return "KEY_END";
- default:
- return (char *)keyname(key);
+ return strdup("KEY_END");
+ }
+
+ if (key >= 0x80) {
+ asprintf(&res, "U+%04X", key);
+ return res;
+ } else {
+ return strdup((char *)keyname(key));
}
}