Skip to content

Commit 57039fb

Browse files
authored
fix(core): add windows 7 notification support (#4491)
1 parent 1949aa8 commit 57039fb

File tree

14 files changed

+219
-10
lines changed

14 files changed

+219
-10
lines changed

.changes/is-windows-7.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch
3+
---
4+
5+
Added `platform::is_windows_7`.

.changes/win7-notifications.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Added `Notification::notify` API behind the `windows7-compat` Cargo feature, which includes Windows 7 support.

.github/workflows/lint-fmt-core.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
clippy:
5151
- { args: '', key: 'empty' }
5252
- {
53-
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart',
53+
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart',
5454
key: 'all'
5555
}
5656
- { args: '--features custom-protocol', key: 'custom-protocol' }

.github/workflows/test-core.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,4 @@ jobs:
8989
run: |
9090
cargo test
9191
cargo test --features api-all
92-
cargo test --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart
92+
cargo test --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart

.github/workflows/udeps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
clippy:
3030
- {
3131
path: './core/tauri/Cargo.toml',
32-
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,http-multipart'
32+
args: '--features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart'
3333
}
3434
- { path: './core/tauri-build/Cargo.toml', args: '--all-features' }
3535
- { path: './core/tauri-codegen/Cargo.toml', args: '--all-features' }

core/tauri-utils/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ semver = "1"
3838
[target."cfg(target_os = \"linux\")".dependencies]
3939
heck = "0.4"
4040

41+
[target."cfg(windows)".dependencies.windows]
42+
version = "0.37.0"
43+
features = [
44+
"alloc",
45+
"implement",
46+
"Win32_Foundation",
47+
"Win32_System_Com",
48+
"Win32_System_LibraryLoader",
49+
"Win32_System_SystemInformation"
50+
]
51+
4152
[features]
4253
build = [ "proc-macro2", "quote" ]
4354
compression = [ "brotli" ]

core/tauri-utils/src/platform.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,75 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<Path
196196

197197
res
198198
}
199+
200+
#[cfg(windows)]
201+
pub use windows_platform::is_windows_7;
202+
203+
#[cfg(windows)]
204+
mod windows_platform {
205+
use windows::Win32::{
206+
Foundation::FARPROC,
207+
System::{
208+
LibraryLoader::{GetProcAddress, LoadLibraryA},
209+
SystemInformation::OSVERSIONINFOW,
210+
},
211+
};
212+
213+
/// Checks if we're running on Windows 7.
214+
pub fn is_windows_7() -> bool {
215+
if let Some(v) = get_windows_ver() {
216+
// windows 7 is 6.1
217+
if v.0 == 6 && v.1 == 1 {
218+
return true;
219+
}
220+
}
221+
false
222+
}
223+
224+
fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
225+
assert_eq!(library.chars().last(), Some('\0'));
226+
assert_eq!(function.chars().last(), Some('\0'));
227+
228+
let module = unsafe { LoadLibraryA(library) }.unwrap_or_default();
229+
if module.is_invalid() {
230+
None
231+
} else {
232+
Some(unsafe { GetProcAddress(module, function) })
233+
}
234+
}
235+
236+
macro_rules! get_function {
237+
($lib:expr, $func:ident) => {
238+
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
239+
.map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
240+
};
241+
}
242+
243+
/// Returns a tuple of (major, minor, buildnumber)
244+
fn get_windows_ver() -> Option<(u32, u32, u32)> {
245+
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
246+
let handle = get_function!("ntdll.dll", RtlGetVersion);
247+
if let Some(rtl_get_version) = handle {
248+
unsafe {
249+
let mut vi = OSVERSIONINFOW {
250+
dwOSVersionInfoSize: 0,
251+
dwMajorVersion: 0,
252+
dwMinorVersion: 0,
253+
dwBuildNumber: 0,
254+
dwPlatformId: 0,
255+
szCSDVersion: [0; 128],
256+
};
257+
258+
let status = (rtl_get_version)(&mut vi as _);
259+
260+
if status >= 0 {
261+
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
262+
} else {
263+
None
264+
}
265+
}
266+
} else {
267+
None
268+
}
269+
}
270+
}

core/tauri/Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ features = [
2424
"wry",
2525
"custom-protocol",
2626
"api-all",
27+
"windows7-compat",
2728
"cli",
2829
"updater",
2930
"fs-extract-api",
@@ -105,10 +106,11 @@ objc = "0.2"
105106

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

109-
[target."cfg(windows)".dependencies.windows]
110-
version = "0.37.0"
111-
features = [ "Win32_Foundation" ]
111+
[target."cfg(windows)".dependencies.windows]
112+
version = "0.37.0"
113+
features = [ "Win32_Foundation" ]
112114

113115
[build-dependencies]
114116
heck = "0.4"
@@ -162,6 +164,7 @@ macos-private-api = [
162164
"tauri-runtime/macos-private-api",
163165
"tauri-runtime-wry/macos-private-api"
164166
]
167+
windows7-compat = [ "win7-notifications" ]
165168
window-data-url = [ "data-url" ]
166169
api-all = [
167170
"clipboard-all",

core/tauri/src/api/notification.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,28 @@ impl Notification {
7373
}
7474

7575
/// Shows the notification.
76+
///
77+
/// # Examples
78+
///
79+
/// ```no_run
80+
/// use tauri::api::notification::Notification;
81+
///
82+
/// // on an actual app, remove the string argument
83+
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
84+
/// Notification::new(&context.config().tauri.bundle.identifier)
85+
/// .title("Tauri")
86+
/// .body("Tauri is awesome!")
87+
/// .show()
88+
/// .unwrap();
89+
/// ```
90+
///
91+
/// ## Platform-specific
92+
///
93+
/// - **Windows**: Not supported on Windows 7. If your app targets it, enable the `windows7-compat` feature and use [`Self::notify`].
94+
#[cfg_attr(
95+
all(not(doc_cfg), feature = "windows7-compat"),
96+
deprecated = "This function does not work on Windows 7. Use `Self::notify` instead."
97+
)]
7698
pub fn show(self) -> crate::api::Result<()> {
7799
let mut notification = notify_rust::Notification::new();
78100
if let Some(body) = self.body {
@@ -108,7 +130,76 @@ impl Notification {
108130
}
109131

110132
crate::async_runtime::spawn(async move {
111-
notification.show().expect("failed to show notification");
133+
let _ = notification.show();
134+
});
135+
136+
Ok(())
137+
}
138+
139+
/// Shows the notification. This API is similar to [`Self::show`], but it also works on Windows 7.
140+
///
141+
/// # Examples
142+
///
143+
/// ```no_run
144+
/// use tauri::api::notification::Notification;
145+
///
146+
/// // on an actual app, remove the string argument
147+
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
148+
/// let identifier = context.config().tauri.bundle.identifier.clone();
149+
///
150+
/// tauri::Builder::default()
151+
/// .setup(move |app| {
152+
/// Notification::new(&identifier)
153+
/// .title("Tauri")
154+
/// .body("Tauri is awesome!")
155+
/// .notify(&app.handle())
156+
/// .unwrap();
157+
/// Ok(())
158+
/// })
159+
/// .run(context)
160+
/// .expect("error while running tauri application");
161+
/// ```
162+
#[cfg(feature = "windows7-compat")]
163+
#[cfg_attr(doc_cfg, doc(cfg(feature = "windows7-compat")))]
164+
#[allow(unused_variables)]
165+
pub fn notify<R: crate::Runtime>(self, app: &crate::AppHandle<R>) -> crate::api::Result<()> {
166+
#[cfg(windows)]
167+
{
168+
if crate::utils::platform::is_windows_7() {
169+
self.notify_win7(app)
170+
} else {
171+
#[allow(deprecated)]
172+
self.show()
173+
}
174+
}
175+
#[cfg(not(windows))]
176+
{
177+
#[allow(deprecated)]
178+
self.show()
179+
}
180+
}
181+
182+
#[cfg(all(windows, feature = "windows7-compat"))]
183+
fn notify_win7<R: crate::Runtime>(self, app: &crate::AppHandle<R>) -> crate::api::Result<()> {
184+
let app = app.clone();
185+
let default_window_icon = app.manager.inner.default_window_icon.clone();
186+
let _ = app.run_on_main_thread(move || {
187+
let mut notification = win7_notifications::Notification::new();
188+
if let Some(body) = self.body {
189+
notification.body(&body);
190+
}
191+
if let Some(title) = self.title {
192+
notification.summary(&title);
193+
}
194+
if let Some(crate::Icon::Rgba {
195+
rgba,
196+
width,
197+
height,
198+
}) = default_window_icon
199+
{
200+
notification.icon(rgba, width, height);
201+
}
202+
let _ = notification.show();
112203
});
113204

114205
Ok(())

core/tauri/src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ impl<R: Runtime> AssetResolver<R> {
321321
#[derive(Debug)]
322322
pub struct AppHandle<R: Runtime> {
323323
runtime_handle: R::Handle,
324-
manager: WindowManager<R>,
324+
pub(crate) manager: WindowManager<R>,
325325
#[cfg(feature = "global-shortcut")]
326326
global_shortcut_manager: R::GlobalShortcutManager,
327327
#[cfg(feature = "clipboard")]

0 commit comments

Comments
 (0)