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