Search code examples
rustgtk3gtk-rs

gtk-rs: how to listen for keyboard events


I am using gtk-rs and want to be able to detect when any key is pressed.

From some searching online, it seems like the way to do that in C is with gtk_widget_add_events and then g_signal_connect. This answer has a good explanation.

In Rust, I can call Widget::add_events. I also found several definitions for g_signal_connect_*. But, these functions are unsafe, undocumented, and seem to take C types as arguments.

My question is:

  1. In order to use gobject_sys::g_signal_connect_closure, how do I create a GObject and GClosure. In Rust? Can rust structs and closures be converted to that?
  2. Is there a better and more idiomatic way to listen for key events? I have trouble believing that doing such a basic thing would require such an esoteric interface. I have seen some support for specific keyboard shortcuts or keyboard acceleration groups but I haven't been able to find any documentation or examples for just listening for key press events.

Solution

  • I figured it out.

    Thansk @Jmb, Widget::connect was the right way to go. But, that function is undocumented and has some very weird types. Here's how I figured out how to use it:

    window
        .connect("key_press_event", false, |values| {
            // "values" is a 2-long slice of glib::value::Value, which wrap G-types
            // You can unwrap them if you know what type they are going to be ahead of time
            // values[0] is the window and values[1] is the event
            let raw_event = &values[1].get::<gdk::Event>().unwrap().unwrap();
            // You have to cast to the correct event type to access some of the fields
            match raw_event.downcast_ref::<gdk::EventKey>() {
                Some(event) => {
                    println!("key value: {:?}", std::char::from_u32(event.get_keyval()));
                    println!("modifiers: {:?}", event.get_state());
                },
                None => {},
            }
    
            // You need to return Some() of a glib Value wrapping a bool
            let result = glib::value::Value::from_type(glib::types::Type::Bool);
            // I can't figure out how to actually set the value of result
            // Luckally returning false is good enough for now.
            Some(result)
        })
        .unwrap();