feat: display witchcraft

This commit is contained in:
2025-03-03 19:20:28 +00:00
parent 898caf1430
commit cde0752da2
12 changed files with 659 additions and 169 deletions

299
rust/Cargo.lock generated
View File

@ -4,13 +4,19 @@ version = 4
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.24.2" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [ dependencies = [
"gimli", "gimli",
] ]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "adler2" name = "adler2"
version = "2.0.0" version = "2.0.0"
@ -67,6 +73,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.96" version = "1.0.96"
@ -341,6 +356,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@ -349,17 +375,17 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.74" version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
dependencies = [ dependencies = [
"addr2line", "addr2line",
"cc",
"cfg-if", "cfg-if",
"libc", "libc",
"miniz_oxide", "miniz_oxide 0.7.4",
"object", "object",
"rustc-demangle", "rustc-demangle",
"windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -637,12 +663,54 @@ dependencies = [
"inout", "inout",
] ]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]] [[package]]
name = "closure" name = "closure"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6173fd61b610d15a7566dd7b7620775627441c4ab9dac8906e17cb93a24b782" checksum = "d6173fd61b610d15a7566dd7b7620775627441c4ab9dac8906e17cb93a24b782"
[[package]]
name = "color-eyre"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.2.0" version = "2.2.0"
@ -885,7 +953,7 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim 0.11.1",
"syn 2.0.98", "syn 2.0.98",
] ]
@ -1050,6 +1118,20 @@ dependencies = [
"syn 2.0.98", "syn 2.0.98",
] ]
[[package]]
name = "displayz"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b411b792aa2feace76922db6aa2e748f089370aa994c8da4f5c0b48e701a25"
dependencies = [
"color-eyre",
"env_logger",
"log",
"structopt",
"thiserror 1.0.69",
"winsafe 0.0.10",
]
[[package]] [[package]]
name = "dlib" name = "dlib"
version = "0.5.2" version = "0.5.2"
@ -1189,6 +1271,19 @@ dependencies = [
"syn 2.0.98", "syn 2.0.98",
] ]
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@ -1242,6 +1337,16 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@ -1274,7 +1379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide 0.8.5",
] ]
[[package]] [[package]]
@ -1602,9 +1707,9 @@ dependencies = [
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.31.1" version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "gio" name = "gio"
@ -1803,6 +1908,15 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@ -1815,6 +1929,15 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.4.0" version = "0.4.0"
@ -1896,6 +2019,12 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.6.0" version = "1.6.0"
@ -2147,6 +2276,12 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"
@ -2513,6 +2648,15 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.5" version = "0.8.5"
@ -2927,9 +3071,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.7" version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -3032,6 +3176,12 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.18.3" version = "0.18.3"
@ -3339,7 +3489,7 @@ dependencies = [
"crc32fast", "crc32fast",
"fdeflate", "fdeflate",
"flate2", "flate2",
"miniz_oxide", "miniz_oxide 0.8.5",
] ]
[[package]] [[package]]
@ -3350,7 +3500,7 @@ checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"concurrent-queue", "concurrent-queue",
"hermit-abi", "hermit-abi 0.4.0",
"pin-project-lite", "pin-project-lite",
"rustix", "rustix",
"tracing", "tracing",
@ -4140,6 +4290,15 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "shared_child" name = "shared_child"
version = "1.0.1" version = "1.0.1"
@ -4285,6 +4444,7 @@ dependencies = [
"derive_builder", "derive_builder",
"derive_more 2.0.1", "derive_more 2.0.1",
"directories", "directories",
"displayz",
"flate2", "flate2",
"futures", "futures",
"junction", "junction",
@ -4306,6 +4466,7 @@ dependencies = [
"tauri-plugin-shell", "tauri-plugin-shell",
"tauri-plugin-single-instance", "tauri-plugin-single-instance",
"tokio", "tokio",
"winsafe 0.0.23",
"zip", "zip",
] ]
@ -4340,12 +4501,42 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck 0.3.3",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -4863,6 +5054,24 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thin-slice" name = "thin-slice"
version = "0.1.1" version = "0.1.1"
@ -4909,6 +5118,16 @@ dependencies = [
"syn 2.0.98", "syn 2.0.98",
] ]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.37" version = "0.3.37"
@ -5151,6 +5370,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
] ]
[[package]] [[package]]
@ -5269,6 +5510,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.6" version = "0.2.6"
@ -5333,6 +5580,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.10.0" version = "1.10.0"
@ -5345,6 +5598,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.2.0" version = "0.2.0"
@ -6172,6 +6431,18 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "winsafe"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac97bb0cb7c33e34890fbd50ca7aeac738530ba3ba967d456d075703d36007f1"
[[package]]
name = "winsafe"
version = "0.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a096fc628cb2c601e13c401ca0c354806424a7f5716000d69b76044eb8e624b9"
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
version = "0.33.0" version = "0.33.0"

View File

@ -44,3 +44,6 @@ tauri-plugin-fs = "2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
[target.'cfg(target_os = "windows")'.dependencies]
winsafe = { version = "0.0.23", features = ["user"] }
displayz = "0.1.0"

View File

@ -25,7 +25,8 @@ pub async fn startline(app: AppHandle) -> Result<(), String> {
let hash = appd.sum_packages(p); let hash = appd.sum_packages(p);
liner::line_up(p, hash).await liner::line_up(p, hash).await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
start::start(p, app_copy)
start::start(p, app_copy).await
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
} else { } else {
Err("No profile".to_owned()) Err("No profile".to_owned())
@ -211,6 +212,17 @@ pub async fn write_profile_data(
} }
} }
#[tauri::command]
pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
log::debug!("invoke: list_platform_capabilities");
#[cfg(target_os = "windows")]
return Ok(vec!["display".to_owned()]);
#[cfg(target_os = "linux")]
return Ok(vec!["wine".to_owned()]);
}
#[tauri::command] #[tauri::command]
pub async fn set_cfg( pub async fn set_cfg(
state: State<'_, Mutex<AppData>>, state: State<'_, Mutex<AppData>>,
@ -226,3 +238,32 @@ pub async fn set_cfg(
Ok(()) Ok(())
} }
#[tauri::command]
#[cfg(target_os = "windows")]
pub async fn list_displays() -> Result<Vec<(String, String)>, String> {
use winsafe::prelude::NativeBitflag;
log::debug!("invoke: list_displays");
let mut res = Vec::new();
for displ_dev in winsafe::EnumDisplayDevices(None, None) {
if let Ok(displ_dev) = displ_dev {
if displ_dev.StateFlags.has(winsafe::co::DISPLAY_DEVICE::ATTACHED_TO_DESKTOP) {
res.push((displ_dev.DeviceName(), displ_dev.DeviceString()));
}
} else {
break;
}
}
Ok(res)
}
#[tauri::command]
#[cfg(not(target_os = "windows"))]
pub async fn list_displays() -> Result<Vec<String>, ()> {
log::debug!("invoke: list_displays");
Ok(Vec::new())
}

93
rust/src/display.rs Normal file
View File

@ -0,0 +1,93 @@
use crate::profile::Profile;
use anyhow::{Result, anyhow};
#[cfg(target_os = "windows")]
pub struct DisplayInfo {
primary: String,
target: String,
target_rotation: displayz::Orientation
}
#[cfg(not(target_os = "windows"))]
pub async fn prepare_display(p: &Profile) -> Result<()> {
Ok(())
}
#[cfg(target_os = "windows")]
pub async fn prepare_display(p: &Profile) -> Result<Option<DisplayInfo>> {
use displayz::{query_displays, Orientation};
let display_name = p.get_str("display", "default");
let rotation = p.get_int("display-rotation", 0);
if display_name == "default" {
log::debug!("prepare display: skip");
return Ok(None);
}
let display_set = query_displays()?;
let primary = display_set
.displays()
.find(|display| display.is_primary())
.ok_or_else(|| anyhow!("Primary display not found"))?;
let target = display_set
.displays()
.find(|display| display.name() == display_name)
.ok_or_else(|| anyhow!("Display {} not found", display_name))?;
target.set_primary()?;
let settings = target.settings()
.as_ref()
.ok_or_else(|| anyhow!("Unable to query display settings"))?;
let res = DisplayInfo {
primary: primary.name().to_owned(),
target: target.name().to_owned(),
target_rotation: settings.borrow().orientation
};
match rotation {
90 => settings.borrow_mut().orientation = Orientation::Portrait,
270 => settings.borrow_mut().orientation = Orientation::PortraitFlipped,
_ => ()
};
display_set.apply()?;
displayz::refresh()?;
log::debug!("prepare display: done");
Ok(Some(res))
}
#[cfg(target_os = "windows")]
pub async fn undo_display(info: DisplayInfo) -> Result<()> {
use displayz::query_displays;
let display_set = query_displays()?;
let primary = display_set
.displays()
.find(|display| display.name() == info.primary)
.ok_or_else(|| anyhow!("Display {} not found", info.primary))?;
let target = display_set
.displays()
.find(|display| display.name() == info.target)
.ok_or_else(|| anyhow!("Display {} not found", info.target))?;
primary.set_primary()?;
let settings = target.settings()
.as_ref()
.ok_or_else(|| anyhow!("Unable to query display settings"))?;
settings.borrow_mut().orientation = info.target_rotation;
display_set.apply()?;
displayz::refresh()?;
log::debug!("undo display: done");
Ok(())
}

View File

@ -8,6 +8,7 @@ mod start;
mod liner; mod liner;
mod download_handler; mod download_handler;
mod appdata; mod appdata;
mod display;
use closure::closure; use closure::closure;
use appdata::AppData; use appdata::AppData;
@ -100,6 +101,7 @@ pub async fn run(_args: Vec<String>) {
cmd::install_package, cmd::install_package,
cmd::delete_package, cmd::delete_package,
cmd::toggle_package, cmd::toggle_package,
cmd::list_profiles, cmd::list_profiles,
cmd::init_profile, cmd::init_profile,
cmd::load_profile, cmd::load_profile,
@ -107,9 +109,13 @@ pub async fn run(_args: Vec<String>) {
cmd::save_current_profile, cmd::save_current_profile,
cmd::read_profile_data, cmd::read_profile_data,
cmd::write_profile_data, cmd::write_profile_data,
cmd::startline, cmd::startline,
cmd::kill, cmd::kill,
cmd::list_platform_capabilities,
cmd::set_cfg, cmd::set_cfg,
cmd::list_displays,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View File

@ -70,7 +70,7 @@ async fn prepare_packages(p: &Profile) -> Result<()> {
Ok(()) Ok(())
} }
pub async fn prepare_config(p: &Profile) -> Result<()> { async fn prepare_config(p: &Profile) -> Result<()> {
let dir_out = p.dir(); let dir_out = p.dir();
let ini_in_raw = fs::read_to_string(p.data.exe_dir.join("segatools.ini")).await?; let ini_in_raw = fs::read_to_string(p.data.exe_dir.join("segatools.ini")).await?;

View File

@ -1,16 +1,16 @@
use anyhow::Result; use anyhow::Result;
use std::fs::File; use std::fs::File;
use std::path::PathBuf;
use tokio::process::Command; use tokio::process::Command;
use tauri::{AppHandle, Emitter}; use tauri::{AppHandle, Emitter};
use std::process::Stdio; use std::process::Stdio;
use crate::display::{prepare_display, undo_display};
use crate::profile::Profile; use crate::profile::Profile;
use crate::util; use crate::util;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
static CREATE_NO_WINDOW: u32 = 0x08000000; static CREATE_NO_WINDOW: u32 = 0x08000000;
pub fn start(p: &Profile, app: AppHandle) -> Result<()> { pub async fn start(p: &Profile, app: AppHandle) -> Result<()> {
use tokio::task::JoinSet; use tokio::task::JoinSet;
let ini_path = p.dir().join("segatools.ini"); let ini_path = p.dir().join("segatools.ini");
@ -20,15 +20,20 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
let mut game_builder; let mut game_builder;
let mut amd_builder; let mut amd_builder;
#[allow(unused_variables)]
let display_info;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
game_builder = Command::new(p.exe_dir.join("inject.exe")); game_builder = Command::new(p.data.exe_dir.join("inject.exe"));
amd_builder = Command::new("cmd.exe"); amd_builder = Command::new("cmd.exe");
display_info = prepare_display(p).await?;
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
let wine = p.data.wine_runtime.clone() let wine = p.data.wine_runtime.clone()
.unwrap_or_else(|| PathBuf::from("/usr/bin/wine")); .unwrap_or_else(|| std::path::PathBuf::from("/usr/bin/wine"));
game_builder = Command::new(&wine); game_builder = Command::new(&wine);
amd_builder = Command::new(&wine); amd_builder = Command::new(&wine);
@ -134,6 +139,15 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
log::debug!("Fin"); log::debug!("Fin");
_ = app.emit("launch-end", ""); _ = app.emit("launch-end", "");
#[cfg(target_os = "windows")]
{
if let Some(display_info) = display_info {
if let Err(e) = undo_display(display_info).await {
log::error!("undo display failed: {}", e);
}
}
}
}); });
Ok(()) Ok(())

View File

@ -6,10 +6,9 @@ import TabList from 'primevue/tablist';
import TabPanel from 'primevue/tabpanel'; import TabPanel from 'primevue/tabpanel';
import TabPanels from 'primevue/tabpanels'; import TabPanels from 'primevue/tabpanels';
import Tabs from 'primevue/tabs'; import Tabs from 'primevue/tabs';
import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
import ModList from './ModList.vue'; import ModList from './ModList.vue';
import ModStore from './ModStore.vue'; import ModStore from './ModStore.vue';
import Options from './Options.vue'; import OptionList from './OptionList.vue';
import ProfileList from './ProfileList.vue'; import ProfileList from './ProfileList.vue';
import StartButton from './StartButton.vue'; import StartButton from './StartButton.vue';
import { usePkgStore, usePrfStore } from '../stores'; import { usePkgStore, usePrfStore } from '../stores';
@ -64,7 +63,7 @@ onMounted(async () => {
<ModStore /> <ModStore />
</TabPanel> </TabPanel>
<TabPanel value="2"> <TabPanel value="2">
<Options /> <OptionList />
</TabPanel> </TabPanel>
<TabPanel value="3"> <TabPanel value="3">
<strong>UNDER CONSTRUCTION</strong><br />Many features are <strong>UNDER CONSTRUCTION</strong><br />Many features are

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import Fieldset from 'primevue/fieldset';
defineProps({
title: String,
});
</script>
<template>
<Fieldset :legend="title" :toggleable="true">
<div class="flex w-full flex-col gap-1">
<slot />
</div>
</Fieldset>
</template>

View File

@ -0,0 +1,182 @@
<script setup lang="ts">
import { Ref, computed, ref } from 'vue';
import InputNumber from 'primevue/inputnumber';
import InputText from 'primevue/inputtext';
import Select from 'primevue/select';
import SelectButton from 'primevue/selectbutton';
import Toggle from 'primevue/toggleswitch';
import OptionCategory from './OptionCategory.vue';
import OptionRow from './OptionRow.vue';
import { invoke } from '../invoke';
import { usePrfStore } from '../stores';
const prf = usePrfStore();
const _cfg = <T extends string | number | boolean>(key: string, dflt: T) =>
computed({
get() {
return (prf.cfg(key) as T) ?? dflt;
},
async set(value) {
await prf.setCfg(key, value ?? dflt);
},
});
const cfgIntel = _cfg('intel', false);
const cfgRezW = _cfg('rez-w', 1080);
const cfgRezH = _cfg('rez-h', 1920);
const cfgDisplayMode = _cfg('display-mode', 'borderless');
const displayModeList = [
{ title: 'Window', value: 'window' },
{ title: 'Borderless window', value: 'borderless' },
{ title: 'Fullscreen', value: 'fullscreen' },
];
const cfgDisplay = _cfg('display', 'default');
const cfgDisplayRotation = _cfg('display-rotation', 0);
const displayRotationList = [
{ title: 'Unchanged', value: 0 },
{ title: 'Portrait', value: 90 },
{ title: 'Portrait (flipped)', value: 270 },
];
const cfgAime = _cfg('aime', false);
const aimeCode = ref('');
const capabilities: Ref<string[]> = ref([]);
const displayList: Ref<{ title: string; value: string }[]> = ref([
{
title: 'Primary',
value: 'default',
},
]);
invoke('read_profile_data', {
path: 'aime.txt',
})
.then((v: unknown) => {
if (typeof v === 'string') {
aimeCode.value = v;
} else {
aimeCode.value = '';
}
})
.catch(() => {
aimeCode.value = '';
});
invoke('list_platform_capabilities')
.then(async (v: unknown) => {
if (Array.isArray(v)) {
capabilities.value.push(...v);
}
if (capabilities.value.includes('display')) {
for (const [devName, devString] of (await invoke(
'list_displays'
)) as Array<[string, string]>) {
displayList.value.push({
title: `${devName.replace('\\\\.\\', '')} (${devString})`,
value: devName,
});
}
}
})
.catch(() => {});
const aimeCodeModel = computed({
get() {
return aimeCode.value;
},
async set(value: string) {
aimeCode.value = value;
if (value.match(/^[0-9]{20}$/)) {
await invoke('write_profile_data', {
path: 'aime.txt',
content: aimeCode.value,
});
}
},
});
</script>
<template>
<OptionCategory title="Display options">
<OptionRow
v-if="capabilities.includes('display')"
title="Target display"
>
<Select
v-model="cfgDisplay"
:options="displayList"
option-label="title"
option-value="value"
></Select>
</OptionRow>
<OptionRow id="resolution" title="Resolution">
<InputNumber
class="shrink"
size="small"
:min="480"
:max="9999"
:use-grouping="false"
v-model="cfgRezW"
/>
x
<InputNumber
class="shrink"
size="small"
:min="640"
:max="9999"
:use-grouping="false"
v-model="cfgRezH"
/>
</OptionRow>
<OptionRow title="Display mode">
<SelectButton
v-model="cfgDisplayMode"
:options="displayModeList"
option-label="title"
option-value="value"
/>
</OptionRow>
<OptionRow
title="Display rotation"
v-if="capabilities.includes('display')"
>
<SelectButton
v-model="cfgDisplayRotation"
:options="displayRotationList"
option-label="title"
option-value="value"
:disabled="cfgDisplay === 'default'"
/>
</OptionRow>
</OptionCategory>
<OptionCategory title="Misc">
<OptionRow title="OpenSSL bug workaround for Intel ≥10th gen">
<Toggle v-model="cfgIntel" />
</OptionRow>
<OptionRow title="Aime emulation">
<Toggle inputId="switch2" v-model="cfgAime" />
</OptionRow>
<OptionRow title="Aime code">
<InputText
class="shrink"
size="small"
:disabled="prf.cfg('aime') !== true"
:maxlength="20"
placeholder="00000000000000000000"
v-model="aimeCodeModel"
/>
</OptionRow>
</OptionCategory>
</template>
<style>
#resolution .p-inputnumber-input {
width: 4rem;
}
.p-inputtext {
font-family: monospace;
}
</style>

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
defineProps({
title: String,
});
</script>
<template>
<div class="flex flex-row w-full p-2 gap-2 items-center">
<div class="grow">{{ title }}</div>
<slot />
</div>
</template>

View File

@ -1,146 +0,0 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import Fieldset from 'primevue/fieldset';
import InputNumber from 'primevue/inputnumber';
import InputText from 'primevue/inputtext';
import RadioButton from 'primevue/radiobutton';
import Toggle from 'primevue/toggleswitch';
import { invoke } from '../invoke';
import { usePrfStore } from '../stores';
const prf = usePrfStore();
const _cfg = <T extends string | number | boolean>(key: string, dflt: T) =>
computed({
get() {
return (prf.cfg(key) as T) ?? dflt;
},
async set(value) {
await prf.setCfg(key, value ?? dflt);
},
});
const cfgIntel = _cfg('intel', false);
const cfgRezW = _cfg('rez-w', 1080);
const cfgRezH = _cfg('rez-h', 1920);
const cfgDisplayMode = _cfg('display-mode', 'borderless');
const cfgAime = _cfg('aime', false);
const aimeCode = ref('');
(async () => {
try {
aimeCode.value = await invoke('read_profile_data', {
path: 'aime.txt',
});
} catch (e) {
aimeCode.value = '';
}
})();
const aimeCodeModel = computed({
get() {
return aimeCode.value;
},
async set(value: string) {
aimeCode.value = value;
if (value.match(/^[0-9]{20}$/)) {
await invoke('write_profile_data', {
path: 'aime.txt',
content: aimeCode.value,
});
}
},
});
</script>
<template>
<Fieldset legend="Launch options" :toggleable="true">
<div class="flex w-full flex-col gap-1">
<label for="switch1" class="flex flex-row w-full p-2">
<div class="grow">
OpenSSL bug workaround for Intel 10th gen
</div>
<Toggle inputId="switch1" v-model="cfgIntel" />
</label>
<label
id="resolution"
class="flex flex-row w-full p-2 gap-2 items-center"
>
<div class="grow">Resolution</div>
<InputNumber
class="shrink"
size="small"
:min="480"
:max="9999"
:use-grouping="false"
v-model="cfgRezW"
/>
x
<InputNumber
class="shrink"
size="small"
:min="640"
:max="9999"
:use-grouping="false"
v-model="cfgRezH"
/>
</label>
<label class="flex flex-row w-full p-2 gap-2">
<div class="grow">Display mode</div>
<div class="flex items-center gap-2">
<RadioButton
v-model="cfgDisplayMode"
inputId="ingredient1"
name="window"
value="window"
/>
<label for="ingredient1">window</label>
</div>
<div class="flex items-center gap-2">
<RadioButton
v-model="cfgDisplayMode"
inputId="ingredient2"
name="borderless"
value="borderless"
/>
<label for="ingredient2">borderless window</label>
</div>
<div class="flex items-center gap-2">
<RadioButton
v-model="cfgDisplayMode"
inputId="ingredient3"
name="fullscreen"
value="fullscreen"
/>
<label for="ingredient3">fullscreen</label>
</div>
</label>
<label for="switch2" class="flex flex-row w-full p-2">
<div class="grow">Aime emulation</div>
<Toggle inputId="switch2" v-model="cfgAime" />
</label>
<label class="flex flex-row w-full p-2 items-center">
<div class="grow">Aime code</div>
<InputText
class="shrink"
size="small"
:disabled="prf.cfg('aime') !== true"
:maxlength="20"
placeholder="00000000000000000000"
v-model="aimeCodeModel"
/>
</label>
</div>
</Fieldset>
</template>
<style>
#resolution .p-inputnumber-input {
width: 4rem;
}
.p-inputtext {
font-family: monospace;
}
</style>