0

Is there a possibility of calling the function main without the function _end_win being called?

#!/usr/bin/env perl
use warnings;
use strict;
use 5.10.0;
use Term::ReadKey;

use constant {
    NEXT_getch      => -1,
    CONTROL_C       => 0x03,
    CONTROL_D       => 0x04,
    KEY_ENTER       => 0x0d,
    KEY_RIGHT       => 0x1b5b43,
    KEY_LEFT        => 0x1b5b44,
};


say main();

sub main {
    my $arg = {};
    $arg->{handle_out} = *STDOUT;
    _init_scr( $arg );
    while ( 1 ) {
        my $c = _getch( $arg );
        if ( ! defined $c ) {
            _end_win( $arg );
            warn "EOT";
            return;
        }
        next if $c == NEXT_getch;
        given ( $c ) {
            when ( $c >= 97 && $c <= 122 ) {
                print chr $c;
                $arg->{string} .= chr $c;
            }
            when ( $c == KEY_RIGHT ) {
                print '>';
                $arg->{string} .= '>';
            }
            when ( $c == KEY_LEFT ) {
                print '<';
                $arg->{string} .= '<';
            }
            when ( $c == CONTROL_D ) {
                _end_win( $arg );
                return;
            }
            when ( $c == CONTROL_C ) {
                _end_win( $arg );
                print STDERR "^C";
                kill( 'INT', $$ );
                return;
            }
            when ( $c == KEY_ENTER ) {
                _end_win( $arg );
                return $arg->{string};
            }
        }
    }
}

sub _init_scr {
    my ( $arg ) = @_;
    $arg->{old_handle} = select( $arg->{handle_out} );
    $arg->{backup_flush} = $|;
    $| = 1;
    Term::ReadKey::ReadMode 'ultra-raw';
}

sub _end_win {
    my ( $arg ) = @_;
    print "\n\r";
    Term::ReadKey::ReadMode 'restore';
    $| = $arg->{backup_flush};
    select( $arg->{old_handle} );
}

sub _getch {
    my ( $arg ) = @_;
    my $c1 = ReadKey 0;
    return if ! defined $c1;
    if ( $c1 eq "\e" ) {
        my $c2 = ReadKey 0.10;
        if ( ! defined $c2 ) { return NEXT_getch; }
        elsif ( $c2 eq 'C' ) { return KEY_RIGHT; }
        elsif ( $c2 eq 'D' ) { return KEY_LEFT; }
        elsif ( $c2 eq '[' ) {
            my $c3 = ReadKey 0;
            if ( $c3 eq 'C' ) { return KEY_RIGHT; }
            elsif ( $c3 eq 'D' ) { return KEY_LEFT; }
            else {
                return NEXT_getch;
            }
        }
        else {
            return NEXT_getch;
        }
    }
    else {
        return ord $c1;
    }
}
sid_com
  • 24,137
  • 26
  • 96
  • 187
  • I'm not sure what you're asking. You could take the `_end_win` calls out, but I don't think that's what you mean. – Borodin May 08 '13 at 09:20
  • What are you actually trying to accomplish? I can think of several ways to prevent `_end_win` from ever being called (e.g., `kill -9` the process when it waits for user input), but, without knowing your purpose, it's impossible to say which might be relevant to you. – Dave Sherohman May 08 '13 at 10:55
  • I'd like to know, if this is save. – sid_com May 08 '13 at 11:14
  • @DaveSherohman: For example if I put a `die;` before the `_end_win` in the KEY_ENTER block and press the ENTER the terminal breaks and I have to reset the terminal. So with safe I mean here: this could not happen ( unless I insert a `die` or similar). – sid_com May 08 '13 at 13:40
  • Are you asking if the functions used can throw exceptions (now or one day)? – ikegami May 08 '13 at 13:45

1 Answers1

1

To ensure that the terminal is reset when your program exits, put the reset code into an END block. For example, you could replace your _end_win sub with:

END {
    print "\n\r";
    Term::ReadKey::ReadMode 'restore';
}

(I removed the lines resetting $| and the selected output filehandle since the process is exiting anyhow, so they're about to become irrelevant.)

An END block will always run when the program terminates in a "normal" way, such as calling exit or die or hitting the end of the executable code. It does not fire when the process terminates due to receiving a signal; it looks like you're handling the ctrl-C character directly, but you may want to consider adding a %SIG{INT} handler as well, in case someone sends you a kill -2.

Dave Sherohman
  • 45,363
  • 14
  • 64
  • 102