Search code examples
perlgtk3

How to check for CTRL+s in Gtk3?


How can I check if a key is pressed together with a specific modifier in Gtk3 ? For example, here I want to check if CTRL+s is pressed:

use feature qw(say);
use strict;
use warnings;
use Gtk3 '-init';
use Glib qw(TRUE FALSE);

my $window = Gtk3::Window->new('toplevel');
$window->set_title('Hello world');
$window->set_position('center');
$window->set_default_size(500, 500);
$window->signal_connect('delete-event' => sub {
    Gtk3->main_quit();
});
$window->signal_connect('key-press-event' => sub { handle_key( @_ ) } );
$window->show_all();
Gtk3->main();

sub handle_key {
    my ( $widget, $event) = @_;

    my $key = Gtk3::Gdk::keyval_name( $event->keyval );
    if ( ($key eq 's') && control_pressed( $event ) ) {
        say "CTRL+S";
    }
    return FALSE; # FALSE -> means propagate key further
}

How should I defined the function control_pressed() above?


Solution

  • The $event->state is of type Gtk3::Gdk::ModifierType which has overloaded operators.

    Flags have some additional magic abilities in the form of overloaded operators:

    + or |   union of two flagsets ("add")
    -        difference of two flagsets ("sub", "remove")   
    * or &   intersection of two bitsets ("and")   
    / or ^   symmetric difference ("xor", you will rarely need this)
    >=       contains-operator ("is the left set a superset of the right set?")
    ==       equality
    

    In addition, flags in boolean context indicate whether they are empty or not, which allows you to write common operations naturally:

    $widget->set_events ($widget->get_events - "motion_notify_mask");  
    $widget->set_events ($widget->get_events - ["motion_notify_mask",
                                              "button_press_mask"]);
    
    # shift pressed (both work, it's a matter of taste)   
    if ($event->state >= "shift-mask") { ...   
    if ($event->state * "shift-mask") { ...
    
    # either shift OR control pressed?   
    if ($event->state * ["shift-mask", "control-mask"]) { ...
    
    # both shift AND control pressed?
    if ($event->state >= ["shift-mask", "control-mask"]) { ...
    

    Also, the constants for the GdkModifierType enum becomes lower case strings in Perl. So the following should work:

    sub control_pressed {
        my ( $event ) = @_;
    
        return $event->state & 'control-mask';
    }