diff --git a/terminal.d b/terminal.d index d995bbb..60cb52f 100644 --- a/terminal.d +++ b/terminal.d @@ -1272,9 +1272,7 @@ struct Terminal { readTermcap(); if(type == ConsoleOutputType.cellular) { - doTermcap("ti"); - clear(); - moveTo(0, 0, ForceOption.alwaysSend); // we need to know where the cursor is for some features to work, and moving it is easier than querying it + goCellular(); } if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) { @@ -1283,12 +1281,80 @@ struct Terminal { } - // EXPERIMENTAL do not use yet - Terminal alternateScreen() { - assert(this.type != ConsoleOutputType.cellular); + private void goCellular() { + version(Win32Console) { + hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null); + if(hConsole == INVALID_HANDLE_VALUE) { + import std.conv; + throw new Exception(to!string(GetLastError())); + } - this.flush(); - return Terminal(ConsoleOutputType.cellular); + 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/ms683193%28v=vs.85%29.aspx + */ + COORD size; + /* + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(hConsole, &sbi); + size.X = cast(short) GetSystemMetrics(SM_CXMIN); + size.Y = cast(short) GetSystemMetrics(SM_CYMIN); + */ + + // FIXME: this sucks, maybe i should just revert it. but there shouldn't be scrollbars in cellular mode + //size.X = 80; + //size.Y = 24; + //SetConsoleScreenBufferSize(hConsole, size); + + GetConsoleCursorInfo(hConsole, &originalCursorInfo); + + clear(); + } else { + doTermcap("ti"); + clear(); + moveTo(0, 0, ForceOption.alwaysSend); // we need to know where the cursor is for some features to work, and moving it is easier than querying it + } + } + + private void goLinear() { + version(Win32Console) { + auto stdo = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleActiveScreenBuffer(stdo); + if(hConsole !is stdo) + CloseHandle(hConsole); + + hConsole = stdo; + } else { + doTermcap("te"); + } + } + + private ConsoleOutputType originalType; + private bool typeChanged; + + // EXPERIMENTAL do not use yet + /++ + It is not valid to call this if you constructed with minimalProcessing. + +/ + void enableAlternateScreen(bool active) { + assert(type != ConsoleOutputType.minimalProcessing); + + if(active) { + if(type == ConsoleOutputType.cellular) + return; // already set + + flush(); + goCellular(); + type = ConsoleOutputType.cellular; + } else { + if(type == ConsoleOutputType.linear) + return; // already set + + flush(); + goLinear(); + type = ConsoleOutputType.linear; + } } version(Windows) { @@ -1306,33 +1372,7 @@ struct Terminal { initializeVt(); } else { if(type == ConsoleOutputType.cellular) { - hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null); - if(hConsole == INVALID_HANDLE_VALUE) { - import std.conv; - throw new Exception(to!string(GetLastError())); - } - - 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/ms683193%28v=vs.85%29.aspx - */ - COORD size; - /* - CONSOLE_SCREEN_BUFFER_INFO sbi; - GetConsoleScreenBufferInfo(hConsole, &sbi); - size.X = cast(short) GetSystemMetrics(SM_CXMIN); - size.Y = cast(short) GetSystemMetrics(SM_CYMIN); - */ - - // FIXME: this sucks, maybe i should just revert it. but there shouldn't be scrollbars in cellular mode - //size.X = 80; - //size.Y = 24; - //SetConsoleScreenBufferSize(hConsole, size); - - GetConsoleCursorInfo(hConsole, &originalCursorInfo); - - clear(); + goCellular(); } else { hConsole = GetStdHandle(STD_OUTPUT_HANDLE); } @@ -1384,7 +1424,7 @@ struct Terminal { if(UseVtSequences) { if(type == ConsoleOutputType.cellular) { - doTermcap("te"); + goLinear(); } version(TerminalDirectToEmulator) { if(usingDirectEmulator) { @@ -1425,10 +1465,7 @@ struct Terminal { SetConsoleOutputCP(oldCp); SetConsoleCP(oldCpIn); - auto stdo = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleActiveScreenBuffer(stdo); - if(hConsole !is stdo) - CloseHandle(hConsole); + goLinear(); } version(TerminalDirectToEmulator) @@ -5285,7 +5322,40 @@ class LineGetter { } bool insertMode = true; - bool multiLineMode = false; + + private ConsoleOutputType original = cast(ConsoleOutputType) -1; + private bool multiLineModeOn = false; + private int startOfLineXOriginal; + private int startOfLineYOriginal; + void multiLineMode(bool on) { + if(original == -1) { + original = terminal.type; + startOfLineXOriginal = startOfLineX; + startOfLineYOriginal = startOfLineY; + } + + if(on) { + terminal.enableAlternateScreen = true; + startOfLineX = 0; + startOfLineY = 0; + } + else if(original == ConsoleOutputType.linear) { + terminal.enableAlternateScreen = false; + } + + if(!on) { + startOfLineX = startOfLineXOriginal; + startOfLineY = startOfLineYOriginal; + } + + multiLineModeOn = on; + } + bool multiLineMode() { return multiLineModeOn; } + + void toggleMultiLineMode() { + multiLineMode = !multiLineModeOn; + redraw(); + } private dchar[] line; private int cursorPosition = 0; @@ -5558,6 +5628,12 @@ class LineGetter { written++; lineLength--; + + if(lg.multiLineMode) { + if(ch == '\n') { + lineLength = lg.terminal.width; + } + } } void drawContent(T)(T towrite, int highlightBegin = 0, int highlightEnd = 0, bool inverted = false, int lineidx = -1) { @@ -5571,8 +5647,14 @@ class LineGetter { } foreach(idx, dchar ch; towrite) { - if(lineLength <= 0) - break; + if(lineLength <= 0) { + if(lg.multiLineMode) { + if(ch == '\n') + lineLength = lg.terminal.width; + continue; + } else + break; + } static if(is(T == dchar[])) { if(lineidx != -1 && colorChars == 0) { @@ -5587,7 +5669,7 @@ class LineGetter { } switch(ch) { - case '\n': specialChar('n'); break; + case '\n': lg.multiLineMode ? regularChar('\n') : specialChar('n'); break; case '\r': specialChar('r'); break; case '\a': specialChar('a'); break; case '\t': specialChar('t'); break; @@ -5666,18 +5748,22 @@ class LineGetter { if(!cdi.populated) return; - if(UseVtSequences && !_drawWidthMax) { - terminal.writeStringRaw("\033[K"); + if(!multiLineMode) { + if(UseVtSequences && !_drawWidthMax) { + terminal.writeStringRaw("\033[K"); + } else { + // FIXME: graphemes + if(cdi.written + promptLength < lastDrawLength) + foreach(i; cdi.written + promptLength .. lastDrawLength) + terminal.write(" "); + lastDrawLength = cdi.written; + } + // if echoChar is null then we don't want to reflect the position at all + terminal.moveTo(startOfLineX + ((echoChar == 0) ? 0 : cdi.cursorPositionToDrawX) + promptLength, startOfLineY + cdi.cursorPositionToDrawY); } else { - // FIXME: graphemes - if(cdi.written + promptLength < lastDrawLength) - foreach(i; cdi.written + promptLength .. lastDrawLength) - terminal.write(" "); - lastDrawLength = cdi.written; + if(echoChar != 0) + terminal.moveTo(cdi.cursorPositionToDrawX, cdi.cursorPositionToDrawY); } - - // if echoChar is null then we don't want to reflect the position at all - terminal.moveTo(startOfLineX + ((echoChar == 0) ? 0 : cdi.cursorPositionToDrawX) + promptLength, startOfLineY + cdi.cursorPositionToDrawY); endRedraw(); // make sure the cursor is turned back on } @@ -5714,17 +5800,26 @@ class LineGetter { } terminal.moveTo(startOfLineX, startOfLineY); + if(multiLineMode) + terminal.clear(); + Drawer drawer = Drawer(this); drawer.lineLength = availableLineLength(); if(drawer.lineLength < 0) throw new Exception("too narrow terminal to draw"); - terminal.color(promptColor, background); - terminal.write(prompt); - terminal.color(regularForeground, background); + if(!multiLineMode) { + terminal.color(promptColor, background); + terminal.write(prompt); + terminal.color(regularForeground, background); + } auto towrite = line[horizontalScrollPosition .. $]; + if(multiLineMode) { + towrite = line[]; + horizontalScrollPosition = 0; // FIXME + } auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition; auto cursorPositionToDrawY = 0; @@ -5762,8 +5857,27 @@ class LineGetter { CoreRedrawInfo cri; cri.populated = true; cri.written = drawer.written; - cri.cursorPositionToDrawX = cursorPositionToDrawX; - cri.cursorPositionToDrawY = cursorPositionToDrawY; + if(multiLineMode) { + cursorPositionToDrawX = 0; + cursorPositionToDrawY = 0; + // would be better if it did this in the same drawing pass... + foreach(idx, dchar ch; line) { + if(idx == cursorPosition) + break; + if(ch == '\n') { + cursorPositionToDrawX = 0; + cursorPositionToDrawY++; + } else { + cursorPositionToDrawX++; + } + } + + cri.cursorPositionToDrawX = cursorPositionToDrawX; + cri.cursorPositionToDrawY = cursorPositionToDrawY; + } else { + cri.cursorPositionToDrawX = cursorPositionToDrawX; + cri.cursorPositionToDrawY = cursorPositionToDrawY; + } return cri; } @@ -6064,6 +6178,88 @@ class LineGetter { int selectionEnd = -1; } + void backwardToNewline() { + while(cursorPosition && line[cursorPosition - 1] != '\n') + cursorPosition--; + phantomCursorX = 0; + } + + void forwardToNewLine() { + while(cursorPosition < line.length && line[cursorPosition] != '\n') + cursorPosition++; + } + + private int phantomCursorX; + + void lineBackward() { + int count; + while(cursorPosition && line[cursorPosition - 1] != '\n') { + cursorPosition--; + count++; + } + if(count > phantomCursorX) + phantomCursorX = count; + + if(cursorPosition == 0) + return; + cursorPosition--; + + while(cursorPosition && line[cursorPosition - 1] != '\n') { + cursorPosition--; + } + + count = phantomCursorX; + while(count) { + if(cursorPosition == line.length) + break; + if(line[cursorPosition] == '\n') + break; + cursorPosition++; + count--; + } + } + + void lineForward() { + int count; + + // see where we are in the current line + auto beginPos = cursorPosition; + while(beginPos && line[beginPos - 1] != '\n') { + beginPos--; + count++; + } + + if(count > phantomCursorX) + phantomCursorX = count; + + // get to the next line + while(cursorPosition < line.length && line[cursorPosition] != '\n') { + cursorPosition++; + } + if(cursorPosition == line.length) + return; + cursorPosition++; + + // get to the same spot in this same line + count = phantomCursorX; + while(count) { + if(cursorPosition == line.length) + break; + if(line[cursorPosition] == '\n') + break; + cursorPosition++; + count--; + } + } + + void pageBackward() { + + } + + void pageForward() { + + } + /++ for integrating into another event loop you can pass individual events to this and @@ -6219,13 +6415,18 @@ class LineGetter { redraw(); } + phantomCursorX = 0; break; case KeyboardEvent.Key.escape: justHitTab = justKilled = false; - cursorPosition = 0; - horizontalScrollPosition = 0; - line = line[0 .. 0]; - line.assumeSafeAppend(); + if(multiLineMode) + multiLineMode = false; + else { + cursorPosition = 0; + horizontalScrollPosition = 0; + line = line[0 .. 0]; + line.assumeSafeAppend(); + } redraw(); break; case KeyboardEvent.Key.F1: @@ -6234,6 +6435,12 @@ class LineGetter { break; case KeyboardEvent.Key.F2: justHitTab = justKilled = false; + + if(ev.modifierState & ModifierState.control) { + toggleMultiLineMode(); + break; + } + line[] &= cast(dchar) ~PRIVATE_BITS_MASK; auto got = editLineInEditor(line, cursorPosition); if(got !is null) { @@ -6363,6 +6570,7 @@ class LineGetter { break; case KeyboardEvent.Key.LeftArrow: justHitTab = justKilled = false; + phantomCursorX = 0; /* if(ev.modifierState & ModifierState.shift) @@ -6395,7 +6603,10 @@ class LineGetter { goto default; case KeyboardEvent.Key.UpArrow: justHitTab = justKilled = false; - loadFromHistory(currentHistoryViewPosition + 1); + if(multiLineMode) + lineBackward(); + else + loadFromHistory(currentHistoryViewPosition + 1); redraw(); break; case 'n', 14: @@ -6404,17 +6615,26 @@ class LineGetter { goto default; case KeyboardEvent.Key.DownArrow: justHitTab = justKilled = false; - loadFromHistory(currentHistoryViewPosition - 1); + if(multiLineMode) + lineForward(); + else + loadFromHistory(currentHistoryViewPosition - 1); redraw(); break; case KeyboardEvent.Key.PageUp: justHitTab = justKilled = false; - loadFromHistory(cast(int) history.length); + if(multiLineMode) + pageBackward(); + else + loadFromHistory(cast(int) history.length); redraw(); break; case KeyboardEvent.Key.PageDown: justHitTab = justKilled = false; - loadFromHistory(0); + if(multiLineMode) + pageForward(); + else + loadFromHistory(0); redraw(); break; case 'a', 1: // this one conflicts with Windows-style select all... @@ -6429,7 +6649,11 @@ class LineGetter { goto case; case KeyboardEvent.Key.Home: justHitTab = justKilled = false; - cursorPosition = 0; + if(multiLineMode) { + backwardToNewline(); + } else { + cursorPosition = 0; + } horizontalScrollPosition = 0; redraw(); break; @@ -6439,8 +6663,12 @@ class LineGetter { goto case; case KeyboardEvent.Key.End: justHitTab = justKilled = false; - cursorPosition = cast(int) line.length; - scrollToEnd(); + if(multiLineMode) { + forwardToNewLine(); + } else { + cursorPosition = cast(int) line.length; + scrollToEnd(); + } redraw(); break; case 'v', 22: @@ -6679,6 +6907,10 @@ class LineGetter { string finishGettingLine() { import std.conv; + + if(multiLineMode) + multiLineMode = false; + line[] &= cast(dchar) ~PRIVATE_BITS_MASK; auto f = to!string(line);