Переглянути джерело

refactor(core): add blocking dialog APIs, improve docs, closes #3255 (#3270)

Lucas Fernandes Nogueira 3 роки тому
батько
коміт
4818531aba

+ 5 - 0
.changes/refactor-dialog-apis.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Added the `tauri::api::dialog::blocking` module.

+ 440 - 120
core/tauri/src/api/dialog.rs

@@ -2,12 +2,13 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-//! Types and functions related to display dialog.
+//! Use native message and file open/save dialogs.
+//!
+//! This module exposes non-blocking APIs on its root, relying on callback closures
+//! to give results back. This is particularly useful when running dialogs from the main thread.
+//! When using on asynchronous contexts such as async commands, the [`blocking`] APIs are recommended.
 
-#[cfg(any(dialog_open, dialog_save))]
-use std::path::{Path, PathBuf};
-
-use crate::{Runtime, Window};
+pub use nonblocking::*;
 
 #[cfg(not(target_os = "linux"))]
 macro_rules! run_dialog {
@@ -32,142 +33,461 @@ macro_rules! run_dialog {
   }};
 }
 
-/// The file dialog builder.
+macro_rules! run_dialog_sync {
+  ($e:expr) => {{
+    let (tx, rx) = sync_channel(0);
+    let cb = move |response| {
+      tx.send(response).unwrap();
+    };
+    run_dialog!($e, cb);
+    rx.recv().unwrap()
+  }};
+}
+
+macro_rules! file_dialog_builder {
+  () => {
+    /// The file dialog builder.
+    ///
+    /// Constructs file picker dialogs that can select single/multiple files or directories.
+    #[cfg(any(dialog_open, dialog_save))]
+    #[derive(Debug, Default)]
+    pub struct FileDialogBuilder(rfd::FileDialog);
+
+    #[cfg(any(dialog_open, dialog_save))]
+    impl FileDialogBuilder {
+      /// Gets the default file dialog builder.
+      pub fn new() -> Self {
+        Default::default()
+      }
+
+      /// Add file extension filter. Takes in the name of the filter, and list of extensions
+      #[must_use]
+      pub fn add_filter(mut self, name: impl AsRef<str>, extensions: &[&str]) -> Self {
+        self.0 = self.0.add_filter(name.as_ref(), extensions);
+        self
+      }
+
+      /// Set starting directory of the dialog.
+      #[must_use]
+      pub fn set_directory<P: AsRef<Path>>(mut self, directory: P) -> Self {
+        self.0 = self.0.set_directory(directory);
+        self
+      }
+
+      /// Set starting file name of the dialog.
+      #[must_use]
+      pub fn set_file_name(mut self, file_name: &str) -> Self {
+        self.0 = self.0.set_file_name(file_name);
+        self
+      }
+
+      /// Sets the parent window of the dialog.
+      #[must_use]
+      pub fn set_parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self {
+        self.0 = self.0.set_parent(parent);
+        self
+      }
+
+      /// Set the title of the dialog.
+      #[must_use]
+      pub fn set_title(mut self, title: &str) -> Self {
+        self.0 = self.0.set_title(title);
+        self
+      }
+    }
+  };
+}
+
+/// Blocking interfaces for the dialog APIs.
 ///
-/// Constructs file picker dialogs that can select single/multiple files or directories.
-#[cfg(any(dialog_open, dialog_save))]
-#[derive(Debug, Default)]
-pub struct FileDialogBuilder(rfd::FileDialog);
-
-#[cfg(any(dialog_open, dialog_save))]
-impl FileDialogBuilder {
-  /// Gets the default file dialog builder.
-  pub fn new() -> Self {
-    Default::default()
-  }
+/// The blocking APIs will block the current thread to execute instead of relying on callback closures,
+/// which makes them easier to use.
+///
+/// **NOTE:** You cannot block the main thread when executing the dialog APIs, so you must use the [`tauri::api::dialog`] methods instead.
+/// Examples of main thread context are the [`tauri::App::run`] closure and non-async commmands.
+pub mod blocking {
+  use crate::{Runtime, Window};
+  #[cfg(any(dialog_open, dialog_save))]
+  use std::path::{Path, PathBuf};
+  use std::sync::mpsc::sync_channel;
 
-  /// Add file extension filter. Takes in the name of the filter, and list of extensions
-  #[must_use]
-  pub fn add_filter(mut self, name: impl AsRef<str>, extensions: &[&str]) -> Self {
-    self.0 = self.0.add_filter(name.as_ref(), extensions);
-    self
-  }
+  file_dialog_builder!();
 
-  /// Set starting directory of the dialog.
-  #[must_use]
-  pub fn set_directory<P: AsRef<Path>>(mut self, directory: P) -> Self {
-    self.0 = self.0.set_directory(directory);
-    self
-  }
+  impl FileDialogBuilder {
+    /// Shows the dialog to select a single file.
+    /// This is a blocking operation,
+    /// and should *NOT* be used when running on the main thread context.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::blocking::FileDialogBuilder;
+    /// #[tauri::command]
+    /// fn my_command() {
+    ///   let file_path = FileDialogBuilder::new().pick_file();
+    ///   // do something with the optional file path here
+    ///   // the file path is `None` if the user closed the dialog
+    /// }
+    /// ```
+    pub fn pick_file(self) -> Option<PathBuf> {
+      run_dialog_sync!(self.0.pick_file())
+    }
 
-  /// Set starting file name of the dialog.
-  #[must_use]
-  pub fn set_file_name(mut self, file_name: &str) -> Self {
-    self.0 = self.0.set_file_name(file_name);
-    self
-  }
+    /// Shows the dialog to select multiple files.
+    /// This is a blocking operation,
+    /// and should *NOT* be used when running on the main thread context.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::blocking::FileDialogBuilder;
+    /// #[tauri::command]
+    /// fn my_command() {
+    ///   let file_path = FileDialogBuilder::new().pick_files();
+    ///   // do something with the optional file paths here
+    ///   // the file paths value is `None` if the user closed the dialog
+    /// }
+    /// ```
+    pub fn pick_files(self) -> Option<Vec<PathBuf>> {
+      run_dialog_sync!(self.0.pick_files())
+    }
 
-  /// Sets the parent window of the dialog.
-  #[must_use]
-  pub fn set_parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self {
-    self.0 = self.0.set_parent(parent);
-    self
-  }
+    /// Shows the dialog to select a single folder.
+    /// This is a blocking operation,
+    /// and should *NOT* be used when running on the main thread context.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::blocking::FileDialogBuilder;
+    /// #[tauri::command]
+    /// fn my_command() {
+    ///   let folder_path = FileDialogBuilder::new().pick_folder();
+    ///   // do something with the optional folder path here
+    ///   // the folder path is `None` if the user closed the dialog
+    /// }
+    /// ```
+    pub fn pick_folder(self) -> Option<PathBuf> {
+      run_dialog_sync!(self.0.pick_folder())
+    }
 
-  /// Set the title of the dialog.
-  pub fn set_title(mut self, title: &str) -> Self {
-    self.0 = self.0.set_title(title);
-    self
+    /// Shows the dialog to save a file.
+    /// This is a blocking operation,
+    /// and should *NOT* be used when running on the main thread context.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::blocking::FileDialogBuilder;
+    /// #[tauri::command]
+    /// fn my_command() {
+    ///   let file_path = FileDialogBuilder::new().save_file();
+    ///   // do something with the optional file path here
+    ///   // the file path is `None` if the user closed the dialog
+    /// }
+    /// ```
+    pub fn save_file(self) -> Option<PathBuf> {
+      run_dialog_sync!(self.0.save_file())
+    }
   }
 
-  /// Pick one file.
-  pub fn pick_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
-    run_dialog!(self.0.pick_file(), f)
+  /// Displays a dialog with a message and an optional title with a "yes" and a "no" button and wait for it to be closed.
+  ///
+  /// This is a blocking operation,
+  /// and should *NOT* be used when running on the main thread context.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::blocking::ask;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// let answer = ask(Some(&window), "Tauri", "Is Tauri awesome?");
+  /// // do something with `answer`
+  /// ```
+  #[allow(unused_variables)]
+  pub fn ask<R: Runtime>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+  ) -> bool {
+    run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo)
   }
 
-  /// Pick multiple files.
-  pub fn pick_files<F: FnOnce(Option<Vec<PathBuf>>) + Send + 'static>(self, f: F) {
-    run_dialog!(self.0.pick_files(), f)
+  /// Displays a dialog with a message and an optional title with an "ok" and a "cancel" button and wait for it to be closed.
+  ///
+  /// This is a blocking operation,
+  /// and should *NOT* be used when running on the main thread context.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::blocking::confirm;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// let answer = confirm(Some(&window), "Tauri", "Are you sure?");
+  /// // do something with `answer`
+  /// ```
+  #[allow(unused_variables)]
+  pub fn confirm<R: Runtime>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+  ) -> bool {
+    run_message_dialog(parent_window, title, message, rfd::MessageButtons::OkCancel)
   }
 
-  /// Pick one folder.
-  pub fn pick_folder<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
-    run_dialog!(self.0.pick_folder(), f)
+  /// Displays a message dialog and wait for it to be closed.
+  ///
+  /// This is a blocking operation,
+  /// and should *NOT* be used when running on the main thread context.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::blocking::message;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// message(Some(&window), "Tauri", "Tauri is awesome!");
+  /// ```
+  #[allow(unused_variables)]
+  pub fn message<R: Runtime>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+  ) {
+    let _ = run_message_dialog(parent_window, title, message, rfd::MessageButtons::Ok);
   }
 
-  /// Opens save file dialog.
-  pub fn save_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
-    run_dialog!(self.0.save_file(), f)
+  #[allow(unused_variables)]
+  fn run_message_dialog<R: Runtime>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+    buttons: rfd::MessageButtons,
+  ) -> bool {
+    let (tx, rx) = sync_channel(1);
+    super::nonblocking::run_message_dialog(
+      parent_window,
+      title,
+      message,
+      buttons,
+      move |response| {
+        tx.send(response).unwrap();
+      },
+    );
+    rx.recv().unwrap()
   }
 }
 
-/// Displays a dialog with a message and an optional title with a "yes" and a "no" button.
-#[allow(unused_variables)]
-pub fn ask<R: Runtime, F: FnOnce(bool) + Send + 'static>(
-  parent_window: Option<&Window<R>>,
-  title: impl AsRef<str>,
-  message: impl AsRef<str>,
-  f: F,
-) {
-  run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f)
-}
+mod nonblocking {
+  use crate::{Runtime, Window};
+  #[cfg(any(dialog_open, dialog_save))]
+  use std::path::{Path, PathBuf};
 
-/// Displays a dialog with a message and an optional title with an "ok" and a "cancel" button.
-#[allow(unused_variables)]
-pub fn confirm<R: Runtime, F: FnOnce(bool) + Send + 'static>(
-  parent_window: Option<&Window<R>>,
-  title: impl AsRef<str>,
-  message: impl AsRef<str>,
-  f: F,
-) {
-  run_message_dialog(
-    parent_window,
-    title,
-    message,
-    rfd::MessageButtons::OkCancel,
-    f,
-  )
-}
+  file_dialog_builder!();
 
-/// Displays a message dialog.
-#[allow(unused_variables)]
-pub fn message<R: Runtime>(
-  parent_window: Option<&Window<R>>,
-  title: impl AsRef<str>,
-  message: impl AsRef<str>,
-) {
-  run_message_dialog(
-    parent_window,
-    title,
-    message,
-    rfd::MessageButtons::Ok,
-    |_| {},
-  )
-}
+  impl FileDialogBuilder {
+    /// Shows the dialog to select a single file.
+    /// This is not a blocking operation,
+    /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+    ///
+    /// For usage in other contexts such as commands, prefer [`Self::pick_file`].
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::FileDialogBuilder;
+    /// tauri::Builder::default()
+    ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
+    ///   .expect("failed to build tauri app")
+    ///   .run(|_app, _event| {
+    ///     FileDialogBuilder::new().pick_file(|file_path| {
+    ///       // do something with the optional file path here
+    ///       // the file path is `None` if the user closed the dialog
+    ///     })
+    ///   })
+    /// ```
+    pub fn pick_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
+      run_dialog!(self.0.pick_file(), f)
+    }
+
+    /// Shows the dialog to select multiple files.
+    /// This is not a blocking operation,
+    /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::FileDialogBuilder;
+    /// tauri::Builder::default()
+    ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
+    ///   .expect("failed to build tauri app")
+    ///   .run(|_app, _event| {
+    ///     FileDialogBuilder::new().pick_files(|file_paths| {
+    ///       // do something with the optional file paths here
+    ///       // the file paths value is `None` if the user closed the dialog
+    ///     })
+    ///   })
+    /// ```
+    pub fn pick_files<F: FnOnce(Option<Vec<PathBuf>>) + Send + 'static>(self, f: F) {
+      run_dialog!(self.0.pick_files(), f)
+    }
+
+    /// Shows the dialog to select a single folder.
+    /// This is not a blocking operation,
+    /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::FileDialogBuilder;
+    /// tauri::Builder::default()
+    ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
+    ///   .expect("failed to build tauri app")
+    ///   .run(|_app, _event| {
+    ///     FileDialogBuilder::new().pick_folder(|folder_path| {
+    ///       // do something with the optional folder path here
+    ///       // the folder path is `None` if the user closed the dialog
+    ///     })
+    ///   })
+    /// ```
+    pub fn pick_folder<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
+      run_dialog!(self.0.pick_folder(), f)
+    }
 
-#[allow(unused_variables)]
-fn run_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
-  parent_window: Option<&Window<R>>,
-  title: impl AsRef<str>,
-  message: impl AsRef<str>,
-  buttons: rfd::MessageButtons,
-  f: F,
-) {
-  let title = title.as_ref().to_string();
-  let message = message.as_ref().to_string();
-  #[allow(unused_mut)]
-  let mut builder = rfd::MessageDialog::new()
-    .set_title(&title)
-    .set_description(&message)
-    .set_buttons(buttons)
-    .set_level(rfd::MessageLevel::Info);
-
-  #[cfg(any(windows, target_os = "macos"))]
-  {
-    if let Some(window) = parent_window {
-      builder = builder.set_parent(window);
+    /// Shows the dialog to save a file.
+    ///
+    /// This is not a blocking operation,
+    /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri::api::dialog::FileDialogBuilder;
+    /// tauri::Builder::default()
+    ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
+    ///   .expect("failed to build tauri app")
+    ///   .run(|_app, _event| {
+    ///     FileDialogBuilder::new().save_file(|file_path| {
+    ///       // do something with the optional file path here
+    ///       // the file path is `None` if the user closed the dialog
+    ///     })
+    ///   })
+    /// ```
+    pub fn save_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
+      run_dialog!(self.0.save_file(), f)
     }
   }
 
-  run_dialog!(builder.show(), f)
+  /// Displays a non-blocking dialog with a message and an optional title with a "yes" and a "no" button.
+  ///
+  /// This is not a blocking operation,
+  /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::ask;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// ask(Some(&window), "Tauri", "Is Tauri awesome?", |answer| {
+  ///   // do something with `answer`
+  /// });
+  /// ```
+  #[allow(unused_variables)]
+  pub fn ask<R: Runtime, F: FnOnce(bool) + Send + 'static>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+    f: F,
+  ) {
+    run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f)
+  }
+
+  /// Displays a non-blocking dialog with a message and an optional title with an "ok" and a "cancel" button.
+  ///
+  /// This is not a blocking operation,
+  /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::confirm;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// confirm(Some(&window), "Tauri", "Are you sure?", |answer| {
+  ///   // do something with `answer`
+  /// });
+  /// ```
+  #[allow(unused_variables)]
+  pub fn confirm<R: Runtime, F: FnOnce(bool) + Send + 'static>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+    f: F,
+  ) {
+    run_message_dialog(
+      parent_window,
+      title,
+      message,
+      rfd::MessageButtons::OkCancel,
+      f,
+    )
+  }
+
+  /// Displays a non-blocking message dialog.
+  ///
+  /// This is not a blocking operation,
+  /// and should be used when running on the main thread to avoid deadlocks with the event loop.
+  ///
+  /// # Example
+  ///
+  /// ```rust,no_run
+  /// use tauri::api::dialog::message;
+  /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap();
+  /// # let window = tauri::Manager::get_window(&app, "main").unwrap();
+  /// message(Some(&window), "Tauri", "Tauri is awesome!");
+  /// ```
+  #[allow(unused_variables)]
+  pub fn message<R: Runtime>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+  ) {
+    run_message_dialog(
+      parent_window,
+      title,
+      message,
+      rfd::MessageButtons::Ok,
+      |_| {},
+    )
+  }
+
+  #[allow(unused_variables)]
+  pub(crate) fn run_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
+    parent_window: Option<&Window<R>>,
+    title: impl AsRef<str>,
+    message: impl AsRef<str>,
+    buttons: rfd::MessageButtons,
+    f: F,
+  ) {
+    let title = title.as_ref().to_string();
+    let message = message.as_ref().to_string();
+    #[allow(unused_mut)]
+    let mut builder = rfd::MessageDialog::new()
+      .set_title(&title)
+      .set_description(&message)
+      .set_buttons(buttons)
+      .set_level(rfd::MessageLevel::Info);
+
+    #[cfg(any(windows, target_os = "macos"))]
+    {
+      if let Some(window) = parent_window {
+        builder = builder.set_parent(window);
+      }
+    }
+
+    run_dialog!(builder.show(), f)
+  }
 }

+ 14 - 25
core/tauri/src/endpoints/dialog.rs

@@ -4,14 +4,12 @@
 
 use super::{InvokeContext, InvokeResponse};
 #[cfg(any(dialog_open, dialog_save))]
-use crate::api::dialog::FileDialogBuilder;
+use crate::api::dialog::blocking::FileDialogBuilder;
 use crate::Runtime;
 use serde::Deserialize;
 use tauri_macros::{module_command_handler, CommandModule};
 
 use std::path::PathBuf;
-#[cfg(any(dialog_message, dialog_ask, dialog_confirm))]
-use std::sync::mpsc::channel;
 
 #[allow(dead_code)]
 #[derive(Debug, Clone, Deserialize)]
@@ -99,17 +97,15 @@ impl Cmd {
       dialog_builder = dialog_builder.add_filter(filter.name, &extensions);
     }
 
-    let (tx, rx) = channel();
-
-    if options.directory {
-      dialog_builder.pick_folder(move |p| tx.send(p.into()).unwrap());
+    let res = if options.directory {
+      dialog_builder.pick_folder().into()
     } else if options.multiple {
-      dialog_builder.pick_files(move |p| tx.send(p.into()).unwrap());
+      dialog_builder.pick_files().into()
     } else {
-      dialog_builder.pick_file(move |p| tx.send(p.into()).unwrap());
-    }
+      dialog_builder.pick_file().into()
+    };
 
-    Ok(rx.recv().unwrap())
+    Ok(res)
   }
 
   #[module_command_handler(dialog_save, "dialog > save")]
@@ -130,14 +126,13 @@ impl Cmd {
       let extensions: Vec<&str> = filter.extensions.iter().map(|s| &**s).collect();
       dialog_builder = dialog_builder.add_filter(filter.name, &extensions);
     }
-    let (tx, rx) = channel();
-    dialog_builder.save_file(move |p| tx.send(p).unwrap());
-    Ok(rx.recv().unwrap())
+
+    Ok(dialog_builder.save_file())
   }
 
   #[module_command_handler(dialog_message, "dialog > message")]
   fn message_dialog<R: Runtime>(context: InvokeContext<R>, message: String) -> crate::Result<()> {
-    crate::api::dialog::message(
+    crate::api::dialog::blocking::message(
       Some(&context.window),
       &context.window.app_handle.package_info().name,
       message,
@@ -151,14 +146,11 @@ impl Cmd {
     title: Option<String>,
     message: String,
   ) -> crate::Result<bool> {
-    let (tx, rx) = channel();
-    crate::api::dialog::ask(
+    Ok(crate::api::dialog::blocking::ask(
       Some(&context.window),
       title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
       message,
-      move |m| tx.send(m).unwrap(),
-    );
-    Ok(rx.recv().unwrap())
+    ))
   }
 
   #[module_command_handler(dialog_confirm, "dialog > confirm")]
@@ -167,14 +159,11 @@ impl Cmd {
     title: Option<String>,
     message: String,
   ) -> crate::Result<bool> {
-    let (tx, rx) = channel();
-    crate::api::dialog::confirm(
+    Ok(crate::api::dialog::blocking::confirm(
       Some(&context.window),
       title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
       message,
-      move |m| tx.send(m).unwrap(),
-    );
-    Ok(rx.recv().unwrap())
+    ))
   }
 }
 

+ 1 - 7
core/tauri/src/endpoints/notification.rs

@@ -111,18 +111,12 @@ fn request_permission<R: Runtime>(context: &InvokeContext<R>) -> bool {
   if let Some(allow_notification) = settings.allow_notification {
     return allow_notification;
   }
-  let (tx, rx) = std::sync::mpsc::channel();
-  crate::api::dialog::ask(
+  let answer = crate::api::dialog::blocking::ask(
     Some(&context.window),
     "Permissions",
     "This app wants to show notifications. Do you allow?",
-    move |answer| {
-      tx.send(answer).unwrap();
-    },
   );
 
-  let answer = rx.recv().unwrap();
-
   settings.allow_notification = Some(answer);
   let _ = crate::settings::write_settings(
     &context.config,

+ 7 - 14
core/tauri/src/updater/mod.rs

@@ -333,14 +333,12 @@ mod error;
 pub use self::error::Error;
 
 use crate::{
-  api::{dialog::ask, process::restart},
+  api::{dialog::blocking::ask, process::restart},
   runtime::Runtime,
   utils::config::UpdaterConfig,
   Env, Manager, Window,
 };
 
-use std::sync::mpsc::channel;
-
 /// Check for new updates
 pub const EVENT_CHECK_UPDATE: &str = "tauri://update";
 /// New update available
@@ -539,11 +537,9 @@ async fn prompt_for_install<R: Runtime>(
   // remove single & double quote
   let escaped_body = body.replace(&['\"', '\''][..], "");
 
-  let (tx, rx) = channel();
-
   // todo(lemarier): We should review this and make sure we have
   // something more conventional.
-  ask(
+  let should_install = ask(
     Some(&window),
     format!(r#"A new version of {} is available! "#, app_name),
     format!(
@@ -555,10 +551,9 @@ Release Notes:
 {}"#,
       app_name, updater.version, updater.current_version, escaped_body,
     ),
-    move |should_install| tx.send(should_install).unwrap(),
   );
 
-  if rx.recv().unwrap() {
+  if should_install {
     // Launch updater download process
     // macOS we display the `Ready to restart dialog` asking to restart
     // Windows is closing the current App and launch the downloaded MSI when ready (the process stop here)
@@ -567,16 +562,14 @@ Release Notes:
 
     // Ask user if we need to restart the application
     let env = window.state::<Env>().inner().clone();
-    ask(
+    let should_exit = ask(
       Some(&window),
       "Ready to Restart",
       "The installation was successful, do you want to restart the application now?",
-      move |should_exit| {
-        if should_exit {
-          restart(&env);
-        }
-      },
     );
+    if should_exit {
+      restart(&env);
+    }
   }
 
   Ok(())

+ 201 - 81
examples/api/src-tauri/Cargo.lock

@@ -29,7 +29,7 @@ version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "cipher",
  "cpufeatures",
  "opaque-debug",
@@ -101,6 +101,42 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
 
+[[package]]
+name = "async-broadcast"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b"
+dependencies = [
+ "easy-parallel",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "slab",
+]
+
 [[package]]
 name = "async-io"
 version = "1.6.0"
@@ -120,6 +156,43 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "async-lock"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-recursion"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-task"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8"
+
+[[package]]
+name = "async-trait"
+version = "0.1.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "atk"
 version = "0.14.0"
@@ -220,7 +293,7 @@ dependencies = [
  "arrayref",
  "arrayvec 0.7.2",
  "cc",
- "cfg-if 1.0.0",
+ "cfg-if",
  "constant_time_eq",
  "digest 0.10.1",
  "rayon",
@@ -243,9 +316,9 @@ dependencies = [
 
 [[package]]
 name = "block-buffer"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
+checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b"
 dependencies = [
  "generic-array",
 ]
@@ -376,12 +449,6 @@ dependencies = [
  "smallvec",
 ]
 
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -539,7 +606,7 @@ version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
 ]
 
 [[package]]
@@ -548,7 +615,7 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crossbeam-utils",
 ]
 
@@ -558,7 +625,7 @@ version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crossbeam-epoch",
  "crossbeam-utils",
 ]
@@ -569,7 +636,7 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crossbeam-utils",
  "lazy_static",
  "memoffset",
@@ -582,7 +649,7 @@ version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "lazy_static",
 ]
 
@@ -776,7 +843,7 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
 dependencies = [
- "block-buffer 0.10.0",
+ "block-buffer 0.10.1",
  "crypto-common",
  "generic-array",
  "subtle",
@@ -799,7 +866,7 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "dirs-sys-next",
 ]
 
@@ -835,6 +902,12 @@ dependencies = [
  "dtoa",
 ]
 
+[[package]]
+name = "easy-parallel"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946"
+
 [[package]]
 name = "either"
 version = "1.6.1"
@@ -849,9 +922,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
 
 [[package]]
 name = "enumflags2"
-version = "0.6.4"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
+checksum = "a25c90b056b3f84111cf183cbeddef0d3a0bbe9a674f057e1a1533c315f24def"
 dependencies = [
  "enumflags2_derive",
  "serde",
@@ -859,15 +932,21 @@ dependencies = [
 
 [[package]]
 name = "enumflags2_derive"
-version = "0.6.4"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
+checksum = "144ec79496cbab6f84fa125dc67be9264aef22eb8a28da8454d9c33f15108da4"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn",
 ]
 
+[[package]]
+name = "event-listener"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
+
 [[package]]
 name = "fastrand"
 version = "1.7.0"
@@ -893,7 +972,7 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "redox_syscall 0.2.10",
  "winapi",
@@ -905,7 +984,7 @@ version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crc32fast",
  "libc",
  "miniz_oxide 0.4.4",
@@ -1165,7 +1244,7 @@ version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "wasi 0.9.0+wasi-snapshot-preview1",
 ]
@@ -1176,7 +1255,7 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "wasi 0.10.2+wasi-snapshot-preview1",
 ]
@@ -1381,6 +1460,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
 [[package]]
 name = "html5ever"
 version = "0.25.1"
@@ -1491,7 +1576,7 @@ version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
 ]
 
 [[package]]
@@ -1635,7 +1720,7 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
 ]
 
 [[package]]
@@ -1644,7 +1729,7 @@ version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "generator",
  "scoped-tls",
  "serde",
@@ -1767,16 +1852,6 @@ dependencies = [
  "tempfile",
 ]
 
-[[package]]
-name = "nb-connect"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15"
-dependencies = [
- "libc",
- "socket2",
-]
-
 [[package]]
 name = "ndk"
 version = "0.4.0"
@@ -1831,15 +1906,15 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 
 [[package]]
 name = "nix"
-version = "0.17.0"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
 dependencies = [
  "bitflags",
  "cc",
- "cfg-if 0.1.10",
+ "cfg-if",
  "libc",
- "void",
+ "memoffset",
 ]
 
 [[package]]
@@ -1850,9 +1925,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
 
 [[package]]
 name = "notify-rust"
-version = "4.5.5"
+version = "4.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ebab865e67efdd7182a88d76cadbdd2a8d02d1c7a4e16bb7c234016a12cac"
+checksum = "367e1355a950d3e758e414f3ca1b3981a57a2aa1fa3338eb0059f5b230b6ffa4"
 dependencies = [
  "mac-notification-sys",
  "serde",
@@ -1991,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
 dependencies = [
  "bitflags",
- "cfg-if 1.0.0",
+ "cfg-if",
  "foreign-types",
  "libc",
  "once_cell",
@@ -2017,6 +2092,16 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "ordered-stream"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "os_info"
 version = "3.2.0"
@@ -2095,7 +2180,7 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "instant",
  "libc",
  "redox_syscall 0.2.10",
@@ -2270,7 +2355,7 @@ version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "log",
  "wepoll-ffi",
@@ -2283,7 +2368,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "cpufeatures",
  "opaque-debug",
  "universal-hash",
@@ -2629,7 +2714,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
 dependencies = [
- "semver 1.0.4",
+ "semver 1.0.5",
 ]
 
 [[package]]
@@ -2729,9 +2814,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.4"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
+checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
 
 [[package]]
 name = "semver-parser"
@@ -2849,6 +2934,21 @@ dependencies = [
  "stable_deref_trait",
 ]
 
+[[package]]
+name = "sha1"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+dependencies = [
+ "sha1_smol",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
 [[package]]
 name = "sha2"
 version = "0.9.9"
@@ -2856,7 +2956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
 dependencies = [
  "block-buffer 0.9.0",
- "cfg-if 1.0.0",
+ "cfg-if",
  "cpufeatures",
  "digest 0.9.0",
  "opaque-debug",
@@ -3088,7 +3188,7 @@ dependencies = [
 [[package]]
 name = "tao"
 version = "0.5.2"
-source = "git+https://github.com/tauri-apps/tao?branch=next#cf3d3b54ae3e32b4b7577240a93510f46c30593f"
+source = "git+https://github.com/tauri-apps/tao?branch=next#6ee36748252e3ce26e0341464ebbab1d1adb8c28"
 dependencies = [
  "bitflags",
  "cairo-rs",
@@ -3132,7 +3232,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "core-foundation-sys",
  "libc",
  "objc",
@@ -3180,7 +3280,7 @@ dependencies = [
  "raw-window-handle",
  "regex",
  "rfd",
- "semver 1.0.4",
+ "semver 1.0.5",
  "serde",
  "serde_json",
  "serde_repr",
@@ -3306,7 +3406,7 @@ version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "fastrand",
  "libc",
  "redox_syscall 0.2.10",
@@ -3427,7 +3527,7 @@ version = "0.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "pin-project-lite",
  "tracing-attributes",
  "tracing-core",
@@ -3467,9 +3567,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.7"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22"
+checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa"
 dependencies = [
  "ansi_term",
  "lazy_static",
@@ -3599,12 +3699,6 @@ version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
 [[package]]
 name = "waker-fn"
 version = "1.1.0"
@@ -3640,7 +3734,7 @@ version = "0.2.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "wasm-bindgen-macro",
 ]
 
@@ -3665,7 +3759,7 @@ version = "0.4.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "js-sys",
  "wasm-bindgen",
  "web-sys",
@@ -4093,39 +4187,65 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
 
 [[package]]
 name = "zbus"
-version = "1.9.1"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2326acc379a3ac4e34b794089f5bdb17086bf29a5fdf619b7b4cc772dc2e9dad"
+checksum = "7bb86f3d4592e26a48b2719742aec94f8ae6238ebde20d98183ee185d1275e9a"
 dependencies = [
+ "async-broadcast",
+ "async-channel",
+ "async-executor",
  "async-io",
+ "async-lock",
+ "async-recursion",
+ "async-task",
+ "async-trait",
  "byteorder",
  "derivative",
  "enumflags2",
- "fastrand",
- "futures",
- "nb-connect",
+ "event-listener",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "hex",
+ "lazy_static",
  "nix",
  "once_cell",
- "polling",
- "scoped-tls",
+ "ordered-stream",
+ "rand 0.8.4",
  "serde",
  "serde_repr",
+ "sha1",
+ "static_assertions",
+ "winapi",
  "zbus_macros",
+ "zbus_names",
  "zvariant",
 ]
 
 [[package]]
 name = "zbus_macros"
-version = "1.9.1"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a482c56029e48681b89b92b5db3c446db0915e8dd1052c0328a574eda38d5f93"
+checksum = "36823cc10fddc3c6b19f048903262dacaf8274170e9a255784bdd8b4570a8040"
 dependencies = [
- "proc-macro-crate 0.1.5",
+ "proc-macro-crate 1.1.0",
  "proc-macro2",
  "quote",
+ "regex",
  "syn",
 ]
 
+[[package]]
+name = "zbus_names"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "zvariant",
+]
+
 [[package]]
 name = "zip"
 version = "0.5.13"
@@ -4171,9 +4291,9 @@ dependencies = [
 
 [[package]]
 name = "zvariant"
-version = "2.10.0"
+version = "3.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525"
+checksum = "49ea5dc38b2058fae6a5b79009388143dadce1e91c26a67f984a0fc0381c8033"
 dependencies = [
  "byteorder",
  "enumflags2",
@@ -4185,9 +4305,9 @@ dependencies = [
 
 [[package]]
 name = "zvariant_derive"
-version = "2.10.0"
+version = "3.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9"
+checksum = "8c2cecc5a61c2a053f7f653a24cd15b3b0195d7f7ddb5042c837fb32e161fb7a"
 dependencies = [
  "proc-macro-crate 1.1.0",
  "proc-macro2",