【问题标题】:How can I catch a `shift`-`key` combination with Win32::Console::Input?如何使用 Win32::Console::Input 捕捉 `shift`-`key` 组合?
【发布时间】:2012-04-17 16:59:46
【问题描述】:

如何使用此脚本捕获 shift-some-key 组合?
当我按下箭头键时,我得到了我期望的结果,但是当我按下 shift-tab 时,它不会返回 KEY_BTAB 值。

use warnings;
use 5.12.0;
use Win32::Console qw(STD_INPUT_HANDLE ENABLE_MOUSE_INPUT);
use constant {
    RIGHT_ALT_PRESSED  => 0x0001,
    LEFT_ALT_PRESSED   => 0x0002,
    RIGHT_CTRL_PRESSED => 0x0004,
    LEFT_CTRL_PRESSED  => 0x0008,
    SHIFT_PRESSED      => 0x0010,

    VK_LEFT     => 0x25,
    VK_UP       => 0x26,    
    VK_RIGHT    => 0x27,    
    VK_DOWN     => 0x28,    
    VK_TAB      => 0x09,    
};
use constant SHIFTED_MASK =>
    RIGHT_ALT_PRESSED |
    LEFT_ALT_PRESSED |
    RIGHT_CTRL_PRESSED |
    LEFT_CTRL_PRESSED |
    SHIFT_PRESSED;

my %d = (
    KEY_DOWN            => 258,
    KEY_UP              => 259,
    KEY_LEFT            => 260,
    KEY_RIGHT           => 261,
    KEY_BTAB            => 353,
);

my $con_in = Win32::Console->new(STD_INPUT_HANDLE);
$con_in->Mode(ENABLE_MOUSE_INPUT);

while ( 1 ) {
        my $key = getch();
        say "<$key>";
        last if $key == 113;
}

sub getch {
    my @event = $con_in->Input();
    my $event_type = shift( @event );
    if ( defined $event_type and $event_type == 1 ) { 
        my ( $key_down, $repeat_c, $vkcode, $vsccode, $char, $ctrl_ks ) = @event;
        if ( $char ) {
            return $char;
        }
        else {
            if ( $vkcode == VK_UP and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
                return $d{KEY_UP};
            }
            elsif ( $vkcode == VK_DOWN and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
                return $d{KEY_DOWN};
            }  
            elsif ( $vkcode == VK_RIGHT and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
                return $d{KEY_RIGHT};
            }        
            elsif ( $vkcode == VK_LEFT and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
                return $d{KEY_LEFT};
            } 
            elsif ( $vkcode == VK_TAB and $ctrl_ks == SHIFT_PRESSED ) {
                return $d{KEY_BTAB};    # <--
            }
            else {
                say "beep";
            }
        }
    }
}

按下shifttab时的输出:





以这种方式编辑getch例程后

sub getch {
    my @event = $con_in->Input();
    my $event_type = shift( @event );
    if ( defined $event_type and $event_type == 1 ) { 
        my ( $key_down, $repeat_count, $virtual_keycode, $virtual_scancode, $char, $ctrl_key_state ) = @event;
        if ( $char ) {
            if ( $key_down ) {
                return $char for $repeat_count;
            }
        }
        else {
            if ( $virtual_keycode == VK_UP and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
                if ( $key_down ) {
                    return $d{KEY_UP} for $repeat_count;
                } 
            }
            elsif ( $virtual_keycode == VK_DOWN and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
                if ( $key_down ) {          
                    return $d{KEY_DOWN} for $repeat_count;
                }
            }  
            elsif ( $virtual_keycode == VK_RIGHT and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
                if ( $key_down ) {
                    return $d{KEY_RIGHT} for $repeat_count;
                }
            }        
            elsif ( $virtual_keycode == VK_LEFT and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
                if ( $key_down ) {
                    return $d{KEY_LEFT} for $repeat_count;
                }
            } 
            elsif ( $virtual_keycode == VK_TAB and ( $ctrl_key_state & SHIFTED_MASK ) == SHIFT_PRESSED ) {
                if ( $key_down ) {
                    return $d{KEY_BTAB} for $repeat_count;
                }
            }
            else {
                say "beep";
            }
        }
    }
}

我得到这个输出:





【问题讨论】:

  • 不要让我们陷入悬念。按 Shift+Tab 会返回什么?
  • 好像还有一些错误。
  • return EXPR for EXPR 应该是什么意思?!?
  • 我想得益于 ikegami 的回答和您的评论,这并没有多大意义。

标签: perl winapi input console


【解决方案1】:

当我运行您的代码时,$ctrl_ks 在我点击 Shift+Tab 时的值为 48 (0x0030),而在 Shift 时为 32 (0x0020) 被释放。我认为您要进行的检查是

elsif ($vkcode==VK_TAB and ($ctrl_ks & SHIFT_PRESSED)==SHIFT_PRESSED) {
    return $d{KEY_BTAB};

【讨论】:

  • ( $ctrl_ks &amp; SHIFTED_MASK ) == SHIFT_PRESSED,如果他只想要 Shift+TAB 而不是 Ctrl+Shift+TAB 或 Shift+Alt+TAB
【解决方案2】:

首先,$char 设置为 9,因此您永远不会收到支票。将 if ($char) 检查移到更合适的位置。


其次,您的支票是错误的。如果 Caps Lock 处于打开状态,则以下操作将不起作用。

elsif ( $vkcode == VK_TAB and $ctrl_ks == SHIFT_PRESSED )

您应该只检查您感兴趣的标志。

elsif ($vkcode==VK_TAB and ( $ctrl_ks & SHIFTED_MASK ) == SHIFT_PRESSED)

最后,有时您多次按下只会收到一次通知。这是由$repeat_count 发出的信号。您忽略了这一点,因此您可能会忽略键。

您尝试在第二个 sn-p 中处理 $repeat_count,但惨遭失败。部分问题是你从我的另一个答案中复制了for $repeat_count,而它应该是for 1..$repeat_count,另一个问题是即使$repeat_count 大于一个,你也只返回一个值。


my @kbd_queue;
sub getch {
    my @event;
    if (@kbd_queue) {
       @event = ( 1, @{ pop @kbd_queue } );
    } else {
       @event = $con_in->Input();
    }

    my $event_type = shift( @event );
    if ( defined $event_type and $event_type == 1 ) { 
        my ( $key_down, $repeat_count, $virtual_keycode, $virtual_scancode, $char, $ctrl_key_state ) = @event;
        return -1 if !$key_down;

        if ( $virtual_keycode == VK_UP and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
            push @kbd_queue, \@event for 2..$repeat_count;
            return $d{KEY_UP};
        } 
        ...
        elsif ( $virtual_keycode == VK_TAB and ( $ctrl_key_state & SHIFTED_MASK ) == SHIFT_PRESSED ) {
            push @kbd_queue, \@event for 2..$repeat_count;
            return $d{KEY_BTAB};
        }
        elsif ( $char ) {
            push @kbd_queue, \@event for 2..$repeat_count;
            return $char;
        }
        else {
            say "beep";
        }
    }
}

你应该把它转换成表格驱动的东西。

【讨论】:

    猜你喜欢
    • 2018-10-30
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 2016-09-13
    • 2016-10-07
    • 1970-01-01
    相关资源
    最近更新 更多