#How can I spawn a window every x amount of time?

39 messages ยท Page 1 of 1 (latest)

vocal thistle
#

Hi, I wanted to build an app that requires me to spawn a window every x amount of time. The flow looks like:

  • Window opens
  • When event from the frontend (solid.js in my case, but id doesn't matter i guess) is send to the current window it closes
  • After 10 seconds (doesn't matter, could be 2 hours) the window appears again

I have very little experience with Rust (though learning about it as much as I can). This is what I came up with but it doesn't work. Tauri terminates when I call .close() on the created window.

In addition I don't know how to handle waiting for an event before calling sleep. I am doing appWindow.emit("break-end") on the frontend.

Any suggestions / docs I could look at?

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            let handle = app.handle();

            tauri::async_runtime::spawn(async move {
                loop {
                    // Spawn window
                    let window = tauri::WindowBuilder::new(
                        &handle,
                        "app",
                        tauri::WindowUrl::App("index.html".into()),
                    )
                    .build()
                    .unwrap();

                    // I really would like to make this happen on an event from the solid app.
                    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
                    window.close().unwrap(); // This closes the whole app.

                    // Wait for 10 seconds before spawning another window.
                    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
                }
            });

            Ok(())
        })
        .run(tauri::generate_context!())
        .unwrap();
}
vocal thistle
#

Well, I have found the answer for tauri exiting on window close. This is my code for now.

fn main() {
    let app = tauri::Builder::default()
        .setup(|app| {
            let handle = app.handle();

            tauri::async_runtime::spawn(async move {
                loop {
                    let window = tauri::WindowBuilder::new(
                        &handle,
                        "app",
                        tauri::WindowUrl::App("index.html".into()),
                    )
                    .build()
                    .unwrap();

                    // I really would like to make this happen on an event from the solid app.
                    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;

                    window.close().unwrap();

                    // Wait for 10 seconds before spawning another window.
                    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
                }
            });

            Ok(())
        })
        .build(tauri::generate_context!())
        .expect("failed to build the tauri app");

    app.run(|_app_handle, event| match event {
        tauri::RunEvent::ExitRequested { api, .. } => api.prevent_exit(),
        _ => {}
    })
}
rocky edge
#
tauri::Builder::default()
  .build(tauri::generate_context!())
  .expect("error while building tauri application")
  .run(|_app_handle, event| match event {
    tauri::RunEvent::ExitRequested { api, .. } => {
      api.prevent_exit();
    }
    _ => {}
  });
vocal thistle
#

Yeah ๐Ÿ˜„

#

But still, is there a way to block in this async block until event arrives?

#

At the end I would like to have a setTimeout in my solid app, which would emit an event.

rocky edge
#

I have a little project that does basically what you wanna do. Just a small learning project, made it yesterday. I can upload it to github in a while, just gotta wake up a bit more

The basics of it is using a channel that you recv so it pauses until you get a signal, and on the window you add listeners for the close event as well as any extra events you need to handle what the user is doing

vocal thistle
#

I would love that

#

I want to do my own take-a-break reminder, so it would block my screen for some time each hour, but having an additional button that could leave (or postpone) the break would be pretty useful for some situations, that's why I want to have the break-end to be controlled by the frontend app.

rocky edge
#

What my little app does is every N seconds is it pops up a window with a question you gotta answer for it to go away, to help you be forced to learn stuff

vocal thistle
#

oh wow, that is really cool.

rocky edge
#

#off-topic message

#

Doesnt look so cool x)

#

But it does what it should

vocal thistle
#

So, do you have a loop in some thread (or the tokio thing, idk if it is called thread too now, it is 11pm in Poland ๐Ÿ˜‚ ) too?

rocky edge
#

I'll upload it to Github as well, just gotta clean up some stuff first, but here's the basics of it (I use version 2 but this stuff should be more or less identical to version 2)

In main.rs I start a new thread with a function that gets an AppHandle, and I register the prevention of exiting

        .setup(|app| {
            let _ = tauri::async_runtime::spawn(crate::quizzer::run(app.handle()));
            Ok(())
        })
        .build(tauri::generate_context!())
        .expect("error while running tauri application")
        .run(|_app_handle, event| match event {
            tauri::RunEvent::ExitRequested { api, .. } => {
                api.prevent_exit();
            }
            _ => {}
        });
}
#

Lets see, this one is longer, how to write it...

#

In quizzer.rs I have a let (tx, mut rx) = tokio::sync::mpsc::channel(1);

#

I register on the quizzer window the destroyed event to emit a signal

            tauri::WindowEvent::Destroyed => {
                {
                    let state = handle.state::<Mutex<QuizzyState>>();
                    let mut lock = state.lock().unwrap();
                    trace!("Decreasing to {}%", lock.timer.decrement);
                    lock.timer.counter = (lock.timer.counter as f64 * lock.timer.decrement) as u64;
                    trace!("Increasing by: {}", lock.timer.increment as u64);
                    lock.timer.counter += lock.timer.increment as u64;
                }
                let l = sender.lock().unwrap();
                futures::executor::block_on(async move {
                    let _ = l.send(()).await;
                });
            }
#

The recv happens last

        trace!("Recving");
        let _ = rx.recv().await.unwrap();
        let state = app.state::<Mutex<QuizzyState>>();
        let sleep_time = state.lock().unwrap().timer.counter.clone();
        trace!("Sleeping for {} seconds", sleep_time);
        quizzer_window.unlisten(fail_handler);
        tokio::time::sleep(tokio::time::Duration::from_secs(sleep_time)).await;
vocal thistle
#

oooooooooh right

#

thanks

rocky edge
#

No problem ๐Ÿ™‚ I'l upload it so you can check the full code but that's the gist of what's going on

vocal thistle
#

btw let me know when you put this app on Github

#

I can help with ui a little if you want

rocky edge
#

This btw is how I listen to window events

        let handle = app.app_handle();
        let fail_handler = quizzer_window.once("fail", move |_| {
            let state = handle.state::<Mutex<QuizzyState>>();
            let mut lock = state.lock().unwrap();
            let mut decrease = lock.timer.increment * 2.0;
            if lock.timer.counter > 1 {
                while decrease > lock.timer.counter as f64 {
                    decrease = decrease * 0.5;
                }
                trace!("Decreasing by: {}", decrease as u64);
                lock.timer.counter -= decrease as u64;
            }
        });
vocal thistle
#

oh, so you are not using sleep for sleeping?

rocky edge
#

I am using recv to detect when the window has exited, then once I get the signal I start the sleep

vocal thistle
rocky edge
#

There we go

vocal thistle
#

Thanks a lot, I will give it a look tomorrow (<24h)

rocky edge
vocal thistle
#

okay, got it

rocky edge
#

Most of the magic you want is in crates/backend/src/quizzer.rs

vocal thistle
#

The moment I saw mpsc::channel I remembered I have watched a crust of rust episode abuot channels

rocky edge
#

Jon Gjengset has such great videos on Rust ๐Ÿ™‚