win32 console as a runtime option

This commit is contained in:
Adam D. Ruppe 2023-11-22 18:56:44 -05:00
parent 405bd59c9d
commit 65d5983303
1 changed files with 251 additions and 176 deletions

View File

@ -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;
} }
}
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 { } else {
this.usingDirectEmulator = true;
}
if(!usingDirectEmulator) {
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) {
if(UseVtSequences)
writeStringRaw("\033[0m");
else version(Win32Console) if(UseWin32Console) {
SetConsoleTextAttribute( SetConsoleTextAttribute(
hConsole, hConsole,
originalSbi.wAttributes); originalSbi.wAttributes);
else }
writeStringRaw("\033[0m"); }
_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,6 +2154,10 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
} }
} }
} else version(Win32Console) { } else version(Win32Console) {
// if(_writeDelegate !is null)
// _writeDelegate(writeBuffer);
if(UseWin32Console) {
import std.conv; import std.conv;
// FIXME: I'm not sure I'm actually happy with this allocation but // FIXME: I'm not sure I'm actually happy with this allocation but
// it probably isn't a big deal. At least it has unicode support now. // it probably isn't a big deal. At least it has unicode support now.
@ -2134,6 +2167,10 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
WriteConsoleW(hConsole, writeBufferw.ptr, cast(DWORD)writeBufferw.length, &written, null); WriteConsoleW(hConsole, writeBufferw.ptr, cast(DWORD)writeBufferw.length, &written, null);
writeBufferw = writeBufferw[written .. $]; 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) {
if(UseWin32Console) {
CONSOLE_SCREEN_BUFFER_INFO info; CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(terminal.hConsole, &info); GetConsoleScreenBufferInfo(terminal.hConsole, &info);
_cursorX = info.dwCursorPosition.X; _cursorX = info.dwCursorPosition.X;
_cursorY = info.dwCursorPosition.Y; _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) {