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>
This commit is contained in:
Lukas Fleischer 2017-08-30 16:27:29 +02:00
parent 8544e4a570
commit 578091f051
3 changed files with 112 additions and 30 deletions

View File

@ -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);

View File

@ -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."));

View File

@ -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));
}
}