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:
parent
8544e4a570
commit
578091f051
@ -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);
|
||||
|
@ -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."));
|
||||
|
134
src/keys.c
134
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user