Skip to content

Commit de3af89

Browse files
committed
Use the home and windows-sys crates to get the storage dir
1 parent e78a47c commit de3af89

File tree

5 files changed

+75
-45
lines changed

5 files changed

+75
-45
lines changed

Cargo.lock

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,7 @@ dependencies = [
11801180
"glow",
11811181
"glutin",
11821182
"glutin-winit",
1183+
"home",
11831184
"image",
11841185
"js-sys",
11851186
"log",
@@ -1200,6 +1201,7 @@ dependencies = [
12001201
"web-time",
12011202
"wgpu",
12021203
"winapi",
1204+
"windows-sys 0.52.0",
12031205
"winit",
12041206
]
12051207

@@ -2059,11 +2061,11 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
20592061

20602062
[[package]]
20612063
name = "home"
2062-
version = "0.5.5"
2064+
version = "0.5.9"
20632065
source = "registry+https://github.com/rust-lang/crates.io-index"
2064-
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
2066+
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
20652067
dependencies = [
2066-
"windows-sys 0.48.0",
2068+
"windows-sys 0.52.0",
20672069
]
20682070

20692071
[[package]]

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ document-features = " 0.2.8"
7777
glow = "0.13"
7878
glutin = "0.32.0"
7979
glutin-winit = "0.5.0"
80+
home = "0.5.9"
8081
image = { version = "0.25", default-features = false }
8182
log = { version = "0.4", features = ["std"] }
8283
nohash-hasher = "0.2"
@@ -95,6 +96,7 @@ wgpu = { version = "22.1.0", default-features = false, features = [
9596
# Make the renderer `Sync` even on wasm32, because it makes the code simpler:
9697
"fragile-send-sync-non-atomic-wasm",
9798
] }
99+
windows-sys = "0.52"
98100
winit = { version = "0.30.5", default-features = false }
99101

100102
[workspace.lints.rust]

crates/eframe/Cargo.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,12 @@ android-native-activity = ["egui-winit/android-native-activity"]
5757
## If you plan on specifying your own fonts you may disable this feature.
5858
default_fonts = ["egui/default_fonts"]
5959

60-
## Enable the directories dependency for a more precise file path for the persistence feature.
61-
## Not needed if you provide your own path, or if the simpler default path resolvers are good enough.
62-
directories = ["dep:directories"]
63-
6460
## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow).
6561
glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"]
6662

6763
## Enable saving app state to disk.
6864
persistence = [
65+
"dep:home",
6966
"egui-winit/serde",
7067
"egui/persistence",
7168
"ron",
@@ -150,7 +147,6 @@ image = { workspace = true, features = ["png"] } # Needed for app icon
150147
winit = { workspace = true, default-features = false, features = ["rwh_06"] }
151148

152149
# optional native:
153-
directories = { version = "5", optional = true }
154150
egui-wgpu = { workspace = true, optional = true, features = [
155151
"winit",
156152
] } # if wgpu is used, use it with winit
@@ -160,6 +156,7 @@ pollster = { version = "0.3", optional = true } # needed for wgpu
160156
# this can be done at the same time we expose x11/wayland features of winit crate.
161157
glutin = { workspace = true, optional = true }
162158
glutin-winit = { workspace = true, optional = true }
159+
home = { workspace = true, optional = true }
163160
puffin = { workspace = true, optional = true }
164161
wgpu = { workspace = true, optional = true, features = [
165162
# Let's enable some backends so that users can use `eframe` out-of-the-box
@@ -187,6 +184,7 @@ objc2-app-kit = { version = "0.2.0", features = [
187184
# windows:
188185
[target.'cfg(any(target_os = "windows"))'.dependencies]
189186
winapi = { version = "0.3.9", features = ["winuser"] }
187+
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_System_Com"] }
190188

191189
# -------------------------------------------
192190
# web:
@@ -255,3 +253,7 @@ wgpu = { workspace = true, optional = true, features = [
255253
# without having to explicitly opt-in to backends
256254
"webgpu",
257255
] }
256+
257+
# Native dev dependencies for testing
258+
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
259+
directories = "5"

crates/eframe/src/epi.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ pub struct NativeOptions {
351351
/// persisted (only if the "persistence" feature is enabled).
352352
pub persist_window: bool,
353353

354-
/// The folder where `eframe` will store the app state. If not set, eframe will get the paths
355-
/// from [directories].
354+
/// The folder where `eframe` will store the app state. If not set, eframe will use a default
355+
/// data storage path for each target system.
356356
pub persistence_path: Option<std::path::PathBuf>,
357357

358358
/// Controls whether to apply dithering to minimize banding artifacts.

crates/eframe/src/native/file_storage.rs

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,60 +10,78 @@ use std::{
1010
/// [`egui::ViewportBuilder::app_id`] of [`crate::NativeOptions::viewport`]
1111
/// or the title argument to [`crate::run_native`].
1212
///
13-
/// On native, if the `directories` feature is enabled, the path is picked using
14-
/// [`directories::ProjectDirs::data_dir`](https://docs.rs/directories/5.0.1/directories/struct.ProjectDirs.html#method.data_dir)
15-
/// which is:
13+
/// On native, the path is:
1614
/// * Linux: `/home/UserName/.local/share/APP_ID`
1715
/// * macOS: `/Users/UserName/Library/Application Support/APP_ID`
1816
/// * Windows: `C:\Users\UserName\AppData\Roaming\APP_ID\data`
19-
///
20-
/// If the `directories` feature is not enabled, it uses a naive approximation that returns the
21-
/// same result for the most common systems.
2217
pub fn storage_dir(app_id: &str) -> Option<PathBuf> {
23-
#[cfg(feature = "directories")]
24-
{
25-
directories_storage_dir(app_id)
26-
}
27-
#[cfg(not(feature = "directories"))]
28-
{
29-
naive_storage_dir(app_id)
30-
}
31-
}
32-
33-
#[cfg(feature = "directories")]
34-
#[inline]
35-
fn directories_storage_dir(app_id: &str) -> Option<PathBuf> {
36-
directories::ProjectDirs::from("", "", app_id)
37-
.map(|proj_dirs| proj_dirs.data_dir().to_path_buf())
38-
}
39-
40-
#[allow(dead_code)]
41-
#[inline]
42-
fn naive_storage_dir(app_id: &str) -> Option<PathBuf> {
4318
use egui::os::OperatingSystem as OS;
4419
use std::env::var_os;
4520
match OS::from_target_os() {
4621
OS::Nix => var_os("XDG_DATA_HOME")
4722
.map(PathBuf::from)
48-
.or_else(|| var_os("HOME").map(|s| PathBuf::from(s).join(".local").join("share")))
23+
.filter(|p| p.is_absolute())
24+
.or_else(|| home::home_dir().map(|p| p.join(".local").join("share")))
4925
.map(|p| {
5026
p.join(
5127
app_id
5228
.to_lowercase()
5329
.replace(|c: char| c.is_ascii_whitespace(), ""),
5430
)
5531
}),
56-
OS::Mac => var_os("HOME").map(|s| {
57-
PathBuf::from(s)
58-
.join("Library")
32+
OS::Mac => home::home_dir().map(|p| {
33+
p.join("Library")
5934
.join("Application Support")
6035
.join(app_id.replace(|c: char| c.is_ascii_whitespace(), "-"))
6136
}),
62-
OS::Windows => var_os("APPDATA").map(|s| PathBuf::from(s).join(app_id).join("data")),
37+
OS::Windows => roaming_appdata().map(|p| p.join(app_id).join("data")),
6338
OS::Unknown | OS::Android | OS::IOS => None,
6439
}
6540
}
6641

42+
// Adapted from
43+
// https://github.com/rust-lang/cargo/blob/59ecb11a298f0c3740216220361024cf05ed5b5a/crates/home/src/windows.rs#L19-L42
44+
#[cfg(all(windows, not(target_vendor = "uwp")))]
45+
fn roaming_appdata() -> Option<PathBuf> {
46+
use std::ffi::OsString;
47+
use std::os::windows::ffi::OsStringExt;
48+
use std::path::PathBuf;
49+
use std::ptr;
50+
use std::slice;
51+
52+
use windows_sys::Win32::Foundation::S_OK;
53+
use windows_sys::Win32::System::Com::CoTaskMemFree;
54+
use windows_sys::Win32::UI::Shell::{
55+
FOLDERID_RoamingAppData, SHGetKnownFolderPath, KF_FLAG_DONT_VERIFY,
56+
};
57+
unsafe {
58+
let mut path = ptr::null_mut();
59+
match SHGetKnownFolderPath(
60+
&FOLDERID_RoamingAppData,
61+
KF_FLAG_DONT_VERIFY as u32,
62+
std::ptr::null_mut(),
63+
&mut path,
64+
) {
65+
S_OK => {
66+
let path_slice = slice::from_raw_parts(path, wcslen(path));
67+
let s = OsString::from_wide(&path_slice);
68+
CoTaskMemFree(path.cast());
69+
Some(PathBuf::from(s))
70+
}
71+
_ => {
72+
// Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
73+
CoTaskMemFree(path.cast());
74+
None
75+
}
76+
}
77+
}
78+
}
79+
80+
#[cfg(any(not(windows), target_vendor = "uwp"))]
81+
fn roaming_appdata() -> Option<PathBuf> {
82+
None
83+
}
84+
6785
// ----------------------------------------------------------------------------
6886

6987
/// A key-value store backed by a [RON](https://github.com/ron-rs/ron) file on disk.
@@ -219,14 +237,20 @@ where
219237

220238
#[cfg(test)]
221239
mod tests {
222-
#[cfg(feature = "directories")]
240+
use super::*;
241+
242+
fn directories_storage_dir(app_id: &str) -> Option<PathBuf> {
243+
directories::ProjectDirs::from("", "", app_id)
244+
.map(|proj_dirs| proj_dirs.data_dir().to_path_buf())
245+
}
246+
223247
#[test]
224-
fn naive_path_matches_directories() {
225-
use super::{directories_storage_dir, naive_storage_dir};
248+
fn storage_path_matches_directories() {
249+
use super::storage_dir;
226250
for app_id in [
227251
"MyApp", "My App", "my_app", "my-app", "My.App", "my/app", "my:app", r"my\app",
228252
] {
229-
assert_eq!(directories_storage_dir(app_id), naive_storage_dir(app_id));
253+
assert_eq!(directories_storage_dir(app_id), storage_dir(app_id));
230254
}
231255
}
232256
}

0 commit comments

Comments
 (0)