mirror of https://github.com/adamdruppe/arsd.git
dark bg, clipboard, and vt on windows beginning factor
This commit is contained in:
parent
944abf3c72
commit
c911434c8b
254
terminal.d
254
terminal.d
|
@ -1,4 +1,6 @@
|
||||||
// for optional dependency
|
// for optional dependency
|
||||||
|
// 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
|
||||||
/++
|
/++
|
||||||
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples].
|
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples].
|
||||||
|
|
||||||
|
@ -17,8 +19,8 @@
|
||||||
|
|
||||||
As a user, if you have to forcibly kill your program and the event doesn't work, there's still ctrl+\
|
As a user, if you have to forcibly kill your program and the event doesn't work, there's still ctrl+\
|
||||||
|
|
||||||
On Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work. Most functions basically
|
On old Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work. Most functions basically
|
||||||
work now though.
|
work now with newer Mac OS versions though.
|
||||||
|
|
||||||
Future_Roadmap:
|
Future_Roadmap:
|
||||||
$(LIST
|
$(LIST
|
||||||
|
@ -185,8 +187,10 @@ version(Posix) {
|
||||||
// capabilities.
|
// capabilities.
|
||||||
//version = Demo
|
//version = Demo
|
||||||
|
|
||||||
version(Windows)
|
version(Windows) {
|
||||||
|
version(VtEscapeCodes) {} // cool
|
||||||
version=Win32Console;
|
version=Win32Console;
|
||||||
|
}
|
||||||
|
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
import core.sys.windows.windows;
|
import core.sys.windows.windows;
|
||||||
|
@ -198,27 +202,30 @@ version(Win32Console) {
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
|
|
||||||
|
version=VtEscapeCodes;
|
||||||
|
|
||||||
import core.sys.posix.termios;
|
import core.sys.posix.termios;
|
||||||
import core.sys.posix.unistd;
|
import core.sys.posix.unistd;
|
||||||
import unix = core.sys.posix.unistd;
|
import unix = core.sys.posix.unistd;
|
||||||
import core.sys.posix.sys.types;
|
import core.sys.posix.sys.types;
|
||||||
import core.sys.posix.sys.time;
|
import core.sys.posix.sys.time;
|
||||||
import core.stdc.stdio;
|
import core.stdc.stdio;
|
||||||
|
|
||||||
|
import core.sys.posix.sys.ioctl;
|
||||||
|
}
|
||||||
|
|
||||||
|
version(VtEscapeCodes) {
|
||||||
|
|
||||||
|
enum UseVtSequences = true;
|
||||||
|
|
||||||
|
version(Windows) {} else
|
||||||
private {
|
private {
|
||||||
enum RED_BIT = 1;
|
enum RED_BIT = 1;
|
||||||
enum GREEN_BIT = 2;
|
enum GREEN_BIT = 2;
|
||||||
enum BLUE_BIT = 4;
|
enum BLUE_BIT = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(linux) {
|
|
||||||
extern(C) int ioctl(int, int, ...);
|
|
||||||
enum int TIOCGWINSZ = 0x5413;
|
|
||||||
} else version(OSX) {
|
|
||||||
import core.stdc.config;
|
|
||||||
extern(C) int ioctl(int, c_ulong, ...);
|
|
||||||
enum TIOCGWINSZ = 1074295912;
|
|
||||||
} else static assert(0, "confirm the value of tiocgwinsz");
|
|
||||||
|
|
||||||
struct winsize {
|
struct winsize {
|
||||||
ushort ws_row;
|
ushort ws_row;
|
||||||
ushort ws_col;
|
ushort ws_col;
|
||||||
|
@ -373,6 +380,8 @@ an|ansi|ansi-bbs|ANSI terminals (emulators):\
|
||||||
:tc=vt-generic:
|
:tc=vt-generic:
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
} else {
|
||||||
|
enum UseVtSequences = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A modifier for [Color]
|
/// A modifier for [Color]
|
||||||
|
@ -543,7 +552,6 @@ struct Terminal {
|
||||||
void delegate(in void[]) _writeDelegate; // used to override the unix write() system call, set it magically
|
void delegate(in void[]) _writeDelegate; // used to override the unix write() system call, set it magically
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Posix) {
|
|
||||||
bool terminalInFamily(string[] terms...) {
|
bool terminalInFamily(string[] terms...) {
|
||||||
import std.process;
|
import std.process;
|
||||||
import std.string;
|
import std.string;
|
||||||
|
@ -555,6 +563,7 @@ struct Terminal {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(Posix) {
|
||||||
// This is a filthy hack because Terminal.app and OS X are garbage who don't
|
// This is a filthy hack because Terminal.app and OS X are garbage who don't
|
||||||
// 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!)
|
||||||
|
@ -566,6 +575,7 @@ struct Terminal {
|
||||||
auto term = environment.get("TERM");
|
auto term = environment.get("TERM");
|
||||||
return term == "xterm-256color";
|
return term == "xterm-256color";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static string[string] termcapDatabase;
|
static string[string] termcapDatabase;
|
||||||
static void readTermcapFile(bool useBuiltinTermcap = false) {
|
static void readTermcapFile(bool useBuiltinTermcap = false) {
|
||||||
|
@ -610,6 +620,7 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(useBuiltinTermcap) {
|
if(useBuiltinTermcap) {
|
||||||
|
version(VtEscapeCodes)
|
||||||
foreach(line; splitLines(builtinTermcap)) {
|
foreach(line; splitLines(builtinTermcap)) {
|
||||||
handleTermcapLine(line);
|
handleTermcapLine(line);
|
||||||
}
|
}
|
||||||
|
@ -839,7 +850,6 @@ struct Terminal {
|
||||||
writeStringRaw(buffer[0 .. bufferPos]);
|
writeStringRaw(buffer[0 .. bufferPos]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint tcaps;
|
uint tcaps;
|
||||||
|
|
||||||
|
@ -847,7 +857,8 @@ struct Terminal {
|
||||||
return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
|
return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
|
||||||
}
|
}
|
||||||
bool clipboardSupported() {
|
bool clipboardSupported() {
|
||||||
return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
|
version(Win32Console) return true;
|
||||||
|
else return (tcaps & TerminalCapabilities.arsdImage) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported)
|
// only supported on my custom terminal emulator. guarded behind if(inlineImagesSupported)
|
||||||
|
@ -918,7 +929,7 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stubs that are dependent on tcaps
|
// dependent on tcaps...
|
||||||
void displayInlineImage()(ubyte[] imageData) {
|
void displayInlineImage()(ubyte[] imageData) {
|
||||||
if(inlineImagesSupported) {
|
if(inlineImagesSupported) {
|
||||||
import std.base64;
|
import std.base64;
|
||||||
|
@ -934,7 +945,7 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
void demandUserAttention() {
|
void demandUserAttention() {
|
||||||
version(Posix) {
|
if(UseVtSequences) {
|
||||||
if(!terminalInFamily("linux"))
|
if(!terminalInFamily("linux"))
|
||||||
writeStringRaw("\033]5001;1\007");
|
writeStringRaw("\033]5001;1\007");
|
||||||
}
|
}
|
||||||
|
@ -947,12 +958,6 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestPasteFromClipboard() {
|
|
||||||
if(clipboardSupported) {
|
|
||||||
writeStringRaw("\033]52;c;?\007");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void requestCopyToPrimary(string text) {
|
void requestCopyToPrimary(string text) {
|
||||||
if(clipboardSupported) {
|
if(clipboardSupported) {
|
||||||
import std.base64;
|
import std.base64;
|
||||||
|
@ -960,9 +965,16 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestPasteFromPrimary() {
|
bool hasDefaultDarkBackground() {
|
||||||
if(clipboardSupported) {
|
version(Win32Console) {
|
||||||
writeStringRaw("\033]52;p;?\007");
|
return !(defaultBackgroundColor & 0xf0);
|
||||||
|
} else {
|
||||||
|
// FIXME: there is probably a better way to do this
|
||||||
|
// but like idk how reliable it is.
|
||||||
|
if(terminalInFamily("linux"))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,8 +994,6 @@ struct Terminal {
|
||||||
this.getSizeOverride = getSizeOverride;
|
this.getSizeOverride = getSizeOverride;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
readTermcap();
|
|
||||||
|
|
||||||
if(type == ConsoleOutputType.minimalProcessing) {
|
if(type == ConsoleOutputType.minimalProcessing) {
|
||||||
_suppressDestruction = true;
|
_suppressDestruction = true;
|
||||||
return;
|
return;
|
||||||
|
@ -992,6 +1002,12 @@ struct Terminal {
|
||||||
tcaps = getTerminalCapabilities(fdIn, fdOut);
|
tcaps = getTerminalCapabilities(fdIn, fdOut);
|
||||||
//writeln(tcaps);
|
//writeln(tcaps);
|
||||||
|
|
||||||
|
initializeVt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeVt() {
|
||||||
|
readTermcap();
|
||||||
|
|
||||||
if(type == ConsoleOutputType.cellular) {
|
if(type == ConsoleOutputType.cellular) {
|
||||||
doTermcap("ti");
|
doTermcap("ti");
|
||||||
clear();
|
clear();
|
||||||
|
@ -1001,6 +1017,7 @@ struct Terminal {
|
||||||
if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) {
|
if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) {
|
||||||
writeStringRaw("\033[22;0t"); // save window title on a stack (support seems spotty, but it doesn't hurt to have it)
|
writeStringRaw("\033[22;0t"); // save window title on a stack (support seems spotty, but it doesn't hurt to have it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPERIMENTAL do not use yet
|
// EXPERIMENTAL do not use yet
|
||||||
|
@ -1019,6 +1036,10 @@ struct Terminal {
|
||||||
version(Win32Console)
|
version(Win32Console)
|
||||||
/// ditto
|
/// ditto
|
||||||
this(ConsoleOutputType type) {
|
this(ConsoleOutputType type) {
|
||||||
|
if(UseVtSequences) {
|
||||||
|
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
initializeVt();
|
||||||
|
} else {
|
||||||
if(type == ConsoleOutputType.cellular) {
|
if(type == ConsoleOutputType.cellular) {
|
||||||
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) {
|
||||||
|
@ -1028,8 +1049,8 @@ struct Terminal {
|
||||||
|
|
||||||
SetConsoleActiveScreenBuffer(hConsole);
|
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/ms686125%28v=vs.85%29.aspx
|
||||||
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.aspx
|
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.aspx
|
||||||
*/
|
*/
|
||||||
COORD size;
|
COORD size;
|
||||||
/*
|
/*
|
||||||
|
@ -1068,6 +1089,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
SetConsoleCP(65001); // UTF-8
|
SetConsoleCP(65001); // UTF-8
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
private Color defaultBackgroundColor = Color.black;
|
private Color defaultBackgroundColor = Color.black;
|
||||||
|
@ -1079,12 +1101,13 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
// 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...
|
||||||
bool _suppressDestruction;
|
bool _suppressDestruction;
|
||||||
|
|
||||||
version(Posix)
|
|
||||||
~this() {
|
~this() {
|
||||||
if(_suppressDestruction) {
|
if(_suppressDestruction) {
|
||||||
flush();
|
flush();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(UseVtSequences) {
|
||||||
if(type == ConsoleOutputType.cellular) {
|
if(type == ConsoleOutputType.cellular) {
|
||||||
doTermcap("te");
|
doTermcap("te");
|
||||||
}
|
}
|
||||||
|
@ -1098,14 +1121,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(Windows) {
|
||||||
|
|
||||||
version(Windows)
|
|
||||||
~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();
|
||||||
|
@ -1121,6 +1137,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
SetConsoleActiveScreenBuffer(stdo);
|
SetConsoleActiveScreenBuffer(stdo);
|
||||||
if(hConsole !is stdo)
|
if(hConsole !is stdo)
|
||||||
CloseHandle(hConsole);
|
CloseHandle(hConsole);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lazily initialized and preserved between calls to getline for a bit of efficiency (only a bit)
|
// lazily initialized and preserved between calls to getline for a bit of efficiency (only a bit)
|
||||||
|
@ -1285,7 +1303,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void underline(bool set, ForceOption force = ForceOption.automatic) {
|
void underline(bool set, ForceOption force = ForceOption.automatic) {
|
||||||
if(set == _underlined && force != ForceOption.alwaysSend)
|
if(set == _underlined && force != ForceOption.alwaysSend)
|
||||||
return;
|
return;
|
||||||
version(Posix) {
|
if(UseVtSequences) {
|
||||||
if(set)
|
if(set)
|
||||||
writeStringRaw("\033[4m");
|
writeStringRaw("\033[4m");
|
||||||
else
|
else
|
||||||
|
@ -1329,14 +1347,14 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void moveTo(int x, int y, ForceOption force = ForceOption.automatic) {
|
void moveTo(int x, int y, ForceOption force = ForceOption.automatic) {
|
||||||
if(force != ForceOption.neverSend && (force == ForceOption.alwaysSend || x != _cursorX || y != _cursorY)) {
|
if(force != ForceOption.neverSend && (force == ForceOption.alwaysSend || x != _cursorX || y != _cursorY)) {
|
||||||
executeAutoHideCursor();
|
executeAutoHideCursor();
|
||||||
version(Posix) {
|
if(UseVtSequences) {
|
||||||
doTermcap("cm", y, x);
|
doTermcap("cm", y, x);
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) {
|
||||||
|
|
||||||
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);
|
||||||
} else static assert(0);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_cursorX = x;
|
_cursorX = x;
|
||||||
|
@ -1345,9 +1363,9 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// shows the cursor
|
/// shows the cursor
|
||||||
void showCursor() {
|
void showCursor() {
|
||||||
version(Posix)
|
if(UseVtSequences)
|
||||||
doTermcap("ve");
|
doTermcap("ve");
|
||||||
else {
|
else version(Win32Console) {
|
||||||
CONSOLE_CURSOR_INFO info;
|
CONSOLE_CURSOR_INFO info;
|
||||||
GetConsoleCursorInfo(hConsole, &info);
|
GetConsoleCursorInfo(hConsole, &info);
|
||||||
info.bVisible = true;
|
info.bVisible = true;
|
||||||
|
@ -1357,9 +1375,9 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// hides the cursor
|
/// hides the cursor
|
||||||
void hideCursor() {
|
void hideCursor() {
|
||||||
version(Posix) {
|
if(UseVtSequences) {
|
||||||
doTermcap("vi");
|
doTermcap("vi");
|
||||||
} else {
|
} else version(Win32Console) {
|
||||||
CONSOLE_CURSOR_INFO info;
|
CONSOLE_CURSOR_INFO info;
|
||||||
GetConsoleCursorInfo(hConsole, &info);
|
GetConsoleCursorInfo(hConsole, &info);
|
||||||
info.bVisible = false;
|
info.bVisible = false;
|
||||||
|
@ -1466,7 +1484,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] getSize() {
|
int[] getSize() {
|
||||||
version(Win32Console) {
|
version(Windows) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
GetConsoleScreenBufferInfo( hConsole, &info );
|
GetConsoleScreenBufferInfo( hConsole, &info );
|
||||||
|
|
||||||
|
@ -1615,17 +1633,12 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
// you really, really shouldn't use this unless you know what you are doing
|
// you really, really shouldn't use this unless you know what you are doing
|
||||||
/*private*/ void writeStringRaw(in char[] s) {
|
/*private*/ void writeStringRaw(in char[] s) {
|
||||||
// FIXME: make sure all the data is sent, check for errors
|
|
||||||
version(Posix) {
|
|
||||||
writeBuffer ~= s; // buffer it to do everything at once in flush() calls
|
writeBuffer ~= s; // buffer it to do everything at once in flush() calls
|
||||||
} else version(Win32Console) {
|
|
||||||
writeBuffer ~= s;
|
|
||||||
} else static assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the screen.
|
/// Clears the screen.
|
||||||
void clear() {
|
void clear() {
|
||||||
version(Posix) {
|
if(UseVtSequences) {
|
||||||
doTermcap("cl");
|
doTermcap("cl");
|
||||||
} else version(Win32Console) {
|
} else version(Win32Console) {
|
||||||
// http://support.microsoft.com/kb/99261
|
// http://support.microsoft.com/kb/99261
|
||||||
|
@ -1705,6 +1718,64 @@ struct RealTimeConsoleInput {
|
||||||
@disable this();
|
@disable this();
|
||||||
@disable this(this);
|
@disable this(this);
|
||||||
|
|
||||||
|
/++
|
||||||
|
Requests the system to send paste data as a [PasteEvent] to this stream, if possible.
|
||||||
|
|
||||||
|
See_Also:
|
||||||
|
[Terminal.requestCopyToPrimary]
|
||||||
|
[Terminal.requestCopyToClipboard]
|
||||||
|
[Terminal.clipboardSupported]
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added February 17, 2020.
|
||||||
|
|
||||||
|
It was in Terminal briefly during an undocumented period, but it had to be moved here to have the context needed to send the real time paste event.
|
||||||
|
+/
|
||||||
|
void requestPasteFromClipboard() {
|
||||||
|
version(Win32Console) {
|
||||||
|
HWND hwndOwner = null;
|
||||||
|
if(OpenClipboard(hwndOwner) == 0)
|
||||||
|
throw new Exception("OpenClipboard");
|
||||||
|
scope(exit)
|
||||||
|
CloseClipboard();
|
||||||
|
if(auto dataHandle = GetClipboardData(CF_UNICODETEXT)) {
|
||||||
|
|
||||||
|
if(auto data = cast(wchar*) GlobalLock(dataHandle)) {
|
||||||
|
scope(exit)
|
||||||
|
GlobalUnlock(dataHandle);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
auto d = data;
|
||||||
|
while(*d) {
|
||||||
|
d++;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
string s;
|
||||||
|
s.reserve(len);
|
||||||
|
foreach(idx, dchar ch; data[0 .. len]) {
|
||||||
|
// CR/LF -> LF
|
||||||
|
if(ch == '\r' && idx + 1 < len && data[idx + 1] == '\n')
|
||||||
|
continue;
|
||||||
|
s ~= ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
injectEvent(InputEvent(PasteEvent(s), terminal), InjectionPosition.tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if(terminal.clipboardSupported) {
|
||||||
|
terminal.writeStringRaw("\033]52;c;?\007");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
void requestPasteFromPrimary() {
|
||||||
|
if(terminal.clipboardSupported) {
|
||||||
|
terminal.writeStringRaw("\033]52;p;?\007");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
private int fdOut;
|
private int fdOut;
|
||||||
private int fdIn;
|
private int fdIn;
|
||||||
|
@ -1739,7 +1810,7 @@ struct RealTimeConsoleInput {
|
||||||
GetConsoleMode(inputHandle, &oldInput);
|
GetConsoleMode(inputHandle, &oldInput);
|
||||||
|
|
||||||
DWORD mode = 0;
|
DWORD mode = 0;
|
||||||
mode |= ENABLE_PROCESSED_INPUT /* 0x01 */; // this gives Ctrl+C which we probably want to be similar to linux
|
//mode |= ENABLE_PROCESSED_INPUT /* 0x01 */; // this gives Ctrl+C and automatic paste... which we probably want to be similar to linux
|
||||||
//if(flags & ConsoleInputFlags.size)
|
//if(flags & ConsoleInputFlags.size)
|
||||||
mode |= ENABLE_WINDOW_INPUT /* 0208 */; // gives size etc
|
mode |= ENABLE_WINDOW_INPUT /* 0208 */; // gives size etc
|
||||||
if(flags & ConsoleInputFlags.echo)
|
if(flags & ConsoleInputFlags.echo)
|
||||||
|
@ -1980,7 +2051,7 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
bool timedCheckForInput_bypassingBuffer(int milliseconds) {
|
bool timedCheckForInput_bypassingBuffer(int milliseconds) {
|
||||||
version(Win32Console) {
|
version(Win32Console) {
|
||||||
auto response = WaitForSingleObject(terminal.hConsole, milliseconds);
|
auto response = WaitForSingleObject(inputHandle, milliseconds);
|
||||||
if(response == 0)
|
if(response == 0)
|
||||||
return true; // the object is ready
|
return true; // the object is ready
|
||||||
return false;
|
return false;
|
||||||
|
@ -2039,8 +2110,8 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
//char[128] inputBuffer;
|
//char[128] inputBuffer;
|
||||||
//int inputBufferPosition;
|
//int inputBufferPosition;
|
||||||
version(Posix)
|
|
||||||
int nextRaw(bool interruptable = false) {
|
int nextRaw(bool interruptable = false) {
|
||||||
|
version(Posix) {
|
||||||
if(fdIn == -1)
|
if(fdIn == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2067,12 +2138,20 @@ struct RealTimeConsoleInput {
|
||||||
return inputPrefilter ? inputPrefilter(buf[0]) : buf[0];
|
return inputPrefilter ? inputPrefilter(buf[0]) : buf[0];
|
||||||
else
|
else
|
||||||
assert(0); // read too much, should be impossible
|
assert(0); // read too much, should be impossible
|
||||||
|
} else version(Windows) {
|
||||||
|
char[8] buf;
|
||||||
|
DWORD d;
|
||||||
|
import std.conv;
|
||||||
|
if(!ReadFile(inputHandle, buf.ptr, cast(int) buf.length, &d, null))
|
||||||
|
throw new Exception("ReadFile " ~ to!string(GetLastError()));
|
||||||
|
return buf[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Posix)
|
version(Posix)
|
||||||
int delegate(char) inputPrefilter;
|
int delegate(char) inputPrefilter;
|
||||||
|
|
||||||
version(Posix)
|
// for VT
|
||||||
dchar nextChar(int starting) {
|
dchar nextChar(int starting) {
|
||||||
if(starting <= 127)
|
if(starting <= 127)
|
||||||
return cast(dchar) starting;
|
return cast(dchar) starting;
|
||||||
|
@ -2176,8 +2255,17 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
InputEvent[] inputQueue;
|
InputEvent[] inputQueue;
|
||||||
|
|
||||||
version(Win32Console)
|
|
||||||
InputEvent[] readNextEvents() {
|
InputEvent[] readNextEvents() {
|
||||||
|
if(UseVtSequences)
|
||||||
|
return readNextEventsVt();
|
||||||
|
else version(Windows)
|
||||||
|
return readNextEventsWin32();
|
||||||
|
else
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
InputEvent[] readNextEventsWin32() {
|
||||||
terminal.flush(); // make sure all output is sent out before waiting for anything
|
terminal.flush(); // make sure all output is sent out before waiting for anything
|
||||||
|
|
||||||
INPUT_RECORD[32] buffer;
|
INPUT_RECORD[32] buffer;
|
||||||
|
@ -2219,12 +2307,26 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
if(ev.UnicodeChar) {
|
if(ev.UnicodeChar) {
|
||||||
// new style event goes first
|
// new style event goes first
|
||||||
|
|
||||||
|
if(ev.UnicodeChar == 3) {
|
||||||
|
// handling this internally for linux compat too
|
||||||
|
newEvents ~= InputEvent(UserInterruptionEvent(), terminal);
|
||||||
|
} else if(ev.UnicodeChar == '\r') {
|
||||||
|
// translating \r to \n for same result as linux...
|
||||||
|
ke.which = cast(dchar) cast(wchar) '\n';
|
||||||
|
newEvents ~= InputEvent(ke, terminal);
|
||||||
|
|
||||||
|
// old style event then follows as the fallback
|
||||||
|
e.character = cast(dchar) cast(wchar) '\n';
|
||||||
|
newEvents ~= InputEvent(e, terminal);
|
||||||
|
} else {
|
||||||
ke.which = cast(dchar) cast(wchar) ev.UnicodeChar;
|
ke.which = cast(dchar) cast(wchar) ev.UnicodeChar;
|
||||||
newEvents ~= InputEvent(ke, terminal);
|
newEvents ~= InputEvent(ke, terminal);
|
||||||
|
|
||||||
// old style event then follows as the fallback
|
// old style event then follows as the fallback
|
||||||
e.character = cast(dchar) cast(wchar) ev.UnicodeChar;
|
e.character = cast(dchar) cast(wchar) ev.UnicodeChar;
|
||||||
newEvents ~= InputEvent(e, terminal);
|
newEvents ~= InputEvent(e, terminal);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// old style event
|
// old style event
|
||||||
ne.key = cast(NonCharacterKeyEvent.Key) ev.wVirtualKeyCode;
|
ne.key = cast(NonCharacterKeyEvent.Key) ev.wVirtualKeyCode;
|
||||||
|
@ -2302,8 +2404,8 @@ struct RealTimeConsoleInput {
|
||||||
return newEvents;
|
return newEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Posix)
|
// for UseVtSequences....
|
||||||
InputEvent[] readNextEvents() {
|
InputEvent[] readNextEventsVt() {
|
||||||
terminal.flush(); // make sure all output is sent out before we try to get input
|
terminal.flush(); // make sure all output is sent out before we try to get input
|
||||||
|
|
||||||
// we want to starve the read, especially if we're called from an edge-triggered
|
// we want to starve the read, especially if we're called from an edge-triggered
|
||||||
|
@ -2327,7 +2429,7 @@ struct RealTimeConsoleInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The helper reads just one actual event from the pipe...
|
// The helper reads just one actual event from the pipe...
|
||||||
version(Posix)
|
// for UseVtSequences....
|
||||||
InputEvent[] readNextEventsHelper() {
|
InputEvent[] readNextEventsHelper() {
|
||||||
InputEvent[] charPressAndRelease(dchar character) {
|
InputEvent[] charPressAndRelease(dchar character) {
|
||||||
if((flags & ConsoleInputFlags.releasedKeys))
|
if((flags & ConsoleInputFlags.releasedKeys))
|
||||||
|
@ -3711,11 +3813,11 @@ class LineGetter {
|
||||||
auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents | ConsoleInputFlags.noEolWrap);
|
auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents | ConsoleInputFlags.noEolWrap);
|
||||||
//rtci = &i;
|
//rtci = &i;
|
||||||
//scope(exit) rtci = null;
|
//scope(exit) rtci = null;
|
||||||
while(workOnLine(i.nextEvent())) {}
|
while(workOnLine(i.nextEvent(), &i)) {}
|
||||||
} else {
|
} else {
|
||||||
//rtci = input;
|
//rtci = input;
|
||||||
//scope(exit) rtci = null;
|
//scope(exit) rtci = null;
|
||||||
while(workOnLine(input.nextEvent())) {}
|
while(workOnLine(input.nextEvent(), input)) {}
|
||||||
}
|
}
|
||||||
return finishGettingLine();
|
return finishGettingLine();
|
||||||
}
|
}
|
||||||
|
@ -4118,12 +4220,19 @@ class LineGetter {
|
||||||
|
|
||||||
private bool maintainBuffer;
|
private bool maintainBuffer;
|
||||||
|
|
||||||
/// for integrating into another event loop
|
/++
|
||||||
/// you can pass individual events to this and
|
for integrating into another event loop
|
||||||
/// the line getter will work on it
|
you can pass individual events to this and
|
||||||
///
|
the line getter will work on it
|
||||||
/// returns false when there's nothing more to do
|
|
||||||
bool workOnLine(InputEvent e) {
|
returns false when there's nothing more to do
|
||||||
|
|
||||||
|
History:
|
||||||
|
On February 17, 2020, it was changed to take
|
||||||
|
a new argument which should be the input source
|
||||||
|
where the event came from.
|
||||||
|
+/
|
||||||
|
bool workOnLine(InputEvent e, RealTimeConsoleInput* rtti = null) {
|
||||||
switch(e.type) {
|
switch(e.type) {
|
||||||
case InputEvent.Type.EndOfFileEvent:
|
case InputEvent.Type.EndOfFileEvent:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
|
@ -4309,6 +4418,10 @@ class LineGetter {
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
redraw();
|
redraw();
|
||||||
break;
|
break;
|
||||||
|
case ('v' - 'a' + 1):
|
||||||
|
if(rtti)
|
||||||
|
rtti.requestPasteFromClipboard();
|
||||||
|
break;
|
||||||
case KeyboardEvent.Key.Insert:
|
case KeyboardEvent.Key.Insert:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
if(ev.modifierState & ModifierState.shift) {
|
if(ev.modifierState & ModifierState.shift) {
|
||||||
|
@ -4319,8 +4432,11 @@ class LineGetter {
|
||||||
|
|
||||||
// those work on Windows!!!! and many linux TEs too.
|
// those work on Windows!!!! and many linux TEs too.
|
||||||
// but if it does make it here, we'll attempt it at this level
|
// but if it does make it here, we'll attempt it at this level
|
||||||
|
if(rtti)
|
||||||
|
rtti.requestPasteFromClipboard();
|
||||||
} else if(ev.modifierState & ModifierState.control) {
|
} else if(ev.modifierState & ModifierState.control) {
|
||||||
// copy
|
// copy
|
||||||
|
// FIXME
|
||||||
} else {
|
} else {
|
||||||
insertMode = !insertMode;
|
insertMode = !insertMode;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue