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, &lt);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}