前回で画面上にメニューを表示したり、情報を画面下部に出力できるようになったので、入力に戻りました。今回はスクリーンエディタ「もどき」を行入力ルーチン使って作ります。最大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; }
行入力のクセに、カーソルの行マタギの移動を上位層に報告できるようになりました。
素晴らしいスクリーンエディタを作ることが目的ではないので、まあ、適当なところまで作ったら、今度は処理系の作成?車輪の再発明は続くっと。