vt100/src/native_tty.c
now_ms out on_signal therac_native_main
1/* The console program on a real terminal: an xterm, or a VT100 on a serial port2 * (therac-vt100 < /dev/ttyS0 > /dev/ttyS0 after stty 9600 raw). The hand control in the3 * treatment room is two signals: SIGUSR1 = field light, SIGUSR2 = set button. */4#define _DEFAULT_SOURCE5#include <poll.h>6#include <signal.h>7#include <stdio.h>8#include <string.h>9#include <termios.h>10#include <time.h>11#include <unistd.h>1213#include "console.h"14#include "serial.h"1516static serial_line line; /* pacing, so an xterm draws as slowly as a 9600-baud VT100 */17static double now;18static volatile sig_atomic_t quit, field_light, set_button;1920static double now_ms(void) {21 struct timespec ts;22 clock_gettime(CLOCK_MONOTONIC, &ts);23 return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;24}2526static void out(void *ctx, const uint8_t *buf, size_t n) {27 (void)ctx;28 for (size_t i = 0; i < n; i++) serial_put(&line, buf[i], now);29}3031static void on_signal(int sig) {32 if (sig == SIGUSR1) field_light = 1;33 else if (sig == SIGUSR2) set_button = 1;34 else quit = 1;35}3637int therac_native_main(int baud) {38 struct termios saved, raw;39 if (tcgetattr(STDIN_FILENO, &saved) != 0) {40 fprintf(stderr, "therac-vt100: standard input is not a terminal\n");41 return 1;42 }43 fprintf(stderr,44 "therac-vt100: hand control in the treatment room: kill -USR1 %d (field light), "45 "kill -USR2 %d (set button). Ctrl-C quits.\n",46 (int)getpid(), (int)getpid());47 raw = saved;48 raw.c_iflag &= (tcflag_t) ~(IXON | IXOFF | ICRNL | INLCR | IGNCR | ISTRIP | BRKINT | PARMRK);49 raw.c_oflag &= (tcflag_t)~OPOST;50 raw.c_lflag &= (tcflag_t) ~(ECHO | ICANON | IEXTEN);51 raw.c_cflag |= CS8;52 raw.c_cc[VMIN] = 0;53 raw.c_cc[VTIME] = 0;54 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);5556 struct sigaction sa;57 memset(&sa, 0, sizeof sa);58 sa.sa_handler = on_signal;59 sigaction(SIGINT, &sa, NULL);60 sigaction(SIGTERM, &sa, NULL);61 sigaction(SIGUSR1, &sa, NULL);62 sigaction(SIGUSR2, &sa, NULL);6364 now = now_ms();65 serial_init(&line, baud);66 HsStablePtr machine = start_machine();67 static console con;68 console_init(&con, machine, out, NULL);6970 while (!quit) {71 struct pollfd p = {STDIN_FILENO, POLLIN, 0};72 poll(&p, 1, 5);73 now = now_ms();74 uint8_t in[64];75 ssize_t n = (p.revents & POLLIN) ? read(STDIN_FILENO, in, sizeof in) : 0;76 for (ssize_t i = 0; i < n; i++) {77 if (in[i] == 0x13) serial_set_paused(&line, true, now); /* NO SCROLL */78 else if (in[i] == 0x11) serial_set_paused(&line, false, now);79 else console_input(&con, in[i]);80 }81 if (field_light) {82 field_light = 0;83 wrap_external_call(machine, ExtCallFieldLight, BTCheekyPadding, CPCheekyPadding, 0);84 }85 if (set_button) {86 set_button = 0;87 wrap_external_call(machine, ExtCallSet, BTCheekyPadding, CPCheekyPadding, 0);88 }89 time_t t = time(NULL);90 struct tm lt;91 localtime_r(&t, <);92 console_tick(&con, now, (long long)t + lt.tm_gmtoff);93 uint8_t buf[512];94 size_t k = 0;95 int b;96 while (k < sizeof buf && (b = serial_get(&line, now)) >= 0) buf[k++] = (uint8_t)b;97 if (k && write(STDOUT_FILENO, buf, k) < 0) break;98 }99 const char bye[] = "\033[H\033[2J";100 if (write(STDOUT_FILENO, bye, sizeof bye - 1) < 0) { /* nothing to do */ }101 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved);102 return 0;103}