I have this little project i am about to release.
https://github.com/holli-holzer/perl6-Win-VT
I would be happy about a code review and suggestions for a better name.
Discussion in IRC yielded Terminal::Win::VT or Terminal::Win32::VT::Enable as better alternatives. Or even Win32::Console::VT.
This is the gist of it. There are additional files, but those provide just sugar.
unit module Win::VT;
use NativeCall;
constant STD_INPUT_HANDLE = -0xA;
constant STD_OUTPUT_HANDLE = -0xB;
constant ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200;
constant ENABLE_PROCESSED_INPUT = 0x0001;
constant ENABLE_PROCESSED_OUTPUT = 0x0001;
constant ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
constant DWORD := uint32;
constant BOOL := int32;
constant HANDLE := Pointer[void];
sub GetConsoleMode(HANDLE, DWORD is rw) is native('Kernel32') returns BOOL { * };
sub SetConsoleMode(HANDLE, DWORD) is native('Kernel32') returns BOOL { * };
sub GetStdHandle(DWORD) is native('Kernel32') returns HANDLE { * };
my HANDLE $input-handle;
my HANDLE $output-handle;
my DWORD $output-mode;
my DWORD $input-mode;
INIT {
if $*VM.osname eq "mswin32"
{
$input-handle = GetStdHandle( STD_INPUT_HANDLE );
$output-handle = GetStdHandle( STD_OUTPUT_HANDLE );
}
}
sub vt-on(Bool :$vt-input=True, Bool :$vt-output=True ) returns Bool is export(:MANDATORY)
{
return False
unless $input-handle.defined && $output-handle.defined;
return False
unless GetConsoleMode($input-handle, $input-mode) && GetConsoleMode($output-handle, $output-mode);
my $new-input-mode = $input-mode +| ENABLE_PROCESSED_INPUT +| ENABLE_VIRTUAL_TERMINAL_INPUT;
my $new-output-mode = $output-mode +| ENABLE_PROCESSED_OUTPUT +| ENABLE_VIRTUAL_TERMINAL_PROCESSING;
return (
( $vt-input ?? SetConsoleMode($input-handle, $new-input-mode) !! 1 ) &&
( $vt-output ?? SetConsoleMode($output-handle, $new-output-mode) !! 1 )
).Bool;
}
sub vt-off() returns Bool is export(:MANDATORY) {
return (
$input-handle.defined && $output-handle.defined &&
SetConsoleMode($output-handle, $output-mode) &&
SetConsoleMode($input-handle, $input-mode)
).Bool;
}
sub cls() is export(:cls) {
vt-on;
print chr(27) ~ '[2J' ~ chr(27) ~ '[;H';
vt-off;
}
1 Answer 1
Since no one else has replied, here's somewhat of a "rubber duck code review" given that I don't know much about Windows, vt, and NativeCall. Plus I've got a suggestion about the API and module name.
It looks like the not-explicitly-initialized $input-mode
and $output-mode
variables are actually constants implicitly initialized to zero. I think the code would be less confusing if they were declared as constants or even if you used literal 0
s instead of them. Perhaps other code or configuration you have, or have in mind, will vary these values? Anyhow, at the very least, I think a comment making it clear the values the user passes to vt-on
don't alter these, and vt-off
is resetting them no matter what vt-on
did, would be helpful.
From an API perspective, aiui, if one calls vt-on
it turns the vt stuff on for both input and output. You have to pass :vt-input(False)
if you only want vt-output
and vice-versa. And vt-off
always turns both off.
To me, having to write vt-input(False)
is a bit ugly because:
It repeats the
vt
;Mentions
input
when what one is trying to do is controloutput
;Requires explicit use of
False
.
It seems to me it could be more ergonomic to have just one routine, vt
. To switch both input and output on use vt :on
. To switch both off use vt :off
. To switch just one on and leave the other as it is, use vt :on<input>
or vt :on<output>
. To switch just one off and leave the other as it is, use vt :off<input>
or vt :off<output>
.
I've never used vt stuff and could imagine one doesn't need the extra flexibility of the API I suggest but my suggestion is really more about keeping the API sweet than it is about the additional flexibility.
As for the module name...
I currently think we should shift all of our emphasis related to module discovery to an assumption of widespread use of global search via keywords, tags, etc. For now that means eg 'terminal' tag search or 'terminal' keyword search but one day there'll surely be search of a module's Pod and other aspects as well. In summary, imo, don't worry about a name's discoverability.
To the degree that a module's name matters, I think it's beyond discoverability. I think the two key things are considering clustering with names already chosen or likely to be chosen by other module authors, and making a name nice in a use
statement. For these latter aspects I'd say:
Consider following accepted hierarchical namespace norms already established for modules.perl6.org. But don't sweat it.
If there are no obvious relevant norms to follow then keep a module name fairly short and sweet. Do you really need bumpy case or hyphens? Do you really need more than one
::
divider? Then again, don't pick something egregiously short-sighted. You could name it justVT
but a module such as yours doesn't warrant such a name.
Other than that the code all makes sense to me and looks fine to me from a pure P6 perspective.
*::ANSI
maybe? The VT series may have introduced these codes but they were formalized by an ANSI spec and most people would call them "ANSI escape sequences" today. Alsochr(27)
seems like it should be a constant. \$\endgroup\$