Преглед на файлове

console.posx/posy working.

We now seem to know where the cursor is located!
We can use this in harry_idle_event to make sure
our message won't go past the width of the screen.
Steve Thielemann преди 4 години
родител
ревизия
5761a6c3db
променени са 4 файла, в които са добавени 165 реда и са изтрити 10 реда
  1. 13 3
      CMakeLists.txt
  2. 2 0
      render.cpp
  3. 26 7
      terminal.cpp
  4. 124 0
      test-terminal.cpp

+ 13 - 3
CMakeLists.txt

@@ -87,6 +87,8 @@ target_link_libraries(test-render zf_log)
 if(DISABLE_BUFFER_DEBUG)
 target_compile_definitions(test-render PUBLIC NO_BUFFER_DEBUG=1)
 endif()
+add_test(NAME test-render
+  COMMAND test-render)
 
 add_executable(test-mangle test-mangle.cpp wordplay.cpp render.cpp terminal.cpp charman.cpp lastseen.cpp logs_utils.cpp)
 add_dependencies(test-mangle gtest)
@@ -96,11 +98,19 @@ if(DISABLE_BUFFER_DEBUG)
 target_compile_definitions(test-mangle PUBLIC NO_BUFFER_DEBUG=1)
 endif()
 
-add_test(NAME test-render
-  COMMAND test-render)
-
 add_test(NAME test-mangle COMMAND test-mangle)
 
+add_executable(test-terminal test-terminal.cpp terminal.cpp utils.cpp)
+add_dependencies(test-terminal gtest)
+target_link_libraries(test-terminal gtest_main)
+target_link_libraries(test-terminal zf_log)
+if(DISABLE_BUFFER_DEBUG)
+target_compile_definitions(test-terminal PUBLIC NO_BUFFER_DEBUG=1)
+endif()
+
+add_test(NAME test-terminal COMMAND test-terminal)
+
+
 # include(FetchContent)
 # 
 # FetchContent_Populate(

+ 2 - 0
render.cpp

@@ -333,6 +333,7 @@ void process_trigger(int fd, std::string str, size_t &pos) {
 
       for (x = 0; x < i; x++) {
         write(fd, "\b \b", 3);
+        console_receive(&console, "\b \b");
       }
     };
     break;
@@ -457,6 +458,7 @@ const char *process_trigger(int fd, const char *cp) {
 
       for (x = 0; x < i; x++) {
         write(fd, "\b \b", 3);
+        console_receive(&console, "\b \b");
       }
     };
     break;

+ 26 - 7
terminal.cpp

@@ -6,14 +6,14 @@ Everything else, I'm not sure I really care about.   (NNY!)
 
  */
 
+#include "terminal.h"
+#include "utils.h"
+#include "zf_log.h"
 #include <ctype.h>
 #include <stdio.h> // snprintf
 #include <stdlib.h>
 #include <string.h>
-
-#include "terminal.h"
-#include "utils.h"
-#include "zf_log.h"
+#include <vector>
 
 void console_init(struct console_details *cdp) {
   cdp->posx = 0;
@@ -85,6 +85,8 @@ const char *color_restore(struct console_details *cdp) {
   return buffer;
 }
 
+std::vector<std::pair<int, int>> cursor_position_history;
+
 ANSI_TYPE console_ansi(struct console_details *cdp, const char *ansi) {
   const char *cp = ansi;
   const char *last = ansi + strlen(ansi) - 1;
@@ -220,8 +222,8 @@ ANSI_TYPE console_ansi(struct console_details *cdp, const char *ansi) {
         }
 
         // Our positions start at zero, not one.
-        cdp->posx = number - 1;
-        cdp->posy = number2 - 1;
+        cdp->posy = number - 1;
+        cdp->posx = number2 - 1;
         return CURSOR;
 
       case 'J':
@@ -273,6 +275,22 @@ ANSI_TYPE console_ansi(struct console_details *cdp, const char *ansi) {
         }
         return COLOR;
 
+      case 's':
+        // save position
+        cursor_position_history.push_back(std::make_pair(cdp->posx, cdp->posy));
+        return CURSOR;
+      case 'u':
+        // restore position
+        if (cursor_position_history.empty()) {
+          ZF_LOGE("Restore cursor position from empty history.");
+        } else {
+          std::pair<int, int> pos = cursor_position_history.back();
+          cdp->posx = pos.first;
+          cdp->posy = pos.second;
+          cursor_position_history.pop_back();
+        }
+        return CURSOR;
+
       case 't':
       case 'r':
       case 'h':
@@ -387,7 +405,8 @@ void console_string(struct console_details *cdp, const char *chars) {
 
 void console_receive(struct console_details *cdp, std::string chars) {
 
-  ZF_LOGI("console_char %lu chars", chars.size());
+  // This gets noisy when called from render effect ^D
+  // ZF_LOGI("console_char %lu chars", chars.size());
 
   /*  // the c way
   for (int x = 0; x < chars.size(); x++) {

+ 124 - 0
test-terminal.cpp

@@ -0,0 +1,124 @@
+// Not sure where to begin with gtest?
+//
+// What can I test with gtest?
+//
+// googletest/googletest/docs/primer.md
+
+#include "terminal.h"
+#include "gtest/gtest.h"
+#include <string>
+
+/*
+
+struct console_details {
+  int posx, posy;
+  int savedx, savedy;
+  char ansi[20]; // current ANSI command being processed.
+  int in_ansi;
+  int fgcolor; // 0-7 // not 0-15
+  int bgcolor; // 0-7
+  int status;  // 0, 1 or 5 (Blink)
+};
+
+enum ANSI_TYPE { START, CURSOR, COLOR, CLEAR, OTHER };
+
+struct termchar {
+  int in_ansi;
+  ANSI_TYPE ansi; // undefined if in_ansi is false
+};
+
+void console_init(struct console_details *cdp);
+void ansi_color(struct console_details *cdp, int color);
+const char *color_restore(struct console_details *cdp);
+ANSI_TYPE console_ansi(struct console_details *cdp, const char *ansi);
+
+termchar console_char(struct console_details *cdp, char ch);
+void console_receive(struct console_details *cdp, std::string chars);
+
+*/
+
+console_details console;
+const std::string reset("\x1b[0m\x1b[2J\x1b[1;1H");
+
+#define GTEST_COUT std::cerr << "[          ] [ INFO ]"
+
+namespace {
+
+TEST(ConsoleInit, init) {
+  console_init(&console);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 0);
+  ASSERT_EQ(console.in_ansi, 0);
+  ASSERT_EQ(console.ansi[0], 0);
+  ASSERT_EQ(console.status, 0);
+  ASSERT_EQ(console.bgcolor, 0);
+  ASSERT_EQ(console.fgcolor, 7);
+
+  std::string reset("\x1b[0m\x1b[2J\x1b[1;1H");
+  console_receive(&console, reset);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 0);
+  ASSERT_EQ(console.in_ansi, 0);
+  ASSERT_EQ(console.ansi[0], 0);
+  ASSERT_EQ(console.status, 0);
+  ASSERT_EQ(console.bgcolor, 0);
+  ASSERT_EQ(console.fgcolor, 7);
+}
+
+TEST(ConsoleBasic, simpleString) {
+  console_init(&console);
+  std::string send("Hello");
+
+  console_receive(&console, send);
+  ASSERT_NE(console.posx, 0);
+  ASSERT_EQ(console.posx, (int)send.size());
+  ASSERT_EQ(console.posy, 0);
+
+  console_receive(&console, reset);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 0);
+  send = "Hello\r\n";
+  console_receive(&console, send);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 1);
+}
+
+TEST(ConsolePosition, simplePositions) {
+  console_init(&console);
+  std::string send("\x1b[10;5H"); // Y;X
+
+  console_receive(&console, send);
+  ASSERT_EQ(console.posx, 4);
+  ASSERT_EQ(console.posy, 9);
+  std::string save_pos("\x1b[s");
+  std::string restore_pos("\x1b[u");
+  console_receive(&console, save_pos);
+  ASSERT_EQ(console.posx, 4);
+  ASSERT_EQ(console.posy, 9);
+  send = "\x1b[5;10H";
+  console_receive(&console, send);
+  ASSERT_EQ(console.posx, 9);
+  ASSERT_EQ(console.posy, 4);
+  console_receive(&console, restore_pos);
+  ASSERT_EQ(console.posx, 4);
+  ASSERT_EQ(console.posy, 9);
+}
+
+TEST(ConsoleCRNL, NLCRTests) {
+  console_init(&console);
+  std::string send("Hello\r");
+  console_receive(&console, send);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 0);
+  send = "Hello\n";
+  console_receive(&console, send);
+  ASSERT_EQ(console.posy, 1);
+  ASSERT_EQ(console.posx, 5);
+  console_receive(&console, reset);
+  send = "Hello\r\n";
+  console_receive(&console, send);
+  ASSERT_EQ(console.posx, 0);
+  ASSERT_EQ(console.posy, 1);
+}
+
+} // namespace