Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

CSC112 Spring 2016 Examples

2370 views
head	1.1;
access;
symbols;
locks; strict;
comment	@// @;


1.1
date	2016.03.17.15.46.30;	author pngwen;	state Exp;
branches;
next	;


desc
@@


1.1
log
@Initial revision
@
text
@// A small library for doing tty things with an istream
// Revision: $Revision$
// Change Log
//   $Log$

#include <iostream>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <exception>
#include "keystream.h"

//our lone default instance
KeyStream kin;


//this is a low level character buffer, it uses read
class ttyinbuf : public std::streambuf
{
public:
  ttyinbuf()
  {
    if(!isatty(STDIN_FILENO)) {
      throw std::runtime_error{"STDIN is not a tty!"};
    }
    
    //we initially start with no available input
    setg(buf, buf+9, buf+9);
  }

  
protected:
  virtual int underflow()
  {
    //attempt to read a single character
    if(read(STDIN_FILENO, buf+9, 1) <=  0)
      return -1;

    //make the character available
    setg(buf, buf+9, buf + 10);

    return buf[9];
  }
  

private:
  char buf[10];
};


//constructor & Destructor
KeyStream::KeyStream()
{
  //set up our buffer to be stdin
  this->rdbuf(new ttyinbuf);
  
  //get the original termios
  if(tcgetattr(STDIN_FILENO, &_originalTermios)==-1) {
    throw std::runtime_error{"Termios tcgeattr failed."};
  }

  //the default state is cooked!
  cookedMode();
}


KeyStream::~KeyStream()
{
  //restore the original tty state
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &_originalTermios);
}


//macro modes
void
KeyStream::cookedMode()
{
  _echo=true;
  _special=true;
  _signal=true;
  _canonical=true;
  setTermiosFlags();
}


void
KeyStream::rawMode()
{
  _echo=false;
  _special=false;
  _signal=false;
  _canonical=false;
  setTermiosFlags();
}


void
KeyStream::cbreakMode()
{
  _echo=false;
  _canonical=false;
  _signal=true;
  _special=true;
  setTermiosFlags();
}


//specific status flags
bool
KeyStream::echo()
{
  return _echo;
}


void
KeyStream::echo(bool flag)
{
  _echo = flag;
  setTermiosFlags();
}


bool
KeyStream::canonical()
{
  return _canonical;
}


void
KeyStream::canonical(bool flag)
{
  _canonical = flag;
  setTermiosFlags();
}


bool
KeyStream::signal()
{
  return _signal;
}


void
KeyStream::signal(bool flag)
{
  _signal = flag;
  setTermiosFlags();
}


bool
KeyStream::special()
{
  return _special;
}


void
KeyStream::special(bool flag)
{
  _special = flag;
  setTermiosFlags();
}

//extensions to the normal IO stuff
keycode
KeyStream::getKey()
{
  keycode result=0x00;
  char c;

  //we may be getting more than one character!
  c=get();  //get a character
  result = c;

  //if this is not an escape sequence, go ahead and return it
  if(c != 0x1b || !hasInput()) {
    return result;
  }

  //ok, so we have an escape sequence going!, get the second character
  c = get();  //get the next character in the sequence
  result = result << 8 | c;  //add it to the result code

  //If this is not one of our known sequences, then we
  //stop here.  (We also stop if there is nothing else to do)
  if(!hasInput() || (c != 0x5b && c != 0x4f)) {
    return result;
  }

  //still here?  Let there be a third!
  c = get();
  result = result << 8 | c;

  //now, there is only one known situation in which we have a fourth.
  //look for it!
  if(!hasInput() | !(c & 0x30)) {
    return result;
  }

  //ok, 4, but that's it.  NO MORE
  c = get();
  result = result << 8 | c;
  
  return result;
}


bool
KeyStream::hasInput()
{
  struct pollfd fds;
  int pres;

  fds.fd=STDIN_FILENO;
  fds.events=POLLIN;
  pres = poll(&fds, 1, 70);

  return pres==1;
}


void
KeyStream::setTermiosFlags()
{
  //start with the default mode for this tty 
  struct termios t=_originalTermios;

  //tweakify!
  
  //echo handling
  if(_echo) {
    //set the ECHO flag
    t.c_lflag |= ECHO;
  } else {
    //clear the ECHO flag
    t.c_lflag &= ~ECHO;
  }

  //canonical handling
  if(_canonical) {
    t.c_lflag |= ICANON;
    //t.c_iflag |= ICRNL;
  } else {
    t.c_lflag &= ~ICANON;
    //t.c_iflag &= ~ICRNL;
  }

  //singal handling
  if(!_signal) {
    t.c_lflag &= ~(ISIG | IEXTEN);
  }


  //special processing handling
  if(!_special) {
    t.c_iflag &= ~(BRKINT | INPCK | IGNBRK | ISTRIP | IGNCR | IXON | INLCR | PARMRK);
    //t.c_oflag &= ~OPOST;
  }

  t.c_cc[VMIN] = 1;  
  t.c_cc[VTIME] = 0;
  
  //set the ttye info
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) == -1)
    throw std::runtime_error{"Termios tcsetattr failed."};
}
@