# HG changeset patch # User Mikael Berthe # Date 1142768506 -3600 # Node ID 4532a9fe0e8c781d3ce0296f41962b4cf80ce0be # Parent 715952c2f37fc7ee0af40f834eafeadbf8d3459c Handle some keyboard escape sequences Some sequences are not caught by ncurses, so I've made a basic escape sequences interpreter. The patch defines a few sequences for xterm and Gnome terminal (ctrl-arrows). diff -r 715952c2f37f -r 4532a9fe0e8c mcabber/src/main.c --- a/mcabber/src/main.c Fri Mar 17 17:17:21 2006 +0100 +++ b/mcabber/src/main.c Sun Mar 19 12:41:46 2006 +0100 @@ -191,6 +191,7 @@ unsigned int ping; int ret; unsigned int refresh = 0; + keycode kcode; credits(); @@ -271,7 +272,8 @@ scr_LogPrint(LPRINT_DEBUG, "Entering into main loop..."); for (ret = 0 ; ret != 255 ; ) { - key = scr_Getch(); + scr_Getch(&kcode); + key = kcode.value; /* The refresh is really an ugly hack, but we need to call doupdate() from time to time to catch the RESIZE events, because getch keep @@ -279,7 +281,7 @@ However, it allows us to handle an autoaway check here... */ if (key != ERR) { - ret = process_key(key); + ret = process_key(kcode); refresh = 0; } else if (refresh++ > 1) { doupdate(); diff -r 715952c2f37f -r 4532a9fe0e8c mcabber/src/screen.c --- a/mcabber/src/screen.c Fri Mar 17 17:17:21 2006 +0100 +++ b/mcabber/src/screen.c Sun Mar 19 12:41:46 2006 +0100 @@ -92,6 +92,17 @@ static GList *cmdhisto_cur; static char cmdhisto_backup[INPUTLINE_LENGTH+1]; +#define MAX_KEYSEQ_LENGTH 8 + +typedef struct { + char *seqstr; + guint mkeycode; + gint value; +} keyseq; + +GSList *keyseqlist; +static void add_keyseq(char *seqstr, guint mkeycode, gint value); + /* Functions */ @@ -209,6 +220,26 @@ void scr_InitCurses(void) { + /* Key sequences initialization */ + add_keyseq("O5A", MKEY_EQUIV, 521); // Ctrl-Up + add_keyseq("O5B", MKEY_EQUIV, 514); // Ctrl-Down + add_keyseq("O5C", MKEY_EQUIV, 518); // Ctrl-Right + add_keyseq("O5D", MKEY_EQUIV, 516); // Ctrl-Left + add_keyseq("O6A", MKEY_EQUIV, 520); // Ctrl-Shift-Up + add_keyseq("O6B", MKEY_EQUIV, 513); // Ctrl-Shift-Down + add_keyseq("O6C", MKEY_EQUIV, 402); // Ctrl-Shift-Right + add_keyseq("O6D", MKEY_EQUIV, 393); // Ctrl-Shift-Left + + // Xterm + add_keyseq("[1;5A", MKEY_EQUIV, 521); // Ctrl-Up + add_keyseq("[1;5B", MKEY_EQUIV, 514); // Ctrl-Down + add_keyseq("[1;5C", MKEY_EQUIV, 518); // Ctrl-Right + add_keyseq("[1;5D", MKEY_EQUIV, 516); // Ctrl-Left + add_keyseq("[1;6A", MKEY_EQUIV, 520); // Ctrl-Shift-Up + add_keyseq("[1;6B", MKEY_EQUIV, 513); // Ctrl-Shift-Down + add_keyseq("[1;6C", MKEY_EQUIV, 402); // Ctrl-Shift-Right + add_keyseq("[1;6D", MKEY_EQUIV, 393); // Ctrl-Shift-Left + initscr(); raw(); noecho(); @@ -1863,39 +1894,140 @@ refresh_inputline(); } -int scr_Getch(void) +static void add_keyseq(char *seqstr, guint mkeycode, gint value) +{ + keyseq *ks; + + // Let's make sure the length is correct + if (strlen(seqstr) > MAX_KEYSEQ_LENGTH) { + scr_LogPrint(LPRINT_LOGNORM, "add_keyseq(): key sequence is too long!"); + return; + } + + ks = g_new0(keyseq, 1); + ks->seqstr = g_strdup(seqstr); + ks->mkeycode = mkeycode; + ks->value = value; + keyseqlist = g_slist_append(keyseqlist, ks); +} + +// match_keyseq(iseq, &ret) +// Check if "iseq" is a known key escape sequence. +// Return value: +// -1 if "seq" matches no known sequence +// 0 if "seq" could match 1 or more known sequences +// >0 if "seq" matches a key sequence; the mkey code is returned +// and *ret is set to the matching keyseq structure. +static inline guint match_keyseq(int *iseq, keyseq **ret) { - return wgetch(inputWnd); + GSList *ksl; + keyseq *ksp; + char *p, c; + int *i; + int needmore = FALSE; + + for (ksl = keyseqlist; ksl; ksl = g_slist_next(ksl)) { + ksp = ksl->data; + p = ksp->seqstr; + i = iseq; + while (1) { + c = (unsigned char)*i; + if (!*p && !c) { // Match + (*ret) = ksp; + return ksp->mkeycode; + } + if (!c) { + // iseq is too short + needmore = TRUE; + break; + } else if (!*p || c != *p) { + // This isn't a match + break; + } + p++; i++; + } + } + + if (needmore) + return 0; + return -1; +} + +void scr_Getch(keycode *kcode) +{ + keyseq *mks; + int ks[MAX_KEYSEQ_LENGTH+1]; + int i; + + memset(kcode, 0, sizeof(keycode)); + memset(ks, 0, sizeof(ks)); + + kcode->value = wgetch(inputWnd); + if (kcode->value != 27) + return; + + // Check for escape key sequence + for (i=0; i < MAX_KEYSEQ_LENGTH; i++) { + int match; + ks[i] = wgetch(inputWnd); + if (ks[i] == ERR) break; + match = match_keyseq(ks, &mks); + if (match == -1) { + // No such key sequence. Let's increment i as it is a valid key. + i++; + break; + } + if (match > 0) { + // We have a matching sequence + kcode->mcode = mks->mkeycode; + kcode->value = mks->value; + return; + } + } + + // No match. Let's return a meta-key. + if (i > 0) { + kcode->mcode = MKEY_META; + kcode->value = ks[0]; + } + if (i > 1) { + // We need to push some keys back to the keyboard buffer + while (i-- > 1) + ungetch(ks[i]); + } + return; } // process_key(key) // Handle the pressed key, in the command line (bottom). -int process_key(int key) +int process_key(keycode kcode) { - if (key == 27) { - key = scr_Getch(); - if (key == -1 || key == 27) { - // This is a "real" escape... - scr_CheckAutoAway(TRUE); - currentWindow = NULL; - chatmode = FALSE; - if (current_buddy) - buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); - scr_RosterVisibility(1); - scr_UpdateChatStatus(FALSE); - top_panel(chatPanel); - top_panel(inputPanel); - update_panels(); - } else { // Meta - switch (key) { - default: - scr_LogPrint(LPRINT_NORMAL, "Unknown key=M%d", key); - } - } - key = -1; + int key = kcode.value; + + switch (kcode.mcode) { + case 0: + break; + case MKEY_EQUIV: + key = kcode.value; + break; + case MKEY_META: + key = ERR; + switch (kcode.value) { + case 27: + key = 27; + break; + default: + scr_LogPrint(LPRINT_NORMAL, "Unknown key=M%d", kcode.value); + } + break; + default: + scr_LogPrint(LPRINT_NORMAL, "Unknown mkeycode! (%d)", kcode.mcode); + key = ERR; } + switch (key) { - case -1: + case 0: + case ERR: break; case 8: // Ctrl-h case 127: // Backspace too @@ -2031,6 +2163,18 @@ case KEY_RESIZE: scr_Resize(); break; + case 27: // ESC + scr_CheckAutoAway(TRUE); + currentWindow = NULL; + chatmode = FALSE; + if (current_buddy) + buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); + scr_RosterVisibility(1); + scr_UpdateChatStatus(FALSE); + top_panel(chatPanel); + top_panel(inputPanel); + update_panels(); + break; default: if (isprint(key)) { char tmpLine[INPUTLINE_LENGTH+1]; diff -r 715952c2f37f -r 4532a9fe0e8c mcabber/src/screen.h --- a/mcabber/src/screen.h Fri Mar 17 17:17:21 2006 +0100 +++ b/mcabber/src/screen.h Sun Mar 19 12:41:46 2006 +0100 @@ -6,6 +6,16 @@ #include "logprint.h" +// Length of the timestamp & flag prefix in the chat buffer window +#define PREFIX_WIDTH 17 + +#define INPUTLINE_LENGTH 1024 + +// Only used in screen.c; this is the maximum line number +// in a multi-line message. Should be < 1000 +// Note: message length is limited by the HBB_BLOCKSIZE size too +#define MULTILINE_MAX_LINE_NUMBER 299 + enum colors { COLOR_GENERAL = 3, COLOR_HIGHLIGHT, @@ -19,17 +29,18 @@ int COLOR_ATTRIB[COLOR_max]; -// Length of the timestamp & flag prefix in the chat buffer window -#define PREFIX_WIDTH 17 - -#define INPUTLINE_LENGTH 1024 +extern int update_roster; -// Only used in screen.c; this is the maximum line number -// in a multi-line message. Should be < 1000 -// Note: message length is limited by the HBB_BLOCKSIZE size too -#define MULTILINE_MAX_LINE_NUMBER 299 +typedef struct { + int value; + enum { + MKEY_META = 1, + MKEY_EQUIV + } mcode; +} keycode; -extern int update_roster; +void scr_Getch(keycode *kcode); +int process_key(keycode kcode); void scr_InitCurses(void); void scr_TerminateCurses(void); @@ -51,9 +62,6 @@ inline const char *scr_get_multiline(void); inline void scr_Beep(void); -int scr_Getch(void); - -int process_key(int); void scr_CheckAutoAway(bool activity);