Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

CSC112 Spring 2016 Examples

2370 views
1
// A small library for doing tty things with an istream
2
// Revision: $Revision: 1.1 $
3
// Change Log
4
// $Log: keystream.cpp,v $
5
// Revision 1.1 2016/03/17 15:46:30 pngwen
6
// Initial revision
7
//
8
9
#include <iostream>
10
#include <unistd.h>
11
#include <termios.h>
12
#include <sys/ioctl.h>
13
#include <stdlib.h>
14
#include <sys/poll.h>
15
#include <exception>
16
#include "keystream.h"
17
18
//our lone default instance
19
KeyStream kin;
20
21
22
//this is a low level character buffer, it uses read
23
class ttyinbuf : public std::streambuf
24
{
25
public:
26
ttyinbuf()
27
{
28
if(!isatty(STDIN_FILENO)) {
29
throw std::runtime_error{"STDIN is not a tty!"};
30
}
31
32
//we initially start with no available input
33
setg(buf, buf+9, buf+9);
34
}
35
36
37
protected:
38
virtual int underflow()
39
{
40
//attempt to read a single character
41
if(read(STDIN_FILENO, buf+9, 1) <= 0)
42
return -1;
43
44
//make the character available
45
setg(buf, buf+9, buf + 10);
46
47
return buf[9];
48
}
49
50
51
private:
52
char buf[10];
53
};
54
55
56
//constructor & Destructor
57
KeyStream::KeyStream()
58
{
59
//set up our buffer to be stdin
60
this->rdbuf(new ttyinbuf);
61
62
//get the original termios
63
if(tcgetattr(STDIN_FILENO, &_originalTermios)==-1) {
64
throw std::runtime_error{"Termios tcgeattr failed."};
65
}
66
67
//the default state is cooked!
68
cookedMode();
69
}
70
71
72
KeyStream::~KeyStream()
73
{
74
//restore the original tty state
75
tcsetattr(STDIN_FILENO, TCSAFLUSH, &_originalTermios);
76
}
77
78
79
//macro modes
80
void
81
KeyStream::cookedMode()
82
{
83
_echo=true;
84
_special=true;
85
_signal=true;
86
_canonical=true;
87
setTermiosFlags();
88
}
89
90
91
void
92
KeyStream::rawMode()
93
{
94
_echo=false;
95
_special=false;
96
_signal=false;
97
_canonical=false;
98
setTermiosFlags();
99
}
100
101
102
void
103
KeyStream::cbreakMode()
104
{
105
_echo=false;
106
_canonical=false;
107
_signal=true;
108
_special=true;
109
setTermiosFlags();
110
}
111
112
113
//specific status flags
114
bool
115
KeyStream::echo()
116
{
117
return _echo;
118
}
119
120
121
void
122
KeyStream::echo(bool flag)
123
{
124
_echo = flag;
125
setTermiosFlags();
126
}
127
128
129
bool
130
KeyStream::canonical()
131
{
132
return _canonical;
133
}
134
135
136
void
137
KeyStream::canonical(bool flag)
138
{
139
_canonical = flag;
140
setTermiosFlags();
141
}
142
143
144
bool
145
KeyStream::signal()
146
{
147
return _signal;
148
}
149
150
151
void
152
KeyStream::signal(bool flag)
153
{
154
_signal = flag;
155
setTermiosFlags();
156
}
157
158
159
bool
160
KeyStream::special()
161
{
162
return _special;
163
}
164
165
166
void
167
KeyStream::special(bool flag)
168
{
169
_special = flag;
170
setTermiosFlags();
171
}
172
173
//extensions to the normal IO stuff
174
keycode
175
KeyStream::getKey()
176
{
177
keycode result=0x00;
178
char c;
179
180
//we may be getting more than one character!
181
c=get(); //get a character
182
result = c;
183
184
//if this is not an escape sequence, go ahead and return it
185
if(c != 0x1b || !hasInput()) {
186
return result;
187
}
188
189
//ok, so we have an escape sequence going!, get the second character
190
c = get(); //get the next character in the sequence
191
result = result << 8 | c; //add it to the result code
192
193
//If this is not one of our known sequences, then we
194
//stop here. (We also stop if there is nothing else to do)
195
if(!hasInput() || (c != 0x5b && c != 0x4f)) {
196
return result;
197
}
198
199
//still here? Let there be a third!
200
c = get();
201
result = result << 8 | c;
202
203
//now, there is only one known situation in which we have a fourth.
204
//look for it!
205
if(!hasInput() | !(c & 0x30)) {
206
return result;
207
}
208
209
//ok, 4, but that's it. NO MORE
210
c = get();
211
result = result << 8 | c;
212
213
return result;
214
}
215
216
217
bool
218
KeyStream::hasInput()
219
{
220
struct pollfd fds;
221
int pres;
222
223
fds.fd=STDIN_FILENO;
224
fds.events=POLLIN;
225
pres = poll(&fds, 1, 70);
226
227
return pres==1;
228
}
229
230
231
void
232
KeyStream::setTermiosFlags()
233
{
234
//start with the default mode for this tty
235
struct termios t=_originalTermios;
236
237
//tweakify!
238
239
//echo handling
240
if(_echo) {
241
//set the ECHO flag
242
t.c_lflag |= ECHO;
243
} else {
244
//clear the ECHO flag
245
t.c_lflag &= ~ECHO;
246
}
247
248
//canonical handling
249
if(_canonical) {
250
t.c_lflag |= ICANON;
251
//t.c_iflag |= ICRNL;
252
} else {
253
t.c_lflag &= ~ICANON;
254
//t.c_iflag &= ~ICRNL;
255
}
256
257
//singal handling
258
if(!_signal) {
259
t.c_lflag &= ~(ISIG | IEXTEN);
260
}
261
262
263
//special processing handling
264
if(!_special) {
265
t.c_iflag &= ~(BRKINT | INPCK | IGNBRK | ISTRIP | IGNCR | IXON | INLCR | PARMRK);
266
//t.c_oflag &= ~OPOST;
267
}
268
269
t.c_cc[VMIN] = 1;
270
t.c_cc[VTIME] = 0;
271
272
//set the ttye info
273
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) == -1)
274
throw std::runtime_error{"Termios tcsetattr failed."};
275
}
276
277