Fix history by borrowing from curses.
authorTimo Korvola <tkorvola@iki.fi>
Sat, 3 Mar 2018 22:04:40 +0000 (00:04 +0200)
committerDavid Griffith <dave@661.org>
Sun, 11 Mar 2018 11:48:50 +0000 (04:48 -0700)
src/common/frotz.h
src/sdl/generic.c
src/sdl/sf_video.c

index bfa287e91cbc60bab8b68461ba65a9dba7cbfeea..dc1572933a8a8539330c2b754181879afa01f4a7 100644 (file)
@@ -5,6 +5,9 @@
  *
  */
 
+#ifndef FROTZ_H_
+#define FROTZ_H_
+
 /* Unfortunately, frotz's bool definition conflicts with that of curses.
    But since no os_* function uses it, it's safe to let the frotz core see
    this definition, but have the unix port see the curses version. */
@@ -812,3 +815,5 @@ void    resize_screen(void);
 /* This is callable only from resize_screen. */
 bool    os_repaint_window (int win, int ypos_old, int ypos_new, int xpos,
                            int ysize, int xsize);
+
+#endif
index 42abf2f99000ae75d6107ce6eb2aaac21e1e5cb5..2e531f60b9a50a7f2bd2855267f5adb5029e0610 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "../blorb/blorb.h"
 #include "../common/frotz.h"
+#include "generic.h"
 
 FILE *blorb_fp;
 bb_result_t blorb_res;
@@ -252,4 +253,90 @@ void os_warn (const char *s, ...)
     if (len > sizeof(buf))
         os_display_string((zchar *)"(truncated)\n");
     new_line();
-}/* os_warn */
+}
+
+/* These are useful for circular buffers.
+ */
+#define RING_DEC( ptr, beg, end) (ptr > (beg) ? --ptr : (ptr = (end)))
+#define RING_INC( ptr, beg, end) (ptr < (end) ? ++ptr : (ptr = (beg)))
+
+#define MAX_HISTORY 256
+static char *history_buffer[MAX_HISTORY];
+static char **history_next = history_buffer; /* Next available slot. */
+static char **history_view = history_buffer; /* What the user is looking at. */
+#define history_end (history_buffer + MAX_HISTORY - 1)
+
+
+/**
+ * Add the given string to the next available history buffer slot.
+ */
+void gen_add_to_history(zchar *str)
+{
+
+    if (*history_next != NULL)
+        free( *history_next);
+    *history_next = (char *)malloc(strlen((char *)str) + 1);
+    strcpy( *history_next, (char *)str);
+    RING_INC( history_next, history_buffer, history_end);
+    history_view = history_next; /* Reset user frame after each line */
+
+    return;
+}
+
+
+/**
+ * Reset the view to the end of history.
+ */
+void gen_history_reset()
+{
+    history_view = history_next;
+}
+
+
+/**
+ * Copy last available string to str, if possible.  Return 1 if successful.
+ * Only lines of at most maxlen characters will be considered.  In addition
+ * the first searchlen characters of the history entry must match those of str.
+ */
+int gen_history_back(zchar *str, int searchlen, int maxlen)
+{
+    char **prev = history_view;
+
+    do {
+        RING_DEC(history_view, history_buffer, history_end);
+        if ((history_view == history_next)
+            || (*history_view == NULL)) {
+            os_beep(BEEP_HIGH);
+            history_view = prev;
+            return 0;
+        }
+    } while (strlen(*history_view) > (size_t)maxlen
+             || (searchlen != 0
+                 && strncmp((char *)str, *history_view, searchlen)));
+    strcpy((char *)str + searchlen, *history_view + searchlen);
+    return 1;
+}
+
+
+/**
+ * Opposite of gen_history_back, and works in the same way.
+ */
+int gen_history_forward(zchar *str, int searchlen, int maxlen)
+{
+    char **prev = history_view;
+
+    do {
+        RING_INC(history_view, history_buffer, history_end);
+        if ((history_view == history_next)
+            || (*history_view == NULL)) {
+
+            os_beep(BEEP_HIGH);
+            history_view = prev;
+            return 0;
+        }
+    } while (strlen(*history_view) > (size_t) maxlen
+             || (searchlen != 0
+                 && strncmp((char *)str, *history_view, searchlen)));
+    strcpy((char *)str + searchlen, *history_view + searchlen);
+    return 1;
+}
index 4ff8229dea679a2474203d580abebfb326bde898..d2a9dc17b64f6539ee34474474c1a9e31ac12646 100644 (file)
@@ -7,6 +7,8 @@
 \r
 #include <SDL.h>\r
 \r
+#include "generic.h"\r
+\r
 #include "sf_frotz.h"\r
 \r
 static SDL_Rect blitrect = {0,0,0,0};\r
@@ -720,27 +722,6 @@ zchar os_read_key(int timeout, int cursor)
   return sf_read_key(timeout,cursor,0);\r
   }\r
 \r
-#define MAXHISTORY 8192\r
-static zword History[MAXHISTORY] = {0};\r
-static int histptr = 0;\r
-static void addtoHistory( zchar *buf)\r
-  {\r
-  int n = mywcslen(buf)+2;\r
-  int avail = MAXHISTORY - histptr;\r
-  if (n <= 2) return;\r
-  while (avail < n)\r
-    {\r
-    int k;\r
-    if (histptr == 0) return;\r
-    k = History[histptr-1]+2;\r
-    histptr -= k;\r
-    }\r
-  if (histptr) memmove(History+n,History,histptr*sizeof(zword));\r
-  histptr += n;\r
-  n -= 2;\r
-  History[0] = History[n+1] = n;\r
-  memcpy(History+1,buf,n*sizeof(zword));\r
-  }\r
 \r
 /*\r
  * os_read_line\r
@@ -788,182 +769,130 @@ static void addtoHistory( zchar *buf)
  *\r
  */\r
 zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued)\r
-  {\r
-  static int prev_pos = 0;\r
-  static int prev_history = -1;\r
-  int pos = 0;\r
-  int history = -1;\r
-  int ptx,pty;\r
-  SF_textsetting * ts = sf_curtextsetting();\r
-  SDL_Event event;\r
-\r
-//printf("os_read_line mx%d buf[0]%d tm%d w%d c%d\n",max,buf[0],timeout,width,continued);\r
-//     LineInput line;\r
-  sf_flushtext();\r
-//     theWnd->ResetOverhang();\r
-//     theWnd->UpdateMenus();\r
-//     theWnd->RecaseInput(buf);\r
-\r
-       // Find the editing position\r
-  if (continued)\r
-       {\r
-       if (prev_pos <= (int)mywcslen(buf))\r
-               pos = prev_pos;\r
-       }\r
-  else\r
-       pos = mywcslen(buf);\r
-\r
-//printf("pos %d\n",pos);\r
-       // Find the input history position\r
-  if (continued)\r
-       history = prev_history;\r
-\r
-       // Draw the input line\r
-  ptx = ts->cx;\r
-  pty = ts->cy;\r
-//     CPoint point = theWnd->GetTextPoint();\r
-  ptx -= os_string_width(buf); //theWnd->GetTextWidth(buf,mywcslen(buf));\r
-  sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+{\r
+    static int pos = 0, searchpos = -1;\r
+    int ptx,pty;\r
+    int len = mywcslen(buf);\r
+    SF_textsetting * ts = sf_curtextsetting();\r
+    SDL_Event event;\r
+\r
+    //printf("os_read_line mx%d buf[0]%d tm%d w%d c%d\n",max,buf[0],timeout,width,continued);\r
+    // LineInput line;\r
+    sf_flushtext();\r
+    // theWnd->ResetOverhang();\r
+    // theWnd->UpdateMenus();\r
+    // theWnd->RecaseInput(buf);\r
+\r
+    /* Better be careful here or it might segv.  I wonder if we should just\r
+       ignore 'continued' and check for len > 0 instead?  Might work better\r
+       with Beyond Zork. */\r
+    if (!continued || pos > len || searchpos > len) {\r
+        pos = len;\r
+        gen_history_reset();    /* Reset user's history view. */\r
+        searchpos = -1;         /* -1 means initialize from len. */\r
+    }\r
 \r
-  if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout;\r
-//     InputTimer timer(timeout);\r
-  while (true)\r
-    {\r
-               // Get the next input\r
-    while (SDL_PollEvent(&event)) \r
-       {\r
-       zword c;\r
-       if ((c = goodzkey(&event,1))) \r
-               {\r
-//printf("goodzk %4x\n",c);\r
-               if (c == ZC_BACKSPACE)\r
-                       {\r
-                               // Delete the character to the left of the cursor\r
-                       if (pos > 0)\r
-                               {\r
-                               memmove(buf+pos-1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
-                               pos--;\r
-                               sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                               }\r
-                       }\r
-               else if (c == VK_TAB)\r
-                       {\r
-                       if (pos == (int)mywcslen(buf))\r
-                               {\r
-                               zchar extension[10], *s;\r
-                               completion(buf,extension);\r
-\r
-                                       // Add the completion to the input stream\r
-                               for (s = extension; *s != 0; s++)\r
-                                 if (sf_IsValidChar(*s))\r
-                                       buf[pos++] = (*s);\r
-                               buf[pos] = 0;\r
-                               sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                               }\r
-                       }\r
-               else if ((c == ZC_ARROW_LEFT))\r
-                       {\r
-                               // Move the cursor left\r
-                       if (pos > 0)\r
-                               pos--;\r
-                       sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                       }\r
-               else if ((c == ZC_ARROW_RIGHT))\r
-                       {\r
-                               // Move the cursor right\r
-                       if (pos < (int)mywcslen(buf))\r
-                               pos++;\r
-                       sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                       }\r
-               else if ((c == ZC_ARROW_UP))\r
-                       {\r
-                       int n; zword *p;\r
-                       // Move up through the command history\r
-                       if (history >= 0)\r
-                               {\r
-                               n = History[history];\r
-                               if (n) history += (n+2);\r
-                               }\r
-                       else\r
-                               history = 0;\r
-                       n = History[history];\r
-                       p = History + history + 1;\r
-                       pos = 0;\r
-                       while (n) { buf[pos++] = *p++; n--;}\r
-                       buf[pos] = 0;\r
-                       sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                       }\r
-               else if ((c == ZC_ARROW_DOWN))\r
-                       {\r
-                               // Move down through the command history\r
-                       pos = 0;\r
-                       if (history > 0)\r
-                               {\r
-                               int n; zword *p;\r
-                               n = History[history-1];\r
-                               history -= (n+2);\r
-                               p = History + history + 1;\r
-                               while (n) { buf[pos++] = *p++; n--; }\r
-                               }\r
-                       else\r
-                               history = -1;\r
-                       buf[pos] = 0;\r
-                       sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                       }\r
-               else if (is_terminator(c))\r
-                       {\r
-                               // Terminate the current input\r
-                       m_exitPause = false;\r
-                       sf_DrawInput(buf,pos,ptx,pty,width,false);\r
-\r
-                       if ((c == ZC_SINGLE_CLICK) || (c == ZC_DOUBLE_CLICK))\r
-                               {\r
-/*                             mouse_x = input.mousex+1;\r
+    // Draw the input line\r
+    ptx = ts->cx;\r
+    pty = ts->cy;\r
+    // CPoint point = theWnd->GetTextPoint();\r
+    ptx -= os_string_width(buf);       //theWnd->GetTextWidth(buf,mywcslen(buf));\r
+    sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+\r
+    if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout;\r
+    // InputTimer timer(timeout);\r
+    while (true) {\r
+        // Get the next input\r
+        while (SDL_PollEvent(&event)) {\r
+            zword c;\r
+            if ((c = goodzkey(&event,1))) {\r
+                //printf("goodzk %4x\n",c);\r
+                switch (c) {\r
+                case ZC_BACKSPACE:\r
+                    // Delete the character to the left of the cursor\r
+                    if (pos > 0) {\r
+                        memmove(buf+pos-1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
+                        pos--;\r
+                        sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                    }\r
+                    break;\r
+                case VK_TAB:\r
+                    if (pos == (int)mywcslen(buf)) {\r
+                        zchar extension[10], *s;\r
+                        completion(buf,extension);\r
+\r
+                        // Add the completion to the input stream\r
+                        for (s = extension; *s != 0; s++)\r
+                            if (sf_IsValidChar(*s))\r
+                                buf[pos++] = (*s);\r
+                        buf[pos] = 0;\r
+                        sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                    }\r
+                    break;\r
+                case ZC_ARROW_LEFT:\r
+                    // Move the cursor left\r
+                    if (pos > 0)\r
+                        pos--;\r
+                    sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                    break;\r
+                case ZC_ARROW_RIGHT:\r
+                    // Move the cursor right\r
+                    if (pos < (int)mywcslen(buf))\r
+                        pos++;\r
+                    sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                    break;\r
+                case ZC_ARROW_UP:\r
+                case ZC_ARROW_DOWN:\r
+                    if (searchpos < 0)\r
+                        searchpos = mywcslen(buf);\r
+                    if ((c == ZC_ARROW_UP\r
+                         ? gen_history_back : gen_history_forward)(\r
+                                 buf, searchpos, max)) {\r
+                        pos = mywcslen(buf);\r
+                        sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                    }\r
+                    break;\r
+                default:\r
+                    if (is_terminator(c)) {\r
+                        // Terminate the current input\r
+                        m_exitPause = false;\r
+                        sf_DrawInput(buf,pos,ptx,pty,width,false);\r
+\r
+                        if ((c == ZC_SINGLE_CLICK) || (c == ZC_DOUBLE_CLICK)) {\r
+                            /* mouse_x = input.mousex+1;\r
                                mouse_y = input.mousey+1;*/\r
-                               }\r
-                       else if (c == ZC_RETURN)\r
-                               addtoHistory(buf);      //theWnd->AddToInputHistory(buf);\r
-\r
-//                     theWnd->SetLastInput(buf);\r
-                       prev_pos = pos;\r
-                       prev_history = history;\r
-                       return c;\r
-                       }\r
-               else if (sf_IsValidChar(c))\r
-                       {\r
-                               // Add a valid character to the input line\r
-                       if ((int)mywcslen(buf) < max)\r
-                               {\r
-                                       // Get the width of the new input line\r
-                               int len = os_string_width(buf);\r
-                               len += os_char_width(c);\r
-                               len += os_char_width('0');\r
-\r
-//printf("l%d w%d p%d\n",len,width,pos);\r
-                                       // Only allow if the width limit is not exceeded\r
-                               if (len <= width)\r
-                                       {\r
-                                       memmove(buf+pos+1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
-                                       *(buf+pos) = c;\r
-                                       pos++;\r
-                                       sf_DrawInput(buf,pos,ptx,pty,width,true);\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-    if ((timeout) && (sf_ticks() >= mytimeout))\r
-       {\r
-       prev_pos = pos;\r
-       prev_history = history;\r
-       return ZC_TIME_OUT;\r
-       }\r
-    sf_checksound();\r
-    sf_sleep(10);\r
+                        } else if (c == ZC_RETURN)\r
+                            gen_add_to_history(buf);\r
+                        //                     theWnd->SetLastInput(buf);\r
+                        return c;\r
+                    } else if (sf_IsValidChar(c) && mywcslen(buf) < max) {\r
+                        // Add a valid character to the input line\r
+                        // Get the width of the new input line\r
+                        int len = os_string_width(buf);\r
+                        len += os_char_width(c);\r
+                        len += os_char_width('0');\r
+\r
+                        //printf("l%d w%d p%d\n",len,width,pos);\r
+                        // Only allow if the width limit is not exceeded\r
+                        if (len <= width) {\r
+                            memmove(buf+pos+1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
+                            *(buf+pos) = c;\r
+                            pos++;\r
+                            sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        if ((timeout) && (sf_ticks() >= mytimeout)) {\r
+            return ZC_TIME_OUT;\r
+        }\r
+        sf_checksound();\r
+        sf_sleep(10);\r
     }\r
 \r
-  return 0;\r
-  }\r
+    return 0;\r
+}\r
 \r
 // Draw the current input line\r
 void sf_DrawInput(zchar * buffer, int pos, int ptx, int pty, int width, bool cursor)\r