CSC112 Spring 2016 Examples
#include <iostream>1#include <unistd.h>2#include <termios.h>3#include <sys/ioctl.h>4#include <stdlib.h>5#include <sys/poll.h>6#include <exception>7#include "keystream.h"89//our lone default instance10KeyStream kin;111213//this is a low level character buffer, it uses read14class ttyinbuf : public std::streambuf15{16public:17ttyinbuf()18{19if(!isatty(STDIN_FILENO)) {20throw std::runtime_error{"STDIN is not a tty!"};21}2223//we initially start with no available input24setg(buf, buf+9, buf+9);25}262728protected:29virtual int underflow()30{31//attempt to read a single character32if(read(STDIN_FILENO, buf+9, 1) <= 0)33return -1;3435//make the character available36setg(buf, buf+9, buf + 10);3738return buf[9];39}404142private:43char buf[10];44};454647//constructor & Destructor48KeyStream::KeyStream()49{50//set up our buffer to be stdin51this->rdbuf(new ttyinbuf);5253//get the original termios54if(tcgetattr(STDIN_FILENO, &_originalTermios)==-1) {55throw std::runtime_error{"Termios tcgeattr failed."};56}5758//the default state is cooked!59cookedMode();60}616263KeyStream::~KeyStream()64{65//restore the original tty state66tcsetattr(STDIN_FILENO, TCSAFLUSH, &_originalTermios);67}686970//macro modes71void72KeyStream::cookedMode()73{74_echo=true;75_special=true;76_signal=true;77_canonical=true;78setTermiosFlags();79}808182void83KeyStream::rawMode()84{85_echo=false;86_special=false;87_signal=false;88_canonical=false;89setTermiosFlags();90}919293void94KeyStream::cbreakMode()95{96_echo=false;97_canonical=false;98_signal=true;99_special=true;100setTermiosFlags();101}102103104//specific status flags105bool106KeyStream::echo()107{108return _echo;109}110111112void113KeyStream::echo(bool flag)114{115_echo = flag;116setTermiosFlags();117}118119120bool121KeyStream::canonical()122{123return _canonical;124}125126127void128KeyStream::canonical(bool flag)129{130_canonical = flag;131setTermiosFlags();132}133134135bool136KeyStream::signal()137{138return _signal;139}140141142void143KeyStream::signal(bool flag)144{145_signal = flag;146setTermiosFlags();147}148149150bool151KeyStream::special()152{153return _special;154}155156157void158KeyStream::special(bool flag)159{160_special = flag;161setTermiosFlags();162}163164//extensions to the normal IO stuff165keycode166KeyStream::getKey()167{168keycode result=0x00;169char c;170171//we may be getting more than one character!172c=get(); //get a character173result = c;174175//if this is not an escape sequence, go ahead and return it176if(c != 0x1b || !hasInput()) {177return result;178}179180//ok, so we have an escape sequence going!, get the second character181c = get(); //get the next character in the sequence182result = result << 8 | c; //add it to the result code183184//If this is not one of our known sequences, then we185//stop here. (We also stop if there is nothing else to do)186if(!hasInput() || (c != 0x5b && c != 0x4f)) {187return result;188}189190//still here? Let there be a third!191c = get();192result = result << 8 | c;193194//now, there is only one known situation in which we have a fourth.195//look for it!196if(!hasInput() | !(c & 0x30)) {197return result;198}199200//ok, 4, but that's it. NO MORE201c = get();202result = result << 8 | c;203204return result;205}206207208bool209KeyStream::hasInput()210{211struct pollfd fds;212int pres;213214fds.fd=STDIN_FILENO;215fds.events=POLLIN;216pres = poll(&fds, 1, 70);217218return pres==1;219}220221222void223KeyStream::setTermiosFlags()224{225//start with the default mode for this tty226struct termios t=_originalTermios;227228//tweakify!229230//echo handling231if(_echo) {232//set the ECHO flag233t.c_lflag |= ECHO;234} else {235//clear the ECHO flag236t.c_lflag &= ~ECHO;237}238239//canonical handling240if(_canonical) {241t.c_lflag |= ICANON;242//t.c_iflag |= ICRNL;243} else {244t.c_lflag &= ~ICANON;245//t.c_iflag &= ~ICRNL;246}247248//singal handling249if(!_signal) {250t.c_lflag &= ~(ISIG | IEXTEN);251}252253254//special processing handling255if(!_special) {256t.c_iflag &= ~(BRKINT | INPCK | IGNBRK | ISTRIP | IGNCR | IXON | INLCR | PARMRK);257//t.c_oflag &= ~OPOST;258}259260t.c_cc[VMIN] = 1;261t.c_cc[VTIME] = 0;262263//set the ttye info264if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) == -1)265throw std::runtime_error{"Termios tcsetattr failed."};266}267268269