This commit is contained in:
boreddevnl
2026-03-02 21:27:44 +01:00
parent d38e8b97e2
commit 80fce3d0e9
15 changed files with 1047 additions and 99 deletions

View File

@@ -22,15 +22,15 @@
#include "net_defs.h"
#include "man_entries.h"
#define CMD_COLS 116
#define CMD_ROWS 41
#define DEFAULT_CMD_COLS 116
#define DEFAULT_CMD_ROWS 41
#define MAX_CMD_COLS 256
#define LINE_HEIGHT 10
#define CHAR_WIDTH 8
#define COLOR_RED 0xFFFF0000
#define TXT_BUFFER_SIZE 4096
#define TXT_VISIBLE_LINES (CMD_ROWS - 2)
// --- Structs ---
typedef struct {
@@ -82,16 +82,40 @@ typedef struct {
Window win_cmd;
// Shell State
static CharCell screen_buffer[CMD_ROWS][CMD_COLS];
static int terminal_cols = DEFAULT_CMD_COLS;
static int terminal_rows = DEFAULT_CMD_ROWS;
static CharCell *screen_buffer = NULL;
static int cursor_row = 0;
static int cursor_col = 0;
static uint32_t current_color = COLOR_DARK_TEXT;
static uint32_t current_color = 0xFFFFFFFF; // Default light text
static uint32_t current_bg_color = 0; // 0 = translucent/default
static CmdState *cmd_state = NULL; // Will be set in cmd_init
static int current_prompt_len = 0;
static size_t cmd_strlen(const char *s) {
size_t len = 0;
while (s[len]) len++;
return len;
}
static int cmd_strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
}
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
// ANSI State Machine
static int ansi_state = 0;
#define ANSI_MAX_PARAMS 8
static int ansi_params[ANSI_MAX_PARAMS];
static int ansi_param_count = 0;
static bool ansi_private_mode = false;
// Pager State
static CmdMode current_mode = MODE_SHELL;
static char pager_wrapped_lines[2000][CMD_COLS + 1];
static char pager_wrapped_lines[2000][MAX_CMD_COLS + 1];
static int pager_total_lines = 0;
static int pager_top_line = 0;
@@ -123,19 +147,52 @@ void cmd_reset_msg_count(void) {
msg_count = 0;
}
// --- Helpers ---
static size_t cmd_strlen(const char *str) {
size_t len = 0;
while (str[len]) len++;
return len;
static uint32_t ansi_get_256_color(int n) {
if (n < 16) {
static uint32_t base[16] = {
0xFF000000, 0xFFAA0000, 0xFF00AA00, 0xFFAA5500, 0xFF0000AA, 0xFFAA00AA, 0xFF00AAAA, 0xFFAAAAAA,
0xFF555555, 0xFFFF5555, 0xFF55FF55, 0xFFFFFF55, 0xFF5555FF, 0xFFFF55FF, 0xFF55FFFF, 0xFFFFFFFF
};
return base[n];
}
if (n >= 232) {
uint8_t v = (n - 232) * 10 + 8;
return 0xFF000000 | (v << 16) | (v << 8) | v;
}
n -= 16;
static uint8_t levels[] = {0, 95, 135, 175, 215, 255};
int r = levels[n / 36];
int g = levels[(n / 6) % 6];
int b = levels[n % 6];
return 0xFF000000 | (r << 16) | (g << 8) | b;
}
static int cmd_strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
static void ansi_handle_sgr() {
if (ansi_param_count == 0) {
current_color = 0xFFFFFFFF;
current_bg_color = 0;
return;
}
for (int i = 0; i < ansi_param_count; i++) {
int p = ansi_params[i];
if (p == 0) { current_color = 0xFFFFFFFF; current_bg_color = 0; }
else if (p >= 30 && p <= 37) current_color = ansi_get_256_color(p - 30);
else if (p >= 40 && p <= 47) current_bg_color = ansi_get_256_color(p - 40);
else if (p >= 90 && p <= 97) current_color = ansi_get_256_color(p - 90 + 8);
else if (p >= 100 && p <= 107) current_bg_color = ansi_get_256_color(p - 100 + 8);
else if (p == 38 || p == 48) {
bool is_fg = (p == 38);
if (i + 2 < ansi_param_count && ansi_params[i+1] == 5) {
uint32_t c = ansi_get_256_color(ansi_params[i+2]);
if (is_fg) current_color = c; else current_bg_color = c;
i += 2;
} else if (i + 4 < ansi_param_count && ansi_params[i+1] == 2) {
uint32_t c = 0xFF000000 | ((ansi_params[i+2] & 0xFF) << 16) | ((ansi_params[i+3] & 0xFF) << 8) | (ansi_params[i+4] & 0xFF);
if (is_fg) current_color = c; else current_bg_color = c;
i += 4;
}
}
}
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
static void cmd_strcpy(char *dest, const char *src) {
@@ -453,12 +510,12 @@ void cmd_set_current_color(uint32_t color) {
}
// --- History ---
#define HISTORY_MAX 16
static char cmd_history[HISTORY_MAX][CMD_COLS + 1];
#define HISTORY_MAX 64
static char cmd_history[HISTORY_MAX][MAX_CMD_COLS + 1];
static int history_head = 0;
static int history_len = 0;
static int history_pos = -1;
static char history_save_buf[CMD_COLS + 1];
static char history_save_buf[MAX_CMD_COLS + 1];
static void cmd_history_add(const char *cmd) {
if (!cmd || !*cmd) return;
@@ -505,18 +562,18 @@ void cmd_print_prompt(void) {
void cmd_clear_line_content(void) {
int prompt_len = current_prompt_len;
for (int i = prompt_len; i < CMD_COLS; i++) {
screen_buffer[cursor_row][i].c = ' ';
screen_buffer[cursor_row][i].color = current_color;
for (int i = prompt_len; i < terminal_cols; i++) {
screen_buffer[cursor_row * terminal_cols + i].c = ' ';
screen_buffer[cursor_row * terminal_cols + i].color = current_color;
}
cursor_col = prompt_len;
}
static void cmd_set_line_content(const char *str) {
cmd_clear_line_content();
while (*str && cursor_col < CMD_COLS) {
screen_buffer[cursor_row][cursor_col].c = *str;
screen_buffer[cursor_row][cursor_col].color = current_color;
while (*str && cursor_col < terminal_cols) {
screen_buffer[cursor_row * terminal_cols + cursor_col].c = *str;
screen_buffer[cursor_row * terminal_cols + cursor_col].color = current_color;
cursor_col++;
str++;
}
@@ -524,15 +581,15 @@ static void cmd_set_line_content(const char *str) {
static void cmd_scroll_up() {
for (int r = 1; r < CMD_ROWS; r++) {
for (int c = 0; c < CMD_COLS; c++) {
screen_buffer[r - 1][c] = screen_buffer[r][c];
for (int r = 1; r < terminal_rows; r++) {
for (int c = 0; c < terminal_cols; c++) {
screen_buffer[(r - 1) * terminal_cols + c] = screen_buffer[r * terminal_cols + c];
}
}
// Clear bottom row
for (int c = 0; c < CMD_COLS; c++) {
screen_buffer[CMD_ROWS - 1][c].c = ' ';
screen_buffer[CMD_ROWS - 1][c].color = shell_config.default_text_color;
for (int c = 0; c < terminal_cols; c++) {
screen_buffer[(terminal_rows - 1) * terminal_cols + c].c = ' ';
screen_buffer[(terminal_rows - 1) * terminal_cols + c].color = shell_config.default_text_color;
}
}
@@ -552,10 +609,80 @@ void cmd_putchar(char c) {
fat32_write(redirect_file, &c, 1);
return;
}
if (ansi_state == 0) {
if (c == '\x1b') {
ansi_state = 1;
return;
}
} else if (ansi_state == 1) {
if (c == '[') {
ansi_state = 2;
ansi_param_count = 0;
for(int i=0; i<ANSI_MAX_PARAMS; i++) ansi_params[i] = 0;
ansi_private_mode = false;
return;
} else {
ansi_state = 0; // Unsupported ESC sequence
}
} else if (ansi_state == 2) {
if (c == '?') {
ansi_private_mode = true;
return;
} else if (c >= '0' && c <= '9') {
if (ansi_param_count < ANSI_MAX_PARAMS) {
ansi_params[ansi_param_count] = ansi_params[ansi_param_count] * 10 + (c - '0');
}
return;
} else if (c == ';') {
ansi_param_count++;
return;
} else {
// End of sequence
if (ansi_param_count < ANSI_MAX_PARAMS) ansi_param_count++;
if (c == 'm') {
ansi_handle_sgr();
} else if (c == 'H' || c == 'f') {
int r = ansi_params[0] > 0 ? ansi_params[0] - 1 : 0;
int col = ansi_params[1] > 0 ? ansi_params[1] - 1 : 0;
if (r >= terminal_rows) r = terminal_rows - 1;
if (col >= terminal_cols) col = terminal_cols - 1;
cursor_row = r;
cursor_col = col;
} else if (c == 'A') {
int n = ansi_params[0] > 0 ? ansi_params[0] : 1;
cursor_row -= n; if (cursor_row < 0) cursor_row = 0;
} else if (c == 'B') {
int n = ansi_params[0] > 0 ? ansi_params[0] : 1;
cursor_row += n; if (cursor_row >= terminal_rows) cursor_row = terminal_rows - 1;
} else if (c == 'C') {
int n = ansi_params[0] > 0 ? ansi_params[0] : 1;
cursor_col += n; if (cursor_col >= terminal_cols) cursor_col = terminal_cols - 1;
} else if (c == 'D') {
int n = ansi_params[0] > 0 ? ansi_params[0] : 1;
cursor_col -= n; if (cursor_col < 0) cursor_col = 0;
} else if (c == 'J') {
if (ansi_params[0] == 2) {
cmd_screen_clear();
}
} else if (c == 'K') {
for (int i = cursor_col; i < terminal_cols; i++) {
screen_buffer[cursor_row * terminal_cols + i].c = ' ';
screen_buffer[cursor_row * terminal_cols + i].color = current_color;
}
}
ansi_state = 0;
return;
}
}
if (c == '\n') {
cursor_col = 0;
cursor_row++;
} else if (c == '\r') {
cursor_col = 0;
} else if (c == '\t') {
int spaces = 4 - (cursor_col % 4);
for (int i = 0; i < spaces; i++) cmd_putchar(' ');
@@ -563,28 +690,29 @@ void cmd_putchar(char c) {
} else if (c == '\b') {
if (cursor_col > 0) {
cursor_col--;
screen_buffer[cursor_row][cursor_col].c = ' ';
screen_buffer[cursor_row][cursor_col].color = shell_config.default_text_color;
screen_buffer[cursor_row * terminal_cols + cursor_col].c = ' ';
screen_buffer[cursor_row * terminal_cols + cursor_col].color = shell_config.default_text_color;
}
} else {
if (cursor_col >= CMD_COLS) {
if (cursor_col >= terminal_cols) {
cursor_col = 0;
cursor_row++;
}
if (cursor_row >= CMD_ROWS) {
if (cursor_row >= terminal_rows) {
cmd_scroll_up();
cursor_row = CMD_ROWS - 1;
cursor_row = terminal_rows - 1;
}
screen_buffer[cursor_row][cursor_col].c = c;
screen_buffer[cursor_row][cursor_col].color = current_color;
screen_buffer[cursor_row * terminal_cols + cursor_col].c = c;
screen_buffer[cursor_row * terminal_cols + cursor_col].color = current_color;
// Optionally set background color if we support it in CharCell
cursor_col++;
}
if (cursor_row >= CMD_ROWS) {
if (cursor_row >= terminal_rows) {
cmd_scroll_up();
cursor_row = CMD_ROWS - 1;
cursor_row = terminal_rows - 1;
}
// Trigger repaint so output from syscalls is immediately visible
@@ -644,10 +772,10 @@ int cmd_get_cursor_col(void) {
// Public for CLI apps to use - clear the terminal screen
void cmd_screen_clear() {
for(int r=0; r<CMD_ROWS; r++) {
for(int c=0; c<CMD_COLS; c++) {
screen_buffer[r][c].c = ' ';
screen_buffer[r][c].color = COLOR_DARK_TEXT;
for(int r=0; r<terminal_rows; r++) {
for(int c=0; c<terminal_cols; c++) {
screen_buffer[r * terminal_cols + c].c = ' ';
screen_buffer[r * terminal_cols + c].color = COLOR_DARK_TEXT;
}
}
cursor_row = 0;
@@ -681,7 +809,7 @@ void pager_wrap_content(const char **lines, int count) {
int remaining = len - processed;
int chunk_len = remaining;
if (chunk_len > CMD_COLS) chunk_len = CMD_COLS;
if (chunk_len > terminal_cols) chunk_len = terminal_cols;
// If cutting a word, backtrack to last space
if (chunk_len < remaining) { // Only check if actually wrapping
@@ -1649,12 +1777,13 @@ static void cmd_paint(Window *win) {
if (current_mode == MODE_PAGER) {
// Draw Pager Content (Wrapped)
for (int i = 0; i < CMD_ROWS && (pager_top_line + i) < pager_total_lines; i++) {
for (int i = 0; i < terminal_rows && (pager_top_line + i) < pager_total_lines; i++) {
draw_string(start_x, start_y + (i * LINE_HEIGHT), pager_wrapped_lines[pager_top_line + i], shell_config.default_text_color);
}
// Status Bar
draw_string(start_x, start_y + (CMD_ROWS * LINE_HEIGHT), "-- Press Q to quit --", shell_config.default_text_color);
// Status Bar
draw_string(start_x, start_y + (terminal_rows * LINE_HEIGHT), "-- Press Q to quit --", shell_config.default_text_color);
} else {
// Draw Cursor
if (win->focused) {
@@ -1663,11 +1792,11 @@ static void cmd_paint(Window *win) {
}
// Draw Shell Buffer
for (int r = 0; r < CMD_ROWS; r++) {
for (int c = 0; c < CMD_COLS; c++) {
char ch = screen_buffer[r][c].c;
for (int r = 0; r < terminal_rows; r++) {
for (int c = 0; c < terminal_cols; c++) {
char ch = screen_buffer[r * terminal_cols + c].c;
if (ch != 0 && ch != ' ') {
uint32_t color = screen_buffer[r][c].color;
uint32_t color = screen_buffer[r * terminal_cols + c].color;
// If cursor is on this character, and cursor color is bright, use background color for char
if (r == cursor_row && c == cursor_col && win->focused) {
color = shell_config.bg_color;
@@ -1677,6 +1806,90 @@ static void cmd_paint(Window *win) {
}
}
}
// Column/Row Indicator in top-right
char size_buf[32];
itoa(terminal_cols, size_buf);
int p = 0; while(size_buf[p]) p++; size_buf[p++] = 'x'; itoa(terminal_rows, size_buf + p);
draw_string(win->x + win->w - 80, win->y + 5, size_buf, 0xFFAAAAAA);
}
void cmd_handle_resize(Window *win, int w, int h) {
(void)win;
int new_cols = (w - 20) / CHAR_WIDTH;
int new_rows = (h - 50) / LINE_HEIGHT;
if (new_cols < 20) new_cols = 20;
if (new_rows < 5) new_rows = 5;
if (new_cols == terminal_cols && new_rows == terminal_rows) return;
CharCell *new_buffer = (CharCell*)kmalloc(new_rows * new_cols * sizeof(CharCell));
for (int i = 0; i < new_rows * new_cols; i++) {
new_buffer[i].c = ' ';
new_buffer[i].color = shell_config.default_text_color;
}
// Reflow Logic: Copy characters and track cursor
int new_r = 0;
int new_c = 0;
int target_cursor_r = 0;
int target_cursor_c = 0;
for (int r = 0; r < terminal_rows; r++) {
// Find logical end of this line for copying
int last_c = terminal_cols - 1;
while (last_c >= 0 && screen_buffer[r * terminal_cols + last_c].c == ' ') last_c--;
// If this is the cursor row, we MUST copy up to the cursor col at least
if (r == cursor_row) {
if (last_c < cursor_col) last_c = cursor_col;
}
for (int c = 0; c <= last_c; c++) {
if (r == cursor_row && c == cursor_col) {
target_cursor_r = new_r;
target_cursor_c = new_c;
}
if (new_c >= new_cols) {
new_c = 0;
new_r++;
}
if (new_r < new_rows) {
new_buffer[new_r * new_cols + new_c] = screen_buffer[r * terminal_cols + c];
new_c++;
}
}
// If it was the cursor row, we've found our target position
if (r == cursor_row) break;
// Force newline after each source line if it wasn't a wrap
if (last_c < terminal_cols - 1) {
new_c = 0;
new_r++;
}
}
kfree(screen_buffer);
screen_buffer = new_buffer;
terminal_rows = new_rows;
terminal_cols = new_cols;
cursor_row = target_cursor_r;
cursor_col = target_cursor_c;
if (cursor_row >= terminal_rows) cursor_row = terminal_rows - 1;
if (cursor_col >= terminal_cols) cursor_col = terminal_cols - 1;
}
void cmd_handle_click(Window *win, int x, int y) {
(void)win;
// Basic mouse support: convert pixel to char coords
int c = (x - 10) / CHAR_WIDTH;
int r = (y - 25) / LINE_HEIGHT;
if (r >= 0 && r < terminal_rows && c >= 0 && c < terminal_cols) {
// If telnet or other app wants mouse reporting, we'd handle it here
}
}
static void cmd_key(Window *target, char c) {
@@ -1687,24 +1900,24 @@ static void cmd_key(Window *target, char c) {
} else if (c == 17) { // UP
if (pager_top_line > 0) pager_top_line--;
} else if (c == 18) { // DOWN
if (pager_top_line < pager_total_lines - CMD_ROWS) pager_top_line++;
if (pager_top_line < pager_total_lines - terminal_rows) pager_top_line++;
}
return;
}
// Shell Mode
if (c == '\n') { // Enter
char cmd_buf[CMD_COLS + 1];
int len = 0;
int prompt_len = current_prompt_len;
for (int i = prompt_len; i < CMD_COLS; i++) {
char ch = screen_buffer[cursor_row][i].c;
if (ch == 0 || (ch == ' ' && i > prompt_len && screen_buffer[cursor_row][i+1].c == 0)) break;
cmd_buf[len++] = ch;
}
while (len > 0 && cmd_buf[len-1] == ' ') len--;
cmd_buf[len] = 0;
if (c == '\n') { // Enter
char cmd_buf[512]; // Use a fixed large enough buffer or dynamic
int len = 0;
int prompt_len = current_prompt_len;
for (int i = prompt_len; i < terminal_cols; i++) {
char ch = screen_buffer[cursor_row * terminal_cols + i].c;
if (ch == 0 || (ch == ' ' && i > prompt_len && (i + 1 < terminal_cols) && screen_buffer[cursor_row * terminal_cols + i + 1].c == 0)) break;
if (len < 511) cmd_buf[len++] = ch;
}
while (len > 0 && cmd_buf[len-1] == ' ') len--;
cmd_buf[len] = 0;
cmd_putchar('\n');
current_color = shell_config.default_text_color;
@@ -1723,9 +1936,9 @@ static void cmd_key(Window *target, char c) {
// Save current line
int len = 0;
int prompt_len = current_prompt_len;
for (int i = prompt_len; i < CMD_COLS; i++) {
char ch = screen_buffer[cursor_row][i].c;
if (ch == 0 || (ch == ' ' && i > prompt_len && screen_buffer[cursor_row][i+1].c == 0)) break;
for (int i = prompt_len; i < terminal_cols; i++) {
char ch = screen_buffer[cursor_row * terminal_cols + i].c;
if (ch == 0 || (ch == ' ' && i > prompt_len && (i + 1 < terminal_cols) && screen_buffer[cursor_row * terminal_cols + i + 1].c == 0)) break;
history_save_buf[len++] = ch;
}
while (len > 0 && history_save_buf[len-1] == ' ') len--;
@@ -1756,27 +1969,27 @@ static void cmd_key(Window *target, char c) {
cursor_col--;
}
} else if (c == 20) { // RIGHT
if (cursor_col < CMD_COLS - 1) {
if (cursor_col < terminal_cols - 1) {
cursor_col++;
}
} else if (c == '\b') { // Backspace
if (cursor_col > current_prompt_len) {
// Shift characters to the left
for (int i = cursor_col; i < CMD_COLS; i++) {
screen_buffer[cursor_row][i - 1] = screen_buffer[cursor_row][i];
for (int i = cursor_col; i < terminal_cols; i++) {
screen_buffer[cursor_row * terminal_cols + i - 1] = screen_buffer[cursor_row * terminal_cols + i];
}
screen_buffer[cursor_row][CMD_COLS - 1].c = ' ';
screen_buffer[cursor_row * terminal_cols + terminal_cols - 1].c = ' ';
cursor_col--;
wm_mark_dirty(win_cmd.x, win_cmd.y, win_cmd.w, win_cmd.h);
}
} else {
if (c >= 32 && c <= 126) {
// Shift characters to the right
for (int i = CMD_COLS - 1; i > cursor_col; i--) {
screen_buffer[cursor_row][i] = screen_buffer[cursor_row][i - 1];
for (int i = terminal_cols - 1; i > cursor_col; i--) {
screen_buffer[cursor_row * terminal_cols + i] = screen_buffer[cursor_row * terminal_cols + i - 1];
}
screen_buffer[cursor_row][cursor_col].c = c;
screen_buffer[cursor_row][cursor_col].color = current_color;
screen_buffer[cursor_row * terminal_cols + cursor_col].c = c;
screen_buffer[cursor_row * terminal_cols + cursor_col].color = current_color;
cursor_col++;
wm_mark_dirty(win_cmd.x, win_cmd.y, win_cmd.w, win_cmd.h);
}
@@ -2109,19 +2322,28 @@ void cmd_init(void) {
// Load config after files are created
cmd_load_config();
// Default sizes
terminal_cols = DEFAULT_CMD_COLS;
terminal_rows = DEFAULT_CMD_ROWS;
// Allocate screen buffer
screen_buffer = (CharCell*)kmalloc(terminal_cols * terminal_rows * sizeof(CharCell));
win_cmd.title = shell_config.title_text;
win_cmd.x = 50;
win_cmd.y = 50;
win_cmd.w = (CMD_COLS * CHAR_WIDTH) + 20;
win_cmd.h = (CMD_ROWS * LINE_HEIGHT) + 50;
win_cmd.w = (terminal_cols * CHAR_WIDTH) + 20;
win_cmd.h = (terminal_rows * LINE_HEIGHT) + 50;
win_cmd.visible = false;
win_cmd.focused = false;
win_cmd.z_index = 0;
win_cmd.paint = cmd_paint;
win_cmd.handle_key = cmd_key;
win_cmd.handle_click = NULL;
win_cmd.handle_click = cmd_handle_click;
win_cmd.handle_right_click = NULL;
win_cmd.handle_resize = cmd_handle_resize;
win_cmd.resizable = true;
// Initialize cmd state (per-window context)
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));