diff --git a/README.md b/README.md index ac2c342..2ffc46f 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,23 @@ I have [a patreon](https://www.patreon.com/adam_d_ruppe) and my (almost) [weekly Thanks go to Nick Sabalausky, Trass3r, Stanislav Blinov, ketmar, maartenvd, and many others over the years for input and patches. Several of the modules are also ports of other C code, see the comments in those files for their original authors. + +# Conventions + +Many http-based functions in the lib also support unix sockets as an alternative to tcp. + +With cgi.d, use + + --host unix:/path/here + +or, on Linux: + + --host abstract:/path/here + +after compiling with `-version=embedded_httpd_thread` to serve http on the given socket. (`abstract:` does a unix socket in the Linux-specific abstract namespace). + +With http2.d, use + + Uri("http://whatever_host/path?args").viaUnixSocket("/path/here") + +any time you are constructing a client. Note that `navigateTo` may lose the unix socket unless you specify it again. diff --git a/terminal.d b/terminal.d index fd126b8..28ab334 100644 --- a/terminal.d +++ b/terminal.d @@ -560,6 +560,8 @@ struct Terminal { // work the way they're advertised. I just have to best-guess hack and hope it // doesn't break anything else. (If you know a better way, let me know!) bool isMacTerminal() { + // it gives 1,2 in getTerminalCapabilities... + // FIXME import std.process; import std.string; auto term = environment.get("TERM"); @@ -853,6 +855,9 @@ struct Terminal { return; } + auto info = getTerminalCapabilities(fdIn, fdOut); + //writeln(info); + if(type == ConsoleOutputType.cellular) { doTermcap("ti"); clear(); @@ -1619,6 +1624,8 @@ struct RealTimeConsoleInput { if(!(flags & ConsoleInputFlags.echo)) f |= ECHO; + // \033Z or \033[c + n.c_lflag &= ~f; tcsetattr(fdIn, TCSANOW, &n); } @@ -2490,6 +2497,7 @@ struct RealTimeConsoleInput { // starting at 70 i do some magic for like shift+enter etc. // this only happens on my own terminal emulator. + case "70": return keyPressAndRelease(NonCharacterKeyEvent.Key.ScrollLock, modifierState); case "78": return keyPressAndRelease2('\b', modifierState); case "79": return keyPressAndRelease2('\t', modifierState); case "83": return keyPressAndRelease2('\n', modifierState); @@ -2610,6 +2618,7 @@ struct KeyboardEvent { End = 0x23 + 0xF0000, /// . PageUp = 0x21 + 0xF0000, /// . PageDown = 0x22 + 0xF0000, /// . + ScrollLock = 0x91 + 0xF0000, /// unlikely to work outside my custom terminal emulator } @@ -2665,6 +2674,7 @@ struct NonCharacterKeyEvent { End = 0x23, /// . PageUp = 0x21, /// . PageDown = 0x22, /// . + ScrollLock = 0x91, /// unlikely to work outside my terminal emulator } Key key; /// . @@ -3011,6 +3021,127 @@ void main() { } } +version(Posix) +private int[] getTerminalCapabilities(int fdIn, int fdOut) { + if(fdIn == -1 || fdOut == -1) + return null; + + import std.conv; + import core.stdc.errno; + import core.sys.posix.unistd; + + ubyte[128] hack2; + termios old; + ubyte[128] hack; + tcgetattr(fdIn, &old); + auto n = old; + n.c_lflag &= ~(ICANON | ECHO); + tcsetattr(fdIn, TCSANOW, &n); + scope(exit) + tcsetattr(fdIn, TCSANOW, &old); + + // drain the buffer? meh + + string cmd = "\033[c"; + auto err = write(fdOut, cmd.ptr, cmd.length); + if(err != cmd.length) { + throw new Exception("couldn't ask terminal for ID"); + } + + // reading directly to bypass any buffering + int retries = 16; + int len; + ubyte[96] buffer; + try_again: + + + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10 * 1000; + + fd_set fs; + FD_ZERO(&fs); + + FD_SET(fdIn, &fs); + if(select(fdIn + 1, &fs, null, null, &tv) == -1) { + goto try_again; + } + + if(FD_ISSET(fdIn, &fs)) { + auto len2 = read(fdIn, &buffer[len], buffer.length - len); + if(len2 <= 0) { + retries--; + if(retries > 0) + goto try_again; + throw new Exception("can't get terminal id"); + } else { + len += len2; + } + } else { + // no data... assume terminal doesn't support giving an answer + return null; + } + + ubyte[] answer; + bool hasAnswer(ubyte[] data) { + if(data.length < 4) + return false; + answer = null; + size_t start; + int position = 0; + foreach(idx, ch; data) { + switch(position) { + case 0: + if(ch == '\033') { + start = idx; + position++; + } + break; + case 1: + if(ch == '[') + position++; + else + position = 0; + break; + case 2: + if(ch == '?') + position++; + else + position = 0; + break; + case 3: + // body + if(ch == 'c') { + answer = data[start .. idx + 1]; + return true; + } else if(ch == ';' || (ch >= '0' && ch <= '9')) { + // good, keep going + } else { + // invalid, drop it + position = 0; + } + break; + default: assert(0); + } + } + return false; + } + + auto got = buffer[0 .. len]; + if(!hasAnswer(got)) { + goto try_again; + } + auto gots = cast(char[]) answer[3 .. $-1]; + + import std.string; + + auto pieces = split(gots, ";"); + int[] ret; + foreach(p; pieces) + ret ~= p.to!int; + return ret; +} + /** FIXME: support lines that wrap FIXME: better controls maybe