#[cfg(windows)]
pub fn gui_available() -> bool {
use windows_sys::Win32::System::StationsAndDesktops::{
GetProcessWindowStation, GetUserObjectInformationW, UOI_FLAGS, USEROBJECTFLAGS,
};
use windows_sys::Win32::UI::WindowsAndMessaging::WSF_VISIBLE;
unsafe {
let station = GetProcessWindowStation();
if station.is_null() {
return false;
}
let mut flags = USEROBJECTFLAGS {
fInherit: 0,
fReserved: 0,
dwFlags: 0,
};
let ok = GetUserObjectInformationW(
station,
UOI_FLAGS,
&mut flags as *mut USEROBJECTFLAGS as *mut core::ffi::c_void,
std::mem::size_of::<USEROBJECTFLAGS>() as u32,
std::ptr::null_mut(),
);
ok != 0 && (flags.dwFlags & WSF_VISIBLE as u32) != 0
}
}
#[cfg(not(windows))]
pub fn gui_available() -> bool {
std::env::var_os("DISPLAY")
.map(|v| !v.is_empty())
.unwrap_or(false)
|| std::env::var_os("WAYLAND_DISPLAY")
.map(|v| !v.is_empty())
.unwrap_or(false)
}
#[cfg(windows)]
pub fn stdout_is_tty() -> bool {
use windows_sys::Win32::System::Console::{GetConsoleMode, GetStdHandle, STD_OUTPUT_HANDLE};
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if handle.is_null() {
return false;
}
let mut mode: u32 = 0;
GetConsoleMode(handle, &mut mode) != 0
}
}
#[cfg(not(windows))]
pub fn stdout_is_tty() -> bool {
unsafe { libc::isatty(1) == 1 }
}
#[cfg(windows)]
pub fn attach_parent_console() -> bool {
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::Storage::FileSystem::{
CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
};
use windows_sys::Win32::System::Console::{
ATTACH_PARENT_PROCESS, AttachConsole, GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE, SetStdHandle,
};
unsafe {
if AttachConsole(ATTACH_PARENT_PROCESS) == 0 {
return false;
}
let wire = |name: &[u16], std_id: u32| {
if !GetStdHandle(std_id).is_null() {
return;
}
let handle = CreateFileW(
name.as_ptr(),
0x8000_0000u32 | 0x4000_0000u32,
FILE_SHARE_READ | FILE_SHARE_WRITE,
std::ptr::null(),
OPEN_EXISTING,
0,
std::ptr::null_mut(),
);
if handle != INVALID_HANDLE_VALUE {
SetStdHandle(std_id, handle);
}
};
let conin: Vec<u16> = "CONIN$ ".encode_utf16().collect();
let conout: Vec<u16> = "CONOUT$ ".encode_utf16().collect();
wire(&conin, STD_INPUT_HANDLE);
wire(&conout, STD_OUTPUT_HANDLE);
wire(&conout, STD_ERROR_HANDLE);
true
}
}
#[cfg(not(windows))]
pub fn attach_parent_console() -> bool {
true
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UiCapabilities {
pub gui: bool,
pub webview2: bool,
pub tty: bool,
}
impl UiCapabilities {
pub fn probe() -> Self {
Self {
gui: gui_available(),
webview2: crate::env_probe::webview2_available(),
tty: stdout_is_tty(),
}
}
}
#[cfg(windows)]
pub fn webview2_available() -> bool {
if std::env::var_os("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER").is_some() {
return true;
}
if std::env::var_os("SHUN_FORCE_FALLBACK").is_some() {
return false;
}
use winreg::RegKey;
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ};
const CLIENT: &str = r"Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}";
let probes = [
(
HKEY_LOCAL_MACHINE,
format!(r"SOFTWARE\WOW6432Node\{CLIENT}"),
),
(HKEY_LOCAL_MACHINE, format!(r"SOFTWARE\{CLIENT}")),
(HKEY_CURRENT_USER, format!(r"SOFTWARE\{CLIENT}")),
];
probes.iter().any(|(root, path)| {
RegKey::predef(*root)
.open_subkey_with_flags(path, KEY_READ)
.and_then(|key| key.get_value::<String, _>("pv"))
.is_ok_and(|version| !version.is_empty() && version != "0.0.0.0")
})
}
#[cfg(not(windows))]
pub fn webview2_available() -> bool {
if std::env::var_os("SHUN_FORCE_FALLBACK").is_some() {
return false;
}
true
}