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