Files
gw-basic-2026/platform/hal_posix.c
T
Eremey Valetov 1f4c460f4f Phase 5: CI, terminal I/O, Sixel graphics, classic programs
Add GitHub Actions CI with automated build and test. Implement real
terminal I/O with raw mode (enable_raw/disable_raw, proper INKEY$
polling via VMIN=0/VTIME=0, INPUT$ function). Add Sixel graphics
engine with virtual framebuffer (SCREEN 1: 320x200, SCREEN 2:
640x200), Bresenham line drawing, midpoint circle, flood fill PAINT,
DRAW mini-language parser, and Sixel encoder with RLE. Replace all
graphics stubs with real implementations (PSET, LINE, CIRCLE, DRAW,
PAINT, COLOR, SCREEN, POINT). Fix AND/OR/XOR operator precedence
to be lower than relational operators. Add 13 classic test programs
(39 total). Bump version to 0.5.0.
2026-02-10 16:46:34 -05:00

171 lines
3.6 KiB
C

#include "hal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
static struct termios orig_termios;
static int raw_mode = 0;
static int termios_saved = 0;
static int cursor_row = 0;
static int cursor_col = 0;
static int pending_char = -1;
static void posix_putch(int ch)
{
putchar(ch);
if (ch == '\n') {
cursor_row++;
cursor_col = 0;
} else if (ch == '\r') {
cursor_col = 0;
} else {
cursor_col++;
}
}
static void posix_puts(const char *s)
{
while (*s)
posix_putch(*s++);
}
static int posix_getch(void)
{
fflush(stdout);
if (pending_char >= 0) {
int ch = pending_char;
pending_char = -1;
return ch;
}
if (raw_mode) {
/* Set VMIN=1 to block until a byte arrives */
struct termios t;
tcgetattr(STDIN_FILENO, &t);
t.c_cc[VMIN] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &t);
unsigned char ch;
read(STDIN_FILENO, &ch, 1);
t.c_cc[VMIN] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &t);
return ch;
}
return getchar();
}
static bool posix_kbhit(void)
{
if (pending_char >= 0)
return true;
if (!raw_mode)
return false;
unsigned char ch;
ssize_t n = read(STDIN_FILENO, &ch, 1);
if (n == 1) {
pending_char = ch;
return true;
}
return false;
}
static void posix_locate(int row, int col)
{
printf("\033[%d;%dH", row, col);
fflush(stdout);
cursor_row = row - 1;
cursor_col = col - 1;
}
static int posix_get_cursor_row(void) { return cursor_row; }
static int posix_get_cursor_col(void) { return cursor_col; }
static void posix_cls(void)
{
printf("\033[2J\033[H");
fflush(stdout);
cursor_row = 0;
cursor_col = 0;
}
static void posix_set_width(int cols)
{
(void)cols;
}
static void posix_enable_raw(void)
{
if (raw_mode || !isatty(STDIN_FILENO))
return;
if (!termios_saved) {
tcgetattr(STDIN_FILENO, &orig_termios);
termios_saved = 1;
}
struct termios raw = orig_termios;
cfmakeraw(&raw);
raw.c_oflag |= OPOST; /* keep output processing (newline translation) */
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
raw_mode = 1;
}
static void posix_disable_raw(void)
{
if (!raw_mode)
return;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
raw_mode = 0;
}
static void posix_write_raw(const char *data, int len)
{
fflush(stdout);
write(STDOUT_FILENO, data, len);
}
static void posix_init(void)
{
setbuf(stdout, NULL);
}
static void posix_shutdown(void)
{
if (raw_mode) {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
raw_mode = 0;
}
}
static hal_ops_t posix_hal = {
.putch = posix_putch,
.puts = posix_puts,
.getch = posix_getch,
.kbhit = posix_kbhit,
.locate = posix_locate,
.get_cursor_row = posix_get_cursor_row,
.get_cursor_col = posix_get_cursor_col,
.cls = posix_cls,
.set_width = posix_set_width,
.enable_raw = posix_enable_raw,
.disable_raw = posix_disable_raw,
.write_raw = posix_write_raw,
.screen_width = 80,
.screen_height = 25,
.is_tty = false,
.init = posix_init,
.shutdown = posix_shutdown,
};
hal_ops_t *hal_posix_create(void)
{
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
if (ws.ws_col > 0) posix_hal.screen_width = ws.ws_col;
if (ws.ws_row > 0) posix_hal.screen_height = ws.ws_row;
}
posix_hal.is_tty = isatty(STDIN_FILENO);
return &posix_hal;
}