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

View File

@ -25,7 +25,8 @@ pub async fn startline(app: AppHandle) -> Result<(), String> {
let hash = appd.sum_packages(p);
liner::line_up(p, hash).await
.map_err(|e| e.to_string())?;
start::start(p, app_copy)
start::start(p, app_copy).await
.map_err(|e| e.to_string())
} else {
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]
pub async fn set_cfg(
state: State<'_, Mutex<AppData>>,
@ -226,3 +238,32 @@ pub async fn set_cfg(
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 download_handler;
mod appdata;
mod display;
use closure::closure;
use appdata::AppData;
@ -100,6 +101,7 @@ pub async fn run(_args: Vec<String>) {
cmd::install_package,
cmd::delete_package,
cmd::toggle_package,
cmd::list_profiles,
cmd::init_profile,
cmd::load_profile,
@ -107,9 +109,13 @@ pub async fn run(_args: Vec<String>) {
cmd::save_current_profile,
cmd::read_profile_data,
cmd::write_profile_data,
cmd::startline,
cmd::kill,
cmd::list_platform_capabilities,
cmd::set_cfg,
cmd::list_displays,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View File

@ -70,7 +70,7 @@ async fn prepare_packages(p: &Profile) -> Result<()> {
Ok(())
}
pub async fn prepare_config(p: &Profile) -> Result<()> {
async fn prepare_config(p: &Profile) -> Result<()> {
let dir_out = p.dir();
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 std::fs::File;
use std::path::PathBuf;
use tokio::process::Command;
use tauri::{AppHandle, Emitter};
use std::process::Stdio;
use crate::display::{prepare_display, undo_display};
use crate::profile::Profile;
use crate::util;
#[cfg(target_os = "windows")]
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;
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 amd_builder;
#[allow(unused_variables)]
let display_info;
#[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");
display_info = prepare_display(p).await?;
}
#[cfg(target_os = "linux")]
{
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);
amd_builder = Command::new(&wine);
@ -134,6 +139,15 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
log::debug!("Fin");
_ = 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(())