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 >= 0xF0 ? 4 : \
|
||||||
((unsigned char)ch >= 0xE0 ? 3 : \
|
((unsigned char)ch >= 0xE0 ? 3 : \
|
||||||
((unsigned char)ch >= 0xC0 ? 2 : 1)))))
|
((unsigned char)ch >= 0xC0 ? 2 : 1)))))
|
||||||
|
#define UTF8_ISMULTI(ch) ((unsigned char)ch >= 0x80)
|
||||||
#define UTF8_ISCONT(ch) ((unsigned char)ch >= 0x80 && \
|
#define UTF8_ISCONT(ch) ((unsigned char)ch >= 0x80 && \
|
||||||
(unsigned char)ch <= 0xBF)
|
(unsigned char)ch <= 0xBF)
|
||||||
|
|
||||||
@ -871,11 +872,12 @@ void keys_free(void);
|
|||||||
void keys_dump_defaults(char *);
|
void keys_dump_defaults(char *);
|
||||||
const char *keys_get_label(enum key);
|
const char *keys_get_label(enum key);
|
||||||
enum key keys_get_action(int);
|
enum key keys_get_action(int);
|
||||||
|
int keys_wgetch(WINDOW *);
|
||||||
enum key keys_get(WINDOW *, int *, int *);
|
enum key keys_get(WINDOW *, int *, int *);
|
||||||
int keys_assign_binding(int, enum key);
|
int keys_assign_binding(int, enum key);
|
||||||
void keys_remove_binding(int, enum key);
|
void keys_remove_binding(int, enum key);
|
||||||
int keys_str2int(const char *);
|
int keys_str2int(const char *);
|
||||||
const char *keys_int2str(int);
|
char *keys_int2str(int);
|
||||||
int keys_action_count_keys(enum key);
|
int keys_action_count_keys(enum key);
|
||||||
const char *keys_action_firstkey(enum key);
|
const char *keys_action_firstkey(enum key);
|
||||||
const char *keys_action_nkey(enum key, int);
|
const char *keys_action_nkey(enum key, int);
|
||||||
|
@ -972,10 +972,10 @@ void custom_keys_config(void)
|
|||||||
(col - WINCOL) / 2,
|
(col - WINCOL) / 2,
|
||||||
_("Press the key you want to assign to:"),
|
_("Press the key you want to assign to:"),
|
||||||
keys_get_label(selrow), 0);
|
keys_get_label(selrow), 0);
|
||||||
ch = wgetch(grabwin);
|
ch = keys_wgetch(grabwin);
|
||||||
|
|
||||||
/* First check if this key would be recognized by calcurse. */
|
/* First check if this key would be recognized by calcurse. */
|
||||||
if (keys_str2int(keys_int2str(ch)) == -1) {
|
if (ch < 0) {
|
||||||
not_recognized = 1;
|
not_recognized = 1;
|
||||||
WARN_MSG(_("This key is not yet recognized by calcurse, "
|
WARN_MSG(_("This key is not yet recognized by calcurse, "
|
||||||
"please choose another one."));
|
"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 llist_t keys[NBKEYS];
|
||||||
static enum key actions[MAXKEYVAL];
|
static enum key actions[MAXKEYVAL];
|
||||||
|
|
||||||
|
struct key_ext {
|
||||||
|
int ch;
|
||||||
|
enum key action;
|
||||||
|
};
|
||||||
|
|
||||||
|
llist_t actions_ext;
|
||||||
|
|
||||||
#define gettext_noop(s) s
|
#define gettext_noop(s) s
|
||||||
static struct keydef_s keydef[NBKEYS] = {
|
static struct keydef_s keydef[NBKEYS] = {
|
||||||
{ "generic-cancel", "ESC", gettext_noop("Cancel") },
|
{ "generic-cancel", "ESC", gettext_noop("Cancel") },
|
||||||
@ -135,6 +142,7 @@ void keys_init(void)
|
|||||||
|
|
||||||
for (i = 0; i < MAXKEYVAL; i++)
|
for (i = 0; i < MAXKEYVAL; i++)
|
||||||
actions[i] = KEY_UNDEF;
|
actions[i] = KEY_UNDEF;
|
||||||
|
LLIST_INIT(&actions_ext);
|
||||||
for (i = 0; i < NBKEYS; i++)
|
for (i = 0; i < NBKEYS; i++)
|
||||||
LLIST_INIT(&keys[i]);
|
LLIST_INIT(&keys[i]);
|
||||||
}
|
}
|
||||||
@ -179,12 +187,56 @@ const char *keys_get_label(enum key key)
|
|||||||
return keydef[key].label;
|
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)
|
enum key keys_get_action(int pressed)
|
||||||
{
|
{
|
||||||
if (pressed < 0 || pressed > MAXKEYVAL)
|
if (pressed < 0) {
|
||||||
return -1;
|
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];
|
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)
|
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;
|
*reg = 0;
|
||||||
do {
|
do {
|
||||||
*count = *count * 10 + ch - '0';
|
*count = *count * 10 + ch - '0';
|
||||||
ch = wgetch(win);
|
ch = keys_wgetch(win);
|
||||||
}
|
}
|
||||||
while ((ch == '0' && *count > 0)
|
while ((ch == '0' && *count > 0)
|
||||||
|| (ch >= '1' && ch <= '9'));
|
|| (ch >= '1' && ch <= '9'));
|
||||||
@ -205,7 +257,7 @@ enum key keys_get(WINDOW *win, int *count, int *reg)
|
|||||||
*count = 1;
|
*count = 1;
|
||||||
|
|
||||||
if (ch == '"') {
|
if (ch == '"') {
|
||||||
ch = wgetch(win);
|
ch = keys_wgetch(win);
|
||||||
if (ch >= '1' && ch <= '9') {
|
if (ch >= '1' && ch <= '9') {
|
||||||
*reg = ch - '1' + 1;
|
*reg = ch - '1' + 1;
|
||||||
} else if (ch >= 'a' && ch <= 'z') {
|
} else if (ch >= 'a' && ch <= 'z') {
|
||||||
@ -213,10 +265,10 @@ enum key keys_get(WINDOW *win, int *count, int *reg)
|
|||||||
} else if (ch == '_') {
|
} else if (ch == '_') {
|
||||||
*reg = REG_BLACK_HOLE;
|
*reg = REG_BLACK_HOLE;
|
||||||
}
|
}
|
||||||
ch = wgetch(win);
|
ch = keys_wgetch(win);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ch = wgetch(win);
|
ch = keys_wgetch(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
@ -232,45 +284,64 @@ static void add_key_str(enum key action, int key)
|
|||||||
if (action > NBKEYS)
|
if (action > NBKEYS)
|
||||||
return;
|
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)
|
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;
|
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 {
|
} else {
|
||||||
actions[key] = action;
|
actions[key] = action;
|
||||||
add_key_str(action, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_key_str(action, key);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void del_key_str(enum key action, int key)
|
static void del_key_str(enum key action, int key)
|
||||||
{
|
{
|
||||||
llist_item_t *i;
|
llist_item_t *i;
|
||||||
char oldstr[BUFSIZ];
|
char *oldstr = keys_int2str(key);;
|
||||||
|
|
||||||
if (action > NBKEYS)
|
if (action > NBKEYS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
strncpy(oldstr, keys_int2str(key), BUFSIZ);
|
|
||||||
|
|
||||||
LLIST_FOREACH(&keys[action], i) {
|
LLIST_FOREACH(&keys[action], i) {
|
||||||
if (strcmp(LLIST_GET_DATA(i), oldstr) == 0) {
|
if (strcmp(LLIST_GET_DATA(i), oldstr) == 0) {
|
||||||
LLIST_REMOVE(&keys[action], i);
|
LLIST_REMOVE(&keys[action], i);
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
mem_free(oldstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keys_remove_binding(int key, enum key action)
|
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;
|
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)
|
int keys_str2int(const char *key)
|
||||||
@ -285,6 +356,8 @@ int keys_str2int(const char *key)
|
|||||||
return CTRL((int)key[1]);
|
return CTRL((int)key[1]);
|
||||||
else if (starts_with(key, "C-"))
|
else if (starts_with(key, "C-"))
|
||||||
return CTRL((int)key[strlen("C-")]);
|
return CTRL((int)key[strlen("C-")]);
|
||||||
|
else if (starts_with(key, "U+"))
|
||||||
|
return strtol(&key[2], NULL, 16);
|
||||||
else if (!strcmp(key, "TAB"))
|
else if (!strcmp(key, "TAB"))
|
||||||
return TAB;
|
return TAB;
|
||||||
else if (!strcmp(key, "ESC"))
|
else if (!strcmp(key, "ESC"))
|
||||||
@ -307,29 +380,36 @@ int keys_str2int(const char *key)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *keys_int2str(int key)
|
char *keys_int2str(int key)
|
||||||
{
|
{
|
||||||
|
char *res;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case TAB:
|
case TAB:
|
||||||
return "TAB";
|
return strdup("TAB");
|
||||||
case SPACE:
|
case SPACE:
|
||||||
return "SPC";
|
return strdup("SPC");
|
||||||
case ESCAPE:
|
case ESCAPE:
|
||||||
return "ESC";
|
return strdup("ESC");
|
||||||
case KEY_UP:
|
case KEY_UP:
|
||||||
return "UP";
|
return strdup("UP");
|
||||||
case KEY_DOWN:
|
case KEY_DOWN:
|
||||||
return "DWN";
|
return strdup("DWN");
|
||||||
case KEY_LEFT:
|
case KEY_LEFT:
|
||||||
return "LFT";
|
return strdup("LFT");
|
||||||
case KEY_RIGHT:
|
case KEY_RIGHT:
|
||||||
return "RGT";
|
return strdup("RGT");
|
||||||
case KEY_HOME:
|
case KEY_HOME:
|
||||||
return "KEY_HOME";
|
return strdup("KEY_HOME");
|
||||||
case KEY_END:
|
case KEY_END:
|
||||||
return "KEY_END";
|
return strdup("KEY_END");
|
||||||
default:
|
}
|
||||||
return (char *)keyname(key);
|
|
||||||
|
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