Added scroll up/down for windows

This commit is contained in:
nathan 2016-05-28 21:44:59 +02:00
parent aca4c493b0
commit 6f367aec7e
3 changed files with 88 additions and 22 deletions

View File

@ -31,14 +31,22 @@
package dorkbox.console.output;
import static dorkbox.console.util.windows.Kernel32.ASSERT;
import static dorkbox.console.util.windows.Kernel32.FillConsoleOutputAttribute;
import static dorkbox.console.util.windows.Kernel32.FillConsoleOutputCharacterW;
import static dorkbox.console.util.windows.Kernel32.GetConsoleScreenBufferInfo;
import static dorkbox.console.util.windows.Kernel32.SetConsoleCursorPosition;
import static dorkbox.console.util.windows.Kernel32.SetConsoleTextAttribute;
import java.io.IOException;
import java.io.OutputStream;
import com.sun.jna.ptr.IntByReference;
import dorkbox.console.util.windows.CONSOLE_SCREEN_BUFFER_INFO;
import dorkbox.console.util.windows.COORD;
import dorkbox.console.util.windows.HANDLE;
import dorkbox.console.util.windows.Kernel32;
import dorkbox.console.util.windows.SMALL_RECT;
/**
* A Windows ANSI escape processor, uses JNA direct-mapping to access native platform API's to change the console attributes.
@ -77,7 +85,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
}
private final HANDLE console;
private final CONSOLE_SCREEN_BUFFER_INFO originalInfo = new CONSOLE_SCREEN_BUFFER_INFO();
private CONSOLE_SCREEN_BUFFER_INFO originalInfo = new CONSOLE_SCREEN_BUFFER_INFO();
private volatile CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
private volatile boolean negative;
@ -101,13 +109,13 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
}
out.flush();
ASSERT(Kernel32.GetConsoleScreenBufferInfo(console, originalInfo), "Could not get the screen info");
ASSERT(GetConsoleScreenBufferInfo(console, originalInfo), "Could not get the screen info");
}
private
void getConsoleInfo() throws IOException {
out.flush();
ASSERT(Kernel32.GetConsoleScreenBufferInfo(console, info), "Could not get the screen info:");
ASSERT(GetConsoleScreenBufferInfo(console, info), "Could not get the screen info:");
}
private
@ -124,12 +132,12 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
attributes = (short) (attributes & 0xFF00 | fg | bg);
}
ASSERT(Kernel32.SetConsoleTextAttribute(console, attributes), "Could not set text attributes");
ASSERT(SetConsoleTextAttribute(console, attributes), "Could not set text attributes");
}
private
void applyCursorPosition() throws IOException {
ASSERT(Kernel32.SetConsoleCursorPosition(console, info.cursorPosition.asValue()), "Could not set cursor position");
ASSERT(SetConsoleCursorPosition(console, info.cursorPosition.asValue()), "Could not set cursor position");
}
@Override
@ -164,8 +172,8 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
topLeft.y = info.window.top;
int screenLength = info.window.height() * info.size.x;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, screenLength, topLeft.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', screenLength, topLeft.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, screenLength, topLeft.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', screenLength, topLeft.asValue(), written), "Could not fill console");
break;
case ERASE_TO_BEGINNING:
COORD topLeft2 = new COORD();
@ -173,14 +181,14 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
topLeft2.y = info.window.top;
int lengthToCursor = (info.cursorPosition.y - info.window.top) * info.size.x + info.cursorPosition.x;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToCursor, topLeft2.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', lengthToCursor, topLeft2.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToCursor, topLeft2.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', lengthToCursor, topLeft2.asValue(), written), "Could not fill console");
break;
case ERASE_TO_END:
int lengthToEnd = (info.window.bottom - info.cursorPosition.y) * info.size.x + info.size.x - info.cursorPosition.x;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToEnd, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToEnd, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition.asValue(), written), "Could not fill console");
}
}
@ -194,21 +202,21 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
COORD currentRow = info.cursorPosition.asValue();
currentRow.x = (short) 0;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, info.size.x, currentRow.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', info.size.x, currentRow.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, info.size.x, currentRow.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', info.size.x, currentRow.asValue(), written), "Could not fill console");
break;
case ERASE_TO_BEGINNING:
COORD leftColCurrRow2 = info.cursorPosition.asValue();
leftColCurrRow2.x = (short) 0;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, info.cursorPosition.x, leftColCurrRow2.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', info.cursorPosition.x, leftColCurrRow2.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, info.cursorPosition.x, leftColCurrRow2.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', info.cursorPosition.x, leftColCurrRow2.asValue(), written), "Could not fill console");
break;
case ERASE_TO_END:
int lengthToLastCol = info.size.x - info.cursorPosition.x;
ASSERT(Kernel32.FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToLastCol, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(Kernel32.FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputAttribute(console, originalInfo.attributes, lengthToLastCol, info.cursorPosition.asValue(), written), "Could not fill console");
ASSERT(FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition.asValue(), written), "Could not fill console");
}
}
@ -299,20 +307,30 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
@Override
protected
void processScrollDown(final int optionInt) throws IOException {
void processScrollDown(final int count) throws IOException {
scroll((short) -count);
}
@Override
protected
void processScrollUp(final int optionInt) throws IOException {
void processScrollUp(final int count) throws IOException {
scroll((short) count);
}
protected
void processCursorUpLine(final int count) throws IOException {
getConsoleInfo();
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count);
info.cursorPosition.x = 0;
applyCursorPosition();
}
protected
void processCursorDownLine(final int count) throws IOException {
getConsoleInfo();
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y + count);
info.cursorPosition.x = 0;
applyCursorPosition();
}
@Override
@ -364,6 +382,45 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
applyCursorPosition();
}
/**
* @param rowsToScroll negative to go down, positive to go up.
*
* Scroll up and new lines are added at the bottom, scroll down and new lines are added at the
* top (per the definition).
*
* Windows doesn't EXACTLY do this, since it will use whatever content is still on the buffer
* and show THAT instead of blank lines. If the content is moved enough so that it runs OFF the
* buffer, blank lines will be shown.
*/
private void scroll(short rowsToScroll) throws IOException {
if (rowsToScroll == 0) {
return;
}
// Get the current screen buffer window position.
getConsoleInfo();
SMALL_RECT.ByReference scrollRect = new SMALL_RECT.ByReference();
COORD.ByValue coordDest = new COORD.ByValue();
// the content that will be scrolled
scrollRect.top = (short) (0);
scrollRect.bottom = (short) (Short.MAX_VALUE);
scrollRect.left = 0;
scrollRect.right = (short) (info.size.x - 1);
// The destination for the scroll rectangle is xxx row up/down.
coordDest.x = 0;
coordDest.y = (short) (-rowsToScroll);
// fill the space with whatever color was already there with spaces
IntByReference attribs = new IntByReference();
attribs.setValue(info.attributes);
// The clipping rectangle is the same as the scrolling rectangle, so we pass NULL
ASSERT(Kernel32.ScrollConsoleScreenBufferW(console, scrollRect, null, coordDest, attribs), "Could not scroll console");
}
@Override
public void close() throws IOException {
super.close();

View File

@ -85,7 +85,7 @@ public class Kernel32 {
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx
*/
public static native
int FormatMessageA(int flags, Pointer source, int messageId, int languageId, byte[] buffer, int size, long[] args);
int FormatMessageW(int flags, Pointer source, int messageId, int languageId, byte[] buffer, int size, long[] args);
/**
@ -95,11 +95,19 @@ public class Kernel32 {
int GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo);
/**
* https://msdn.microsoft.com/en-us/library/ms686025%28VS.85%29.aspx
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx
*/
public static native
int SetConsoleCursorPosition(HANDLE consoleOutput, COORD.ByValue cursorPosition);
/**
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms685107(v=vs.85).aspx
*/
public static native
int ScrollConsoleScreenBufferW(HANDLE consoleOutput, SMALL_RECT.ByReference scrollRect, SMALL_RECT.ByReference clipRect, COORD.ByValue destinationOrigin, IntByReference fillAttributes);
/**
* https://msdn.microsoft.com/en-us/library/ms682662%28VS.85%29.aspx
@ -148,7 +156,7 @@ public class Kernel32 {
int errorCode = Native.getLastError();
int bufferSize = 160;
byte data[] = new byte[bufferSize];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, Pointer.NULL, errorCode, 0, data, bufferSize, null);
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, Pointer.NULL, errorCode, 0, data, bufferSize, null);
return new String(data);
}

View File

@ -24,6 +24,7 @@ import com.sun.jna.Structure;
* https://msdn.microsoft.com/en-us/library/ms686311%28VS.85%29.aspx
*/
public class SMALL_RECT extends Structure {
static public class ByReference extends SMALL_RECT implements Structure.ByReference { }
public short left;
public short top;