replace ncurses with termios
This commit is contained in:
187
src/Aquarium.cpp
187
src/Aquarium.cpp
@@ -8,19 +8,86 @@
|
||||
#include "Waterline.h"
|
||||
#include "Whale.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int g_maxCells = 0;
|
||||
|
||||
// ANSI color codes
|
||||
namespace ANSI {
|
||||
const char *RESET = "\033[0m";
|
||||
const char *BOLD = "\033[1m";
|
||||
const char *CLEAR_SCREEN = "\033[2J";
|
||||
const char *CURSOR_HOME = "\033[H";
|
||||
const char *HIDE_CURSOR = "\033[?25l";
|
||||
const char *SHOW_CURSOR = "\033[?25h";
|
||||
|
||||
// Colors (foreground)
|
||||
const char *BLACK = "\033[30m";
|
||||
const char *RED = "\033[31m";
|
||||
const char *GREEN = "\033[32m";
|
||||
const char *YELLOW = "\033[33m";
|
||||
const char *BLUE = "\033[34m";
|
||||
const char *MAGENTA = "\033[35m";
|
||||
const char *CYAN = "\033[36m";
|
||||
const char *WHITE = "\033[37m";
|
||||
|
||||
// Move cursor to position
|
||||
std::string moveTo(int row, int col) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "\033[%d;%dH", row + 1, col + 1);
|
||||
return std::string(buffer);
|
||||
}
|
||||
} // namespace ANSI
|
||||
|
||||
// Global terminal state
|
||||
static struct termios original_termios;
|
||||
static bool termios_saved = false;
|
||||
|
||||
// Signal handler for cleanup
|
||||
void cleanup_terminal(int sig) {
|
||||
if (termios_saved) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
|
||||
}
|
||||
printf("%s%s", ANSI::SHOW_CURSOR, ANSI::RESET);
|
||||
fflush(stdout);
|
||||
if (sig != 0) {
|
||||
exit(sig);
|
||||
}
|
||||
}
|
||||
|
||||
Aquarium::Aquarium() {
|
||||
initscr();
|
||||
noecho();
|
||||
cbreak();
|
||||
nodelay(stdscr, TRUE);
|
||||
curs_set(0);
|
||||
initColors();
|
||||
timeout(100);
|
||||
getmaxyx(stdscr, height, width);
|
||||
// Save original terminal settings
|
||||
if (tcgetattr(STDIN_FILENO, &original_termios) == 0) {
|
||||
termios_saved = true;
|
||||
}
|
||||
|
||||
// Set up signal handlers for cleanup
|
||||
signal(SIGINT, cleanup_terminal);
|
||||
signal(SIGTERM, cleanup_terminal);
|
||||
signal(SIGQUIT, cleanup_terminal);
|
||||
|
||||
// Set terminal to raw mode
|
||||
struct termios raw = original_termios;
|
||||
raw.c_lflag &= ~(ECHO | ICANON | ISIG);
|
||||
raw.c_iflag &= ~(IXON | ICRNL);
|
||||
raw.c_oflag &= ~(OPOST);
|
||||
raw.c_cc[VMIN] = 0; // Non-blocking read
|
||||
raw.c_cc[VTIME] = 1; // 100ms timeout
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
|
||||
// Initialize display
|
||||
printf("%s%s%s", ANSI::CLEAR_SCREEN, ANSI::CURSOR_HOME, ANSI::HIDE_CURSOR);
|
||||
fflush(stdout);
|
||||
|
||||
// Get terminal size
|
||||
getTerminalSize();
|
||||
|
||||
currentFrame.assign(height, std::vector<Cell>(width));
|
||||
previousFrame.assign(height, std::vector<Cell>(width));
|
||||
@@ -31,6 +98,18 @@ Aquarium::Aquarium() {
|
||||
}
|
||||
}
|
||||
|
||||
void Aquarium::getTerminalSize() {
|
||||
struct winsize ws;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
||||
height = ws.ws_row;
|
||||
width = ws.ws_col;
|
||||
} else {
|
||||
// Fallback to reasonable defaults
|
||||
height = 24;
|
||||
width = 80;
|
||||
}
|
||||
}
|
||||
|
||||
void Aquarium::ensureEntitiesSorted() {
|
||||
if (entities_need_sorting) {
|
||||
std::sort(entities.begin(), entities.end(),
|
||||
@@ -109,11 +188,13 @@ void Aquarium::redraw() {
|
||||
}
|
||||
|
||||
void Aquarium::resize() {
|
||||
clear();
|
||||
getmaxyx(stdscr, height, width);
|
||||
printf("%s%s", ANSI::CLEAR_SCREEN, ANSI::CURSOR_HOME);
|
||||
fflush(stdout);
|
||||
|
||||
getTerminalSize();
|
||||
|
||||
if (g_maxCells && height * width > g_maxCells) {
|
||||
endwin();
|
||||
cleanup_terminal(0);
|
||||
std::cerr << "Error: Terminal too large. Maximum allowed area is "
|
||||
<< g_maxCells << " cells, but current size is "
|
||||
<< (height * width) << ".\n";
|
||||
@@ -191,18 +272,28 @@ void Aquarium::drawToFrame(int y, int x, const std::string &line,
|
||||
|
||||
void Aquarium::initColorLookup() {
|
||||
for (int i = 0; i < 256; ++i)
|
||||
colorLookup[i] = 8; // Default black
|
||||
colorLookup['r'] = 1;
|
||||
colorLookup['g'] = 2;
|
||||
colorLookup['y'] = 3;
|
||||
colorLookup['b'] = 4;
|
||||
colorLookup['m'] = 5;
|
||||
colorLookup['c'] = 6;
|
||||
colorLookup['w'] = 7;
|
||||
colorLookup['k'] = 8;
|
||||
colorLookup[i] = ANSI::BLACK; // Default black
|
||||
|
||||
colorLookup['r'] = ANSI::RED;
|
||||
colorLookup['g'] = ANSI::GREEN;
|
||||
colorLookup['y'] = ANSI::YELLOW;
|
||||
colorLookup['b'] = ANSI::BLUE;
|
||||
colorLookup['m'] = ANSI::MAGENTA;
|
||||
colorLookup['c'] = ANSI::CYAN;
|
||||
colorLookup['w'] = ANSI::WHITE;
|
||||
colorLookup['k'] = ANSI::BLACK;
|
||||
}
|
||||
|
||||
void Aquarium::initColors() {
|
||||
// This function is kept for compatibility but does nothing
|
||||
// since we're using ANSI colors directly
|
||||
}
|
||||
|
||||
void Aquarium::renderToScreen() {
|
||||
static std::string output;
|
||||
output.clear();
|
||||
output.reserve(height * width * 20); // Reserve space for efficiency
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
const Cell &newCell = currentFrame[y][x];
|
||||
@@ -210,30 +301,48 @@ void Aquarium::renderToScreen() {
|
||||
|
||||
if (newCell != oldCell) {
|
||||
oldCell = newCell;
|
||||
move(y, x);
|
||||
|
||||
int colorPair =
|
||||
colorLookup[static_cast<unsigned char>(newCell.colorChar)];
|
||||
attrset(COLOR_PAIR(colorPair) | (newCell.bold ? A_BOLD : A_NORMAL));
|
||||
addch(newCell.ch);
|
||||
// Move cursor to position
|
||||
output += ANSI::moveTo(y, x);
|
||||
|
||||
// Set color and attributes
|
||||
output += ANSI::RESET; // Reset first
|
||||
if (newCell.bold) {
|
||||
output += ANSI::BOLD;
|
||||
}
|
||||
output += colorLookup[static_cast<unsigned char>(newCell.colorChar)];
|
||||
|
||||
// Add the character
|
||||
output += newCell.ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Aquarium::initColors() {
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
init_pair(1, COLOR_RED, COLOR_BLACK);
|
||||
init_pair(2, COLOR_GREEN, COLOR_BLACK);
|
||||
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
|
||||
init_pair(4, COLOR_BLUE, COLOR_BLACK);
|
||||
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
|
||||
init_pair(6, COLOR_CYAN, COLOR_BLACK);
|
||||
init_pair(7, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(8, COLOR_BLACK, COLOR_BLACK);
|
||||
// Output everything at once for better performance
|
||||
if (!output.empty()) {
|
||||
printf("%s", output.c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
Aquarium::~Aquarium() { endwin(); }
|
||||
// Check for input (non-blocking)
|
||||
int Aquarium::checkInput() {
|
||||
char c;
|
||||
if (read(STDIN_FILENO, &c, 1) == 1) {
|
||||
return c;
|
||||
}
|
||||
return -1; // No input available
|
||||
}
|
||||
|
||||
// Check if terminal was resized (you'll need to call this periodically)
|
||||
bool Aquarium::checkResize() {
|
||||
struct winsize ws;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
||||
if (ws.ws_row != height || ws.ws_col != width) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Aquarium::~Aquarium() { cleanup_terminal(0); }
|
||||
|
||||
Reference in New Issue
Block a user