From 3015138a5aaf6be04cd8e71f04fc2b2bc0e30144 Mon Sep 17 00:00:00 2001 From: jfreegman Date: Tue, 13 Oct 2020 16:12:55 -0400 Subject: [PATCH] Manually attempt to decode input char sequences This is currently a fallback method for when the terminal doesn't detect ctrl arrow sequences, but it is generalized for future additions --- src/chat.c | 25 ++++++--- src/friendlist.c | 19 ++++--- src/groupchat.c | 28 +++++++--- src/prompt.c | 25 +++++++-- src/windows.c | 139 +++++++++++++++++++++++++++++++++++++---------- src/windows.h | 2 +- 6 files changed, 180 insertions(+), 58 deletions(-) diff --git a/src/chat.c b/src/chat.c index 64d8d1b..c254e49 100644 --- a/src/chat.c +++ b/src/chat.c @@ -1002,7 +1002,10 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id); } -static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) +/* + * Return true if input is recognized by handler + */ +bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; @@ -1014,7 +1017,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) UNUSED_VAR(y); if (y2 <= 0 || x2 <= 0) { - return; + return false; } if (ctx->pastemode && key == '\r') { @@ -1023,7 +1026,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (self->help->active) { help_onKey(self, key); - return; + return true; } if (ltr || key == '\n') { /* char is printable */ @@ -1033,16 +1036,21 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) set_self_typingstatus(self, m, 1); } - return; + return true; } if (line_info_onKey(self, key)) { - return; + return true; } - input_handle(self, key, x, x2); + if (input_handle(self, key, x, x2)) { + return true; + } + + int input_ret = false; if (key == '\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */ + input_ret = true; int diff = -1; /* TODO: make this not suck */ @@ -1080,6 +1088,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } } else if (key == '\r') { + input_ret = true; rm_trailing_spaces_buf(ctx); if (!wstring_is_empty(ctx->line)) { @@ -1096,7 +1105,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (line[0] == '/') { if (strcmp(line, "/close") == 0) { kill_chat_window(self, m); - return; + return input_ret; } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { send_action(self, ctx, m, line + strlen("/me ")); } else { @@ -1125,6 +1134,8 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (ctx->len <= 0 && ctx->self_is_typing) { set_self_typingstatus(self, m, 0); } + + return input_ret; } static void chat_onDraw(ToxWindow *self, Tox *m) diff --git a/src/friendlist.c b/src/friendlist.c index f6b1a7f..77f45ad 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -825,25 +825,28 @@ static void unblock_friend(Tox *m, uint32_t bnum) sort_friendlist_index(); } -static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) +/* + * Return true if input is recognized by handler + */ +static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { if (self->help->active) { help_onKey(self, key); - return; + return true; } if (key == 'h') { help_init_menu(self); - return; + return true; } if (!blocklist_view && !Friends.num_friends && (key != KEY_RIGHT && key != KEY_LEFT)) { - return; + return true; } if (blocklist_view && !Blocked.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT)) { - return; + return true; } int f = 0; @@ -860,11 +863,11 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) del_friend_deactivate(m, key); } - return; + return true; } if (key == ltr) { - return; + return true; } switch (key) { @@ -914,6 +917,8 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) break; } + + return true; } #define FLIST_OFST 6 /* Accounts for space at top and bottom */ diff --git a/src/groupchat.c b/src/groupchat.c index 673b2cd..fccc321 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -480,7 +480,10 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a } } -static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) +/* + * Return true if input is recognized by handler + */ +static bool groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { ChatContext *ctx = self->chatwin; @@ -491,12 +494,12 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) UNUSED_VAR(y); if (x2 <= 0 || y2 <= 0) { - return; + return false; } if (self->help->active) { help_onKey(self, key); - return; + return true; } if (ctx->pastemode && key == '\r') { @@ -505,18 +508,22 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (ltr || key == '\n') { /* char is printable */ input_new_char(self, key, x, x2); - return; + return true; } if (line_info_onKey(self, key)) { - return; + return true; } if (input_handle(self, key, x, x2)) { - return; + return true; } + bool input_ret = false; + if (key == '\t') { /* TAB key: auto-completes peer name or command */ + input_ret = true; + if (ctx->len > 0) { int diff; @@ -534,7 +541,6 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } #endif - else { diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE); } @@ -551,16 +557,20 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) sound_notify(self, notif_error, 0, NULL); } } else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */ + input_ret = true; const int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; if (groupchats[self->num].side_pos < (int64_t) groupchats[self->num].num_peers - L) { ++groupchats[self->num].side_pos; } } else if (key == user_settings->key_peer_list_up) { + input_ret = true; + if (groupchats[self->num].side_pos > 0) { --groupchats[self->num].side_pos; } } else if (key == '\r') { + input_ret = true; rm_trailing_spaces_buf(ctx); if (!wstring_is_empty(ctx->line)) { @@ -577,7 +587,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (line[0] == '/') { if (strcmp(line, "/close") == 0) { delete_groupchat(self, m, self->num); - return; + return true; } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { send_group_action(self, ctx, m, line + strlen("/me ")); } else { @@ -596,6 +606,8 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) wmove(self->window, y2 - CURS_Y_OFFSET, 0); reset_buf(ctx); } + + return input_ret; } static void groupchat_onDraw(ToxWindow *self, Tox *m) diff --git a/src/prompt.c b/src/prompt.c index 0fd65c8..0ab0c4f 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -212,7 +212,10 @@ static int add_friend_request(const char *public_key, const char *data) return -1; } -static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) +/* + * Return true if input is recognized by handler + */ +static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { ChatContext *ctx = self->chatwin; @@ -223,7 +226,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) UNUSED_VAR(y); if (x2 <= 0 || y2 <= 0) { - return; + return false; } if (ctx->pastemode && key == '\r') { @@ -233,21 +236,27 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) /* ignore non-menu related input if active */ if (self->help->active) { help_onKey(self, key); - return; + return true; } if (ltr || key == '\n') { /* char is printable */ input_new_char(self, key, x, x2); - return; + return true; } if (line_info_onKey(self, key)) { - return; + return true; } - input_handle(self, key, x, x2); + if (input_handle(self, key, x, x2)) { + return true; + } + + int input_ret = false; if (key == '\t') { /* TAB key: auto-completes command */ + input_ret = true; + if (ctx->len > 1 && ctx->line[0] == '/') { int diff = -1; @@ -285,6 +294,8 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) sound_notify(self, notif_error, 0, NULL); } } else if (key == '\r') { + input_ret = true; + rm_trailing_spaces_buf(ctx); if (!wstring_is_empty(ctx->line)) { @@ -305,6 +316,8 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) wmove(self->window, y2 - CURS_Y_OFFSET, 0); reset_buf(ctx); } + + return input_ret; } static void prompt_onDraw(ToxWindow *self, Tox *m) diff --git a/src/windows.c b/src/windows.c index 7a96def..d5eb1e5 100644 --- a/src/windows.c +++ b/src/windows.c @@ -567,6 +567,91 @@ static void draw_bar(void) refresh(); } +/* + * Gets current char from stdscr and puts it in ch. + * + * Return 1 if char is printable. + * Return 0 if char is not printable. + * Return -1 on error. + */ +static int get_current_char(wint_t *ch) +{ + wint_t tmpchar = 0; + bool is_printable = false; + +#ifdef HAVE_WIDECHAR + int status = wget_wch(stdscr, &tmpchar); + + if (status == ERR) { + return -1; + } + + if (status == OK) { + is_printable = iswprint(tmpchar); + } + +#else + tmpchar = getch(); + + if (tmpchar == ERR) { + return -1; + } + + is_printable = isprint(tmpchar); +#endif /* HAVE_WIDECHAR */ + + *ch = tmpchar; + + return (int) is_printable; +} + +static struct key_sequence_codes { + wchar_t *code; + wint_t key; +} Keys[] = { + { L"[1;5D", T_KEY_C_LEFT }, + { L"[1;5C", T_KEY_C_RIGHT }, + { NULL, 0 } +}; + +/* + * Return key code corresponding to character sequence queued in stdscr. + * Return -1 if sequence is unknown. + */ +#define MAX_SEQUENCE_SIZE 5 +static wint_t get_input_sequence_code(void) +{ + wchar_t code[MAX_SEQUENCE_SIZE + 1]; + + size_t length = 0; + wint_t ch = 0; + + for (size_t i = 0; i < MAX_SEQUENCE_SIZE; ++i) { + int res = get_current_char(&ch); + + if (res < 0) { + break; + } + + ++length; + code[i] = (wchar_t) ch; + } + + if (length == 0) { + return -1; + } + + code[length] = L'\0'; + + for (size_t i = 0; Keys[i].key != 0; ++i) { + if (wcscmp(code, Keys[i].code) == 0) { + return Keys[i].key; + } + } + + return -1; +} + void draw_active_window(Tox *m) { ToxWindow *a = windows[active_window_index]; @@ -579,51 +664,47 @@ void draw_active_window(Tox *m) a->alert = WINDOW_ALERT_NONE; pthread_mutex_unlock(&Winthread.lock); - wint_t ch = 0; - draw_bar(); touchwin(a->window); a->onDraw(a, m); wrefresh(a->window); - /* Handle input */ - bool ltr; -#ifdef HAVE_WIDECHAR - int status = wget_wch(stdscr, &ch); + wint_t ch = 0; + int printable = get_current_char(&ch); - if (status == ERR) { + if (printable < 0) { return; } - if (status == OK) { - ltr = iswprint(ch); - } else { /* if (status == KEY_CODE_YES) */ - ltr = false; - } - -#else - ch = getch(); - - if (ch == ERR) { - return; - } - - /* TODO verify if this works */ - ltr = isprint(ch); -#endif /* HAVE_WIDECHAR */ - - if (!ltr && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) { + if (printable == 0 && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) { set_next_window((int) ch); - } else { + return; + } else if (printable == 0 && !a->is_friendlist) { pthread_mutex_lock(&Winthread.lock); - a->onKey(a, m, ch, ltr); + bool input_ret = a->onKey(a, m, ch, (bool) printable); pthread_mutex_unlock(&Winthread.lock); + + if (input_ret) { + return; + } + + // if an unprintable key code is unrecognized by input handler we attempt to manually decode char sequence + wint_t tmp = get_input_sequence_code(); + + if (tmp != (wint_t) -1) { + ch = tmp; + } } + + pthread_mutex_lock(&Winthread.lock); + a->onKey(a, m, ch, (bool) printable); + pthread_mutex_unlock(&Winthread.lock); } -/* refresh inactive windows to prevent scrolling bugs. - call at least once per second */ +/* Refresh inactive windows to prevent scrolling bugs. + * Call at least once per second. + */ void refresh_inactive_windows(void) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { diff --git a/src/windows.h b/src/windows.h index 503b286..3d0ed0f 100644 --- a/src/windows.h +++ b/src/windows.h @@ -113,7 +113,7 @@ typedef struct Help Help; struct ToxWindow { /* ncurses */ - void(*onKey)(ToxWindow *, Tox *, wint_t, bool); + bool(*onKey)(ToxWindow *, Tox *, wint_t, bool); void(*onDraw)(ToxWindow *, Tox *); void(*onInit)(ToxWindow *, Tox *);