changeset 761:4532a9fe0e8c

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).
author Mikael Berthe <mikael@lilotux.net>
date Sun, 19 Mar 2006 12:41:46 +0100
parents 715952c2f37f
children 96d46e00524a
files mcabber/src/main.c mcabber/src/screen.c mcabber/src/screen.h
diffstat 3 files changed, 193 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- 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();
--- 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];
--- 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);