getch.cpp

(plain text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include <string>
#include <cassert>
#include <curses.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;

bool getline (std::string &s);

int main (int argc, char *argv[]) {
  // set up Curses environment (for the 'getch' function)
  initscr();
  nodelay(stdscr, 1);
  noecho();
  refresh();

  string line;
  getline(line);
  cout << "You typed : " << line << endl;

  // close up Curses environment
  endwin();

  return 0;
}

bool getline (std::string &s) {
  string line;
  bool newline = false;

  static const char batons[] = { '-', '\\', '|', '/' };
  int iBaton = 0;

  struct timeval tv = {};

  while (cin && !newline) {
    cout << batons[iBaton % 4] << '\b' << flush;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(0, &fds);
    const int readable_descriptors = select(1, &fds, 0, 0, &tv);

    // got a character!
    if (readable_descriptors == 1) {
      char c = getch();

      // newline
      if (c == '\n') {
	s = line;
	newline = true;
	cout << " \b\r\n";
      }
	
      // backspace/delete
      else if (c == 8 || c == 127) {
	// erase the last character (and the baton) if one exists
	if (!line.empty()) {
	  line.erase(line.end() - 1);
	  cout << "\b  \b\b";
	}
      }

      // printable (i.e., not special) character
      else if (isprint(c)) {
	line += c;
	cout << char(c);
      }
    }

    // timeout
    else if (readable_descriptors == 0) {
      tv.tv_usec = 100 * 1000; // reset timeout to 100ms
      ++iBaton %= 4;
    }

    // error
    else {
      cin.clear(cin.failbit);
    }
  }

  return cin;
}