lots of cool stuff

This commit is contained in:
Adam D. Ruppe 2020-01-18 21:51:50 -05:00
parent f95927b193
commit c2b29d1e21
1 changed files with 177 additions and 100 deletions

View File

@ -955,6 +955,10 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
version(Windows) version(Windows)
~this() { ~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();
@ -1411,7 +1415,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
// assert(s.indexOf("\033") == -1); // assert(s.indexOf("\033") == -1);
// tracking cursor position // tracking cursor position
foreach(ch; s) { // FIXME: by grapheme?
foreach(dchar ch; s) {
switch(ch) { switch(ch) {
case '\n': case '\n':
_cursorX = 0; _cursorX = 0;
@ -1425,8 +1430,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
_cursorX += _cursorX % 8; // FIXME: get the actual tabstop, if possible _cursorX += _cursorX % 8; // FIXME: get the actual tabstop, if possible
break; break;
default: default:
if(ch <= 127) // way of only advancing once per dchar instead of per code unit _cursorX++;
_cursorX++;
} }
if(_wrapAround && _cursorX > width) { if(_wrapAround && _cursorX > width) {
@ -1760,7 +1764,12 @@ struct RealTimeConsoleInput {
} }
} }
bool _suppressDestruction;
~this() { ~this() {
if(_suppressDestruction)
return;
// the delegate thing doesn't actually work for this... for some reason // the delegate thing doesn't actually work for this... for some reason
version(Posix) version(Posix)
if(fdIn != -1) if(fdIn != -1)
@ -2198,6 +2207,23 @@ struct RealTimeConsoleInput {
]; ];
} }
InputEvent[] keyPressAndRelease2(dchar c, uint modifiers = 0) {
if((flags & ConsoleInputFlags.releasedKeys))
return [
InputEvent(KeyboardEvent(true, c, modifiers), terminal),
InputEvent(KeyboardEvent(false, c, modifiers), terminal),
// old style event
InputEvent(CharacterEvent(CharacterEvent.Type.Pressed, c, modifiers), terminal),
InputEvent(CharacterEvent(CharacterEvent.Type.Released, c, modifiers), terminal),
];
else return [
InputEvent(KeyboardEvent(true, c, modifiers), terminal),
// old style event
InputEvent(CharacterEvent(CharacterEvent.Type.Pressed, c, modifiers), terminal)
];
}
char[30] sequenceBuffer; char[30] sequenceBuffer;
// this assumes you just read "\033[" // this assumes you just read "\033["
@ -2285,7 +2311,7 @@ struct RealTimeConsoleInput {
default: default:
// don't know it, just ignore // don't know it, just ignore
//import std.stdio; //import std.stdio;
//writeln(cap); //terminal.writeln(cap);
} }
return null; return null;
@ -2388,92 +2414,110 @@ struct RealTimeConsoleInput {
return [InputEvent(m, terminal)]; return [InputEvent(m, terminal)];
default: default:
// look it up in the termcap key database // screen doesn't actually do the modifiers, but
auto cap = terminal.findSequenceInTermcap(sequence); // it uses the same format so this branch still works fine.
if(cap !is null) { if(terminal.terminalInFamily("xterm", "screen")) {
return translateTermcapName(cap); import std.conv, std.string;
} else { auto terminator = sequence[$ - 1];
if(terminal.terminalInFamily("xterm")) { auto parts = sequence[2 .. $ - 1].split(";");
import std.conv, std.string; // parts[0] and terminator tells us the key
auto terminator = sequence[$ - 1]; // parts[1] tells us the modifierState
auto parts = sequence[2 .. $ - 1].split(";");
// parts[0] and terminator tells us the key
// parts[1] tells us the modifierState
uint modifierState; uint modifierState;
int modGot; int modGot;
if(parts.length > 1) if(parts.length > 1)
modGot = to!int(parts[1]); modGot = to!int(parts[1]);
mod_switch: switch(modGot) { mod_switch: switch(modGot) {
case 2: modifierState |= ModifierState.shift; break; case 2: modifierState |= ModifierState.shift; break;
case 3: modifierState |= ModifierState.alt; break; case 3: modifierState |= ModifierState.alt; break;
case 4: modifierState |= ModifierState.shift | ModifierState.alt; break; case 4: modifierState |= ModifierState.shift | ModifierState.alt; break;
case 5: modifierState |= ModifierState.control; break; case 5: modifierState |= ModifierState.control; break;
case 6: modifierState |= ModifierState.shift | ModifierState.control; break; case 6: modifierState |= ModifierState.shift | ModifierState.control; break;
case 7: modifierState |= ModifierState.alt | ModifierState.control; break; case 7: modifierState |= ModifierState.alt | ModifierState.control; break;
case 8: modifierState |= ModifierState.shift | ModifierState.alt | ModifierState.control; break; case 8: modifierState |= ModifierState.shift | ModifierState.alt | ModifierState.control; break;
case 9: case 9:
.. ..
case 16: case 16:
modifierState |= ModifierState.meta; modifierState |= ModifierState.meta;
if(modGot != 9) { if(modGot != 9) {
modGot -= 8; modGot -= 8;
goto mod_switch;
}
break;
// this is an extension in my own terminal emulator
case 20:
..
case 36:
modifierState |= ModifierState.windows;
modGot -= 20;
goto mod_switch; goto mod_switch;
default: }
} break;
switch(terminator) { // this is an extension in my own terminal emulator
case 'A': return keyPressAndRelease(NonCharacterKeyEvent.Key.UpArrow, modifierState); case 20:
case 'B': return keyPressAndRelease(NonCharacterKeyEvent.Key.DownArrow, modifierState); ..
case 'C': return keyPressAndRelease(NonCharacterKeyEvent.Key.RightArrow, modifierState); case 36:
case 'D': return keyPressAndRelease(NonCharacterKeyEvent.Key.LeftArrow, modifierState); modifierState |= ModifierState.windows;
modGot -= 20;
goto mod_switch;
default:
}
case 'H': return keyPressAndRelease(NonCharacterKeyEvent.Key.Home, modifierState); switch(terminator) {
case 'F': return keyPressAndRelease(NonCharacterKeyEvent.Key.End, modifierState); case 'A': return keyPressAndRelease(NonCharacterKeyEvent.Key.UpArrow, modifierState);
case 'B': return keyPressAndRelease(NonCharacterKeyEvent.Key.DownArrow, modifierState);
case 'C': return keyPressAndRelease(NonCharacterKeyEvent.Key.RightArrow, modifierState);
case 'D': return keyPressAndRelease(NonCharacterKeyEvent.Key.LeftArrow, modifierState);
case 'P': return keyPressAndRelease(NonCharacterKeyEvent.Key.F1, modifierState); case 'H': return keyPressAndRelease(NonCharacterKeyEvent.Key.Home, modifierState);
case 'Q': return keyPressAndRelease(NonCharacterKeyEvent.Key.F2, modifierState); case 'F': return keyPressAndRelease(NonCharacterKeyEvent.Key.End, modifierState);
case 'R': return keyPressAndRelease(NonCharacterKeyEvent.Key.F3, modifierState);
case 'S': return keyPressAndRelease(NonCharacterKeyEvent.Key.F4, modifierState);
case '~': // others case 'P': return keyPressAndRelease(NonCharacterKeyEvent.Key.F1, modifierState);
switch(parts[0]) { case 'Q': return keyPressAndRelease(NonCharacterKeyEvent.Key.F2, modifierState);
case "5": return keyPressAndRelease(NonCharacterKeyEvent.Key.PageUp, modifierState); case 'R': return keyPressAndRelease(NonCharacterKeyEvent.Key.F3, modifierState);
case "6": return keyPressAndRelease(NonCharacterKeyEvent.Key.PageDown, modifierState); case 'S': return keyPressAndRelease(NonCharacterKeyEvent.Key.F4, modifierState);
case "2": return keyPressAndRelease(NonCharacterKeyEvent.Key.Insert, modifierState);
case "3": return keyPressAndRelease(NonCharacterKeyEvent.Key.Delete, modifierState);
case "15": return keyPressAndRelease(NonCharacterKeyEvent.Key.F5, modifierState); case '~': // others
case "17": return keyPressAndRelease(NonCharacterKeyEvent.Key.F6, modifierState); switch(parts[0]) {
case "18": return keyPressAndRelease(NonCharacterKeyEvent.Key.F7, modifierState); case "1": return keyPressAndRelease(NonCharacterKeyEvent.Key.Home, modifierState);
case "19": return keyPressAndRelease(NonCharacterKeyEvent.Key.F8, modifierState); case "4": return keyPressAndRelease(NonCharacterKeyEvent.Key.End, modifierState);
case "20": return keyPressAndRelease(NonCharacterKeyEvent.Key.F9, modifierState); case "5": return keyPressAndRelease(NonCharacterKeyEvent.Key.PageUp, modifierState);
case "21": return keyPressAndRelease(NonCharacterKeyEvent.Key.F10, modifierState); case "6": return keyPressAndRelease(NonCharacterKeyEvent.Key.PageDown, modifierState);
case "23": return keyPressAndRelease(NonCharacterKeyEvent.Key.F11, modifierState); case "2": return keyPressAndRelease(NonCharacterKeyEvent.Key.Insert, modifierState);
case "24": return keyPressAndRelease(NonCharacterKeyEvent.Key.F12, modifierState); case "3": return keyPressAndRelease(NonCharacterKeyEvent.Key.Delete, modifierState);
default:
}
break;
default: case "15": return keyPressAndRelease(NonCharacterKeyEvent.Key.F5, modifierState);
} case "17": return keyPressAndRelease(NonCharacterKeyEvent.Key.F6, modifierState);
} else if(terminal.terminalInFamily("rxvt")) { case "18": return keyPressAndRelease(NonCharacterKeyEvent.Key.F7, modifierState);
// FIXME: figure these out. rxvt seems to just change the terminator while keeping the rest the same case "19": return keyPressAndRelease(NonCharacterKeyEvent.Key.F8, modifierState);
// though it isn't consistent. ugh. case "20": return keyPressAndRelease(NonCharacterKeyEvent.Key.F9, modifierState);
} else { case "21": return keyPressAndRelease(NonCharacterKeyEvent.Key.F10, modifierState);
// maybe we could do more terminals, but linux doesn't even send it and screen just seems to pass through, so i don't think so; xterm prolly covers most them anyway case "23": return keyPressAndRelease(NonCharacterKeyEvent.Key.F11, modifierState);
// so this space is semi-intentionally left blank case "24": return keyPressAndRelease(NonCharacterKeyEvent.Key.F12, modifierState);
// starting at 70 i do some magic for like shift+enter etc.
// this only happens on my own terminal emulator.
case "78": return keyPressAndRelease2('\b', modifierState);
case "79": return keyPressAndRelease2('\t', modifierState);
case "83": return keyPressAndRelease2('\n', modifierState);
default:
}
break;
default:
}
} else if(terminal.terminalInFamily("rxvt")) {
// look it up in the termcap key database
string cap = terminal.findSequenceInTermcap(sequence);
if(cap !is null) {
//terminal.writeln("found in termcap " ~ cap);
return translateTermcapName(cap);
}
// FIXME: figure these out. rxvt seems to just change the terminator while keeping the rest the same
// though it isn't consistent. ugh.
} else {
// maybe we could do more terminals, but linux doesn't even send it and screen just seems to pass through, so i don't think so; xterm prolly covers most them anyway
// so this space is semi-intentionally left blank
//terminal.writeln("wtf ", sequence[1..$]);
// look it up in the termcap key database
string cap = terminal.findSequenceInTermcap(sequence);
if(cap !is null) {
//terminal.writeln("found in termcap " ~ cap);
return translateTermcapName(cap);
} }
} }
} }
@ -3067,7 +3111,21 @@ class LineGetter {
//bool reverseVideo; //bool reverseVideo;
/// Set this if you want a prompt to be drawn with the line. It does NOT support color in string. /// Set this if you want a prompt to be drawn with the line. It does NOT support color in string.
string prompt; @property void prompt(string p) {
this.prompt_ = p;
promptLength = 0;
foreach(dchar c; p)
promptLength++;
}
/// ditto
@property string prompt() {
return this.prompt_;
}
private string prompt_;
private int promptLength;
/// Turn on auto suggest if you want a greyed thing of what tab /// Turn on auto suggest if you want a greyed thing of what tab
/// would be able to fill in as you type. /// would be able to fill in as you type.
@ -3316,7 +3374,7 @@ class LineGetter {
} }
int availableLineLength() { int availableLineLength() {
return terminal.width - startOfLineX - cast(int) prompt.length - 1; return terminal.width - startOfLineX - promptLength - 1;
} }
private int lastDrawLength = 0; private int lastDrawLength = 0;
@ -3340,7 +3398,7 @@ class LineGetter {
auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition; auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition;
auto cursorPositionToDrawY = 0; auto cursorPositionToDrawY = 0;
int written = cast(int) prompt.length; int written = promptLength;
void specialChar(char c) { void specialChar(char c) {
terminal.color(regularForeground, specialCharBackground); terminal.color(regularForeground, specialCharBackground);
@ -3395,14 +3453,14 @@ class LineGetter {
} }
} }
// FIXME: graphemes and utf-8 on suggestion/prompt // FIXME: graphemes
if(written < lastDrawLength) if(written < lastDrawLength)
foreach(i; written .. lastDrawLength) foreach(i; written .. lastDrawLength)
terminal.write(" "); terminal.write(" ");
lastDrawLength = written; lastDrawLength = written;
terminal.moveTo(startOfLineX + cursorPositionToDrawX + cast(int) prompt.length, startOfLineY + cursorPositionToDrawY); terminal.moveTo(startOfLineX + cursorPositionToDrawX + promptLength, startOfLineY + cursorPositionToDrawY);
} }
/// Starts getting a new line. Call workOnLine and finishGettingLine afterward. /// Starts getting a new line. Call workOnLine and finishGettingLine afterward.
@ -3436,13 +3494,21 @@ class LineGetter {
horizontalScrollPosition = cursorPosition; horizontalScrollPosition = cursorPosition;
horizontalScrollPosition -= terminal.width / 2; horizontalScrollPosition -= terminal.width / 2;
// align on a code point boundary // align on a code point boundary
while(horizontalScrollPosition > 0 && (line[horizontalScrollPosition] & 0x80)) aligned(horizontalScrollPosition, -1);
horizontalScrollPosition--;
if(horizontalScrollPosition < 0) if(horizontalScrollPosition < 0)
horizontalScrollPosition = 0; horizontalScrollPosition = 0;
} }
} }
private void aligned(ref int what, int direction) {
// whereas line is right now dchar[] no need for this
// at least until we go by grapheme...
/*
while(what > 0 && what < line.length && ((line[what] & 0b1100_0000) == 0b1000_0000))
what += direction;
*/
}
private void initializeWithSize(bool firstEver = false) { private void initializeWithSize(bool firstEver = false) {
auto x = startOfLineX; auto x = startOfLineX;
@ -3454,6 +3520,8 @@ class LineGetter {
} }
lastDrawLength = terminal.width; lastDrawLength = terminal.width;
version(Win32Console)
lastDrawLength -= 1; // I don't like this but Windows resizing is different anyway and it is liable to scroll if i go over..
redraw(); redraw();
} }
@ -3518,9 +3586,17 @@ class LineGetter {
} }
throw new Exception("Couldn't get cursor position to initialize get line " ~ to!string(len) ~ " " ~ to!string(errno)); throw new Exception("Couldn't get cursor position to initialize get line " ~ to!string(len) ~ " " ~ to!string(errno));
} }
regot:
auto got = buffer[0 .. len]; auto got = buffer[0 .. len];
if(got.length < 6) if(got.length < 6) {
throw new Exception("not enough cursor reply answer"); auto len2 = read(terminal.fdIn, &buffer[len], buffer.length - len);
if(len2 <= 0)
throw new Exception("not enough cursor reply answer");
else {
len += len2;
goto regot;
}
}
if(got[0] != '\033' || got[1] != '[' || got[$-1] != 'R') { if(got[0] != '\033' || got[1] != '[' || got[$-1] != 'R') {
retries--; retries--;
if(retries > 0) if(retries > 0)
@ -3637,9 +3713,12 @@ class LineGetter {
while(cursorPosition && line[cursorPosition - 1] != ' ') while(cursorPosition && line[cursorPosition - 1] != ' ')
cursorPosition--; cursorPosition--;
} }
aligned(cursorPosition, -1);
if(!multiLineMode) { if(!multiLineMode) {
if(cursorPosition < horizontalScrollPosition) if(cursorPosition < horizontalScrollPosition) {
horizontalScrollPosition--; horizontalScrollPosition--;
aligned(horizontalScrollPosition, -1);
}
} }
redraw(); redraw();
@ -3656,9 +3735,12 @@ class LineGetter {
if(cursorPosition > line.length) if(cursorPosition > line.length)
cursorPosition = cast(int) line.length; cursorPosition = cast(int) line.length;
} }
aligned(cursorPosition, 1);
if(!multiLineMode) { if(!multiLineMode) {
if(cursorPosition >= horizontalScrollPosition + availableLineLength()) if(cursorPosition >= horizontalScrollPosition + availableLineLength()) {
horizontalScrollPosition++; horizontalScrollPosition++;
aligned(horizontalScrollPosition, 1);
}
} }
redraw(); redraw();
@ -3705,12 +3787,8 @@ class LineGetter {
// shift+insert = request paste // shift+insert = request paste
// ctrl+insert = request copy. but that needs a selection // ctrl+insert = request copy. but that needs a selection
// those work on Windows!!!!o // those work on Windows!!!! and many linux TEs too.
// but if it does make it here, we'll attempt it at this level
/*
change cursor capabilitiy
*/
} else if(ev.modifierState & ModifierState.control) { } else if(ev.modifierState & ModifierState.control) {
// copy // copy
} else { } else {
@ -3758,8 +3836,7 @@ class LineGetter {
if(me.eventType == MouseEvent.Type.Pressed) { if(me.eventType == MouseEvent.Type.Pressed) {
if(me.buttons & MouseEvent.Button.Left) { if(me.buttons & MouseEvent.Button.Left) {
if(me.y == startOfLineY) { if(me.y == startOfLineY) {
// FIXME: prompt.length should be graphemes or at least code poitns int p = me.x - startOfLineX - promptLength + horizontalScrollPosition;
int p = me.x - startOfLineX - cast(int) prompt.length + horizontalScrollPosition;
if(p >= 0 && p < line.length) { if(p >= 0 && p < line.length) {
justHitTab = false; justHitTab = false;
cursorPosition = p; cursorPosition = p;