mirror of https://github.com/adamdruppe/arsd.git
win32 console as a runtime option
This commit is contained in:
parent
405bd59c9d
commit
65d5983303
427
terminal.d
427
terminal.d
|
@ -2,6 +2,9 @@
|
||||||
// for VT on Windows P s = 1 8 → Report the size of the text area in characters as CSI 8 ; height ; width t
|
// 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
|
// could be used to have the TE volunteer the size
|
||||||
|
|
||||||
|
// FIXME: have some flags or formal api to set color to vtsequences even on pipe etc on demand.
|
||||||
|
|
||||||
|
|
||||||
// FIXME: the resume signal needs to be handled to set the terminal back in proper mode.
|
// FIXME: the resume signal needs to be handled to set the terminal back in proper mode.
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -314,6 +317,8 @@ version(Posix) {
|
||||||
|
|
||||||
version(TerminalDirectToEmulator) {
|
version(TerminalDirectToEmulator) {
|
||||||
version=VtEscapeCodes;
|
version=VtEscapeCodes;
|
||||||
|
version(Windows)
|
||||||
|
version=Win32Console;
|
||||||
} else version(Windows) {
|
} else version(Windows) {
|
||||||
version(VtEscapeCodes) {} // cool
|
version(VtEscapeCodes) {} // cool
|
||||||
version=Win32Console;
|
version=Win32Console;
|
||||||
|
@ -328,11 +333,7 @@ version(Windows)
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
private {
|
__gshared bool UseWin32Console = true;
|
||||||
enum RED_BIT = 4;
|
|
||||||
enum GREEN_BIT = 2;
|
|
||||||
enum BLUE_BIT = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma(lib, "user32");
|
pragma(lib, "user32");
|
||||||
}
|
}
|
||||||
|
@ -353,20 +354,7 @@ version(Posix) {
|
||||||
|
|
||||||
version(VtEscapeCodes) {
|
version(VtEscapeCodes) {
|
||||||
|
|
||||||
enum UseVtSequences = true;
|
__gshared bool UseVtSequences = true;
|
||||||
|
|
||||||
version(TerminalDirectToEmulator) {
|
|
||||||
private {
|
|
||||||
enum RED_BIT = 1;
|
|
||||||
enum GREEN_BIT = 2;
|
|
||||||
enum BLUE_BIT = 4;
|
|
||||||
}
|
|
||||||
} else version(Windows) {} else
|
|
||||||
private {
|
|
||||||
enum RED_BIT = 1;
|
|
||||||
enum GREEN_BIT = 2;
|
|
||||||
enum BLUE_BIT = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct winsize {
|
struct winsize {
|
||||||
ushort ws_row;
|
ushort ws_row;
|
||||||
|
@ -533,10 +521,10 @@ enum Bright = 0x08;
|
||||||
/// See also: [Bright]
|
/// See also: [Bright]
|
||||||
enum Color : ushort {
|
enum Color : ushort {
|
||||||
black = 0, /// .
|
black = 0, /// .
|
||||||
red = RED_BIT, /// .
|
red = 1, /// .
|
||||||
green = GREEN_BIT, /// .
|
green = 2, /// .
|
||||||
yellow = red | green, /// .
|
yellow = red | green, /// .
|
||||||
blue = BLUE_BIT, /// .
|
blue = 4, /// .
|
||||||
magenta = red | blue, /// .
|
magenta = red | blue, /// .
|
||||||
cyan = blue | green, /// .
|
cyan = blue | green, /// .
|
||||||
white = red | green | blue, /// .
|
white = red | green | blue, /// .
|
||||||
|
@ -621,20 +609,7 @@ struct Terminal {
|
||||||
} else {
|
} else {
|
||||||
if(what != currentCursor_ || force == ForceOption.alwaysSend) {
|
if(what != currentCursor_ || force == ForceOption.alwaysSend) {
|
||||||
currentCursor_ = what;
|
currentCursor_ = what;
|
||||||
version(Win32Console) {
|
if(UseVtSequences) {
|
||||||
final switch(what) {
|
|
||||||
case TerminalCursor.DEFAULT:
|
|
||||||
SetConsoleCursorInfo(hConsole, &originalCursorInfo);
|
|
||||||
break;
|
|
||||||
case TerminalCursor.insert:
|
|
||||||
case TerminalCursor.block:
|
|
||||||
CONSOLE_CURSOR_INFO info;
|
|
||||||
GetConsoleCursorInfo(hConsole, &info);
|
|
||||||
info.dwSize = what == TerminalCursor.insert ? 1 : 100;
|
|
||||||
SetConsoleCursorInfo(hConsole, &info);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final switch(what) {
|
final switch(what) {
|
||||||
case TerminalCursor.DEFAULT:
|
case TerminalCursor.DEFAULT:
|
||||||
if(terminalInFamily("linux"))
|
if(terminalInFamily("linux"))
|
||||||
|
@ -657,6 +632,19 @@ struct Terminal {
|
||||||
writeStringRaw("\033[2 q");
|
writeStringRaw("\033[2 q");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
|
final switch(what) {
|
||||||
|
case TerminalCursor.DEFAULT:
|
||||||
|
SetConsoleCursorInfo(hConsole, &originalCursorInfo);
|
||||||
|
break;
|
||||||
|
case TerminalCursor.insert:
|
||||||
|
case TerminalCursor.block:
|
||||||
|
CONSOLE_CURSOR_INFO info;
|
||||||
|
GetConsoleCursorInfo(hConsole, &info);
|
||||||
|
info.dwSize = what == TerminalCursor.insert ? 1 : 100;
|
||||||
|
SetConsoleCursorInfo(hConsole, &info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -727,12 +715,20 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool terminalInFamily(string[] terms...) {
|
bool terminalInFamily(string[] terms...) {
|
||||||
|
version(Win32Console) if(UseWin32Console)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// we're not writing to a terminal at all!
|
||||||
|
if(!stdoutIsTerminal || !stdinIsTerminal)
|
||||||
|
return false;
|
||||||
|
|
||||||
import std.process;
|
import std.process;
|
||||||
import std.string;
|
import std.string;
|
||||||
version(TerminalDirectToEmulator)
|
version(TerminalDirectToEmulator)
|
||||||
auto term = "xterm";
|
auto term = "xterm";
|
||||||
else
|
else
|
||||||
auto term = environment.get("TERM");
|
auto term = environment.get("TERM");
|
||||||
|
|
||||||
foreach(t; terms)
|
foreach(t; terms)
|
||||||
if(indexOf(term, t) != -1)
|
if(indexOf(term, t) != -1)
|
||||||
return true;
|
return true;
|
||||||
|
@ -745,12 +741,15 @@ struct Terminal {
|
||||||
// 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!)
|
||||||
bool isMacTerminal() {
|
bool isMacTerminal() {
|
||||||
|
return false;
|
||||||
|
/+
|
||||||
// it gives 1,2 in getTerminalCapabilities...
|
// it gives 1,2 in getTerminalCapabilities...
|
||||||
// FIXME
|
// FIXME
|
||||||
import std.process;
|
import std.process;
|
||||||
import std.string;
|
import std.string;
|
||||||
auto term = environment.get("TERM");
|
auto term = environment.get("TERM");
|
||||||
return term == "xterm-256color";
|
return term == "xterm-256color";
|
||||||
|
+/
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
bool isMacTerminal() { return false; }
|
bool isMacTerminal() { return false; }
|
||||||
|
@ -904,6 +903,9 @@ struct Terminal {
|
||||||
|
|
||||||
// Looks up a termcap item and tries to execute it. Returns false on failure
|
// Looks up a termcap item and tries to execute it. Returns false on failure
|
||||||
bool doTermcap(T...)(string key, T t) {
|
bool doTermcap(T...)(string key, T t) {
|
||||||
|
if(!stdoutIsTerminal)
|
||||||
|
return false;
|
||||||
|
|
||||||
import std.conv;
|
import std.conv;
|
||||||
auto fs = getTermcap(key);
|
auto fs = getTermcap(key);
|
||||||
if(fs is null)
|
if(fs is null)
|
||||||
|
@ -1060,7 +1062,9 @@ struct Terminal {
|
||||||
+/
|
+/
|
||||||
bool saveCursorPosition()
|
bool saveCursorPosition()
|
||||||
{
|
{
|
||||||
version (Win32Console)
|
if(UseVtSequences)
|
||||||
|
return doTermcap("sc");
|
||||||
|
else version (Win32Console) if(UseWin32Console)
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
|
@ -1074,14 +1078,16 @@ struct Terminal {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
assert(0);
|
||||||
return doTermcap("sc");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
bool restoreCursorPosition()
|
bool restoreCursorPosition()
|
||||||
{
|
{
|
||||||
version (Win32Console)
|
if(UseVtSequences)
|
||||||
|
// FIXME: needs to update cursorX and cursorY
|
||||||
|
return doTermcap("rc");
|
||||||
|
else version (Win32Console) if(UseWin32Console)
|
||||||
{
|
{
|
||||||
if (cursorPositionStack.length > 0)
|
if (cursorPositionStack.length > 0)
|
||||||
{
|
{
|
||||||
|
@ -1093,11 +1099,7 @@ struct Terminal {
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
assert(0);
|
||||||
{
|
|
||||||
// FIXME: needs to update cursorX and cursorY
|
|
||||||
return doTermcap("rc");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported)
|
// only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported)
|
||||||
|
@ -1288,18 +1290,29 @@ struct Terminal {
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
if(!integratedTerminalEmulatorConfiguration.fallbackToDegradedTerminal)
|
if(!integratedTerminalEmulatorConfiguration.fallbackToDegradedTerminal)
|
||||||
throw e;
|
throw e;
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.usingDirectEmulator = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!usingDirectEmulator) {
|
if(integratedTerminalEmulatorConfiguration.preferDegradedTerminal)
|
||||||
|
this.usingDirectEmulator = false;
|
||||||
|
|
||||||
|
// FIXME is this really correct logic?
|
||||||
|
if(!stdinIsTerminal || !stdoutIsTerminal)
|
||||||
|
this.usingDirectEmulator = false;
|
||||||
|
|
||||||
|
if(usingDirectEmulator) {
|
||||||
|
version(Win32Console)
|
||||||
|
UseWin32Console = false;
|
||||||
|
UseVtSequences = true;
|
||||||
|
} else {
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
posixInitialize(type, 0, 1, null);
|
posixInitialize(type, 0, 1, null);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else version(Win32Console) {
|
||||||
throw new Exception("Total wtf - are you on a windows system without a gui?!?");
|
UseVtSequences = false;
|
||||||
|
UseWin32Console = true; // this might be set back to false by windowsInitialize but that's ok
|
||||||
|
windowsInitialize(type);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
@ -1377,8 +1390,46 @@ struct Terminal {
|
||||||
this(ConsoleOutputType type, int fdIn = 0, int fdOut = 1, int[] delegate() getSizeOverride = null) {
|
this(ConsoleOutputType type, int fdIn = 0, int fdOut = 1, int[] delegate() getSizeOverride = null) {
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
posixInitialize(type, fdIn, fdOut, getSizeOverride);
|
posixInitialize(type, fdIn, fdOut, getSizeOverride);
|
||||||
|
} else version(Win32Console)
|
||||||
|
this(ConsoleOutputType type) {
|
||||||
|
windowsInitialize(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(Win32Console)
|
||||||
|
void windowsInitialize(ConsoleOutputType type) {
|
||||||
|
_initialized = true;
|
||||||
|
if(UseVtSequences) {
|
||||||
|
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
initializeVt();
|
||||||
|
} else {
|
||||||
|
if(type == ConsoleOutputType.cellular) {
|
||||||
|
goCellular();
|
||||||
|
} else {
|
||||||
|
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(GetConsoleScreenBufferInfo(hConsole, &originalSbi) != 0) {
|
||||||
|
defaultForegroundColor = win32ConsoleColorToArsdTerminalColor(originalSbi.wAttributes & 0x0f);
|
||||||
|
defaultBackgroundColor = win32ConsoleColorToArsdTerminalColor((originalSbi.wAttributes >> 4) & 0x0f);
|
||||||
|
} else {
|
||||||
|
// throw new Exception("not a user-interactive terminal");
|
||||||
|
UseWin32Console = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is unnecessary since I use the W versions of other functions
|
||||||
|
// and can cause weird font bugs, so I'm commenting unless some other
|
||||||
|
// need comes up.
|
||||||
|
/*
|
||||||
|
oldCp = GetConsoleOutputCP();
|
||||||
|
SetConsoleOutputCP(65001); // UTF-8
|
||||||
|
|
||||||
|
oldCpIn = GetConsoleCP();
|
||||||
|
SetConsoleCP(65001); // UTF-8
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
version(Posix)
|
version(Posix)
|
||||||
private void posixInitialize(ConsoleOutputType type, int fdIn = 0, int fdOut = 1, int[] delegate() getSizeOverride = null) {
|
private void posixInitialize(ConsoleOutputType type, int fdIn = 0, int fdOut = 1, int[] delegate() getSizeOverride = null) {
|
||||||
this.fdIn = fdIn;
|
this.fdIn = fdIn;
|
||||||
|
@ -1412,7 +1463,14 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goCellular() {
|
private void goCellular() {
|
||||||
version(Win32Console) {
|
if(!Terminal.stdoutIsTerminal)
|
||||||
|
throw new Exception("Cannot go to cellular mode with redirected output");
|
||||||
|
|
||||||
|
if(UseVtSequences) {
|
||||||
|
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
|
||||||
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
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) {
|
||||||
import std.conv;
|
import std.conv;
|
||||||
|
@ -1440,23 +1498,19 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
GetConsoleCursorInfo(hConsole, &originalCursorInfo);
|
GetConsoleCursorInfo(hConsole, &originalCursorInfo);
|
||||||
|
|
||||||
clear();
|
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() {
|
private void goLinear() {
|
||||||
version(Win32Console) {
|
if(UseVtSequences) {
|
||||||
|
doTermcap("te");
|
||||||
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
auto stdo = GetStdHandle(STD_OUTPUT_HANDLE);
|
auto stdo = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
SetConsoleActiveScreenBuffer(stdo);
|
SetConsoleActiveScreenBuffer(stdo);
|
||||||
if(hConsole !is stdo)
|
if(hConsole !is stdo)
|
||||||
CloseHandle(hConsole);
|
CloseHandle(hConsole);
|
||||||
|
|
||||||
hConsole = stdo;
|
hConsole = stdo;
|
||||||
} else {
|
|
||||||
doTermcap("te");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1492,44 +1546,11 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Win32Console)
|
|
||||||
/// ditto
|
|
||||||
this(ConsoleOutputType type) {
|
|
||||||
_initialized = true;
|
|
||||||
if(UseVtSequences) {
|
|
||||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
initializeVt();
|
|
||||||
} else {
|
|
||||||
if(type == ConsoleOutputType.cellular) {
|
|
||||||
goCellular();
|
|
||||||
} else {
|
|
||||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GetConsoleScreenBufferInfo(hConsole, &originalSbi) == 0)
|
|
||||||
throw new Exception("not a user-interactive terminal");
|
|
||||||
|
|
||||||
defaultForegroundColor = cast(Color) (originalSbi.wAttributes & 0x0f);
|
|
||||||
defaultBackgroundColor = cast(Color) ((originalSbi.wAttributes >> 4) & 0x0f);
|
|
||||||
|
|
||||||
// this is unnecessary since I use the W versions of other functions
|
|
||||||
// and can cause weird font bugs, so I'm commenting unless some other
|
|
||||||
// need comes up.
|
|
||||||
/*
|
|
||||||
oldCp = GetConsoleOutputCP();
|
|
||||||
SetConsoleOutputCP(65001); // UTF-8
|
|
||||||
|
|
||||||
oldCpIn = GetConsoleCP();
|
|
||||||
SetConsoleCP(65001); // UTF-8
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
private Color defaultBackgroundColor = Color.black;
|
private Color defaultBackgroundColor = Color.black;
|
||||||
private Color defaultForegroundColor = Color.white;
|
private Color defaultForegroundColor = Color.white;
|
||||||
UINT oldCp;
|
// UINT oldCp;
|
||||||
UINT oldCpIn;
|
// UINT oldCpIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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...
|
||||||
|
@ -1582,7 +1603,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(Win32Console) {
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
flush(); // make sure user data is all flushed before resetting
|
flush(); // make sure user data is all flushed before resetting
|
||||||
reset();
|
reset();
|
||||||
showCursor();
|
showCursor();
|
||||||
|
@ -1591,12 +1612,16 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
lineGetter.dispose();
|
lineGetter.dispose();
|
||||||
|
|
||||||
|
|
||||||
|
/+
|
||||||
SetConsoleOutputCP(oldCp);
|
SetConsoleOutputCP(oldCp);
|
||||||
SetConsoleCP(oldCpIn);
|
SetConsoleCP(oldCpIn);
|
||||||
|
+/
|
||||||
|
|
||||||
goLinear();
|
goLinear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flush();
|
||||||
|
|
||||||
version(TerminalDirectToEmulator)
|
version(TerminalDirectToEmulator)
|
||||||
if(usingDirectEmulator && guiThread !is null) {
|
if(usingDirectEmulator && guiThread !is null) {
|
||||||
guiThread.join();
|
guiThread.join();
|
||||||
|
@ -1647,15 +1672,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
_currentForegroundRGB = foreground;
|
_currentForegroundRGB = foreground;
|
||||||
_currentBackgroundRGB = background;
|
_currentBackgroundRGB = background;
|
||||||
|
|
||||||
version(Win32Console) {
|
if(UseVtSequences) {
|
||||||
flush();
|
|
||||||
ushort setTob = cast(ushort) approximate16Color(background);
|
|
||||||
ushort setTof = cast(ushort) approximate16Color(foreground);
|
|
||||||
SetConsoleTextAttribute(
|
|
||||||
hConsole,
|
|
||||||
cast(ushort)((setTob << 4) | setTof));
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// FIXME: if the terminal reliably does support 24 bit color, use it
|
// FIXME: if the terminal reliably does support 24 bit color, use it
|
||||||
// instead of the round off. But idk how to detect that yet...
|
// instead of the round off. But idk how to detect that yet...
|
||||||
|
|
||||||
|
@ -1694,13 +1711,55 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
+/
|
+/
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} version(Win32Console) if(UseWin32Console) {
|
||||||
|
flush();
|
||||||
|
ushort setTob = arsdTerminalColorToWin32ConsoleColor(approximate16Color(background));
|
||||||
|
ushort setTof = arsdTerminalColorToWin32ConsoleColor(approximate16Color(foreground));
|
||||||
|
SetConsoleTextAttribute(
|
||||||
|
hConsole,
|
||||||
|
cast(ushort)((setTob << 4) | setTof));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the current color. See enum [Color] for the values and note colors can be [arsd.docs.general_concepts#bitmasks|bitwise-or] combined with [Bright].
|
/// Changes the current color. See enum [Color] for the values and note colors can be [arsd.docs.general_concepts#bitmasks|bitwise-or] combined with [Bright].
|
||||||
void color(int foreground, int background, ForceOption force = ForceOption.automatic, bool reverseVideo = false) {
|
void color(int foreground, int background, ForceOption force = ForceOption.automatic, bool reverseVideo = false) {
|
||||||
|
if(!stdoutIsTerminal)
|
||||||
|
return;
|
||||||
if(force != ForceOption.neverSend) {
|
if(force != ForceOption.neverSend) {
|
||||||
version(Win32Console) {
|
if(UseVtSequences) {
|
||||||
|
import std.process;
|
||||||
|
// I started using this envvar for my text editor, but now use it elsewhere too
|
||||||
|
// if we aren't set to dark, assume light
|
||||||
|
/*
|
||||||
|
if(getenv("ELVISBG") == "dark") {
|
||||||
|
// LowContrast on dark bg menas
|
||||||
|
} else {
|
||||||
|
foreground ^= LowContrast;
|
||||||
|
background ^= LowContrast;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
ushort setTof = cast(ushort) foreground & ~Bright;
|
||||||
|
ushort setTob = cast(ushort) background & ~Bright;
|
||||||
|
|
||||||
|
if(foreground & Color.DEFAULT)
|
||||||
|
setTof = 9; // ansi sequence for reset
|
||||||
|
if(background == Color.DEFAULT)
|
||||||
|
setTob = 9;
|
||||||
|
|
||||||
|
import std.string;
|
||||||
|
|
||||||
|
if(force == ForceOption.alwaysSend || reverseVideo != this.reverseVideo || foreground != _currentForeground || background != _currentBackground) {
|
||||||
|
writeStringRaw(format("\033[%dm\033[3%dm\033[4%dm\033[%dm",
|
||||||
|
(foreground != Color.DEFAULT && (foreground & Bright)) ? 1 : 0,
|
||||||
|
cast(int) setTof,
|
||||||
|
cast(int) setTob,
|
||||||
|
reverseVideo ? 7 : 27
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
// assuming a dark background on windows, so LowContrast == dark which means the bit is NOT set on hardware
|
// assuming a dark background on windows, so LowContrast == dark which means the bit is NOT set on hardware
|
||||||
/*
|
/*
|
||||||
foreground ^= LowContrast;
|
foreground ^= LowContrast;
|
||||||
|
@ -1731,38 +1790,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
SetConsoleTextAttribute(
|
SetConsoleTextAttribute(
|
||||||
hConsole,
|
hConsole,
|
||||||
cast(ushort)((setTob << 4) | setTof));
|
cast(ushort)((arsdTerminalColorToWin32ConsoleColor(cast(Color) setTob) << 4) | arsdTerminalColorToWin32ConsoleColor(cast(Color) setTof)));
|
||||||
}
|
|
||||||
} else {
|
|
||||||
import std.process;
|
|
||||||
// I started using this envvar for my text editor, but now use it elsewhere too
|
|
||||||
// if we aren't set to dark, assume light
|
|
||||||
/*
|
|
||||||
if(getenv("ELVISBG") == "dark") {
|
|
||||||
// LowContrast on dark bg menas
|
|
||||||
} else {
|
|
||||||
foreground ^= LowContrast;
|
|
||||||
background ^= LowContrast;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ushort setTof = cast(ushort) foreground & ~Bright;
|
|
||||||
ushort setTob = cast(ushort) background & ~Bright;
|
|
||||||
|
|
||||||
if(foreground & Color.DEFAULT)
|
|
||||||
setTof = 9; // ansi sequence for reset
|
|
||||||
if(background == Color.DEFAULT)
|
|
||||||
setTob = 9;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
if(force == ForceOption.alwaysSend || reverseVideo != this.reverseVideo || foreground != _currentForeground || background != _currentBackground) {
|
|
||||||
writeStringRaw(format("\033[%dm\033[3%dm\033[4%dm\033[%dm",
|
|
||||||
(foreground != Color.DEFAULT && (foreground & Bright)) ? 1 : 0,
|
|
||||||
cast(int) setTof,
|
|
||||||
cast(int) setTob,
|
|
||||||
reverseVideo ? 7 : 27
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1927,12 +1955,15 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// Returns the terminal to normal output colors
|
/// Returns the terminal to normal output colors
|
||||||
void reset() {
|
void reset() {
|
||||||
version(Win32Console)
|
if(stdoutIsTerminal) {
|
||||||
SetConsoleTextAttribute(
|
if(UseVtSequences)
|
||||||
hConsole,
|
writeStringRaw("\033[0m");
|
||||||
originalSbi.wAttributes);
|
else version(Win32Console) if(UseWin32Console) {
|
||||||
else
|
SetConsoleTextAttribute(
|
||||||
writeStringRaw("\033[0m");
|
hConsole,
|
||||||
|
originalSbi.wAttributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_underlined = false;
|
_underlined = false;
|
||||||
_italics = false;
|
_italics = false;
|
||||||
|
@ -1974,8 +2005,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
executeAutoHideCursor();
|
executeAutoHideCursor();
|
||||||
if(UseVtSequences) {
|
if(UseVtSequences) {
|
||||||
doTermcap("cm", y, x);
|
doTermcap("cm", y, x);
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
|
|
||||||
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);
|
||||||
|
@ -1990,7 +2020,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void showCursor() {
|
void showCursor() {
|
||||||
if(UseVtSequences)
|
if(UseVtSequences)
|
||||||
doTermcap("ve");
|
doTermcap("ve");
|
||||||
else version(Win32Console) {
|
else version(Win32Console) if(UseWin32Console) {
|
||||||
CONSOLE_CURSOR_INFO info;
|
CONSOLE_CURSOR_INFO info;
|
||||||
GetConsoleCursorInfo(hConsole, &info);
|
GetConsoleCursorInfo(hConsole, &info);
|
||||||
info.bVisible = true;
|
info.bVisible = true;
|
||||||
|
@ -2002,7 +2032,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void hideCursor() {
|
void hideCursor() {
|
||||||
if(UseVtSequences) {
|
if(UseVtSequences) {
|
||||||
doTermcap("vi");
|
doTermcap("vi");
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
CONSOLE_CURSOR_INFO info;
|
CONSOLE_CURSOR_INFO info;
|
||||||
GetConsoleCursorInfo(hConsole, &info);
|
GetConsoleCursorInfo(hConsole, &info);
|
||||||
info.bVisible = false;
|
info.bVisible = false;
|
||||||
|
@ -2022,12 +2052,11 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
private void executeAutoHideCursor() {
|
private void executeAutoHideCursor() {
|
||||||
if(autoHidingCursor) {
|
if(autoHidingCursor) {
|
||||||
version(Win32Console)
|
if(UseVtSequences) {
|
||||||
hideCursor();
|
|
||||||
else if(UseVtSequences) {
|
|
||||||
// prepend the hide cursor command so it is the first thing flushed
|
// prepend the hide cursor command so it is the first thing flushed
|
||||||
writeBuffer = "\033[?25l" ~ writeBuffer;
|
writeBuffer = "\033[?25l" ~ writeBuffer;
|
||||||
}
|
} else version(Win32Console) if(UseWin32Console)
|
||||||
|
hideCursor();
|
||||||
|
|
||||||
autoHiddenCursor = true;
|
autoHiddenCursor = true;
|
||||||
autoHidingCursor = false; // already been done, don't insert the command again
|
autoHidingCursor = false; // already been done, don't insert the command again
|
||||||
|
@ -2056,7 +2085,10 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// Changes the terminal's title
|
/// Changes the terminal's title
|
||||||
void setTitle(string t) {
|
void setTitle(string t) {
|
||||||
version(Win32Console) {
|
import std.string;
|
||||||
|
if(terminalInFamily("xterm", "rxvt", "screen", "tmux"))
|
||||||
|
writeStringRaw(format("\033]0;%s\007", t));
|
||||||
|
else version(Win32Console) if(UseWin32Console) {
|
||||||
wchar[256] buffer;
|
wchar[256] buffer;
|
||||||
size_t bufferLength;
|
size_t bufferLength;
|
||||||
foreach(wchar ch; t)
|
foreach(wchar ch; t)
|
||||||
|
@ -2067,10 +2099,6 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
else
|
else
|
||||||
buffer[$-1] = 0;
|
buffer[$-1] = 0;
|
||||||
SetConsoleTitleW(buffer.ptr);
|
SetConsoleTitleW(buffer.ptr);
|
||||||
} else {
|
|
||||||
import std.string;
|
|
||||||
if(terminalInFamily("xterm", "rxvt", "screen", "tmux"))
|
|
||||||
writeStringRaw(format("\033]0;%s\007", t));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2106,6 +2134,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
if(_writeDelegate !is null) {
|
if(_writeDelegate !is null) {
|
||||||
_writeDelegate(writeBuffer);
|
_writeDelegate(writeBuffer);
|
||||||
|
writeBuffer = null;
|
||||||
} else {
|
} else {
|
||||||
ssize_t written;
|
ssize_t written;
|
||||||
|
|
||||||
|
@ -2125,14 +2154,22 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) {
|
||||||
import std.conv;
|
// if(_writeDelegate !is null)
|
||||||
// FIXME: I'm not sure I'm actually happy with this allocation but
|
// _writeDelegate(writeBuffer);
|
||||||
// it probably isn't a big deal. At least it has unicode support now.
|
|
||||||
wstring writeBufferw = to!wstring(writeBuffer);
|
if(UseWin32Console) {
|
||||||
while(writeBufferw.length) {
|
import std.conv;
|
||||||
DWORD written;
|
// FIXME: I'm not sure I'm actually happy with this allocation but
|
||||||
WriteConsoleW(hConsole, writeBufferw.ptr, cast(DWORD)writeBufferw.length, &written, null);
|
// it probably isn't a big deal. At least it has unicode support now.
|
||||||
writeBufferw = writeBufferw[written .. $];
|
wstring writeBufferw = to!wstring(writeBuffer);
|
||||||
|
while(writeBufferw.length) {
|
||||||
|
DWORD written;
|
||||||
|
WriteConsoleW(hConsole, writeBufferw.ptr, cast(DWORD)writeBufferw.length, &written, null);
|
||||||
|
writeBufferw = writeBufferw[written .. $];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
import std.stdio;
|
||||||
|
stdout.rawWrite(writeBuffer); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
writeBuffer = null;
|
writeBuffer = null;
|
||||||
|
@ -2151,6 +2188,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] getSizeInternal() {
|
private int[] getSizeInternal() {
|
||||||
|
if(!stdoutIsTerminal)
|
||||||
|
throw new Exception("unable to get size of non-terminal");
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
GetConsoleScreenBufferInfo( hConsole, &info );
|
GetConsoleScreenBufferInfo( hConsole, &info );
|
||||||
|
@ -2350,7 +2389,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void clear() {
|
void clear() {
|
||||||
if(UseVtSequences) {
|
if(UseVtSequences) {
|
||||||
doTermcap("cl");
|
doTermcap("cl");
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) if(UseWin32Console) {
|
||||||
// http://support.microsoft.com/kb/99261
|
// http://support.microsoft.com/kb/99261
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
|
@ -2379,7 +2418,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
if(UseVtSequences) {
|
if(UseVtSequences) {
|
||||||
writeStringRaw("\033[0K");
|
writeStringRaw("\033[0K");
|
||||||
}
|
}
|
||||||
else version(Windows) {
|
else version(Win32Console) if(UseWin32Console) {
|
||||||
updateCursorPosition();
|
updateCursorPosition();
|
||||||
auto x = _cursorX;
|
auto x = _cursorX;
|
||||||
auto y = _cursorY;
|
auto y = _cursorY;
|
||||||
|
@ -2408,7 +2447,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
terminal.writeln(line);
|
terminal.writeln(line);
|
||||||
---
|
---
|
||||||
)
|
)
|
||||||
You really shouldn't call this if stdin isn't actually a user-interactive terminal! So if you expect people to pipe data to your app, check for that or use something else. See [stdinIsTerminal].
|
You really shouldn't call this if stdin isn't actually a user-interactive terminal! However, if it isn't, it will simply read one line from the pipe without writing the prompt. See [stdinIsTerminal].
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
prompt = the prompt to give the user. For example, `"Your name: "`.
|
prompt = the prompt to give the user. For example, `"Your name: "`.
|
||||||
|
@ -2423,8 +2462,16 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
Always pass a string if you want it to display a string.
|
Always pass a string if you want it to display a string.
|
||||||
|
|
||||||
The `prefilledData` (and overload with it as second param) was added on January 1, 2023 (dub v10.10 / v11.0).
|
The `prefilledData` (and overload with it as second param) was added on January 1, 2023 (dub v10.10 / v11.0).
|
||||||
|
|
||||||
|
On November 7, 2023 (dub v11.3), this function started returning stdin.readln in the event that the instance is not connected to a terminal.
|
||||||
+/
|
+/
|
||||||
string getline(string prompt = null, dchar echoChar = dchar.init, string prefilledData = null) {
|
string getline(string prompt = null, dchar echoChar = dchar.init, string prefilledData = null) {
|
||||||
|
if(!stdoutIsTerminal || !stdinIsTerminal) {
|
||||||
|
import std.stdio;
|
||||||
|
import std.string;
|
||||||
|
return readln().chomp;
|
||||||
|
}
|
||||||
|
|
||||||
if(lineGetter is null)
|
if(lineGetter is null)
|
||||||
lineGetter = new LineGetter(&this);
|
lineGetter = new LineGetter(&this);
|
||||||
// since the struct might move (it shouldn't, this should be unmovable!) but since
|
// since the struct might move (it shouldn't, this should be unmovable!) but since
|
||||||
|
@ -2500,12 +2547,16 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void updateCursorPosition_impl() {
|
private void updateCursorPosition_impl() {
|
||||||
|
if(!stdinIsTerminal || !stdoutIsTerminal)
|
||||||
|
throw new Exception("cannot update cursor position on non-terminal");
|
||||||
auto terminal = &this;
|
auto terminal = &this;
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
if(UseWin32Console) {
|
||||||
GetConsoleScreenBufferInfo(terminal.hConsole, &info);
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
_cursorX = info.dwCursorPosition.X;
|
GetConsoleScreenBufferInfo(terminal.hConsole, &info);
|
||||||
_cursorY = info.dwCursorPosition.Y;
|
_cursorX = info.dwCursorPosition.X;
|
||||||
|
_cursorY = info.dwCursorPosition.Y;
|
||||||
|
}
|
||||||
} else version(Posix) {
|
} else version(Posix) {
|
||||||
// request current cursor position
|
// request current cursor position
|
||||||
|
|
||||||
|
@ -4780,6 +4831,8 @@ version(Posix)
|
||||||
private uint /* TerminalCapabilities bitmask */ getTerminalCapabilities(int fdIn, int fdOut) {
|
private uint /* TerminalCapabilities bitmask */ getTerminalCapabilities(int fdIn, int fdOut) {
|
||||||
if(fdIn == -1 || fdOut == -1)
|
if(fdIn == -1 || fdOut == -1)
|
||||||
return TerminalCapabilities.minimal;
|
return TerminalCapabilities.minimal;
|
||||||
|
if(!isatty(fdIn) || !isatty(fdOut))
|
||||||
|
return TerminalCapabilities.minimal;
|
||||||
|
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import core.stdc.errno;
|
import core.stdc.errno;
|
||||||
|
@ -8415,15 +8468,37 @@ RGB xtermPaletteIndexToColor(int paletteIdx) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
int approximate16Color(RGB color) {
|
Color approximate16Color(RGB color) {
|
||||||
int c;
|
int c;
|
||||||
c |= color.r > 64 ? RED_BIT : 0;
|
c |= color.r > 64 ? 1 : 0;
|
||||||
c |= color.g > 64 ? GREEN_BIT : 0;
|
c |= color.g > 64 ? 2 : 0;
|
||||||
c |= color.b > 64 ? BLUE_BIT : 0;
|
c |= color.b > 64 ? 4 : 0;
|
||||||
|
|
||||||
c |= (((color.r + color.g + color.b) / 3) > 80) ? Bright : 0;
|
c |= (((color.r + color.g + color.b) / 3) > 80) ? Bright : 0;
|
||||||
|
|
||||||
return c;
|
return cast(Color) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color win32ConsoleColorToArsdTerminalColor(ushort c) {
|
||||||
|
ushort v = cast(ushort) c;
|
||||||
|
auto b1 = v & 1;
|
||||||
|
auto b2 = v & 2;
|
||||||
|
auto b3 = v & 4;
|
||||||
|
auto b4 = v & 8;
|
||||||
|
|
||||||
|
return cast(Color) ((b1 << 2) | b2 | (b3 >> 2) | b4);
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort arsdTerminalColorToWin32ConsoleColor(Color c) {
|
||||||
|
assert(c != Color.DEFAULT);
|
||||||
|
|
||||||
|
ushort v = cast(ushort) c;
|
||||||
|
auto b1 = v & 1;
|
||||||
|
auto b2 = v & 2;
|
||||||
|
auto b3 = v & 4;
|
||||||
|
auto b4 = v & 8;
|
||||||
|
|
||||||
|
return cast(ushort) ((b1 << 2) | b2 | (b3 >> 2) | b4);
|
||||||
}
|
}
|
||||||
|
|
||||||
version(TerminalDirectToEmulator) {
|
version(TerminalDirectToEmulator) {
|
||||||
|
|
Loading…
Reference in New Issue