devices/terminal.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
#include <stdio.h>
#include <string.h>
#include "terminal.h"
#ifndef _WIN32
#include <termios.h>
#include <unistd.h>
static struct termios terminal_orig_termios;
static int terminal_raw_mode_active = 0;
#endif
/* terminal device */
#define TERMINAL_INPUT 0x0
#define TERMINAL_OUTPUT 0x1
#define TERMINAL_ON_KEYPRESS 0x2
#define TERMINAL_ON_ARGUMENT 0x4
#define TERMINAL_POLL_INTERVAL_MS 100
#define TERMINAL_ERROR_NOT_A_TTY 0x11
//// Platform-specific non-blocking key input
#ifdef _WIN32
#include <conio.h>
#include <windows.h>
static int getch_timeout(int timeout_ms)
{
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
if (WaitForSingleObject(h, timeout_ms) == WAIT_OBJECT_0 && _kbhit())
return _getch();
return 0;
}
#else
static int getch_timeout(int timeout_ms)
{
(void)timeout_ms; // bounded by VMIN=0/VTIME in terminal_setup()
int c = getchar();
return (c == EOF) ? 0 : c;
}
#endif
//// Argument feeding state
static int arg_index = 1;
static int arg_char_pos = 0;
static int exec_args_pos = 0;
//// Generic save/restore
void terminal_save(xyw_byte *data, xyw_byte *state)
{
(void)data;
memcpy(&state[0], &arg_index, sizeof(arg_index));
memcpy(&state[4], &arg_char_pos, sizeof(arg_char_pos));
memcpy(&state[8], &exec_args_pos, sizeof(exec_args_pos));
}
void terminal_restore(xyw_byte *data, const xyw_byte *state)
{
(void)data;
memcpy(&arg_index, &state[0], sizeof(arg_index));
memcpy(&arg_char_pos, &state[4], sizeof(arg_char_pos));
memcpy(&exec_args_pos, &state[8], sizeof(exec_args_pos));
}
//// Poll: argument feeding and keypress handling
int terminal_poll(xyw_byte *data)
{
// Feed command line arguments
xyw_word on_arg_handler = XYW_PEEKW(&data[TERMINAL_ON_ARGUMENT]);
if (!xyw_argv_processed && on_arg_handler)
{
if (xyw_exec_args_active)
{
char c = xyw_exec_args[exec_args_pos];
if (c == ' ')
{
data[TERMINAL_INPUT] = XYW_ARGUMENT_SEPARATOR;
exec_args_pos++;
}
else if (c != '\0')
{
data[TERMINAL_INPUT] = (xyw_byte)c;
exec_args_pos++;
}
else
{
data[TERMINAL_INPUT] = XYW_END_OF_ARGUMENTS;
xyw_argv_processed = 1;
xyw_exec_args_active = 0;
}
}
else if (arg_index < xyw_argc)
{
char c = xyw_argv[arg_index][arg_char_pos];
if (c != '\0')
{
data[TERMINAL_INPUT] = (xyw_byte)c;
arg_char_pos++;
}
else if (arg_index < xyw_argc - 1)
{
data[TERMINAL_INPUT] = XYW_ARGUMENT_SEPARATOR;
arg_index++;
arg_char_pos = 0;
}
else
{
data[TERMINAL_INPUT] = XYW_END_OF_ARGUMENTS;
xyw_argv_processed = 1;
}
}
else
{
data[TERMINAL_INPUT] = XYW_END_OF_ARGUMENTS;
xyw_argv_processed = 1;
}
xyw_vm_set_running(1);
xyw_vm_push_frame(XYW_DEVICE_AREA_START);
*pc = on_arg_handler;
return 1;
}
// Keypress handling
xyw_word on_keypress_handler = XYW_PEEKW(&data[TERMINAL_ON_KEYPRESS]);
if (on_keypress_handler)
{
int c = getch_timeout(TERMINAL_POLL_INTERVAL_MS);
if (c > 0)
{
if (c == 3 || c == 4)
{
xyw_vm_set_waiting(0);
return 0;
}
data[TERMINAL_INPUT] = (xyw_byte)c;
xyw_vm_set_running(1);
xyw_vm_push_frame(XYW_DEVICE_AREA_START);
*pc = on_keypress_handler;
return 1;
}
return -1; // active handler, no event yet (keep polling)
}
return 0;
}
//// Device init/teardown/IO
void terminal_setup(xyw_byte *data, xyw_byte *error)
{
(void)data;
(void)error;
// Reset arg feeding state for fresh image
arg_index = 1;
arg_char_pos = 0;
exec_args_pos = 0;
#ifndef _WIN32
struct termios raw;
if (!isatty(STDIN_FILENO))
{
*error = TERMINAL_ERROR_NOT_A_TTY;
return;
}
if (tcgetattr(STDIN_FILENO, &terminal_orig_termios) != 0)
{
XYW_DBG("terminal: tcgetattr failed, raw mode not enabled\n");
*error = TERMINAL_ERROR_NOT_A_TTY;
return;
}
raw = terminal_orig_termios;
raw.c_lflag &= ~(ICANON | ECHO | ISIG);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) != 0)
{
XYW_DBG("terminal: tcsetattr failed, raw mode not enabled\n");
*error = TERMINAL_ERROR_NOT_A_TTY;
return;
}
terminal_raw_mode_active = 1;
#endif
}
void terminal_teardown(xyw_byte *data)
{
(void)data;
#ifndef _WIN32
if (terminal_raw_mode_active)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_orig_termios);
terminal_raw_mode_active = 0;
}
#endif
}
void terminal_output(xyw_byte *data, xyw_byte addr, xyw_byte *error)
{
(void)error;
if (addr == TERMINAL_OUTPUT) {
fputc(data[addr], stdout);
fflush(stdout);
}
}
xyw_byte terminal_input(xyw_byte *data, xyw_byte addr, xyw_byte *error)
{
(void)error;
return data[addr];
}
|