172cd2e07f1cea190925021472465325e72589f47Rob Landley/* interestingtimes.c - cursor control
272cd2e07f1cea190925021472465325e72589f47Rob Landley *
372cd2e07f1cea190925021472465325e72589f47Rob Landley * Copyright 2015 Rob Landley <rob@landley.net>
472cd2e07f1cea190925021472465325e72589f47Rob Landley */
572cd2e07f1cea190925021472465325e72589f47Rob Landley
672cd2e07f1cea190925021472465325e72589f47Rob Landley#include "toys.h"
772cd2e07f1cea190925021472465325e72589f47Rob Landley
85b2644cafc8a619b617ba0fbb5473667dbd634baRob Landleyint xgettty(void)
95b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley{
105b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley  int i, j;
115b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley
125b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley  for (i = 0; i<3; i++) if (isatty(j = (i+1)%3)) return j;
135b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley
145b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley  return xopen("/dev/tty", O_RDWR);
155b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley}
165b2644cafc8a619b617ba0fbb5473667dbd634baRob Landley
1772cd2e07f1cea190925021472465325e72589f47Rob Landley// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
1872cd2e07f1cea190925021472465325e72589f47Rob Landley// set x=80 y=25 before calling to provide defaults. Returns 0 if couldn't
1972cd2e07f1cea190925021472465325e72589f47Rob Landley// determine size.
2072cd2e07f1cea190925021472465325e72589f47Rob Landley
2172cd2e07f1cea190925021472465325e72589f47Rob Landleyint terminal_size(unsigned *xx, unsigned *yy)
2272cd2e07f1cea190925021472465325e72589f47Rob Landley{
2372cd2e07f1cea190925021472465325e72589f47Rob Landley  struct winsize ws;
2472cd2e07f1cea190925021472465325e72589f47Rob Landley  unsigned i, x = 0, y = 0;
2572cd2e07f1cea190925021472465325e72589f47Rob Landley  char *s;
2672cd2e07f1cea190925021472465325e72589f47Rob Landley
2772cd2e07f1cea190925021472465325e72589f47Rob Landley  // stdin, stdout, stderr
2872cd2e07f1cea190925021472465325e72589f47Rob Landley  for (i=0; i<3; i++) {
2972cd2e07f1cea190925021472465325e72589f47Rob Landley    memset(&ws, 0, sizeof(ws));
3072cd2e07f1cea190925021472465325e72589f47Rob Landley    if (!ioctl(i, TIOCGWINSZ, &ws)) {
3172cd2e07f1cea190925021472465325e72589f47Rob Landley      if (ws.ws_col) x = ws.ws_col;
3272cd2e07f1cea190925021472465325e72589f47Rob Landley      if (ws.ws_row) y = ws.ws_row;
3372cd2e07f1cea190925021472465325e72589f47Rob Landley
3472cd2e07f1cea190925021472465325e72589f47Rob Landley      break;
3572cd2e07f1cea190925021472465325e72589f47Rob Landley    }
3672cd2e07f1cea190925021472465325e72589f47Rob Landley  }
3772cd2e07f1cea190925021472465325e72589f47Rob Landley  s = getenv("COLUMNS");
3872cd2e07f1cea190925021472465325e72589f47Rob Landley  if (s) sscanf(s, "%u", &x);
3972cd2e07f1cea190925021472465325e72589f47Rob Landley  s = getenv("LINES");
4072cd2e07f1cea190925021472465325e72589f47Rob Landley  if (s) sscanf(s, "%u", &y);
4172cd2e07f1cea190925021472465325e72589f47Rob Landley
4272cd2e07f1cea190925021472465325e72589f47Rob Landley  // Never return 0 for either value, leave it at default instead.
4372cd2e07f1cea190925021472465325e72589f47Rob Landley  if (xx && x) *xx = x;
4472cd2e07f1cea190925021472465325e72589f47Rob Landley  if (yy && y) *yy = y;
4572cd2e07f1cea190925021472465325e72589f47Rob Landley
4672cd2e07f1cea190925021472465325e72589f47Rob Landley  return x || y;
4772cd2e07f1cea190925021472465325e72589f47Rob Landley}
4872cd2e07f1cea190925021472465325e72589f47Rob Landley
4972cd2e07f1cea190925021472465325e72589f47Rob Landley// Reset terminal to known state, saving copy of old state if old != NULL.
5072cd2e07f1cea190925021472465325e72589f47Rob Landleyint set_terminal(int fd, int raw, struct termios *old)
5172cd2e07f1cea190925021472465325e72589f47Rob Landley{
5272cd2e07f1cea190925021472465325e72589f47Rob Landley  struct termios termio;
5372cd2e07f1cea190925021472465325e72589f47Rob Landley
5472cd2e07f1cea190925021472465325e72589f47Rob Landley  // Fetch local copy of old terminfo, and copy struct contents to *old if set
5572cd2e07f1cea190925021472465325e72589f47Rob Landley  if (!tcgetattr(fd, &termio) && old) *old = termio;
5672cd2e07f1cea190925021472465325e72589f47Rob Landley
5772cd2e07f1cea190925021472465325e72589f47Rob Landley  // the following are the bits set for an xterm. Linux text mode TTYs by
5872cd2e07f1cea190925021472465325e72589f47Rob Landley  // default add two additional bits that only matter for serial processing
5972cd2e07f1cea190925021472465325e72589f47Rob Landley  // (turn serial line break into an interrupt, and XON/XOFF flow control)
6072cd2e07f1cea190925021472465325e72589f47Rob Landley
6172cd2e07f1cea190925021472465325e72589f47Rob Landley  // Any key unblocks output, swap CR and NL on input
6272cd2e07f1cea190925021472465325e72589f47Rob Landley  termio.c_iflag = IXANY|ICRNL|INLCR;
6372cd2e07f1cea190925021472465325e72589f47Rob Landley  if (toys.which->flags & TOYFLAG_LOCALE) termio.c_iflag |= IUTF8;
6472cd2e07f1cea190925021472465325e72589f47Rob Landley
6572cd2e07f1cea190925021472465325e72589f47Rob Landley  // Output appends CR to NL, does magic undocumented postprocessing
6672cd2e07f1cea190925021472465325e72589f47Rob Landley  termio.c_oflag = ONLCR|OPOST;
6772cd2e07f1cea190925021472465325e72589f47Rob Landley
6872cd2e07f1cea190925021472465325e72589f47Rob Landley  // Leave serial port speed alone
6972cd2e07f1cea190925021472465325e72589f47Rob Landley  // termio.c_cflag = C_READ|CS8|EXTB;
7072cd2e07f1cea190925021472465325e72589f47Rob Landley
7172cd2e07f1cea190925021472465325e72589f47Rob Landley  // Generate signals, input entire line at once, echo output
7272cd2e07f1cea190925021472465325e72589f47Rob Landley  // erase, line kill, escape control characters with ^
7372cd2e07f1cea190925021472465325e72589f47Rob Landley  // erase line char at a time
7472cd2e07f1cea190925021472465325e72589f47Rob Landley  // "extended" behavior: ctrl-V quotes next char, ctrl-R reprints unread chars,
7572cd2e07f1cea190925021472465325e72589f47Rob Landley  // ctrl-W erases word
7672cd2e07f1cea190925021472465325e72589f47Rob Landley  termio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
7772cd2e07f1cea190925021472465325e72589f47Rob Landley
7872cd2e07f1cea190925021472465325e72589f47Rob Landley  if (raw) cfmakeraw(&termio);
7972cd2e07f1cea190925021472465325e72589f47Rob Landley
8072cd2e07f1cea190925021472465325e72589f47Rob Landley  return tcsetattr(fd, TCSANOW, &termio);
8172cd2e07f1cea190925021472465325e72589f47Rob Landley}
8272cd2e07f1cea190925021472465325e72589f47Rob Landley
8372cd2e07f1cea190925021472465325e72589f47Rob Landley// Scan stdin for a keypress, parsing known escape sequences
8472cd2e07f1cea190925021472465325e72589f47Rob Landley// seqs is array of char * strings, ends with NULL ptr
8572cd2e07f1cea190925021472465325e72589f47Rob Landley// Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into seq
8672cd2e07f1cea190925021472465325e72589f47Rob Landley// scratch space is necessary because last char of !seq could start new seq
8772cd2e07f1cea190925021472465325e72589f47Rob Landley// Zero out first byte of scratch before first call to scan_key
8872cd2e07f1cea190925021472465325e72589f47Rob Landley// block=0 allows fetching multiple characters before updating display
8972cd2e07f1cea190925021472465325e72589f47Rob Landleyint scan_key(char *scratch, char **seqs, int block)
9072cd2e07f1cea190925021472465325e72589f47Rob Landley{
9172cd2e07f1cea190925021472465325e72589f47Rob Landley  struct pollfd pfd;
9272cd2e07f1cea190925021472465325e72589f47Rob Landley  int maybe, i, j;
9372cd2e07f1cea190925021472465325e72589f47Rob Landley  char *test;
9472cd2e07f1cea190925021472465325e72589f47Rob Landley
9572cd2e07f1cea190925021472465325e72589f47Rob Landley  for (;;) {
9672cd2e07f1cea190925021472465325e72589f47Rob Landley    pfd.fd = 0;
9772cd2e07f1cea190925021472465325e72589f47Rob Landley    pfd.events = POLLIN;
9872cd2e07f1cea190925021472465325e72589f47Rob Landley    pfd.revents = 0;
9972cd2e07f1cea190925021472465325e72589f47Rob Landley
10072cd2e07f1cea190925021472465325e72589f47Rob Landley    // check sequences
10172cd2e07f1cea190925021472465325e72589f47Rob Landley    maybe = 0;
10272cd2e07f1cea190925021472465325e72589f47Rob Landley    if (*scratch) {
10372cd2e07f1cea190925021472465325e72589f47Rob Landley      for (i = maybe = 0; (test = seqs[i]); i++) {
10472cd2e07f1cea190925021472465325e72589f47Rob Landley        for (j = 0; j<*scratch; j++) if (scratch[j+1] != test[j]) break;
10572cd2e07f1cea190925021472465325e72589f47Rob Landley        if (j == *scratch) {
10672cd2e07f1cea190925021472465325e72589f47Rob Landley          maybe = 1;
10772cd2e07f1cea190925021472465325e72589f47Rob Landley          if (!test[j]) {
10872cd2e07f1cea190925021472465325e72589f47Rob Landley            // We recognized current sequence: consume and return
10972cd2e07f1cea190925021472465325e72589f47Rob Landley            *scratch = 0;
11072cd2e07f1cea190925021472465325e72589f47Rob Landley            return 256+i;
11172cd2e07f1cea190925021472465325e72589f47Rob Landley          }
11272cd2e07f1cea190925021472465325e72589f47Rob Landley        }
11372cd2e07f1cea190925021472465325e72589f47Rob Landley      }
11472cd2e07f1cea190925021472465325e72589f47Rob Landley      // If current data can't be a known sequence, return next raw char
11572cd2e07f1cea190925021472465325e72589f47Rob Landley      if (!maybe) break;
11672cd2e07f1cea190925021472465325e72589f47Rob Landley    }
11772cd2e07f1cea190925021472465325e72589f47Rob Landley
11872cd2e07f1cea190925021472465325e72589f47Rob Landley    // Need more data to decide
11972cd2e07f1cea190925021472465325e72589f47Rob Landley
12072cd2e07f1cea190925021472465325e72589f47Rob Landley    // 30 miliseconds is about the gap between characters at 300 baud
12172cd2e07f1cea190925021472465325e72589f47Rob Landley    if (maybe || !block) if (!xpoll(&pfd, 1, 30*maybe)) break;
12272cd2e07f1cea190925021472465325e72589f47Rob Landley
12372cd2e07f1cea190925021472465325e72589f47Rob Landley    if (1 != read(0, scratch+1+*scratch, 1)) return -1;
12472cd2e07f1cea190925021472465325e72589f47Rob Landley    ++*scratch;
12572cd2e07f1cea190925021472465325e72589f47Rob Landley  }
12672cd2e07f1cea190925021472465325e72589f47Rob Landley
12772cd2e07f1cea190925021472465325e72589f47Rob Landley  // Was not a sequence
12872cd2e07f1cea190925021472465325e72589f47Rob Landley  if (!*scratch) return -2;
12972cd2e07f1cea190925021472465325e72589f47Rob Landley  i = scratch[1];
13072cd2e07f1cea190925021472465325e72589f47Rob Landley  if (--*scratch) memmove(scratch+1, scratch+2, *scratch);
13172cd2e07f1cea190925021472465325e72589f47Rob Landley
13272cd2e07f1cea190925021472465325e72589f47Rob Landley  return i;
13372cd2e07f1cea190925021472465325e72589f47Rob Landley}
134