diff --git a/src/sdtui.c b/src/sdtui.c index e2a5739..f576276 100644 --- a/src/sdtui.c +++ b/src/sdtui.c @@ -41,27 +41,39 @@ #include "stardict.h" -#define KEY_SOH 1 /**< Ctrl-A */ -#define KEY_ENQ 5 /**< Ctrl-E */ -#define KEY_VT 11 /**< Ctrl-K */ -#define KEY_FF 12 /**< Ctrl-L */ -#define KEY_NAK 21 /**< Ctrl-U */ -#define KEY_ETB 23 /**< Ctrl-W */ +#define KEY_SOH 1 //!< Ctrl-A +#define KEY_STX 2 //!< Ctrl-B +#define KEY_ENQ 5 //!< Ctrl-E +#define KEY_ACK 6 //!< Ctrl-F +#define KEY_VT 11 //!< Ctrl-K +#define KEY_FF 12 //!< Ctrl-L +#define KEY_SO 14 //!< Ctrl-N +#define KEY_DLE 16 //!< Ctrl-P +#define KEY_NAK 21 //!< Ctrl-U +#define KEY_ETB 23 //!< Ctrl-W -#define KEY_RETURN 13 /**< Enter */ -#define KEY_ESCAPE 27 /**< Esc */ +#define KEY_RETURN 13 //!< Enter +#define KEY_ESCAPE 27 //!< Esc -// These codes may or may not work, depending on the terminal -// They lie above KEY_MAX, originally discovered on gnome-terminal -#define KEY_CTRL_UP 565 -#define KEY_CTRL_DOWN 524 -#define KEY_CTRL_LEFT 544 -#define KEY_CTRL_RIGHT 559 +typedef enum { + TERMINAL_UNKNOWN, //!< No extra handling + TERMINAL_XTERM, //!< xterm and VTE extra keycodes + TERMINAL_RXVT //!< rxvt extra keycodes +} TerminalType; //!< Type of the terminal -#define KEY_ALT_UP 563 -#define KEY_ALT_DOWN 522 -#define KEY_ALT_LEFT 542 -#define KEY_ALT_RIGHT 557 +typedef enum { + KEY_NOT_RECOGNISED, //!< Not recognised + + KEY_CTRL_UP, //!< Ctrl + Up arrow + KEY_CTRL_DOWN, //!< Ctrl + Down arrow + KEY_CTRL_LEFT, //!< Ctrl + Left arrow + KEY_CTRL_RIGHT, //!< Ctrl + Right arrow + + KEY_ALT_UP, //!< Alt + Up arrow + KEY_ALT_DOWN, //!< Alt + Down arrow + KEY_ALT_LEFT, //!< Alt + Left arrow + KEY_ALT_RIGHT //!< Alt + Right arrow +} ExtraKeyCode; //!< Translated key codes above KEY_MAX #define _(x) x /**< Fake gettext, for now. */ @@ -87,6 +99,58 @@ struct curses_event MEVENT mouse; }; +/** Translate key codes above KEY_MAX returned from ncurses into something + * meaningful, based on the terminal type. The values have been obtained + * experimentally. Some keycodes make ncurses return KEY_ESCAPE, even + * depending on actual terminal settings, thus this is not reliable at all. + * xterm/VTE seems to behave nicely, though. + */ +static guint +translate_extra_keycode (wchar_t code, TerminalType terminal) +{ + switch (terminal) + { + case TERMINAL_XTERM: + switch (code) + { + case 565: return KEY_CTRL_UP; + case 524: return KEY_CTRL_DOWN; + case 544: return KEY_CTRL_LEFT; + case 559: return KEY_CTRL_RIGHT; + + case 563: return KEY_ALT_UP; + case 522: return KEY_ALT_DOWN; + case 542: return KEY_ALT_LEFT; + case 557: return KEY_ALT_RIGHT; + } + break; + case TERMINAL_RXVT: + switch (code) + { + case 521: return KEY_CTRL_UP; + case 514: return KEY_CTRL_DOWN; + } + break; + case TERMINAL_UNKNOWN: + break; + } + return KEY_NOT_RECOGNISED; +} + +/** Get the type of the terminal based on the TERM environment variable. */ +static TerminalType +get_terminal_type (void) +{ + const gchar *term = g_getenv ("TERM"); + if (!term) return TERMINAL_UNKNOWN; + + gchar term_copy[strcspn (term, "-") + 1]; + g_strlcpy (term_copy, term, sizeof term_copy); + if (!strcmp (term_copy, "xterm")) return TERMINAL_XTERM; + if (!strcmp (term_copy, "rxvt")) return TERMINAL_RXVT; + return TERMINAL_UNKNOWN; +} + // --- Application ------------------------------------------------------------- /** Data relating to one entry within the dictionary. */ @@ -103,22 +167,23 @@ struct view_entry struct application { - GIConv utf8_to_wchar; //!< utf-8 -> wchar_t conversion - GIConv wchar_to_utf8; //!< wchar_t -> utf-8 conversion + TerminalType terminal_type; //!< Type of the terminal + GIConv utf8_to_wchar; //!< utf-8 -> wchar_t conversion + GIConv wchar_to_utf8; //!< wchar_t -> utf-8 conversion - StardictDict *dict; //!< The current dictionary + StardictDict * dict; //!< The current dictionary - guint32 top_position; //!< Index of the topmost dict. entry - guint top_offset; //!< Offset into the top entry - guint selected; //!< Offset to the selected definition - GPtrArray *entries; //!< ViewEntry's within the view + guint32 top_position; //!< Index of the topmost dict. entry + guint top_offset; //!< Offset into the top entry + guint selected; //!< Offset to the selected definition + GPtrArray * entries; //!< ViewEntry's within the view - gchar *search_label; //!< Text of the "Search" label - GArray *input; //!< The current search input - guint input_pos; //!< Cursor position within input - gboolean input_confirmed; //!< Input has been confirmed + gchar * search_label; //!< Text of the "Search" label + GArray * input; //!< The current search input + guint input_pos; //!< Cursor position within input + gboolean input_confirmed; //!< Input has been confirmed - gfloat division; //!< Position of the division column + gfloat division; //!< Position of the division column }; @@ -232,6 +297,8 @@ app_init (Application *self, const gchar *filename) exit (EXIT_FAILURE); } + self->terminal_type = get_terminal_type (); + self->top_position = 0; self->top_offset = 0; self->selected = 0; @@ -584,17 +651,49 @@ app_search_for_entry (Application *self) app_redraw_view (self); } +#define SAVE_CURSOR \ + int last_x, last_y; \ + getyx (stdscr, last_y, last_x); + +#define RESTORE_CURSOR \ + move (last_y, last_x); \ + refresh (); + +/** Process input above KEY_MAX. */ +static gboolean +app_process_extra_code (Application *self, CursesEvent *event) +{ + SAVE_CURSOR + switch (translate_extra_keycode (event->code, self->terminal_type)) + { + case KEY_CTRL_UP: + app_one_entry_up (self); + RESTORE_CURSOR + break; + case KEY_CTRL_DOWN: + app_one_entry_down (self); + RESTORE_CURSOR + break; + + case KEY_ALT_LEFT: + self->division = (app_get_left_column_width (self) - 1.) / COLS; + app_redraw_view (self); + RESTORE_CURSOR + break; + case KEY_ALT_RIGHT: + self->division = (app_get_left_column_width (self) + 1.) / COLS; + app_redraw_view (self); + RESTORE_CURSOR + break; + } + return TRUE; +} + /** Process input that's not a character. */ static gboolean app_process_nonchar_code (Application *self, CursesEvent *event) { - int last_x, last_y; - getyx (stdscr, last_y, last_x); - - #define RESTORE_CURSOR \ - move (last_y, last_x); \ - refresh (); - + SAVE_CURSOR switch (event->code) { case KEY_RESIZE: @@ -627,26 +726,6 @@ app_process_nonchar_code (Application *self, CursesEvent *event) } break; - case KEY_CTRL_UP: - app_one_entry_up (self); - RESTORE_CURSOR - break; - case KEY_CTRL_DOWN: - app_one_entry_down (self); - RESTORE_CURSOR - break; - - case KEY_ALT_LEFT: - self->division = (app_get_left_column_width (self) - 1.) / COLS; - app_redraw_view (self); - RESTORE_CURSOR - break; - case KEY_ALT_RIGHT: - self->division = (app_get_left_column_width (self) + 1.) / COLS; - app_redraw_view (self); - RESTORE_CURSOR - break; - case KEY_UP: if (self->selected > 0) { @@ -717,6 +796,9 @@ app_process_nonchar_code (Application *self, CursesEvent *event) app_redraw_top (self); } break; + + default: + return app_process_extra_code (self, event); } return TRUE; } @@ -741,6 +823,14 @@ app_process_curses_event (Application *self, CursesEvent *event) clear (); app_redraw (self); break; + + case KEY_STX: // Ctrl-B -- back + case KEY_ACK: // Ctrl-F -- forward + case KEY_DLE: // Ctrl-P -- previous + case KEY_SO: // Ctrl-N -- next + // TODO map this to something useful + break; + case KEY_SOH: // Ctrl-A -- move to the start of line self->input_pos = 0; app_redraw_top (self);