fix(userland): Fix 'clear' command, backspace keycodes (0x7F/0x08), serial keyboard input & make run target

- Add 'clear' and 'cls' builtin handlers in LazyBox shell
- Output ANSI \033[2J\033[H sequence on tty_clear() to clear serial/UART terminals
- Support both ASCII 0x7F (DEL) and 0x08 (BS) for Backspace in shell and nano editor
- Connect 16550 serial input to keyboard_get_key() on x86_64 for headless terminals
- Add ANSI escape sequence decoding for arrow keys (Up/Down/Left/Right/Home/End/Del)
- Update Makefile 'make run' and 'make qemu' with -display none for smooth terminal execution
- Add 'make run-gui' target for optional graphical SDL/GTK window
This commit is contained in:
subhobhai943 committed 2026-08-18 12:14:40 +05:30
1 parent f5dbc1453c
commit 6ad90542ac
7 files changed
+118 -23

No files matched your search

+12 -1
View File
@@ -53,7 +53,7 @@ ifeq ($(ARCH), x86_64)
TARGET = $(IMAGE) TARGET = $(IMAGE)
QEMU_CMD = qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \ QEMU_CMD = qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \ -netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 -serial stdio -device e1000,netdev=net0 -serial stdio -display none
else ifneq ($(filter $(ARCH), aarch64 arm64),) else ifneq ($(filter $(ARCH), aarch64 arm64),)
CROSS_COMPILE ?= aarch64-linux-gnu- CROSS_COMPILE ?= aarch64-linux-gnu-
@@ -375,6 +375,17 @@ $(IMAGE): $(BUILD_DIR)/boot/boot.bin $(BUILD_DIR)/boot/stage2.bin $(BUILD_DIR)/k
run qemu: $(TARGET) run qemu: $(TARGET)
$(QEMU_CMD) $(QEMU_CMD)
run-gui: $(TARGET)
ifeq ($(ARCH), x86_64)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 -serial stdio
else ifneq ($(filter $(ARCH), aarch64 arm64),)
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 128M -kernel $(BUILD_DIR)/kernel.elf -serial stdio
else
qemu-system-arm -M virt -cpu cortex-a15 -m 128M -kernel $(BUILD_DIR)/kernel.elf -serial stdio
endif
run-server: $(IMAGE) run-server: $(IMAGE)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \ qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \ -netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
+74 -8
View File
@@ -2,6 +2,14 @@
#include <drivers/tty.h> #include <drivers/tty.h>
#include <arch/arch.h> #include <arch/arch.h>
#if defined(__x86_64__)
#include <drivers/serial.h>
#elif defined(__aarch64__)
#include <arch/aarch64/uart.h>
#elif defined(__arm__) || defined(__armv8i__)
#include <arch/armv8i/uart.h>
#endif
#define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_STATUS_PORT 0x64 #define KEYBOARD_STATUS_PORT 0x64
#define KEYBOARD_BUFFER_SIZE 128 #define KEYBOARD_BUFFER_SIZE 128
@@ -123,14 +131,10 @@ void keyboard_init(void) {
#endif #endif
} }
#if defined(__aarch64__)
#include <arch/aarch64/uart.h>
#elif defined(__arm__) || defined(__armv8i__)
#include <arch/armv8i/uart.h>
#endif
bool keyboard_has_key(void) { bool keyboard_has_key(void) {
#if defined(__aarch64__) || defined(__arm__) || defined(__armv8i__) #if defined(__x86_64__)
if (serial_received()) return true;
#elif defined(__aarch64__) || defined(__arm__) || defined(__armv8i__)
if (uart_pl011_has_data()) return true; if (uart_pl011_has_data()) return true;
#endif #endif
return buffer_head != buffer_tail; return buffer_head != buffer_tail;
@@ -140,13 +144,75 @@ uint16_t keyboard_get_key(void) {
while (!keyboard_has_key()) { while (!keyboard_has_key()) {
arch_halt(); arch_halt();
} }
#if defined(__aarch64__) || defined(__arm__) || defined(__armv8i__)
#if defined(__x86_64__)
if (serial_received()) {
char c = serial_read_char();
if ((uint8_t)c == 0x1B) { // Escape sequence
for (volatile int t = 0; t < 2000; t++) {
if (serial_received()) {
char c2 = serial_read_char();
if (c2 == '[') {
for (volatile int t2 = 0; t2 < 2000; t2++) {
if (serial_received()) {
char c3 = serial_read_char();
if (c3 == 'A') return KEY_UP;
if (c3 == 'B') return KEY_DOWN;
if (c3 == 'C') return KEY_RIGHT;
if (c3 == 'D') return KEY_LEFT;
if (c3 == 'H') return KEY_HOME;
if (c3 == 'F') return KEY_END;
if (c3 == '3') {
if (serial_received() && serial_read_char() == '~') return KEY_DELETE;
}
break;
}
}
}
break;
}
}
return 0x1B;
}
if (c == '\r') c = '\n';
if ((uint8_t)c == 0x7F || c == 0x08) c = '\b';
return (uint16_t)(uint8_t)c;
}
#elif defined(__aarch64__) || defined(__arm__) || defined(__armv8i__)
if (uart_pl011_has_data()) { if (uart_pl011_has_data()) {
char c = uart_pl011_getc(); char c = uart_pl011_getc();
if ((uint8_t)c == 0x1B) { // Escape sequence
for (volatile int t = 0; t < 2000; t++) {
if (uart_pl011_has_data()) {
char c2 = uart_pl011_getc();
if (c2 == '[') {
for (volatile int t2 = 0; t2 < 2000; t2++) {
if (uart_pl011_has_data()) {
char c3 = uart_pl011_getc();
if (c3 == 'A') return KEY_UP;
if (c3 == 'B') return KEY_DOWN;
if (c3 == 'C') return KEY_RIGHT;
if (c3 == 'D') return KEY_LEFT;
if (c3 == 'H') return KEY_HOME;
if (c3 == 'F') return KEY_END;
if (c3 == '3') {
if (uart_pl011_has_data() && uart_pl011_getc() == '~') return KEY_DELETE;
}
break;
}
}
}
break;
}
}
return 0x1B;
}
if (c == '\r') c = '\n'; if (c == '\r') c = '\n';
if ((uint8_t)c == 0x7F || c == 0x08) c = '\b';
return (uint16_t)(uint8_t)c; return (uint16_t)(uint8_t)c;
} }
#endif #endif
uint16_t key = key_buffer[buffer_tail]; uint16_t key = key_buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % KEYBOARD_BUFFER_SIZE; buffer_tail = (buffer_tail + 1) % KEYBOARD_BUFFER_SIZE;
return key; return key;
+24 -7
View File
@@ -1,6 +1,7 @@
#include <drivers/tty.h> #include <drivers/tty.h>
#include <drivers/vga.h> #include <drivers/vga.h>
#include <lib/string.h> #include <lib/string.h>
#include <kernel/printk.h>
typedef struct { typedef struct {
uint16_t buffer[TTY_HEIGHT][TTY_WIDTH]; uint16_t buffer[TTY_HEIGHT][TTY_WIDTH];
@@ -42,7 +43,7 @@ static void tty_scroll(tty_t* t) {
} }
} }
for (int x = 0; x < TTY_WIDTH; x++) { for (int x = 0; x < TTY_WIDTH; x++) {
t->buffer[TTY_HEIGHT - 1][x] = vga_entry(' ', t->current_color); t->buffer[TTY_HEIGHT - 1][x] = vga_entry(' ', t->current_color ? t->current_color : 0x07);
} }
t->cursor_row = TTY_HEIGHT - 1; t->cursor_row = TTY_HEIGHT - 1;
} }
@@ -51,12 +52,15 @@ void tty_clear(void) {
tty_t* t = &ttys[current_tty]; tty_t* t = &ttys[current_tty];
for (int y = 0; y < TTY_HEIGHT; y++) { for (int y = 0; y < TTY_HEIGHT; y++) {
for (int x = 0; x < TTY_WIDTH; x++) { for (int x = 0; x < TTY_WIDTH; x++) {
t->buffer[y][x] = vga_entry(' ', t->current_color); t->buffer[y][x] = vga_entry(' ', t->current_color ? t->current_color : 0x07);
} }
} }
t->cursor_row = 0; t->cursor_row = 0;
t->cursor_col = 0; t->cursor_col = 0;
tty_render_current(); tty_render_current();
// Send standard ANSI terminal clear screen sequence to serial / UART
printk("\033[2J\033[H");
} }
void tty_set_cursor(int row, int col) { void tty_set_cursor(int row, int col) {
@@ -74,7 +78,7 @@ void tty_write(const char* data, size_t size) {
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
char c = data[i]; char c = data[i];
// Basic ANSI escape sequence parser (e.g. \033[31m) // ANSI escape sequence parser
if (c == '\033' && i + 1 < size && data[i + 1] == '[') { if (c == '\033' && i + 1 < size && data[i + 1] == '[') {
i += 2; i += 2;
int code = 0; int code = 0;
@@ -91,7 +95,7 @@ void tty_write(const char* data, size_t size) {
else if (code == 35) t->current_color = 0x05; // Magenta else if (code == 35) t->current_color = 0x05; // Magenta
else if (code == 36) t->current_color = 0x03; // Cyan else if (code == 36) t->current_color = 0x03; // Cyan
else if (code == 37) t->current_color = 0x07; // White else if (code == 37) t->current_color = 0x07; // White
else if (code == 90) t->current_color = 0x08; // Bright Black (Dark Grey) else if (code == 90) t->current_color = 0x08; // Bright Black
else if (code == 91) t->current_color = 0x0C; // Bright Red else if (code == 91) t->current_color = 0x0C; // Bright Red
else if (code == 92) t->current_color = 0x0A; // Bright Green else if (code == 92) t->current_color = 0x0A; // Bright Green
else if (code == 93) t->current_color = 0x0E; // Bright Yellow else if (code == 93) t->current_color = 0x0E; // Bright Yellow
@@ -102,8 +106,21 @@ void tty_write(const char* data, size_t size) {
} else if (i < size && data[i] == 'K') { } else if (i < size && data[i] == 'K') {
// Clear from cursor to end of line // Clear from cursor to end of line
for (int x = t->cursor_col; x < TTY_WIDTH; x++) { for (int x = t->cursor_col; x < TTY_WIDTH; x++) {
t->buffer[t->cursor_row][x] = vga_entry(' ', t->current_color); t->buffer[t->cursor_row][x] = vga_entry(' ', t->current_color ? t->current_color : 0x07);
} }
} else if (i < size && data[i] == 'J') {
// Clear entire screen buffer
for (int y = 0; y < TTY_HEIGHT; y++) {
for (int x = 0; x < TTY_WIDTH; x++) {
t->buffer[y][x] = vga_entry(' ', t->current_color ? t->current_color : 0x07);
}
}
t->cursor_row = 0;
t->cursor_col = 0;
} else if (i < size && data[i] == 'H') {
// Cursor Home
t->cursor_row = 0;
t->cursor_col = 0;
} else if (i < size && data[i] == 'D') { } else if (i < size && data[i] == 'D') {
// Move cursor left // Move cursor left
if (t->cursor_col > 0) t->cursor_col--; if (t->cursor_col > 0) t->cursor_col--;
@@ -132,10 +149,10 @@ void tty_write(const char* data, size_t size) {
} else if (c == '\b') { } else if (c == '\b') {
if (t->cursor_col > 0) { if (t->cursor_col > 0) {
t->cursor_col--; t->cursor_col--;
t->buffer[t->cursor_row][t->cursor_col] = vga_entry(' ', t->current_color); t->buffer[t->cursor_row][t->cursor_col] = vga_entry(' ', t->current_color ? t->current_color : 0x07);
} }
} else { } else {
t->buffer[t->cursor_row][t->cursor_col] = vga_entry(c, t->current_color); t->buffer[t->cursor_row][t->cursor_col] = vga_entry(c, t->current_color ? t->current_color : 0x07);
if (++t->cursor_col >= TTY_WIDTH) { if (++t->cursor_col >= TTY_WIDTH) {
t->cursor_col = 0; t->cursor_col = 0;
if (++t->cursor_row >= TTY_HEIGHT) { if (++t->cursor_row >= TTY_HEIGHT) {
+2 -2
View File
@@ -2,8 +2,8 @@
#ifndef _AUTOCONF_H #ifndef _AUTOCONF_H
#define _AUTOCONF_H #define _AUTOCONF_H
#define CONFIG_ARCH "armv8i" #define CONFIG_ARCH "x86_64"
#define CONFIG_ARCH_ARMV8I 1 #define CONFIG_ARCH_X86_64 1
#define CONFIG_BPF_VM 1 #define CONFIG_BPF_VM 1
#define CONFIG_DRV_AHCI 1 #define CONFIG_DRV_AHCI 1
#define CONFIG_DRV_ATA 1 #define CONFIG_DRV_ATA 1
-3
View File
@@ -195,9 +195,6 @@ static void subos_modular_core_boot(void) {
printk(ANSI_BRIGHT_GREEN "ACTIVE\n" ANSI_RESET); printk(ANSI_BRIGHT_GREEN "ACTIVE\n" ANSI_RESET);
// Launch Shell // Launch Shell
#if defined(__x86_64__)
tty_clear();
#endif
shell_run(); shell_run();
while (1) { while (1) {
+2 -1
View File
@@ -287,7 +287,8 @@ int nano_main(int argc, char** argv) {
} }
break; break;
case '\b': // Backspace case '\b': // Backspace (0x08)
case 0x7F: // DEL
if (nano_cursor_col > 0) { if (nano_cursor_col > 0) {
memmove(&nano_lines[nano_cursor_row][nano_cursor_col - 1], memmove(&nano_lines[nano_cursor_row][nano_cursor_col - 1],
&nano_lines[nano_cursor_row][nano_cursor_col], &nano_lines[nano_cursor_row][nano_cursor_col],
+4 -1
View File
@@ -287,6 +287,8 @@ static void shell_process(const char* cmd) {
int idx = (history_head - history_count + i + HISTORY_SIZE) % HISTORY_SIZE; int idx = (history_head - history_count + i + HISTORY_SIZE) % HISTORY_SIZE;
printk(" %2d: %s\n", i + 1, history[idx]); printk(" %2d: %s\n", i + 1, history[idx]);
} }
} else if (strcmp(argv[0], "clear") == 0 || strcmp(argv[0], "cls") == 0) {
tty_clear();
} else if (strcmp(argv[0], "reboot") == 0) { } else if (strcmp(argv[0], "reboot") == 0) {
printk(ANSI_YELLOW "Rebooting...\n" ANSI_RESET); printk(ANSI_YELLOW "Rebooting...\n" ANSI_RESET);
pit_sleep(200); pit_sleep(200);
@@ -392,7 +394,7 @@ void shell_run(void) {
printk("%s%s", prompt, cmd_buffer); printk("%s%s", prompt, cmd_buffer);
continue; continue;
} }
if (c == '\b') { if (c == '\b' || (unsigned char)c == 0x7F || c == 0x08) {
if (cursor_pos > 0) { if (cursor_pos > 0) {
for (size_t i = cursor_pos - 1; i < cmd_len - 1; i++) cmd_buffer[i] = cmd_buffer[i + 1]; for (size_t i = cursor_pos - 1; i < cmd_len - 1; i++) cmd_buffer[i] = cmd_buffer[i + 1];
cmd_len--; cmd_len--;
@@ -400,6 +402,7 @@ void shell_run(void) {
cmd_buffer[cmd_len] = '\0'; cmd_buffer[cmd_len] = '\0';
redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos);
} }
continue;
} else if (c == '\t') { } else if (c == '\t') {
if (cmd_len > 0) { if (cmd_len > 0) {
int matches = 0; int matches = 0;