CSC112 Spring 2016 Examples
// A small library for doing tty things with an istream1// Revision: $Revision: 1.1 $2// Change Log3// $Log: keystream.cpp,v $4// Revision 1.1 2016/03/17 15:46:30 pngwen5// Initial revision6//78#include <iostream>9#include <unistd.h>10#include <termios.h>11#include <sys/ioctl.h>12#include <stdlib.h>13#include <sys/poll.h>14#include <exception>15#include "keystream.h"1617//our lone default instance18KeyStream kin;192021//this is a low level character buffer, it uses read22class ttyinbuf : public std::streambuf23{24public:25ttyinbuf()26{27if(!isatty(STDIN_FILENO)) {28throw std::runtime_error{"STDIN is not a tty!"};29}3031//we initially start with no available input32setg(buf, buf+9, buf+9);33}343536protected:37virtual int underflow()38{39//attempt to read a single character40if(read(STDIN_FILENO, buf+9, 1) <= 0)41return -1;4243//make the character available44setg(buf, buf+9, buf + 10);4546return buf[9];47}484950private:51char buf[10];52};535455//constructor & Destructor56KeyStream::KeyStream()57{58//set up our buffer to be stdin59this->rdbuf(new ttyinbuf);6061//get the original termios62if(tcgetattr(STDIN_FILENO, &_originalTermios)==-1) {63throw std::runtime_error{"Termios tcgeattr failed."};64}6566//the default state is cooked!67cookedMode();68}697071KeyStream::~KeyStream()72{73//restore the original tty state74tcsetattr(STDIN_FILENO, TCSAFLUSH, &_originalTermios);75}767778//macro modes79void80KeyStream::cookedMode()81{82_echo=true;83_special=true;84_signal=true;85_canonical=true;86setTermiosFlags();87}888990void91KeyStream::rawMode()92{93_echo=false;94_special=false;95_signal=false;96_canonical=false;97setTermiosFlags();98}99100101void102KeyStream::cbreakMode()103{104_echo=false;105_canonical=false;106_signal=true;107_special=true;108setTermiosFlags();109}110111112//specific status flags113bool114KeyStream::echo()115{116return _echo;117}118119120void121KeyStream::echo(bool flag)122{123_echo = flag;124setTermiosFlags();125}126127128bool129KeyStream::canonical()130{131return _canonical;132}133134135void136KeyStream::canonical(bool flag)137{138_canonical = flag;139setTermiosFlags();140}141142143bool144KeyStream::signal()145{146return _signal;147}148149150void151KeyStream::signal(bool flag)152{153_signal = flag;154setTermiosFlags();155}156157158bool159KeyStream::special()160{161return _special;162}163164165void166KeyStream::special(bool flag)167{168_special = flag;169setTermiosFlags();170}171172//extensions to the normal IO stuff173keycode174KeyStream::getKey()175{176keycode result=0x00;177char c;178179//we may be getting more than one character!180c=get(); //get a character181result = c;182183//if this is not an escape sequence, go ahead and return it184if(c != 0x1b || !hasInput()) {185return result;186}187188//ok, so we have an escape sequence going!, get the second character189c = get(); //get the next character in the sequence190result = result << 8 | c; //add it to the result code191192//If this is not one of our known sequences, then we193//stop here. (We also stop if there is nothing else to do)194if(!hasInput() || (c != 0x5b && c != 0x4f)) {195return result;196}197198//still here? Let there be a third!199c = get();200result = result << 8 | c;201202//now, there is only one known situation in which we have a fourth.203//look for it!204if(!hasInput() | !(c & 0x30)) {205return result;206}207208//ok, 4, but that's it. NO MORE209c = get();210result = result << 8 | c;211212return result;213}214215216bool217KeyStream::hasInput()218{219struct pollfd fds;220int pres;221222fds.fd=STDIN_FILENO;223fds.events=POLLIN;224pres = poll(&fds, 1, 70);225226return pres==1;227}228229230void231KeyStream::setTermiosFlags()232{233//start with the default mode for this tty234struct termios t=_originalTermios;235236//tweakify!237238//echo handling239if(_echo) {240//set the ECHO flag241t.c_lflag |= ECHO;242} else {243//clear the ECHO flag244t.c_lflag &= ~ECHO;245}246247//canonical handling248if(_canonical) {249t.c_lflag |= ICANON;250//t.c_iflag |= ICRNL;251} else {252t.c_lflag &= ~ICANON;253//t.c_iflag &= ~ICRNL;254}255256//singal handling257if(!_signal) {258t.c_lflag &= ~(ISIG | IEXTEN);259}260261262//special processing handling263if(!_special) {264t.c_iflag &= ~(BRKINT | INPCK | IGNBRK | ISTRIP | IGNCR | IXON | INLCR | PARMRK);265//t.c_oflag &= ~OPOST;266}267268t.c_cc[VMIN] = 1;269t.c_cc[VTIME] = 0;270271//set the ttye info272if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) == -1)273throw std::runtime_error{"Termios tcsetattr failed."};274}275276277