Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ncurses extended key support #1048

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/tig/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,8 @@ typedef bool (*key_visitor_fn)(void *data, const char *group, struct keymap *key
const struct request_info *req_info, const struct run_request *run_req);
bool foreach_key(key_visitor_fn fn, void *data, bool combine_keys);

void init_extended_keys(int pass);
int is_extended_key_name(const char *name, int namelen);
int is_extended_key_value(int value);
#endif
/* vim: set ts=8 sw=8 noexpandtab: */
3 changes: 3 additions & 0 deletions include/tig/tig.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,7 @@ void TIG_NORETURN usage(const char *message);

#endif

void add_to_exit_msg(char *msg);
void print_exit_msg(char *el);

/* vim: set ts=8 sw=8 noexpandtab: */
22 changes: 20 additions & 2 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "tig/draw.h"
#include "tig/display.h"
#include "tig/watch.h"
#include "tig/keys.h"

static void set_terminal_modes(void);

Expand Down Expand Up @@ -571,7 +572,14 @@ report_clear(void)
static void
done_display(void)
{
char el[256] = { "" };

if (cursed) {
char *tp;
tp = tigetstr("el");
if ((tp != NULL) && (tp != (char *)-1)) {
strcpy(el, tp);
}
if (status_win) {
werase(status_win);
doupdate();
Expand All @@ -591,6 +599,8 @@ done_display(void)
tcsetpgrp(opt_tty.fd, opt_tty.opgrp);
signal(SIGTTOU, SIG_DFL);
}

print_exit_msg(el);
}

static void
Expand Down Expand Up @@ -708,6 +718,8 @@ init_display(void)
use_scroll_redrawwin = true;
use_scroll_status_wclear = false;
}

init_extended_keys(1);
}

static bool
Expand Down Expand Up @@ -883,17 +895,23 @@ get_input(int prompt_position, struct key *key)
* is set and the key value is updated to the proper
* ASCII value.
*/
if (KEY_CTL('@') <= key_value && key_value <= KEY_CTL('y') &&
if (KEY_CTL('@') <= key_value && key_value <= KEY_CTL('_') &&
key_value != KEY_RETURN && key_value != KEY_TAB) {
key->modifiers.control = 1;
key_value = key_value | 0x40;
}

if ((key_value >= KEY_MIN && key_value < KEY_MAX) || key_value < 0x1F) {
if ((key_value >= KEY_MIN && key_value < KEY_MAX) || key_value <= 0x1F) {
key->data.value = key_value;
return key->data.value;
}

int ext_value = is_extended_key_value(key_value);
if (ext_value) {
key->data.value = ext_value;
return key->data.value;
}

key->modifiers.multibytes = 1;
key->data.bytes[0] = key_value;

Expand Down
204 changes: 202 additions & 2 deletions src/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ static const struct key_mapping key_mappings[] = {
{ "DoubleQuote", '"' },
};

static int num_extended_keys = 0;
static struct key_mapping *ext_key_mappings = NULL;

static const struct key_mapping *
get_key_mapping(const char *name, size_t namelen)
{
Expand All @@ -258,6 +261,12 @@ get_key_mapping(const char *name, size_t namelen)
return &key_mappings[i];
}

for (i = 0; i < num_extended_keys; i++) {
if (namelen == strlen(ext_key_mappings[i].name) &&
!strncasecmp(ext_key_mappings[i].name, name, namelen))
return &ext_key_mappings[i];
}

return NULL;
}

Expand Down Expand Up @@ -299,6 +308,15 @@ get_key_value(const char **name_ptr, struct key *key)
if (!end)
return error("Missing '>' from key mapping: %s", name);

const char *start = name + 1;
int len = end - start;
int ext_value = is_extended_key_name(start, len);
if (ext_value) {
*name_ptr = end + 1;
key->data.value = ext_value;
return SUCCESS;
}

if (!prefixcmp(name, "<Ctrl-")) {
key->modifiers.control = 1;
return parse_key_value(key, name_ptr, 6, NULL, end);
Expand All @@ -309,8 +327,6 @@ get_key_value(const char **name_ptr, struct key *key)

} else {
const struct key_mapping *mapping;
const char *start = name + 1;
int len = end - start;

mapping = get_key_mapping(start, len);
if (!mapping)
Expand Down Expand Up @@ -380,6 +396,14 @@ get_key_name(const struct key key[], size_t keys, bool quote_comma)
name = key_mappings[j].name;
break;
}

for (j = 0; j < num_extended_keys; j++)
if (ext_key_mappings[j].value == value) {
start = "<";
end = ">";
name = ext_key_mappings[j].name;
break;
}
}

if (!string_format_from(buf, &pos, "%s%s%s", start, name, end))
Expand Down Expand Up @@ -657,4 +681,180 @@ foreach_key(key_visitor_fn visitor, void *data, bool combine_keys)
return true;
}

struct ext_key_table {
const char *tname;
const char *mname;
const char *mname2;
};

static struct ext_key_table ext_keys[] = {
{ "kUP3", "Alt-Up", "A-Up" },
{ "kUP4", "Alt-Shift-Up", "A-S-Up" },
{ "kUP5", "Ctrl-Up", "C-Up" },
{ "kUP6", "Ctrl-Shift-Up", "C-S-Up" },
{ "kUP7", "Alt-Ctrl-Up", "A-C-Up" },
{ "kDN3", "Alt-Down", "A-Down" },
{ "kDN4", "Alt-Shift-Down", "A-S-Down" },
{ "kDN5", "Ctrl-Down", "C-Down" },
{ "kDN6", "Ctrl-Shift-Down" , "C-S-Down" },
{ "kDN7", "Alt-Ctrl-Down", "A-C-Down" },
{ "kRIT3", "Alt-Right", "A-Right" },
{ "kRIT4", "Alt-Shift-Right", "A-S-Right" },
{ "kRIT5", "Ctrl-Right", "C-Right" },
{ "kRIT6", "Ctrl-Shift-Right", "C-S-Right" },
{ "kRIT7", "Alt-Ctrl-Right", "A-C-Right" },
{ "kLFT3", "Alt-Left", "A-Left" },
{ "kLFT4", "Alt-Shift-Left", "A-S-Left" },
{ "kLFT5", "Ctrl-Left", "C-Left" },
{ "kLFT6", "Ctrl-Shift-Left", "C-S-Left" },
{ "kLFT7", "Alt-Ctrl-Left", "A-C-Left" },
{ "kIC3", "Alt-Insert", "A-Insert" },
{ "kIC4", "Alt-Shift-Insert", "A-S-Insert" },
{ "kIC5", "Ctrl-Insert", "C-Insert" },
{ "kIC6", "Ctrl-Shift-Insert", "C-S-Insert" },
{ "kIC7", "Alt-Ctrl-Insert", "A-C-Insert" },
{ "kDC3", "Alt-Delete", "A-Delete" },
{ "kDC4", "Alt-Shift-Delete", "A-S-Delete" },
{ "kDC5", "Ctrl-Delete", "C-Delete" },
{ "kDC6", "Ctrl-Shift-Delete", "C-S-Delete" },
{ "kDC7", "Alt-Ctrl-Delete", "A-C-Delete" },
{ "kHOM3", "Alt-Home", "A-Home" },
{ "kHOM4", "Alt-Shift-Home", "A-S-Home" },
{ "kHOM5", "Ctrl-Home", "C-Home" },
{ "kHOM6", "Ctrl-Shift-Home", "C-S-Home" },
{ "kHOM7", "Alt-Ctrl-Home", "A-C-Home" },
{ "kEND3", "Alt-End", "A-End" },
{ "kEND4", "Alt-Shift-End", "A-S-End" },
{ "kEND5", "Ctrl-End", "C-End" },
{ "kEND6", "Ctrl-Shift-End", "C-S-End" },
{ "kEND7", "Alt-Ctrl-End", "A-C-End" },
{ "kNXT3", "Alt-PageDown", "A-PageDown" },
{ "kNXT4", "Alt-Shift-PageDown", "A-S-PageDown" },
{ "kNXT5", "Ctrl-PageDown", "C-PageDown" },
{ "kNXT6", "Ctrl-Shift-PageDown", "C-S-PageDown" },
{ "kNXT7", "Alt-Ctrl-PageDown", "A-C-PageDown" },
{ "kPRV3", "Alt-PageUp", "A-PageUp" },
{ "kPRV4", "Alt-Shift-PageUp", "A-S-PageUp" },
{ "kPRV5", "Ctrl-PageUp", "C-PageUp" },
{ "kPRV6", "Ctrl-Shift-PageUp", "C-S-PageUp" },
{ "kPRV7", "Alt-Ctrl-PageUp", "A-C-PageUp" }
};

struct ext_key_table2 {
const char *tname;
const char *mname;
const char *mname2;
int valid;
int value;
int value2;
};

static struct ext_key_table2 *ext_key_valmap = NULL;

void
init_extended_keys(int pass)
{
int i, j;

num_extended_keys = ARRAY_SIZE(ext_keys);

if (pass == 0) {
ext_key_valmap = (struct ext_key_table2 *)malloc(num_extended_keys * sizeof(struct ext_key_table2));
ext_key_mappings = (struct key_mapping *)malloc(num_extended_keys * sizeof(struct key_mapping));

for (i = 0; i < num_extended_keys; i++) {
ext_key_mappings[i].name = &ext_keys[i].mname[0];
ext_key_mappings[i].value = 1000000 + i;
ext_key_valmap[i].tname = &ext_keys[i].tname[0];
ext_key_valmap[i].mname = &ext_keys[i].mname[0];
ext_key_valmap[i].mname2 = &ext_keys[i].mname2[0];
ext_key_valmap[i].valid = 0;
ext_key_valmap[i].value = 0;
ext_key_valmap[i].value2 = ext_key_mappings[i].value;
}

return;
}

char *tp;
int num_valid = 0;
for (i = 0; i < num_extended_keys; i++) {
tp = tigetstr(ext_key_valmap[i].tname);
if ((tp != NULL) && (tp != (char *)-1)) {
ext_key_valmap[i].valid |= 2;
num_valid++;
}
}

const char *kn;
int num_added = 0;
for(i=KEY_MAX;i<1023;i++) {
kn = keyname(i);
if (kn != NULL) {
for (j = 0; j < num_extended_keys; j++) {
if (ext_key_valmap[j].valid & 2) {
if (!strcmp(kn, ext_key_valmap[j].tname)) {
ext_key_valmap[j].value = i;
ext_key_valmap[j].valid |= 4;
num_added++;
if (num_added == num_valid)
{
i = 1024;
break;
}
}
}
}
}
}

char warn_msg[256] = { "" };
for (i = 0; i < num_extended_keys; i++) {
if ((ext_key_valmap[i].valid & 1) && !(ext_key_valmap[i].valid & 4)) {
sprintf(warn_msg, "tig warning: <%s> key binding not supported in terminfo (%s)\n",
ext_key_mappings[i].name, ext_key_valmap[i].tname);
add_to_exit_msg(warn_msg);
}
}
}

int
is_extended_key_name(const char *name, int namelen)
{
int i;
int ret = 0;

for(i=0; i<num_extended_keys; i++) {
if (namelen == strlen(ext_key_valmap[i].mname) &&
!strncasecmp(ext_key_valmap[i].mname, name, namelen)) {
ret = ext_key_mappings[i].value;
ext_key_valmap[i].valid |= 1;
break;
} else if (namelen == strlen(ext_key_valmap[i].mname2) &&
!strncasecmp(ext_key_valmap[i].mname2, name, namelen)) {
ret = ext_key_mappings[i].value;
ext_key_valmap[i].valid |= 1;
break;
}
}

return ret;
}

int
is_extended_key_value(int value)
{
int i;
int ret = 0;

for(i=0; i<num_extended_keys; i++) {
if ((ext_key_valmap[i].valid & 4) && (ext_key_valmap[i].value == value)) {
ret = ext_key_valmap[i].value2;
break;
}
}

return ret;
}

/* vim: set ts=8 sw=8 noexpandtab: */
52 changes: 52 additions & 0 deletions src/tig.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,56 @@ handle_git_prefix(void)
return SUCCESS;
}

static size_t exit_len = 0;
static char *exit_msg = NULL;

void
add_to_exit_msg(char *msg)
{
if (msg) {
size_t len = strlen(msg);
if (len == 0)
return;
if (len >= exit_len) {
exit_len = len + 1000;
exit_msg = (char *)realloc(exit_msg, exit_len+1);
if (exit_msg == NULL) {
exit_len = 0;
} else {
strcat(exit_msg, msg);
}
} else {
exit_len += len;
strcat(exit_msg, msg);
}
}
}

void
print_exit_msg(char *el)
{
size_t lel = 0;

if (exit_msg) {
size_t len = strlen(exit_msg);
if (len > 0) {
if (el) {
lel = strlen(el);
}
if (lel > 0) {
char *line = strtok(exit_msg, "\n");
while (line != NULL) {
write(fileno(stdout), el, lel);
fprintf(stderr, "%s\n", line);
line = strtok(NULL, "\n");
}
} else {
fprintf(stderr, "%s", exit_msg);
}
}
}
}

int
main(int argc, const char *argv[])
{
Expand All @@ -822,6 +872,8 @@ main(int argc, const char *argv[])
codeset = nl_langinfo(CODESET);
}

init_extended_keys(0);

die_if_failed(handle_git_prefix(), "Failed to handle GIT_PREFIX");
die_if_failed(load_repo_info(), "Failed to load repo info.");
die_if_failed(load_options(), "Failed to load user config.");
Expand Down