added "iv.dynstring"
[iv.d.git] / _obsolete_dont_use / rawtty.d
blobcac76bb833f5d31e8287a1177a2f0c409f02df05
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // linux tty utilities
18 module iv.rawtty /*is aliced*/;
20 import core.sys.posix.termios : termios;
21 public import std.typecons : Flag, Yes, No;
24 private __gshared termios origMode;
25 private __gshared bool inRawMode = false;
26 private __gshared bool redirected = true; // can be used without synchronization
28 private class XLock {}
29 private shared XLock xlock;
32 /// TTY mode
33 enum TTYMode {
34 BAD = -1, /// some error occured
35 NORMAL, /// normal ('cooked') mode
36 RAW /// 'raw' mode
40 enum TermType {
41 other,
42 rxvt,
43 xterm
46 __gshared TermType termType = TermType.other;
49 /// is TTY stdin or stdout redirected?
50 @property bool ttyIsRedirected () @trusted nothrow @nogc {
51 return redirected;
55 /// get current TTY mode
56 TTYMode ttyGetMode () @trusted nothrow @nogc {
57 return (inRawMode ? TTYMode.RAW : TTYMode.NORMAL);
61 /// returns previous mode or BAD
62 TTYMode ttySetNormal () @trusted @nogc {
63 synchronized(xlock) {
64 if (inRawMode) {
65 import core.sys.posix.termios : tcflush, tcsetattr;
66 import core.sys.posix.termios : TCIOFLUSH, TCSAFLUSH;
67 import core.sys.posix.unistd : STDIN_FILENO;
68 //tcflush(STDIN_FILENO, TCIOFLUSH);
69 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &origMode) < 0) return TTYMode.BAD;
70 inRawMode = false;
71 return TTYMode.RAW;
73 return TTYMode.NORMAL;
78 /// returns previous mode or BAD
79 TTYMode ttySetRaw (Flag!"waitkey" waitKey=Yes.waitkey) @trusted @nogc {
80 if (redirected) return TTYMode.BAD;
81 synchronized(xlock) {
82 if (!inRawMode) {
83 import core.sys.posix.termios : tcflush, tcsetattr;
84 import core.sys.posix.termios : TCIOFLUSH, TCSAFLUSH;
85 import core.sys.posix.termios : BRKINT, CS8, ECHO, ICANON, IEXTEN, INPCK, ISIG, ISTRIP, IXON, ONLCR, OPOST, VMIN, VTIME;
86 import core.sys.posix.unistd : STDIN_FILENO;
87 termios raw = origMode; // modify the original mode
88 //tcflush(STDIN_FILENO, TCIOFLUSH);
89 // input modes: no break, no CR to NL, no parity check, no strip char, no start/stop output control
90 //raw.c_iflag &= ~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
91 // input modes: no break, no parity check, no strip char, no start/stop output control
92 raw.c_iflag &= ~(BRKINT|INPCK|ISTRIP|IXON);
93 // output modes: disable post processing
94 raw.c_oflag &= ~OPOST;
95 raw.c_oflag |= ONLCR;
96 raw.c_oflag = OPOST|ONLCR;
97 // control modes: set 8 bit chars
98 raw.c_cflag |= CS8;
99 // local modes: echoing off, canonical off, no extended functions, no signal chars (^Z,^C)
100 raw.c_lflag &= ~(ECHO|ICANON|IEXTEN|ISIG);
101 // control chars: set return condition: min number of bytes and timer; we want read to return every single byte, without timeout
102 raw.c_cc[VMIN] = (waitKey ? 1 : 0); // wait/poll mode
103 raw.c_cc[VTIME] = 0; // no timer
104 // put terminal in raw mode after flushing
105 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) return TTYMode.BAD;
106 inRawMode = true;
107 return TTYMode.NORMAL;
109 return TTYMode.RAW;
114 /// set wait/poll mode
115 bool ttySetWaitKey (bool doWait) @trusted @nogc {
116 if (redirected) return false;
117 synchronized(xlock) {
118 if (inRawMode) {
119 import core.sys.posix.termios : tcflush, tcgetattr, tcsetattr;
120 import core.sys.posix.termios : TCIOFLUSH, TCSAFLUSH;
121 import core.sys.posix.termios : VMIN;
122 import core.sys.posix.unistd : STDIN_FILENO;
123 termios raw;
124 //tcflush(STDIN_FILENO, TCIOFLUSH);
125 if (tcgetattr(STDIN_FILENO, &raw) == 0) redirected = false;
126 raw.c_cc[VMIN] = (doWait ? 1 : 0); // wait/poll mode
127 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) return false;
128 return true;
131 return false;
135 /// change TTY mode if possible
136 /// returns previous mode or BAD
137 TTYMode ttySetMode (TTYMode mode) @trusted @nogc {
138 // check what we can without locking
139 if (mode == TTYMode.BAD) return TTYMode.BAD;
140 if (redirected) return (mode == TTYMode.NORMAL ? TTYMode.NORMAL : TTYMode.BAD);
141 synchronized(xlock) return (mode == TTYMode.NORMAL ? ttySetNormal() : ttySetRaw());
145 /// return TTY width
146 @property int ttyWidth () @trusted nothrow @nogc {
147 if (!redirected) {
148 import core.sys.posix.sys.ioctl : ioctl, winsize, TIOCGWINSZ;
149 winsize sz = void;
150 if (ioctl(1, TIOCGWINSZ, &sz) != -1) return sz.ws_col;
152 return 80;
156 /// return TTY height
157 @property int ttyHeight () @trusted nothrow @nogc {
158 if (!redirected) {
159 import core.sys.posix.sys.ioctl : ioctl, winsize, TIOCGWINSZ;
160 winsize sz = void;
161 if (ioctl(1, TIOCGWINSZ, &sz) != -1) return sz.ws_row;
162 return sz.ws_row;
164 return 25;
169 * Wait for keypress.
171 * Params:
172 * toMSec = timeout in milliseconds; <0: infinite; 0: don't wait; default is -1
174 * Returns:
175 * true if key was pressed, false if no key was pressed in the given time
177 bool ttyWaitKey (long toMSec=-1) @trusted nothrow @nogc {
178 if (!redirected) {
179 import core.sys.posix.sys.select : fd_set, select, timeval, FD_ISSET, FD_SET, FD_ZERO;
180 import core.sys.posix.unistd : STDIN_FILENO;
181 timeval tv;
182 fd_set fds;
183 FD_ZERO(&fds);
184 FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
185 if (toMSec <= 0) {
186 tv.tv_sec = 0;
187 tv.tv_usec = 0;
188 } else {
189 tv.tv_sec = cast(int)(toMSec/1000);
190 tv.tv_usec = (toMSec%1000)*1000;
192 select(STDIN_FILENO+1, &fds, null, null, (toMSec < 0 ? null : &tv));
193 return FD_ISSET(STDIN_FILENO, &fds);
195 return false;
200 * Check if key was pressed. Don't block.
202 * Returns:
203 * true if key was pressed, false if no key was pressed
205 bool ttyIsKeyHit () @trusted nothrow @nogc {
206 return ttyWaitKey(0);
211 * Read one byte from stdin.
213 * Params:
214 * toMSec = timeout in milliseconds; <0: infinite; 0: don't wait; default is -1
216 * Returns:
217 * read byte or -1 on error/timeout
219 int ttyReadKeyByte (long toMSec=-1) @trusted @nogc {
220 if (!redirected) {
221 import core.sys.posix.unistd : read, STDIN_FILENO;
222 ubyte res;
223 if (toMSec >= 0) {
224 synchronized(xlock) if (ttyWaitKey(toMSec) && read(STDIN_FILENO, &res, 1) == 1) return res;
225 } else {
226 if (read(STDIN_FILENO, &res, 1) == 1) return res;
229 return -1;
233 /// escape sequences --> key names
234 __gshared string[string] ttyKeyTrans;
238 * Read key from stdin.
240 * WARNING! no utf-8 support yet!
242 * Params:
243 * toMSec = timeout in milliseconds; <0: infinite; 0: don't wait; default is -1
244 * toEscMSec = timeout in milliseconds for escape sequences
246 * Returns:
247 * null on error or keyname
249 string ttyReadKey (long toMSec=-1, long toEscMSec=300) @trusted {
250 import std.string : format;
251 int ch = ttyReadKeyByte(toMSec);
252 if (ch < 0) return null; // error
253 if (ch == 8 || ch == 127) return "backspace";
254 if (ch == 9) return "tab";
255 if (ch == 10) return "return";
256 // escape?
257 if (ch == 27) {
258 ch = ttyReadKeyByte(toEscMSec);
259 if (ch < 0 || ch == 27) return "escape";
260 string kk;
261 if (termType != TermType.rxvt && ch == 'O') {
262 ch = ttyReadKeyByte(toEscMSec);
263 if (ch < 0) return "escape";
264 if (ch >= 'A' && ch <= 'Z') kk = "O%c".format(cast(dchar)ch);
265 } else if (ch == '[') {
266 kk = "[";
267 for (;;) {
268 ch = ttyReadKeyByte(toEscMSec);
269 if (ch < 0 || ch == 27) return "escape";
270 kk ~= ch;
271 if (ch != ';' && (ch < '0' || ch > '9')) break;
273 } else if (ch == 9) {
274 return "alt+tab";
275 } else if (ch >= 1 && ch <= 26) {
276 return "alt+^%c".format(cast(dchar)(ch+64));
277 } else if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
278 return "alt+%c".format(cast(dchar)ch);
280 if (kk.length) {
281 auto kn = kk in ttyKeyTrans;
282 return (kn ? *kn : "unknown");
284 return "unknown";
286 if (ch < 32) return "^%c".format(cast(dchar)(ch+64)); // ^X
287 // normal
288 return "%c".format(cast(dchar)ch);
292 private void initKeyTrans () @trusted {
293 import std.string : format;
294 // RXVT
295 // arrows, specials
296 ttyKeyTrans["[A"] = "up";
297 ttyKeyTrans["[B"] = "down";
298 ttyKeyTrans["[C"] = "right";
299 ttyKeyTrans["[D"] = "left";
300 ttyKeyTrans["[2~"] = "insert";
301 ttyKeyTrans["[3~"] = "delete";
302 ttyKeyTrans["[5~"] = "pageup";
303 ttyKeyTrans["[6~"] = "pagedown";
304 ttyKeyTrans["[7~"] = "home";
305 ttyKeyTrans["[8~"] = "end";
306 ttyKeyTrans["[1~"] = "home";
307 ttyKeyTrans["[4~"] = "end";
308 // xterm
309 ttyKeyTrans["OA"] = "up";
310 ttyKeyTrans["OB"] = "down";
311 ttyKeyTrans["OC"] = "right";
312 ttyKeyTrans["OD"] = "left";
313 ttyKeyTrans["[H"] = "home";
314 ttyKeyTrans["[F"] = "end";
315 // arrows and specials with modifiers
316 foreach (immutable i, immutable c; ["shift+", "alt+", "alt+shift+", "ctrl+", "ctrl+shift+", "alt+ctrl+", "alt+ctrl+shift+"]) {
317 string t = "[1;%d".format(i+2);
318 ttyKeyTrans[t~"A"] = c~"up";
319 ttyKeyTrans[t~"B"] = c~"down";
320 ttyKeyTrans[t~"C"] = c~"right";
321 ttyKeyTrans[t~"D"] = c~"left";
323 string t1 = ";%d~".format(i+2);
324 ttyKeyTrans["[2"~t1] = c~"insert";
325 ttyKeyTrans["[3"~t1] = c~"delete";
326 // xterm, spec+f1..f4
327 ttyKeyTrans[t~"P"] = c~"f1";
328 ttyKeyTrans[t~"Q"] = c~"f2";
329 ttyKeyTrans[t~"R"] = c~"f3";
330 ttyKeyTrans[t~"S"] = c~"f4";
331 // xterm, spec+f5..f12
332 foreach (immutable idx, immutable fn; [15, 17, 18, 19, 20, 21, 23, 24]) {
333 string fs = "[%d".format(fn);
334 ttyKeyTrans[fs~t1] = c~format("f%d", idx+5);
336 // xterm
337 ttyKeyTrans["[5"~t1] = c~"pageup";
338 ttyKeyTrans["[6"~t1] = c~"pagedown";
339 ttyKeyTrans[t~"H"] = c~"home";
340 ttyKeyTrans[t~"F"] = c~"end";
342 ttyKeyTrans["[2^"] = "ctrl+insert";
343 ttyKeyTrans["[3^"] = "ctrl+delete";
344 ttyKeyTrans["[5^"] = "ctrl+pageup";
345 ttyKeyTrans["[6^"] = "ctrl+pagedown";
346 ttyKeyTrans["[7^"] = "ctrl+home";
347 ttyKeyTrans["[8^"] = "ctrl+end";
348 ttyKeyTrans["[1^"] = "ctrl+home";
349 ttyKeyTrans["[4^"] = "ctrl+end";
350 ttyKeyTrans["[2$"] = "shift+insert";
351 ttyKeyTrans["[3$"] = "shift+delete";
352 ttyKeyTrans["[5$"] = "shift+pageup";
353 ttyKeyTrans["[6$"] = "shift+pagedown";
354 ttyKeyTrans["[7$"] = "shift+home";
355 ttyKeyTrans["[8$"] = "shift+end";
356 ttyKeyTrans["[1$"] = "shift+home";
357 ttyKeyTrans["[4$"] = "shift+end";
359 ttyKeyTrans["[E"] = "num5"; // xterm
360 // fx, ctrl+fx
361 foreach (immutable i; 1..6) {
362 ttyKeyTrans["[%d~".format(i+10)] = "f%d".format(i);
363 ttyKeyTrans["[%d^".format(i+10)] = "ctrl+f%d".format(i);
364 ttyKeyTrans["[%d@".format(i+10)] = "ctrl+shift+f%d".format(i);
366 foreach (immutable i; 6..11) {
367 ttyKeyTrans["[%d~".format(i+11)] = "f%d".format(i);
368 ttyKeyTrans["[%d^".format(i+11)] = "ctrl+f%d".format(i);
369 ttyKeyTrans["[%d@".format(i+11)] = "ctrl+shift+f%d".format(i);
371 foreach (immutable i; 11..15) {
372 ttyKeyTrans["[%d~".format(i+12)] = "f%d".format(i);
373 ttyKeyTrans["[%d^".format(i+12)] = "ctrl+f%d".format(i);
374 ttyKeyTrans["[%d@".format(i+12)] = "ctrl+shift+f%d".format(i);
376 foreach (immutable i; 15..17) {
377 ttyKeyTrans["[%d~".format(i+13)] = "f%d".format(i);
378 ttyKeyTrans["[%d^".format(i+13)] = "ctrl+f%d".format(i);
379 ttyKeyTrans["[%d@".format(i+13)] = "ctrl+shift+f%d".format(i);
381 foreach (immutable i; 17..21) {
382 ttyKeyTrans["[%d~".format(i+14)] = "f%d".format(i);
383 ttyKeyTrans["[%d^".format(i+14)] = "ctrl+f%d".format(i);
384 ttyKeyTrans["[%d@".format(i+14)] = "ctrl+shift+f%d".format(i);
386 // xterm
387 // f1..f4
388 ttyKeyTrans["OP"] = "f1";
389 ttyKeyTrans["OQ"] = "f2";
390 ttyKeyTrans["OR"] = "f3";
391 ttyKeyTrans["OS"] = "f4";
395 shared static this () {
397 import core.stdc.stdlib : getenv;
398 import core.stdc.string : strcmp;
399 auto tt = getenv("TERM");
400 if (tt) {
401 if (strcmp(tt, "rxvt") == 0) termType = TermType.rxvt;
402 else if (strcmp(tt, "xterm") == 0) termType = TermType.xterm;
405 import core.sys.posix.unistd : isatty, STDIN_FILENO, STDOUT_FILENO;
406 import core.sys.posix.termios : tcgetattr;
407 xlock = new XLock;
408 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
409 if (tcgetattr(STDIN_FILENO, &origMode) == 0) redirected = false;
411 initKeyTrans();
415 shared static ~this () {
416 ttySetNormal();
417 if (xlock) {
418 delete xlock;
419 xlock = null;