dark bg, clipboard, and vt on windows beginning factor

This commit is contained in:
Adam D. Ruppe 2020-02-17 19:57:56 -05:00
parent 944abf3c72
commit c911434c8b
1 changed files with 540 additions and 424 deletions

View File

@ -1,4 +1,6 @@
// for optional dependency // for optional dependency
// for VT on Windows P s = 1 8 → Report the size of the text area in characters as CSI 8 ; height ; width t
// could be used to have the TE volunteer the size
/++ /++
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples]. Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples].
@ -17,8 +19,8 @@
As a user, if you have to forcibly kill your program and the event doesn't work, there's still ctrl+\ As a user, if you have to forcibly kill your program and the event doesn't work, there's still ctrl+\
On Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work. Most functions basically On old Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work. Most functions basically
work now though. work now with newer Mac OS versions though.
Future_Roadmap: Future_Roadmap:
$(LIST $(LIST
@ -185,8 +187,10 @@ version(Posix) {
// capabilities. // capabilities.
//version = Demo //version = Demo
version(Windows) version(Windows) {
version(VtEscapeCodes) {} // cool
version=Win32Console; version=Win32Console;
}
version(Win32Console) { version(Win32Console) {
import core.sys.windows.windows; import core.sys.windows.windows;
@ -198,27 +202,30 @@ version(Win32Console) {
} }
version(Posix) { version(Posix) {
version=VtEscapeCodes;
import core.sys.posix.termios; import core.sys.posix.termios;
import core.sys.posix.unistd; import core.sys.posix.unistd;
import unix = core.sys.posix.unistd; import unix = core.sys.posix.unistd;
import core.sys.posix.sys.types; import core.sys.posix.sys.types;
import core.sys.posix.sys.time; import core.sys.posix.sys.time;
import core.stdc.stdio; import core.stdc.stdio;
import core.sys.posix.sys.ioctl;
}
version(VtEscapeCodes) {
enum UseVtSequences = true;
version(Windows) {} else
private { private {
enum RED_BIT = 1; enum RED_BIT = 1;
enum GREEN_BIT = 2; enum GREEN_BIT = 2;
enum BLUE_BIT = 4; enum BLUE_BIT = 4;
} }
version(linux) {
extern(C) int ioctl(int, int, ...);
enum int TIOCGWINSZ = 0x5413;
} else version(OSX) {
import core.stdc.config;
extern(C) int ioctl(int, c_ulong, ...);
enum TIOCGWINSZ = 1074295912;
} else static assert(0, "confirm the value of tiocgwinsz");
struct winsize { struct winsize {
ushort ws_row; ushort ws_row;
ushort ws_col; ushort ws_col;
@ -373,6 +380,8 @@ an|ansi|ansi-bbs|ANSI terminals (emulators):\
:tc=vt-generic: :tc=vt-generic:
`; `;
} else {
enum UseVtSequences = false;
} }
/// A modifier for [Color] /// A modifier for [Color]
@ -543,7 +552,6 @@ struct Terminal {
void delegate(in void[]) _writeDelegate; // used to override the unix write() system call, set it magically void delegate(in void[]) _writeDelegate; // used to override the unix write() system call, set it magically
} }
version(Posix) {
bool terminalInFamily(string[] terms...) { bool terminalInFamily(string[] terms...) {
import std.process; import std.process;
import std.string; import std.string;
@ -555,6 +563,7 @@ struct Terminal {
return false; return false;
} }
version(Posix) {
// This is a filthy hack because Terminal.app and OS X are garbage who don't // This is a filthy hack because Terminal.app and OS X are garbage who don't
// work the way they're advertised. I just have to best-guess hack and hope it // 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!) // doesn't break anything else. (If you know a better way, let me know!)
@ -566,6 +575,7 @@ struct Terminal {
auto term = environment.get("TERM"); auto term = environment.get("TERM");
return term == "xterm-256color"; return term == "xterm-256color";
} }
}
static string[string] termcapDatabase; static string[string] termcapDatabase;
static void readTermcapFile(bool useBuiltinTermcap = false) { static void readTermcapFile(bool useBuiltinTermcap = false) {
@ -610,6 +620,7 @@ struct Terminal {
} }
if(useBuiltinTermcap) { if(useBuiltinTermcap) {
version(VtEscapeCodes)
foreach(line; splitLines(builtinTermcap)) { foreach(line; splitLines(builtinTermcap)) {
handleTermcapLine(line); handleTermcapLine(line);
} }
@ -839,7 +850,6 @@ struct Terminal {
writeStringRaw(buffer[0 .. bufferPos]); writeStringRaw(buffer[0 .. bufferPos]);
return true; return true;
} }
}
uint tcaps; uint tcaps;
@ -847,7 +857,8 @@ struct Terminal {
return (tcaps & TerminalCapabilities.arsdImage) ? true : false; return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
} }
bool clipboardSupported() { bool clipboardSupported() {
return (tcaps & TerminalCapabilities.arsdImage) ? true : false; version(Win32Console) return true;
else return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
} }
// only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported) // only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported)
@ -918,7 +929,7 @@ struct Terminal {
} }
} }
// stubs that are dependent on tcaps // dependent on tcaps...
void displayInlineImage()(ubyte[] imageData) { void displayInlineImage()(ubyte[] imageData) {
if(inlineImagesSupported) { if(inlineImagesSupported) {
import std.base64; import std.base64;
@ -934,7 +945,7 @@ struct Terminal {
} }
void demandUserAttention() { void demandUserAttention() {
version(Posix) { if(UseVtSequences) {
if(!terminalInFamily("linux")) if(!terminalInFamily("linux"))
writeStringRaw("\033]5001;1\007"); writeStringRaw("\033]5001;1\007");
} }
@ -947,12 +958,6 @@ struct Terminal {
} }
} }
void requestPasteFromClipboard() {
if(clipboardSupported) {
writeStringRaw("\033]52;c;?\007");
}
}
void requestCopyToPrimary(string text) { void requestCopyToPrimary(string text) {
if(clipboardSupported) { if(clipboardSupported) {
import std.base64; import std.base64;
@ -960,9 +965,16 @@ struct Terminal {
} }
} }
void requestPasteFromPrimary() { bool hasDefaultDarkBackground() {
if(clipboardSupported) { version(Win32Console) {
writeStringRaw("\033]52;p;?\007"); return !(defaultBackgroundColor & 0xf0);
} else {
// FIXME: there is probably a better way to do this
// but like idk how reliable it is.
if(terminalInFamily("linux"))
return true;
else
return false;
} }
} }
@ -982,8 +994,6 @@ struct Terminal {
this.getSizeOverride = getSizeOverride; this.getSizeOverride = getSizeOverride;
this.type = type; this.type = type;
readTermcap();
if(type == ConsoleOutputType.minimalProcessing) { if(type == ConsoleOutputType.minimalProcessing) {
_suppressDestruction = true; _suppressDestruction = true;
return; return;
@ -992,6 +1002,12 @@ struct Terminal {
tcaps = getTerminalCapabilities(fdIn, fdOut); tcaps = getTerminalCapabilities(fdIn, fdOut);
//writeln(tcaps); //writeln(tcaps);
initializeVt();
}
void initializeVt() {
readTermcap();
if(type == ConsoleOutputType.cellular) { if(type == ConsoleOutputType.cellular) {
doTermcap("ti"); doTermcap("ti");
clear(); clear();
@ -1001,6 +1017,7 @@ struct Terminal {
if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) { if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) {
writeStringRaw("\033[22;0t"); // save window title on a stack (support seems spotty, but it doesn't hurt to have it) writeStringRaw("\033[22;0t"); // save window title on a stack (support seems spotty, but it doesn't hurt to have it)
} }
} }
// EXPERIMENTAL do not use yet // EXPERIMENTAL do not use yet
@ -1019,6 +1036,10 @@ struct Terminal {
version(Win32Console) version(Win32Console)
/// ditto /// ditto
this(ConsoleOutputType type) { this(ConsoleOutputType type) {
if(UseVtSequences) {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
initializeVt();
} else {
if(type == ConsoleOutputType.cellular) { if(type == ConsoleOutputType.cellular) {
hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null); hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null);
if(hConsole == INVALID_HANDLE_VALUE) { if(hConsole == INVALID_HANDLE_VALUE) {
@ -1028,8 +1049,8 @@ struct Terminal {
SetConsoleActiveScreenBuffer(hConsole); SetConsoleActiveScreenBuffer(hConsole);
/* /*
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686125%28v=vs.85%29.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/ms686125%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.aspx
*/ */
COORD size; COORD size;
/* /*
@ -1068,6 +1089,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
SetConsoleCP(65001); // UTF-8 SetConsoleCP(65001); // UTF-8
*/ */
} }
}
version(Win32Console) { version(Win32Console) {
private Color defaultBackgroundColor = Color.black; private Color defaultBackgroundColor = Color.black;
@ -1079,12 +1101,13 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
// only use this if you are sure you know what you want, since the terminal is a shared resource you generally really want to reset it to normal when you leave... // only use this if you are sure you know what you want, since the terminal is a shared resource you generally really want to reset it to normal when you leave...
bool _suppressDestruction; bool _suppressDestruction;
version(Posix)
~this() { ~this() {
if(_suppressDestruction) { if(_suppressDestruction) {
flush(); flush();
return; return;
} }
if(UseVtSequences) {
if(type == ConsoleOutputType.cellular) { if(type == ConsoleOutputType.cellular) {
doTermcap("te"); doTermcap("te");
} }
@ -1098,14 +1121,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
if(lineGetter !is null) if(lineGetter !is null)
lineGetter.dispose(); lineGetter.dispose();
} } else version(Windows) {
version(Windows)
~this() {
if(_suppressDestruction) {
flush();
return;
}
flush(); // make sure user data is all flushed before resetting flush(); // make sure user data is all flushed before resetting
reset(); reset();
showCursor(); showCursor();
@ -1121,6 +1137,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
SetConsoleActiveScreenBuffer(stdo); SetConsoleActiveScreenBuffer(stdo);
if(hConsole !is stdo) if(hConsole !is stdo)
CloseHandle(hConsole); CloseHandle(hConsole);
}
} }
// lazily initialized and preserved between calls to getline for a bit of efficiency (only a bit) // lazily initialized and preserved between calls to getline for a bit of efficiency (only a bit)
@ -1285,7 +1303,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
void underline(bool set, ForceOption force = ForceOption.automatic) { void underline(bool set, ForceOption force = ForceOption.automatic) {
if(set == _underlined && force != ForceOption.alwaysSend) if(set == _underlined && force != ForceOption.alwaysSend)
return; return;
version(Posix) { if(UseVtSequences) {
if(set) if(set)
writeStringRaw("\033[4m"); writeStringRaw("\033[4m");
else else
@ -1329,14 +1347,14 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
void moveTo(int x, int y, ForceOption force = ForceOption.automatic) { void moveTo(int x, int y, ForceOption force = ForceOption.automatic) {
if(force != ForceOption.neverSend && (force == ForceOption.alwaysSend || x != _cursorX || y != _cursorY)) { if(force != ForceOption.neverSend && (force == ForceOption.alwaysSend || x != _cursorX || y != _cursorY)) {
executeAutoHideCursor(); executeAutoHideCursor();
version(Posix) { if(UseVtSequences) {
doTermcap("cm", y, x); doTermcap("cm", y, x);
} else version(Win32Console) { } else version(Win32Console) {
flush(); // if we don't do this now, the buffering can screw up the position flush(); // if we don't do this now, the buffering can screw up the position
COORD coord = {cast(short) x, cast(short) y}; COORD coord = {cast(short) x, cast(short) y};
SetConsoleCursorPosition(hConsole, coord); SetConsoleCursorPosition(hConsole, coord);
} else static assert(0); }
} }
_cursorX = x; _cursorX = x;
@ -1345,9 +1363,9 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
/// shows the cursor /// shows the cursor
void showCursor() { void showCursor() {
version(Posix) if(UseVtSequences)
doTermcap("ve"); doTermcap("ve");
else { else version(Win32Console) {
CONSOLE_CURSOR_INFO info; CONSOLE_CURSOR_INFO info;
GetConsoleCursorInfo(hConsole, &info); GetConsoleCursorInfo(hConsole, &info);
info.bVisible = true; info.bVisible = true;
@ -1357,9 +1375,9 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
/// hides the cursor /// hides the cursor
void hideCursor() { void hideCursor() {
version(Posix) { if(UseVtSequences) {
doTermcap("vi"); doTermcap("vi");
} else { } else version(Win32Console) {
CONSOLE_CURSOR_INFO info; CONSOLE_CURSOR_INFO info;
GetConsoleCursorInfo(hConsole, &info); GetConsoleCursorInfo(hConsole, &info);
info.bVisible = false; info.bVisible = false;
@ -1466,7 +1484,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
} }
int[] getSize() { int[] getSize() {
version(Win32Console) { version(Windows) {
CONSOLE_SCREEN_BUFFER_INFO info; CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo( hConsole, &info ); GetConsoleScreenBufferInfo( hConsole, &info );
@ -1615,17 +1633,12 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
// you really, really shouldn't use this unless you know what you are doing // you really, really shouldn't use this unless you know what you are doing
/*private*/ void writeStringRaw(in char[] s) { /*private*/ void writeStringRaw(in char[] s) {
// FIXME: make sure all the data is sent, check for errors
version(Posix) {
writeBuffer ~= s; // buffer it to do everything at once in flush() calls writeBuffer ~= s; // buffer it to do everything at once in flush() calls
} else version(Win32Console) {
writeBuffer ~= s;
} else static assert(0);
} }
/// Clears the screen. /// Clears the screen.
void clear() { void clear() {
version(Posix) { if(UseVtSequences) {
doTermcap("cl"); doTermcap("cl");
} else version(Win32Console) { } else version(Win32Console) {
// http://support.microsoft.com/kb/99261 // http://support.microsoft.com/kb/99261
@ -1705,6 +1718,64 @@ struct RealTimeConsoleInput {
@disable this(); @disable this();
@disable this(this); @disable this(this);
/++
Requests the system to send paste data as a [PasteEvent] to this stream, if possible.
See_Also:
[Terminal.requestCopyToPrimary]
[Terminal.requestCopyToClipboard]
[Terminal.clipboardSupported]
History:
Added February 17, 2020.
It was in Terminal briefly during an undocumented period, but it had to be moved here to have the context needed to send the real time paste event.
+/
void requestPasteFromClipboard() {
version(Win32Console) {
HWND hwndOwner = null;
if(OpenClipboard(hwndOwner) == 0)
throw new Exception("OpenClipboard");
scope(exit)
CloseClipboard();
if(auto dataHandle = GetClipboardData(CF_UNICODETEXT)) {
if(auto data = cast(wchar*) GlobalLock(dataHandle)) {
scope(exit)
GlobalUnlock(dataHandle);
int len = 0;
auto d = data;
while(*d) {
d++;
len++;
}
string s;
s.reserve(len);
foreach(idx, dchar ch; data[0 .. len]) {
// CR/LF -> LF
if(ch == '\r' && idx + 1 < len && data[idx + 1] == '\n')
continue;
s ~= ch;
}
injectEvent(InputEvent(PasteEvent(s), terminal), InjectionPosition.tail);
}
}
} else
if(terminal.clipboardSupported) {
terminal.writeStringRaw("\033]52;c;?\007");
}
}
/// ditto
void requestPasteFromPrimary() {
if(terminal.clipboardSupported) {
terminal.writeStringRaw("\033]52;p;?\007");
}
}
version(Posix) { version(Posix) {
private int fdOut; private int fdOut;
private int fdIn; private int fdIn;
@ -1739,7 +1810,7 @@ struct RealTimeConsoleInput {
GetConsoleMode(inputHandle, &oldInput); GetConsoleMode(inputHandle, &oldInput);
DWORD mode = 0; DWORD mode = 0;
mode |= ENABLE_PROCESSED_INPUT /* 0x01 */; // this gives Ctrl+C which we probably want to be similar to linux //mode |= ENABLE_PROCESSED_INPUT /* 0x01 */; // this gives Ctrl+C and automatic paste... which we probably want to be similar to linux
//if(flags & ConsoleInputFlags.size) //if(flags & ConsoleInputFlags.size)
mode |= ENABLE_WINDOW_INPUT /* 0208 */; // gives size etc mode |= ENABLE_WINDOW_INPUT /* 0208 */; // gives size etc
if(flags & ConsoleInputFlags.echo) if(flags & ConsoleInputFlags.echo)
@ -1980,7 +2051,7 @@ struct RealTimeConsoleInput {
bool timedCheckForInput_bypassingBuffer(int milliseconds) { bool timedCheckForInput_bypassingBuffer(int milliseconds) {
version(Win32Console) { version(Win32Console) {
auto response = WaitForSingleObject(terminal.hConsole, milliseconds); auto response = WaitForSingleObject(inputHandle, milliseconds);
if(response == 0) if(response == 0)
return true; // the object is ready return true; // the object is ready
return false; return false;
@ -2039,8 +2110,8 @@ struct RealTimeConsoleInput {
//char[128] inputBuffer; //char[128] inputBuffer;
//int inputBufferPosition; //int inputBufferPosition;
version(Posix)
int nextRaw(bool interruptable = false) { int nextRaw(bool interruptable = false) {
version(Posix) {
if(fdIn == -1) if(fdIn == -1)
return 0; return 0;
@ -2067,12 +2138,20 @@ struct RealTimeConsoleInput {
return inputPrefilter ? inputPrefilter(buf[0]) : buf[0]; return inputPrefilter ? inputPrefilter(buf[0]) : buf[0];
else else
assert(0); // read too much, should be impossible assert(0); // read too much, should be impossible
} else version(Windows) {
char[8] buf;
DWORD d;
import std.conv;
if(!ReadFile(inputHandle, buf.ptr, cast(int) buf.length, &d, null))
throw new Exception("ReadFile " ~ to!string(GetLastError()));
return buf[0];
}
} }
version(Posix) version(Posix)
int delegate(char) inputPrefilter; int delegate(char) inputPrefilter;
version(Posix) // for VT
dchar nextChar(int starting) { dchar nextChar(int starting) {
if(starting <= 127) if(starting <= 127)
return cast(dchar) starting; return cast(dchar) starting;
@ -2176,8 +2255,17 @@ struct RealTimeConsoleInput {
InputEvent[] inputQueue; InputEvent[] inputQueue;
version(Win32Console)
InputEvent[] readNextEvents() { InputEvent[] readNextEvents() {
if(UseVtSequences)
return readNextEventsVt();
else version(Windows)
return readNextEventsWin32();
else
assert(0);
}
version(Windows)
InputEvent[] readNextEventsWin32() {
terminal.flush(); // make sure all output is sent out before waiting for anything terminal.flush(); // make sure all output is sent out before waiting for anything
INPUT_RECORD[32] buffer; INPUT_RECORD[32] buffer;
@ -2219,12 +2307,26 @@ struct RealTimeConsoleInput {
if(ev.UnicodeChar) { if(ev.UnicodeChar) {
// new style event goes first // new style event goes first
if(ev.UnicodeChar == 3) {
// handling this internally for linux compat too
newEvents ~= InputEvent(UserInterruptionEvent(), terminal);
} else if(ev.UnicodeChar == '\r') {
// translating \r to \n for same result as linux...
ke.which = cast(dchar) cast(wchar) '\n';
newEvents ~= InputEvent(ke, terminal);
// old style event then follows as the fallback
e.character = cast(dchar) cast(wchar) '\n';
newEvents ~= InputEvent(e, terminal);
} else {
ke.which = cast(dchar) cast(wchar) ev.UnicodeChar; ke.which = cast(dchar) cast(wchar) ev.UnicodeChar;
newEvents ~= InputEvent(ke, terminal); newEvents ~= InputEvent(ke, terminal);
// old style event then follows as the fallback // old style event then follows as the fallback
e.character = cast(dchar) cast(wchar) ev.UnicodeChar; e.character = cast(dchar) cast(wchar) ev.UnicodeChar;
newEvents ~= InputEvent(e, terminal); newEvents ~= InputEvent(e, terminal);
}
} else { } else {
// old style event // old style event
ne.key = cast(NonCharacterKeyEvent.Key) ev.wVirtualKeyCode; ne.key = cast(NonCharacterKeyEvent.Key) ev.wVirtualKeyCode;
@ -2302,8 +2404,8 @@ struct RealTimeConsoleInput {
return newEvents; return newEvents;
} }
version(Posix) // for UseVtSequences....
InputEvent[] readNextEvents() { InputEvent[] readNextEventsVt() {
terminal.flush(); // make sure all output is sent out before we try to get input terminal.flush(); // make sure all output is sent out before we try to get input
// we want to starve the read, especially if we're called from an edge-triggered // we want to starve the read, especially if we're called from an edge-triggered
@ -2327,7 +2429,7 @@ struct RealTimeConsoleInput {
} }
// The helper reads just one actual event from the pipe... // The helper reads just one actual event from the pipe...
version(Posix) // for UseVtSequences....
InputEvent[] readNextEventsHelper() { InputEvent[] readNextEventsHelper() {
InputEvent[] charPressAndRelease(dchar character) { InputEvent[] charPressAndRelease(dchar character) {
if((flags & ConsoleInputFlags.releasedKeys)) if((flags & ConsoleInputFlags.releasedKeys))
@ -3711,11 +3813,11 @@ class LineGetter {
auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents | ConsoleInputFlags.noEolWrap); auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents | ConsoleInputFlags.noEolWrap);
//rtci = &i; //rtci = &i;
//scope(exit) rtci = null; //scope(exit) rtci = null;
while(workOnLine(i.nextEvent())) {} while(workOnLine(i.nextEvent(), &i)) {}
} else { } else {
//rtci = input; //rtci = input;
//scope(exit) rtci = null; //scope(exit) rtci = null;
while(workOnLine(input.nextEvent())) {} while(workOnLine(input.nextEvent(), input)) {}
} }
return finishGettingLine(); return finishGettingLine();
} }
@ -4118,12 +4220,19 @@ class LineGetter {
private bool maintainBuffer; private bool maintainBuffer;
/// for integrating into another event loop /++
/// you can pass individual events to this and for integrating into another event loop
/// the line getter will work on it you can pass individual events to this and
/// the line getter will work on it
/// returns false when there's nothing more to do
bool workOnLine(InputEvent e) { returns false when there's nothing more to do
History:
On February 17, 2020, it was changed to take
a new argument which should be the input source
where the event came from.
+/
bool workOnLine(InputEvent e, RealTimeConsoleInput* rtti = null) {
switch(e.type) { switch(e.type) {
case InputEvent.Type.EndOfFileEvent: case InputEvent.Type.EndOfFileEvent:
justHitTab = false; justHitTab = false;
@ -4309,6 +4418,10 @@ class LineGetter {
scrollToEnd(); scrollToEnd();
redraw(); redraw();
break; break;
case ('v' - 'a' + 1):
if(rtti)
rtti.requestPasteFromClipboard();
break;
case KeyboardEvent.Key.Insert: case KeyboardEvent.Key.Insert:
justHitTab = false; justHitTab = false;
if(ev.modifierState & ModifierState.shift) { if(ev.modifierState & ModifierState.shift) {
@ -4319,8 +4432,11 @@ class LineGetter {
// those work on Windows!!!! and many linux TEs too. // those work on Windows!!!! and many linux TEs too.
// but if it does make it here, we'll attempt it at this level // but if it does make it here, we'll attempt it at this level
if(rtti)
rtti.requestPasteFromClipboard();
} else if(ev.modifierState & ModifierState.control) { } else if(ev.modifierState & ModifierState.control) {
// copy // copy
// FIXME
} else { } else {
insertMode = !insertMode; insertMode = !insertMode;