blob: 9e18c8ccaef7d63699ee893c8b1fe4dfd858bd68 [file] [log] [blame]
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cctype>
#include <cstdint>
#include "pw_containers/vector.h"
class AnsiDecoder {
public:
AnsiDecoder()
: state_(State::kNormal), cur_val_(0), params_(), buffered_chars_() {}
void ProcessChar(char c) {
switch (state_) {
case State::kNormal:
if (c == kEscapeChar) {
buffered_chars_.push_back(c);
state_ = State::kEscape;
} else {
EmitChar(c);
}
break;
case State::kEscape:
buffered_chars_.push_back(c);
if (c == kCsiChar) {
state_ = State::kCsi;
} else {
Error();
}
break;
case State::kCsi:
buffered_chars_.push_back(c);
if (std::isdigit(c)) {
cur_val_ *= 10;
cur_val_ += static_cast<int>(c - '0');
} else if (c == ';') {
PushParam();
} else {
PushParam();
HandleCsiCommand(c);
state_ = State::kNormal;
}
break;
}
}
protected:
virtual void SetFgColor(uint8_t r, uint8_t g, uint8_t b) {}
virtual void SetBgColor(uint8_t r, uint8_t g, uint8_t b) {}
virtual void EmitChar(char c) {}
private:
static constexpr char kEscapeChar = '\e';
static constexpr char kCsiChar = '[';
enum State {
kNormal,
kEscape,
kCsi,
};
State state_;
int cur_val_;
pw::Vector<int, 10> params_;
pw::Vector<char, 10> buffered_chars_;
void PushParam() {
params_.push_back(cur_val_);
cur_val_ = 0;
}
void HandleCsiCommand(char c) {
switch (c) {
case 'm':
for (auto param : params_) {
HandleSetColor(param);
}
break;
}
params_.clear();
}
struct Color {
uint8_t r;
uint8_t g;
uint8_t b;
};
static constexpr Color kNormalColors[8] = {
{.r = 0, .g = 0, .b = 0}, // Black
{.r = 170, .g = 0, .b = 0}, // Red
{.r = 0, .g = 170, .b = 0}, // Green
{.r = 170, .g = 85, .b = 0}, // Yellow
{.r = 0, .g = 0, .b = 170}, // Blue
{.r = 170, .g = 0, .b = 170}, // Magenta
{.r = 0, .g = 170, .b = 170}, // Cyan
{.r = 170, .g = 170, .b = 170}, // White
};
static constexpr Color kBrightColors[8] = {
{.r = 85, .g = 85, .b = 85}, // Black
{.r = 255, .g = 85, .b = 85}, // Red
{.r = 85, .g = 255, .b = 85}, // Green
{.r = 255, .g = 255, .b = 85}, // Yellow
{.r = 85, .g = 85, .b = 255}, // Blue
{.r = 255, .g = 85, .b = 255}, // Magenta
{.r = 85, .g = 255, .b = 255}, // Cyan
{.r = 255, .g = 255, .b = 255}, // White
};
void HandleSetColor(int val) {
if (val < 0) {
return;
}
if (val == 0) {
auto fg_color = kNormalColors[7];
auto bg_color = kNormalColors[0];
SetBgColor(bg_color.r, bg_color.g, bg_color.b);
SetFgColor(fg_color.r, fg_color.g, fg_color.b);
}
auto color_index = val % 10;
auto color_loc = val / 10;
if (color_index > 7) {
return;
}
switch (color_loc) {
case 3: {
auto color = kNormalColors[color_index];
SetFgColor(color.r, color.g, color.b);
break;
}
case 4: {
auto color = kNormalColors[color_index];
SetBgColor(color.r, color.g, color.b);
break;
}
case 9: {
auto color = kBrightColors[color_index];
SetFgColor(color.r, color.g, color.b);
break;
}
case 10: {
auto color = kBrightColors[color_index];
SetBgColor(color.r, color.g, color.b);
break;
}
default:
break;
}
}
void Error() {
for (auto c : buffered_chars_) {
EmitChar(c);
}
buffered_chars_.clear();
}
};