1

I'm listening to user input in an gtk-rs input element. input.connect_changed triggers when the input changes and input.connect_activate triggers when Enter is pressed.

use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; use std::process::{Command, Output}; fn main() { let app = Application::builder() .application_id("com.jwestall.ui-demo") .build(); app.connect_activate(build_ui); app.run(); } fn run_command(command: &str) -> Output { Command::new("sh") .arg("-c") .arg(command) .output() .unwrap_or_else(|_| panic!("failed to execute {}'", command)) } fn build_ui(app: &Application) { let input = gtk::Entry::builder() .placeholder_text("input") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); let window = ApplicationWindow::builder() .application(app) .title("gtk-app") .child(&input) .build(); window.show_all(); input.connect_changed(|entry| { let input_text = entry.text(); let command = format!("xdotool search --onlyvisible --name {}", input_text); let window_id_output = run_command(&command); if window_id_output.status.success() { println!( "stdout: {}", String::from_utf8_lossy(&window_id_output.stdout) ); } else { println!( "sterr: {}", String::from_utf8_lossy(&window_id_output.stderr) ); } }); input.connect_activate(move |entry| { let input_text = entry.text(); // // `xdotool windowactivate` doesn't produce any output let command = format!("xdotool windowactivate {}", window_id_output); let window_activate_output = run_command(&command); println!("window_activate: {}", window_activate_output); window.hide(); window.close(); }); } 

I want to set window_id_output in input.connect_changed, then use it in input.connect_activate (in the xdotool windowactivate {} command).

How can I use window_id_output this way in these two closures?

Rust Playground

3
  • 2
    If I remember correctly, GTK runs all event handlers in the main thread, so you don't need any locking. The only requirments on your closures is that they are 'static – you don't need Send or Sync. Wrapping window_id_output in Rc<RefCell<…>> should be all you need. Commented Feb 7, 2023 at 15:49
  • @SvenMarnach Could you give me an example? I tried using Rc<RefCell<...>>: play.rust-lang.org/…. But it's producing compiling errors like: move occurs because window_id_output_rc` has type Rc<RefCell<Option│ <Output>>>, which does not implement the Copy trait`. Commented Feb 7, 2023 at 16:09
  • 1
    I'd suggest you read up on Rc and RefCell in the Rust book. Commented Feb 7, 2023 at 16:30

1 Answer 1

1

As Sven Marnach said, you can use Rc<RefCell<..>> to move data between closures.

The simplest example is probably this one, probably how the gtk event loop works anyways:

use std::rc::Rc; use std::cell::RefCell; fn main() { let a = Rc::new(RefCell::new(0)); let a_ref = Rc::clone(&a); let closure_1 = move || { let mut a = a_ref.borrow_mut(); *a += 1; println!("closure_1: {}", &a); }; let a_ref = Rc::clone(&a); let closure_2 = move || { let a = a_ref.borrow(); println!("closure_2: {}", &a); }; for _ in 1..10 { closure_1(); closure_2(); } } 

For your specific case, see a reduced example below (based on your code):

use std::cell::RefCell; use std::rc::Rc; use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; fn main() { let app = Application::builder() .application_id("com.jwestall.ui-demo") .build(); app.connect_activate(build_ui); app.run(); } fn process(s: &str) -> String { format!("you entered '{}'", s) } fn build_ui(app: &Application) { let input = gtk::Entry::builder() .placeholder_text("input") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); let window = ApplicationWindow::builder() .application(app) .title("gtk-app") .child(&input) .build(); window.show_all(); let shared_var = Rc::new(RefCell::new(String::new())); let shared_var_ref = Rc::clone(&shared_var); input.connect_changed(move |entry| { let input_text = entry.text(); let mut shared = shared_var_ref.borrow_mut(); *shared = process(&input_text); }); let shared_var_ref = Rc::clone(&shared_var); input.connect_activate(move |_entry| { let shared = shared_var_ref.borrow(); println!("{}", shared); window.hide(); window.close(); }); } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, it worked! I'm new to Rc and RefCell. I'll have to read more about them.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.