use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use super::{dll_proc, wide_cap};
#[link(name = "kernel32", kind = "raw-dylib")]
unsafe extern "system" {
fn GetFileAttributesW(name: *const u16) -> u32;
fn GetLogicalDrives() -> u32;
fn GetModuleFileNameW(m: isize, buf: *mut u16, n: u32) -> u32;
fn CreateProcessW(app: *const u16, cmd: *mut u16, pa: usize, ta: usize, inherit: i32, flags: u32, env: usize,
dir: *const u16, si: *const StartupInfo, pi: *mut [isize; 3]) -> i32;
fn CloseHandle(h: isize) -> i32;
}
#[repr(C)]
struct StartupInfo {
cb: u32,
rest: [u32; 25],
}
const SH_WPATH: usize = 1040; const SH_WURL: usize = 2048; const SW_SHOWNORMAL: i32 = 1;
const FILE_ATTR_DIRECTORY: u32 = 0x10;
const PATHW_MAX: usize = 1024; const SPAWN_CMD_MAX: usize = 16384; static S_WOPEN: [u16; 5] = [b'o' as u16, b'p' as u16, b'e' as u16, b'n' as u16, 0];
static SH_STATE: AtomicU32 = AtomicU32::new(0);
static SH_EXEC: AtomicUsize = AtomicUsize::new(0);
fn sh_load() -> usize {
while SH_STATE.load(Ordering::Acquire) != 2 {
if SH_STATE.compare_exchange(0, 1, Ordering::AcqRel, Ordering::Acquire).is_err() {
core::hint::spin_loop();
continue;
}
let co = dll_proc(b"ole32.dll\0", b"CoInitializeEx\0");
if co != 0 {
let ci: unsafe extern "system" fn(usize, u32) -> i32 = unsafe { core::mem::transmute(co) };
unsafe { ci(0, 6) };
}
SH_EXEC.store(dll_proc(b"shell32.dll\0", b"ShellExecuteW\0"), Ordering::Relaxed);
SH_STATE.store(2, Ordering::Release);
}
SH_EXEC.load(Ordering::Relaxed)
}
fn shell_exec(file: &[u16], params: Option<&[u16]>) -> bool {
let p = sh_load();
if p == 0 {
return false;
}
let f: unsafe extern "system" fn(isize, *const u16, *const u16, *const u16, *const u16, i32) -> isize =
unsafe { core::mem::transmute(p) };
let pp = params.map_or(core::ptr::null(), |v| v.as_ptr());
(unsafe { f(0, S_WOPEN.as_ptr(), file.as_ptr(), pp, core::ptr::null(), SW_SHOWNORMAL) }) > 32
}
fn dry_run() -> bool {
crate::env("jfgui_OPEN_DRYRUN").is_some_and(|v| v.starts_with('1'))
}
pub fn open_url(url: &str) -> i32 {
if url.is_empty() || !crate::policy::url_allowed(url) {
return -1;
}
if dry_run() {
crate::write_stdout(format!("plat_open_url: ShellExecuteW open {url}\n").as_bytes());
return 0;
}
if shell_exec(&wide_cap(url, SH_WURL), None) { 0 } else { -1 }
}
pub fn open_folder(path: &str) -> i32 {
if path.is_empty() || !crate::policy::fs_allowed(path, crate::policy::FS_READ) {
return -1;
}
let mut w = wide_cap(path, SH_WPATH);
if w.len() <= 1 {
return -1;
}
for c in w.iter_mut() {
if *c == b'/' as u16 {
*c = b'\\' as u16;
}
}
let a = unsafe { GetFileAttributesW(w.as_ptr()) };
if a == u32::MAX {
return -1; }
let is_file = a & FILE_ATTR_DIRECTORY == 0;
if dry_run() {
let s = if is_file {
format!("plat_open_folder: ShellExecuteW open explorer.exe /select,{path}\n")
} else {
format!("plat_open_folder: ShellExecuteW open folder {path}\n")
};
crate::write_stdout(s.as_bytes());
return 0;
}
let ok = if is_file {
let mut param: Vec<u16> = "/select,\"".encode_utf16().collect();
param.extend_from_slice(&w[..w.len() - 1]);
param.extend_from_slice(&[b'"' as u16, 0]);
let exe: Vec<u16> = "explorer.exe\0".encode_utf16().collect();
shell_exec(&exe, Some(¶m))
} else {
shell_exec(&w, None)
};
if ok { 0 } else { -1 }
}
pub fn theme() -> i32 {
let p = dll_proc(b"advapi32.dll\0", b"RegGetValueW\0");
if p == 0 {
return -1;
}
let key = wide_cap("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 128);
let val = wide_cap("AppsUseLightTheme", 32);
let (mut data, mut size) = (0u32, 4u32);
let f: unsafe extern "system" fn(isize, *const u16, *const u16, u32, *mut u32, *mut u32, *mut u32) -> i32 =
unsafe { core::mem::transmute(p) };
let r = unsafe { f(0x8000_0001u32 as i32 as isize, key.as_ptr(), val.as_ptr(), 0x10, core::ptr::null_mut(), &mut data,
&mut size) };
if r != 0 {
return -1;
}
(data == 0) as i32 }
fn reg_dword(key: &str, val: &str) -> Option<u32> {
let p = dll_proc(b"advapi32.dll\0", b"RegGetValueW\0");
if p == 0 {
return None;
}
let key = wide_cap(key, 128);
let val = wide_cap(val, 64);
let (mut data, mut size) = (0u32, 4u32);
let f: unsafe extern "system" fn(isize, *const u16, *const u16, u32, *mut u32, *mut u32, *mut u32) -> i32 =
unsafe { core::mem::transmute(p) };
let r = unsafe { f(0x8000_0001u32 as i32 as isize, key.as_ptr(), val.as_ptr(), 0x10, core::ptr::null_mut(), &mut data,
&mut size) };
(r == 0).then_some(data)
}
pub(crate) fn sys_settings_os(s: &mut crate::SysSettings) {
let t = theme();
if t >= 0 {
s.dark = Some(t == 1);
}
if let Some(c) = reg_dword("Software\\Microsoft\\Windows\\DWM", "AccentColor") {
s.accent = Some((c & 0xFF) << 16 | (c & 0xFF00) | (c >> 16 & 0xFF)); }
if let Some(t) = reg_dword("Software\\Microsoft\\Accessibility", "TextScaleFactor") {
s.text_scale = t.clamp(100, 225);
}
let spi = dll_proc(b"user32.dll\0", b"SystemParametersInfoW\0");
if spi != 0 {
let f: unsafe extern "system" fn(u32, u32, *mut u8, u32) -> i32 = unsafe { core::mem::transmute(spi) };
let mut hc = [0u32; 4];
hc[0] = 16;
if unsafe { f(0x0042, 16, hc.as_mut_ptr() as *mut u8, 0) } != 0 && hc[1] & 1 != 0 {
s.high_contrast = true; s.contrast_colors = sys_colors();
}
let mut on = 1i32;
if unsafe { f(0x1042, 0, &mut on as *mut i32 as *mut u8, 0) } != 0 {
s.reduce_motion = on == 0;
}
let mut smooth = 1i32;
if unsafe { f(0x004A, 0, &mut smooth as *mut i32 as *mut u8, 0) } != 0 {
s.font_aa = Some(smooth != 0);
let mut ty = 1u32;
if smooth != 0 && unsafe { f(0x200A, 0, &mut ty as *mut u32 as *mut u8, 0) } != 0 {
let mut or = 1u32;
let cleartype = ty == 2;
if cleartype && unsafe { f(0x2012, 0, &mut or as *mut u32 as *mut u8, 0) } == 0 {
or = 1;
}
s.font_rgba = Some(if !cleartype {
crate::FONT_RGBA_NONE
} else if or == 0 {
crate::FONT_RGBA_BGR
} else {
crate::FONT_RGBA_RGB
});
}
}
}
let dbl = dll_proc(b"user32.dll\0", b"GetDoubleClickTime\0");
if dbl != 0 {
let f: unsafe extern "system" fn() -> u32 = unsafe { core::mem::transmute(dbl) };
s.double_click_ms = unsafe { f() }.clamp(100, 5000);
}
let blink = dll_proc(b"user32.dll\0", b"GetCaretBlinkTime\0");
if blink != 0 {
let f: unsafe extern "system" fn() -> u32 = unsafe { core::mem::transmute(blink) };
let ms = unsafe { f() };
s.caret_blink_ms = if ms == u32::MAX { 0 } else { ms.min(5000) };
}
}
fn sys_colors() -> Option<crate::HcColors> {
let p = dll_proc(b"user32.dll\0", b"GetSysColor\0");
if p == 0 {
return None;
}
let f: unsafe extern "system" fn(i32) -> u32 = unsafe { core::mem::transmute(p) };
let c = |i: i32| {
let v = unsafe { f(i) };
(v & 0xFF) << 16 | (v & 0xFF00) | (v >> 16 & 0xFF)
};
Some(crate::HcColors {
bg: c(5), text: c(8), highlight: c(13), highlight_text: c(14), button: c(15), button_text: c(18), disabled: c(17), link: c(26), })
}
pub fn home() -> Option<String> {
Some(crate::env("USERPROFILE").unwrap_or_else(|| String::from("C:/")))
}
pub fn drives() -> Vec<String> {
let m = unsafe { GetLogicalDrives() };
(0..26u8).filter(|&k| m & 1 << k != 0).map(|k| format!("{}:/", (b'A' + k) as char)).collect()
}
pub fn restart_self(args: &[&str]) -> i32 {
spawn_self(args)
}
pub fn spawn_self(args: &[&str]) -> i32 {
let mut exe = alloc::vec![0u16; PATHW_MAX];
let n = unsafe { GetModuleFileNameW(0, exe.as_mut_ptr(), PATHW_MAX as u32) } as usize;
if n == 0 || n >= PATHW_MAX {
return -1;
}
let room = SPAWN_CMD_MAX - 4; let mut cmd: Vec<u16> = Vec::with_capacity(n + 64);
cmd.push(b'"' as u16);
for &c in &exe[..n] {
if cmd.len() >= room {
return -1;
}
cmd.push(c);
}
cmd.push(b'"' as u16);
for a in args {
let wa = wide_cap(a, PATHW_MAX);
if cmd.len() >= room {
return -1; }
cmd.push(b' ' as u16);
cmd.push(b'"' as u16);
let mut bs = 0; for &c in &wa[..wa.len() - 1] {
if c == b'\\' as u16 {
bs += 1;
} else {
if c == b'"' as u16 {
for _ in 0..bs + 1 {
if cmd.len() >= room {
return -1;
}
cmd.push(b'\\' as u16);
}
}
bs = 0;
}
if cmd.len() >= room {
return -1;
}
cmd.push(c);
}
for _ in 0..bs {
if cmd.len() >= room {
return -1;
}
cmd.push(b'\\' as u16);
}
if cmd.len() >= room {
return -1;
}
cmd.push(b'"' as u16);
}
cmd.push(0);
let mut exe0 = exe[..n].to_vec();
exe0.push(0);
let si = StartupInfo { cb: 104, rest: [0; 25] };
let mut pi = [0isize; 3]; let ok = unsafe { CreateProcessW(exe0.as_ptr(), cmd.as_mut_ptr(), 0, 0, 0, 0, 0, core::ptr::null(), &si, &mut pi) };
if ok == 0 {
return -1;
}
unsafe {
CloseHandle(pi[0]);
CloseHandle(pi[1]);
}
0
}