初めに
ESP-IDF環境下でESP32へのシリアル入力にgetchar()を使っているのだけど、Visual Studio CodeのPlatformIOのMonitorだと、入力文字が都度送られるため、入力間違いなどがあった時の編集に困ってしまう。
Arduino IDEのシリアルモニタだと出力と入力が分かれており、入力部分はリターンキーを押すまで送信がされないので編集も自由にできる。
ただVisual Studio Codeで開発中、ボードとの入出力のためだけにこのプログラムを利用するのも手間だし。
また、ESP32にもコンソールコンポーネントというのがあり、シリアルモニタでライン編集などができると書かれているのだけど、サンプルプログラムを簡略化したもので試してみたがPlatformIOのMonitor上にエスケープシーケンスが表示され続け使用できるようなものではなかった。
そこで車輪の再発明のようなものだが、PlatformIOのMonitorで入力行の簡易編集も行える文字入力用のクラスを作ってみた。
使い型は、Readlineのインスタンスを用意し、while文の永久ループ内でread()を呼び出しtrueの場合get()で文字列を取得するといった感じ。
read()には引数でプロンプト文字列を指定可能にしており、またこの文字列が変更された場合入力途中でもプロンプト文字列が変更されるようにしている。
この機能は、現在開発しているプログラムに必要なものだったので追加してみた。
文字の編集はバックスペースのみ対応している。
ソースコード
ヘッダーファイル
#pragma once
#include <stddef.h>
namespace Take4
{
class Readline {
private:
static const size_t BufSize = 1024;
char buffer_[BufSize];
size_t pos_;
bool fixedLine_;
bool viewPrompt_;
const char* prevPrompt_;
void deleteChar();
public:
Readline();
~Readline();
// 1行データの取得
// prompt : コマンドプロンプト
// return : trueの場合1行のデータが確定した場合
bool read(const char* prompt);
// 行情報の取得
// 1行の情報が確定していない場合はnullptrになる
const char* get() const;
};
} // namespace Take4
CPPファイル
#include "Readline.h"
#include <iostream>
#include <string.h>
namespace Take4
{
Readline::Readline()
: pos_(0)
, fixedLine_(false)
, viewPrompt_(true)
, prevPrompt_(nullptr)
{
buffer_[0] = 0;
}
Readline::~Readline()
{
}
void Readline::deleteChar()
{
putchar(0x08);
putchar(' ');
putchar(0x08);
}
bool Readline::read(const char* prompt)
{
if (viewPrompt_) {
if (prompt) {
printf(prompt);
}
prevPrompt_ = prompt;
viewPrompt_ = false;
}
else if (prompt != prevPrompt_) {
// プロンプト文字列変更
size_t length = pos_ + strlen(prevPrompt_);
for (size_t i = 0; i < length; i++) {
deleteChar();
}
if (prompt) {
printf(prompt);
for (size_t i = 0; i < pos_; i++) {
putchar(buffer_[i]);
}
prevPrompt_ = prompt;
}
}
auto ch = getchar();
if (ch != EOF) {
if (fixedLine_) {
pos_ = 0;
fixedLine_ = false;
}
if (!iscntrl(ch)) {
putchar(ch);
buffer_[pos_++] = ch;
}
else if (pos_ > 0) {
if (ch == 0x08) {
pos_--;
deleteChar();
}
else {
buffer_[pos_++] = 0;
fixedLine_ = true;
putchar('\n');
viewPrompt_ = true;
return true;
}
}
}
return false;
}
const char* Readline::get() const
{
if (fixedLine_) {
return buffer_;
}
else {
return nullptr;
}
}
} // namespace Take4
コメント