6
\$\begingroup\$

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;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ *::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. Also chr(27) seems like it should be a constant. \$\endgroup\$ – Oh My Goodness Sep 11 at 23:00
4
\$\begingroup\$

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 0s 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 control output;

  • 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 just VT 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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.