IoT何をいまさら(118) ESP32C3版Xiaoで車輪の再発明、スクリーンエディタ?

Joseph Halfmoon

前回で画面上にメニューを表示したり、情報を画面下部に出力できるようになったので、入力に戻りました。今回はスクリーンエディタ「もどき」を行入力ルーチン使って作ります。最大20行、1画面に収まる範囲ですが、カーソルをもっていて編集(上書きと行の後ろの削除)できるもの。まだバグだらけだけれども入力はできそう?

※「IoT何をいまさら」投稿順Indexはこちら

スクリーンエディタ「もどき」

限られたメモリしかないマイコン上ですが、「セルフ開発環境もどき」のためにはやはりエディタが欲しいです。昔のEDLINみたいなラインエディタも考えたのですが、今更、行エディタではやる気がおきませぬ。まあ、行入力ルーチン内でカーソルを左右に動かして一応「編集」ができるようになったので、1画面内でカーソルを上下に動かしながら「スクリーンエディットもどき」をすることは可能であろう、と。

とりあえず以下のような「仕様」としてみました。

    • メイン・メニューから E(ditor で呼び出せる。
    • 1行80文字x20行(バッファサイズは1600文字まで)
    • カーソルを上(CTRL+P)下(CR)左(CTRL+B)右(CTRL+F)に移動して任意の位置の文字を上書きできる
    • カーソル位置から行末までの消去可能(CTRL+D)
    • 行末にカーソルがあるときだけBSキーで一文字消去も可能
    • CTRL+Aで行頭、CTRL+Eで行末
    • CTRL+CでEDITOR終了

行単位のカット&ペーストとSDカードへのセーブ、ロードも予定してますがまだ実装してません。とりあえず一画面に収まる範囲なので、こんな「なんちゃって」エディタでも使えないこともないかと。

今回追加の「スクリーンエディタもどき」のファイル

今回、新たに、editor.ino なるファイルを作成してプロジェクトに追加しました。こんな感じ。

#define EBUFMAX (80)
#define EBUFLIN (20)

char ebuf[EBUFMAX * EBUFLIN];
char* lin[EBUFLIN];
char* empty[EBUFLIN];

void initEditor() {
  for (int i = 0; i < EBUFMAX * EBUFLIN; i++ ) {
    ebuf[i] = '\0';
  }
  for (int i = 0; i < EBUFLIN; i++ ) {
    lin[i] = NULL;
    empty[i] = &ebuf[i*EBUFMAX];
  }
}

void displayScreen() {
  editorHome();
  for (int i = 0; i < EBUFLIN; i++) {
    if (lin[i] != NULL) {
      Serial.println(lin[i]);
    } else {
      break;
    }
  }  
}

bool searchVacant(char** ptr) {
  for (int i = 0; i < EBUFLIN; i++) {
    if (empty[i] != NULL) {
      *ptr = empty[i];
      empty[i] = NULL;
      return true;
    }
  }
  return false;
}

bool startEditor() {
  bool inFlag = true;
  int lineN;
  char opt;
  bool lineRet;
  char* ptr;
  clearScreen();
  char menuStr[] = "EDITOR> CTRL+C for quit";
  blockingMenu(menuStr, false);
  displayScreen();
  editorHome();
  lineN=0;
  while (inFlag) {
    if (lin[lineN] != NULL) {
      dispStat();
      Serial.print(lineN);
      Serial.print(" ");
      Serial.print(strlen(lin[lineN]));
      dispMain();
      lineRet = blockingReadLine(lin[lineN], EBUFMAX, &opt);
    } else {
      if (searchVacant(&ptr)) {
        dispStat();
        Serial.print("NEW");
        dispMain();
        lin[lineN] = ptr;
        lineRet = blockingReadLine(lin[lineN], EBUFMAX, &opt); 
      } else {
        lineRet = false;
        opt = '\0';
        dispStat();
        Serial.print("BUFFER FULL.");
        dispMain();
        inFlag = false;
      }
    }
    if (lineRet) {
      if (lineN < EBUFLIN) {
        lineN++;
        downCursor();
      }
      Serial.print('\r');
    } else {
      if (opt == 0x10) {
        if (lineN > 0) {
          lineN--;
          upCursor();
        }
        Serial.print('\r');
      } else {
        inFlag = false;
      }    
    }
  }       
  clearScreen();
  dispStat();
  Serial.print(lineN);
  dispMain();
  return true;
}
行入力ルーチンの変更

上記のファイル追加に合わせてアチコチ細々と変更(バグも多し)してますが、肝心の行入力にも変更入れているので該当部分のみ。

bool blockingReadLine(char *buf, const int maxBuf, char *opt) {
  char bufIndex = 0;
  char bufUsed = strlen(buf);
  char selchar = 0;
  const int tcMax = 5;
  const char term[tcMax] = {0x3, 0xA, 0xB, 0xD, 0x10};
  while (true) {
    selchar = blgetASCII(0);
    if (endOfLineChar(selchar, term, tcMax)) {
      break;
    }
    if (selchar == 0x08) { //BS key
      if ((bufIndex > 0) && (bufUsed == bufIndex)) {
        bufIndex--;
        bufUsed--;
        Serial.print(selchar);
        Serial.print(' ');
      } else {
        selchar = 0;
      }
    } else if (selchar == 0x1) { //CTRL+A (start of line)
      Serial.print('\r');
      selchar = 0;
      bufIndex= 0;
    } else if (selchar == 0x2) { //CTRL+B (left)
      if (bufIndex >0) {
        leftCursor();
        bufIndex--;
      }
      selchar = 0;
    } else if (selchar == 0x6) { //CTRL+F (right)
      if (bufIndex < bufUsed) {
        rightCursor();
        bufIndex++;
      }
      selchar = 0;
    } else if (selchar == 0x5) { //CTRL+E (end of line)
      while (bufIndex < bufUsed) {
        rightCursor();
        bufIndex++;
      }
      selchar = 0;
    } else if (selchar == 0x4) { //CTRL+D (delete right..EOL)
      char temp = bufIndex;
      saveCursor();
      while (bufIndex < bufUsed) {
        Serial.print(' ');
        bufIndex++;
      }
      restoreCursor();
      bufIndex = temp;
      bufUsed = temp;
      selchar = 0;
    } else if (bufIndex < maxBuf+1) {
      buf[bufIndex++] = selchar;
      if (bufIndex > bufUsed) {
        bufUsed = bufIndex;
      }
    }
    if (selchar != 0) {
      Serial.print(selchar);
    }
  }
  buf[bufUsed] = '\0';
  //CTRL+C, CTRL+K, CTRL+P
  if ((selchar == 0x3) || (selchar == 0xB) || (selchar == 0x10)) {
    *opt = selchar;
    return false;
  }
  return true;
}

行入力のクセに、カーソルの行マタギの移動を上位層に報告できるようになりました。

素晴らしいスクリーンエディタを作ることが目的ではないので、まあ、適当なところまで作ったら、今度は処理系の作成?車輪の再発明は続くっと。

IoT何をいまさら(117) ESP32C3版Xiaoで車輪の再発明、メニューの改良 へ戻る

IoT何をいまさら(119) ESP32C3用のソースをWSL2上でGoogle-test へ進む