mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 05:15:49 -06:00
feat: setup windows system console output detection for dynamic console attach
This commit is contained in:
114
Cargo.lock
generated
114
Cargo.lock
generated
@@ -618,7 +618,8 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"winapi",
|
||||
"windows",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1304,6 +1305,108 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
@@ -1345,6 +1448,15 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
|
||||
@@ -35,9 +35,12 @@ lto = true
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["consoleapi", "fileapi", "handleapi", "processenv", "winbase", "wincon", "winnt", "winuser", "windef", "minwindef"]
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows = { version = "0.61.3", features = ["Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Console"] }
|
||||
windows-sys = { version = "0.60.2", features = ["Win32_System_Console"] }
|
||||
|
||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
|
||||
[target.'cfg(target_os = "emscripten")'.dependencies.sdl2]
|
||||
|
||||
25
src/app.rs
25
src/app.rs
@@ -93,31 +93,6 @@ impl App {
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
// for event in self
|
||||
// .game
|
||||
// .world
|
||||
// .get_non_send_resource_mut::<&'static mut EventPump>()
|
||||
// .unwrap()
|
||||
// .poll_iter()
|
||||
// {
|
||||
// match event {
|
||||
// Event::Window { win_event, .. } => match win_event {
|
||||
// WindowEvent::FocusGained => {
|
||||
// self.focused = true;
|
||||
// }
|
||||
// WindowEvent::FocusLost => {
|
||||
// self.focused = false;
|
||||
// }
|
||||
// _ => {}
|
||||
// },
|
||||
// Event::MouseMotion { x, y, .. } => {
|
||||
// // Convert window coordinates to logical coordinates
|
||||
// self.cursor_pos = Vec2::new(x as f32, y as f32);
|
||||
// }
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
|
||||
let dt = self.last_tick.elapsed().as_secs_f32();
|
||||
self.last_tick = Instant::now();
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Note: This disables the console window on Windows. We manually re-attach to the parent terminal or process later on.
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
use crate::{app::App, constants::LOOP_TIME};
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::time::Duration;
|
||||
use crate::asset::Asset;
|
||||
use crate::error::{AssetError, PlatformError};
|
||||
use crate::platform::CommonPlatform;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
/// Desktop platform implementation.
|
||||
pub struct Platform;
|
||||
@@ -24,6 +25,29 @@ impl CommonPlatform for Platform {
|
||||
}
|
||||
|
||||
fn init_console(&self) -> Result<(), PlatformError> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use windows::Win32::System::Console::GetConsoleWindow;
|
||||
|
||||
// Check if we already have a console window
|
||||
if unsafe { !GetConsoleWindow().0.is_null() } {
|
||||
debug!("Already have a console window");
|
||||
return Ok(());
|
||||
} else {
|
||||
debug!("No existing console window found");
|
||||
}
|
||||
|
||||
if let Some(file_type) = Self::is_output_setup()? {
|
||||
debug!(r#type = file_type, "Existing output detected");
|
||||
} else {
|
||||
debug!("No existing output detected");
|
||||
|
||||
// Try to attach to parent console for direct cargo run
|
||||
Self::attach_to_parent_console()?;
|
||||
info!("Successfully attached to parent console");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -42,3 +66,102 @@ impl CommonPlatform for Platform {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Platform {
|
||||
/// Check if the output stream has been setup by a parent process
|
||||
fn is_output_setup() -> Result<Option<&'static str>, PlatformError> {
|
||||
use windows::Win32::Storage::FileSystem::{
|
||||
GetFileType, FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE, FILE_TYPE_REMOTE, FILE_TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
use windows_sys::Win32::{
|
||||
Foundation::INVALID_HANDLE_VALUE,
|
||||
System::Console::{GetStdHandle, STD_OUTPUT_HANDLE},
|
||||
};
|
||||
|
||||
// Get the process's standard output handle, check if it's invalid
|
||||
let handle = match unsafe { GetStdHandle(STD_OUTPUT_HANDLE) } {
|
||||
INVALID_HANDLE_VALUE => {
|
||||
return Err(PlatformError::ConsoleInit("Invalid handle".to_string()));
|
||||
}
|
||||
handle => handle,
|
||||
};
|
||||
|
||||
// Identify the file type of the handle
|
||||
let (well_known, file_type) = match unsafe {
|
||||
use windows::Win32::Foundation::HANDLE;
|
||||
GetFileType(HANDLE(handle))
|
||||
} {
|
||||
FILE_TYPE_PIPE => (true, "pipe"),
|
||||
FILE_TYPE_CHAR => (true, "char"),
|
||||
FILE_TYPE_DISK => (true, "disk"),
|
||||
FILE_TYPE_UNKNOWN => (false, "unknown"),
|
||||
FILE_TYPE_REMOTE => (false, "remote"),
|
||||
unexpected => {
|
||||
warn!("Unexpected file type: {unexpected:?}");
|
||||
(false, "unknown")
|
||||
}
|
||||
};
|
||||
|
||||
debug!("File type: {file_type:?}, well known: {well_known}");
|
||||
|
||||
// If it's anything recognizable and valid, assume that a parent process has setup an output stream
|
||||
Ok(well_known.then(|| file_type))
|
||||
}
|
||||
|
||||
/// Try to attach to parent console
|
||||
fn attach_to_parent_console() -> Result<(), PlatformError> {
|
||||
use windows::{
|
||||
core::PCSTR,
|
||||
Win32::{
|
||||
Foundation::{GENERIC_READ, GENERIC_WRITE},
|
||||
Storage::FileSystem::{CreateFileA, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING},
|
||||
System::Console::{
|
||||
AttachConsole, FreeConsole, SetStdHandle, ATTACH_PARENT_PROCESS, STD_ERROR_HANDLE, STD_OUTPUT_HANDLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Attach the process to the parent's console
|
||||
unsafe { AttachConsole(ATTACH_PARENT_PROCESS) }
|
||||
.map_err(|e| PlatformError::ConsoleInit(format!("Failed to attach to parent console: {:?}", e)))?;
|
||||
|
||||
let handle = unsafe {
|
||||
let pcstr = PCSTR::from_raw("CONOUT$\0".as_ptr());
|
||||
CreateFileA::<PCSTR>(
|
||||
pcstr,
|
||||
(GENERIC_READ | GENERIC_WRITE).0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES(0),
|
||||
None,
|
||||
)
|
||||
}
|
||||
.map_err(|e| PlatformError::ConsoleInit(format!("Failed to create console handle: {:?}", e)))?;
|
||||
|
||||
// Set the console's output and then error handles
|
||||
if let Some(handle_error) = unsafe { SetStdHandle(STD_OUTPUT_HANDLE, handle) }
|
||||
.map_err(|e| PlatformError::ConsoleInit(format!("Failed to set console output handle: {:?}", e)))
|
||||
.and_then(|_| {
|
||||
unsafe { SetStdHandle(STD_ERROR_HANDLE, handle) }
|
||||
.map_err(|e| PlatformError::ConsoleInit(format!("Failed to set console error handle: {:?}", e)))
|
||||
})
|
||||
.err()
|
||||
{
|
||||
// If either set handle call fails, free the console
|
||||
unsafe { FreeConsole() }
|
||||
// Free the console if the SetStdHandle calls fail
|
||||
.map_err(|free_error| {
|
||||
PlatformError::ConsoleInit(format!(
|
||||
"Failed to free console after SetStdHandle failed: {free_error:?} ({handle_error:?})"
|
||||
))
|
||||
})
|
||||
// And then return the original error if the FreeConsole call succeeds
|
||||
.and(Err(handle_error))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user