Skip to content

Commit

Permalink
fix(core): add windows 7 notification support (#4491)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Jun 28, 2022
1 parent 1949aa8 commit 57039fb
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changes/is-windows-7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri-utils": patch
---

Added `platform::is_windows_7`.
5 changes: 5 additions & 0 deletions .changes/win7-notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Added `Notification::notify` API behind the `windows7-compat` Cargo feature, which includes Windows 7 support.
2 changes: 1 addition & 1 deletion .github/workflows/lint-fmt-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
clippy:
- { args: '', key: 'empty' }
- {
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart',
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart',
key: 'all'
}
- { args: '--features custom-protocol', key: 'custom-protocol' }
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ jobs:
run: |
cargo test
cargo test --features api-all
cargo test --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart
cargo test --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart
2 changes: 1 addition & 1 deletion .github/workflows/udeps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
clippy:
- {
path: './core/tauri/Cargo.toml',
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart'
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart'
}
- { path: './core/tauri-build/Cargo.toml', args: '--all-features' }
- { path: './core/tauri-codegen/Cargo.toml', args: '--all-features' }
Expand Down
11 changes: 11 additions & 0 deletions core/tauri-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ semver = "1"
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.4"

[target."cfg(windows)".dependencies.windows]
version = "0.37.0"
features = [
"alloc",
"implement",
"Win32_Foundation",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_SystemInformation"
]

[features]
build = [ "proc-macro2", "quote" ]
compression = [ "brotli" ]
Expand Down
72 changes: 72 additions & 0 deletions core/tauri-utils/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,75 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<Path

res
}

#[cfg(windows)]
pub use windows_platform::is_windows_7;

#[cfg(windows)]
mod windows_platform {
use windows::Win32::{
Foundation::FARPROC,
System::{
LibraryLoader::{GetProcAddress, LoadLibraryA},
SystemInformation::OSVERSIONINFOW,
},
};

/// Checks if we're running on Windows 7.
pub fn is_windows_7() -> bool {
if let Some(v) = get_windows_ver() {
// windows 7 is 6.1
if v.0 == 6 && v.1 == 1 {
return true;
}
}
false
}

fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));

let module = unsafe { LoadLibraryA(library) }.unwrap_or_default();
if module.is_invalid() {
None
} else {
Some(unsafe { GetProcAddress(module, function) })
}
}

macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
.map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
};
}

/// Returns a tuple of (major, minor, buildnumber)
fn get_windows_ver() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
dwBuildNumber: 0,
dwPlatformId: 0,
szCSDVersion: [0; 128],
};

let status = (rtl_get_version)(&mut vi as _);

if status >= 0 {
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
} else {
None
}
}
} else {
None
}
}
}
9 changes: 6 additions & 3 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ features = [
"wry",
"custom-protocol",
"api-all",
"windows7-compat",
"cli",
"updater",
"fs-extract-api",
Expand Down Expand Up @@ -105,10 +106,11 @@ objc = "0.2"

[target."cfg(windows)".dependencies]
webview2-com = "0.16.0"
win7-notifications = { version = "0.3.0", optional = true }

[target."cfg(windows)".dependencies.windows]
version = "0.37.0"
features = [ "Win32_Foundation" ]
[target."cfg(windows)".dependencies.windows]
version = "0.37.0"
features = [ "Win32_Foundation" ]

[build-dependencies]
heck = "0.4"
Expand Down Expand Up @@ -162,6 +164,7 @@ macos-private-api = [
"tauri-runtime/macos-private-api",
"tauri-runtime-wry/macos-private-api"
]
windows7-compat = [ "win7-notifications" ]
window-data-url = [ "data-url" ]
api-all = [
"clipboard-all",
Expand Down
93 changes: 92 additions & 1 deletion core/tauri/src/api/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,28 @@ impl Notification {
}

/// Shows the notification.
///
/// # Examples
///
/// ```no_run
/// use tauri::api::notification::Notification;
///
/// // on an actual app, remove the string argument
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
/// Notification::new(&context.config().tauri.bundle.identifier)
/// .title("Tauri")
/// .body("Tauri is awesome!")
/// .show()
/// .unwrap();
/// ```
///
/// ## Platform-specific
///
/// - **Windows**: Not supported on Windows 7. If your app targets it, enable the `windows7-compat` feature and use [`Self::notify`].
#[cfg_attr(
all(not(doc_cfg), feature = "windows7-compat"),
deprecated = "This function does not work on Windows 7. Use `Self::notify` instead."
)]
pub fn show(self) -> crate::api::Result<()> {
let mut notification = notify_rust::Notification::new();
if let Some(body) = self.body {
Expand Down Expand Up @@ -108,7 +130,76 @@ impl Notification {
}

crate::async_runtime::spawn(async move {
notification.show().expect("failed to show notification");
let _ = notification.show();
});

Ok(())
}

/// Shows the notification. This API is similar to [`Self::show`], but it also works on Windows 7.
///
/// # Examples
///
/// ```no_run
/// use tauri::api::notification::Notification;
///
/// // on an actual app, remove the string argument
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
/// let identifier = context.config().tauri.bundle.identifier.clone();
///
/// tauri::Builder::default()
/// .setup(move |app| {
/// Notification::new(&identifier)
/// .title("Tauri")
/// .body("Tauri is awesome!")
/// .notify(&app.handle())
/// .unwrap();
/// Ok(())
/// })
/// .run(context)
/// .expect("error while running tauri application");
/// ```
#[cfg(feature = "windows7-compat")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "windows7-compat")))]
#[allow(unused_variables)]
pub fn notify<R: crate::Runtime>(self, app: &crate::AppHandle<R>) -> crate::api::Result<()> {
#[cfg(windows)]
{
if crate::utils::platform::is_windows_7() {
self.notify_win7(app)
} else {
#[allow(deprecated)]
self.show()
}
}
#[cfg(not(windows))]
{
#[allow(deprecated)]
self.show()
}
}

#[cfg(all(windows, feature = "windows7-compat"))]
fn notify_win7<R: crate::Runtime>(self, app: &crate::AppHandle<R>) -> crate::api::Result<()> {
let app = app.clone();
let default_window_icon = app.manager.inner.default_window_icon.clone();
let _ = app.run_on_main_thread(move || {
let mut notification = win7_notifications::Notification::new();
if let Some(body) = self.body {
notification.body(&body);
}
if let Some(title) = self.title {
notification.summary(&title);
}
if let Some(crate::Icon::Rgba {
rgba,
width,
height,
}) = default_window_icon
{
notification.icon(rgba, width, height);
}
let _ = notification.show();
});

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl<R: Runtime> AssetResolver<R> {
#[derive(Debug)]
pub struct AppHandle<R: Runtime> {
runtime_handle: R::Handle,
manager: WindowManager<R>,
pub(crate) manager: WindowManager<R>,
#[cfg(feature = "global-shortcut")]
global_shortcut_manager: R::GlobalShortcutManager,
#[cfg(feature = "clipboard")]
Expand Down
5 changes: 5 additions & 0 deletions core/tauri/src/endpoints/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ impl Cmd {
if let Some(icon) = options.icon {
notification = notification.icon(icon);
}
#[cfg(feature = "windows7-compat")]
{
notification.notify(&context.window.app_handle)?;
}
#[cfg(not(feature = "windows7-compat"))]
notification.show()?;
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions core/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
//! - **cli**: Enables usage of `clap` for CLI argument parsing. Enabled by default if the `cli` config is defined on the `tauri.conf.json` file.
//! - **system-tray**: Enables application system tray API. Enabled by default if the `systemTray` config is defined on the `tauri.conf.json` file.
//! - **macos-private-api**: Enables features only available in **macOS**'s private APIs, currently the `transparent` window functionality and the `fullScreenEnabled` preference setting to `true`. Enabled by default if the `tauri > macosPrivateApi` config flag is set to `true` on the `tauri.conf.json` file.
//! - **windows7-compat**: Enables compatibility with Windows 7 for the notification API.
//! - **window-data-url**: Enables usage of data URLs on the webview.
//! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries.
//! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ pub struct InnerWindowManager<R: Runtime> {

config: Arc<Config>,
assets: Arc<dyn Assets>,
default_window_icon: Option<Icon>,
pub(crate) default_window_icon: Option<Icon>,
pub(crate) app_icon: Option<Vec<u8>>,

package_info: PackageInfo,
Expand Down
18 changes: 17 additions & 1 deletion examples/api/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@ tauri-build = { path = "../../../core/tauri-build", features = ["isolation"] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "global-shortcut", "http-multipart", "icon-ico", "icon-png", "isolation", "macos-private-api", "reqwest-client", "system-tray", "updater"] }
tiny_http = "0.11"

[dependencies.tauri]
path = "../../../core/tauri"
features = [
"api-all",
"cli",
"global-shortcut",
"http-multipart",
"icon-ico",
"icon-png",
"isolation",
"macos-private-api",
"windows7-compat",
"reqwest-client",
"system-tray",
"updater"
]

[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]
Expand Down

0 comments on commit 57039fb

Please sign in to comment.