mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-15 13:22:19 +01:00
feat(e2e): let the test window rename itself and go fullscreen
--rename-to T --after S gives the window another title S seconds after it opens, as a program that sets its title late does. The message WM_APP + 2 takes it fullscreen, borderless over its monitor, and back.
This commit is contained in:
1 parent
e22d82853c
commit
ea449c2d80
1 file changed
+91
-2
@@ -14,8 +14,13 @@
|
||||
//! ```
|
||||
//!
|
||||
//! Options: `--title T`, `--class C` (the window class, `KomorebiTestWin` by
|
||||
//! default), `--color RRGGBB`, `--size WxH`, `--min-size WxH` and
|
||||
//! `--fixed` (not resizable).
|
||||
//! default), `--color RRGGBB`, `--size WxH`, `--min-size WxH`, `--fixed` (not
|
||||
//! resizable) and `--rename-to T` with `--after SECONDS` (the window gives
|
||||
//! itself that title after that long, as a program that sets it late does).
|
||||
//!
|
||||
//! The message `WM_APP + 2` takes the window fullscreen (`wparam` 1: a
|
||||
//! borderless window over its monitor, as a program's own key does) or brings
|
||||
//! it back (`wparam` 0).
|
||||
|
||||
#![cfg(windows)]
|
||||
|
||||
@@ -41,8 +46,12 @@ use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||
use windows::Win32::Graphics::Gdi::DeleteObject;
|
||||
use windows::Win32::Graphics::Gdi::EndPaint;
|
||||
use windows::Win32::Graphics::Gdi::FillRect;
|
||||
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
||||
use windows::Win32::Graphics::Gdi::HGDIOBJ;
|
||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
|
||||
use windows::Win32::Graphics::Gdi::MONITORINFO;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
|
||||
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
|
||||
@@ -50,6 +59,15 @@ use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DestroyWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
|
||||
use windows::Win32::UI::WindowsAndMessaging::HWND_TOP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_FRAMECHANGED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_NOOWNERZORDER;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::HMENU;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IDC_ARROW;
|
||||
@@ -82,6 +100,12 @@ use windows::core::PCWSTR;
|
||||
/// Posted to the window when a command is waiting in [`COMMANDS`].
|
||||
const WM_COMMAND_READY: u32 = WM_APP + 1;
|
||||
|
||||
/// Posted to the window to take it fullscreen (`wparam` 1) or back (0).
|
||||
const WM_FULLSCREEN: u32 = WM_APP + 2;
|
||||
|
||||
/// The style and the rectangle of the window before it went fullscreen.
|
||||
static BEFORE_FULLSCREEN: Mutex<Option<(isize, RECT)>> = Mutex::new(None);
|
||||
|
||||
static COMMANDS: Mutex<VecDeque<String>> = Mutex::new(VecDeque::new());
|
||||
static HWND_RAW: AtomicIsize = AtomicIsize::new(0);
|
||||
static COLOR: AtomicU32 = AtomicU32::new(0x0080_5030);
|
||||
@@ -185,6 +209,49 @@ fn run_command(hwnd: HWND, line: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_fullscreen(hwnd: HWND, on: bool) {
|
||||
let mut before = BEFORE_FULLSCREEN.lock().unwrap();
|
||||
// SAFETY: plain Win32 calls on this process's own window, on the thread
|
||||
// that owns it.
|
||||
unsafe {
|
||||
if on && before.is_none() {
|
||||
let style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
let mut rect = RECT::default();
|
||||
let _ = GetWindowRect(hwnd, &mut rect);
|
||||
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
let mut info = MONITORINFO {
|
||||
cbSize: size_of::<MONITORINFO>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
if GetMonitorInfoW(monitor, &mut info).as_bool() {
|
||||
*before = Some((style, rect));
|
||||
let area = info.rcMonitor;
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, (WS_POPUP | WS_VISIBLE).0 as isize);
|
||||
let _ = SetWindowPos(
|
||||
hwnd,
|
||||
Some(HWND_TOP),
|
||||
area.left,
|
||||
area.top,
|
||||
area.right - area.left,
|
||||
area.bottom - area.top,
|
||||
SWP_FRAMECHANGED | SWP_NOOWNERZORDER,
|
||||
);
|
||||
}
|
||||
} else if !on && let Some((style, rect)) = before.take() {
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, style);
|
||||
let _ = SetWindowPos(
|
||||
hwnd,
|
||||
None,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
SWP_FRAMECHANGED | SWP_NOOWNERZORDER | SWP_NOZORDER,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "system" fn wndproc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
|
||||
match msg {
|
||||
WM_COMMAND_READY => {
|
||||
@@ -200,6 +267,10 @@ extern "system" fn wndproc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM)
|
||||
}
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_FULLSCREEN => {
|
||||
set_fullscreen(hwnd, wparam.0 != 0);
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_ERASEBKGND => LRESULT(1),
|
||||
WM_PAINT => {
|
||||
let mut ps = PAINTSTRUCT::default();
|
||||
@@ -248,6 +319,9 @@ fn main() -> windows::core::Result<()> {
|
||||
let mut size = (400, 300);
|
||||
let mut fixed = false;
|
||||
let mut class_name = wide("KomorebiTestWin");
|
||||
let mut rename: Option<(String, f64)> = None;
|
||||
let mut rename_to = None;
|
||||
let mut after = 0.0;
|
||||
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
@@ -268,12 +342,17 @@ fn main() -> windows::core::Result<()> {
|
||||
*MIN_SIZE.lock().unwrap() = args.next().as_deref().and_then(parse_size);
|
||||
}
|
||||
"--fixed" => fixed = true,
|
||||
"--rename-to" => rename_to = args.next(),
|
||||
"--after" => after = args.next().and_then(|s| s.parse().ok()).unwrap_or(0.0),
|
||||
other => eprintln!("komorebi-testwin: ignoring {other}"),
|
||||
}
|
||||
}
|
||||
if fixed {
|
||||
*FIXED.lock().unwrap() = Some(size);
|
||||
}
|
||||
if let Some(new) = rename_to {
|
||||
rename = Some((new, after));
|
||||
}
|
||||
|
||||
// SAFETY: plain Win32 setup on the main thread, which then runs the loop.
|
||||
unsafe {
|
||||
@@ -309,6 +388,16 @@ fn main() -> windows::core::Result<()> {
|
||||
HWND_RAW.store(hwnd.0 as isize, Ordering::SeqCst);
|
||||
let _ = ShowWindow(hwnd, SW_SHOW);
|
||||
|
||||
if let Some((new, after)) = rename {
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_secs_f64(after));
|
||||
let hwnd = HWND(HWND_RAW.load(Ordering::SeqCst) as *mut _);
|
||||
let title = wide(&new);
|
||||
// SAFETY: `title` is NUL-terminated and outlives the call.
|
||||
let _ = unsafe { SetWindowTextW(hwnd, PCWSTR(title.as_ptr())) };
|
||||
});
|
||||
}
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let hwnd = HWND(HWND_RAW.load(Ordering::SeqCst) as *mut _);
|
||||
for line in std::io::stdin().lock().lines() {
|
||||
|
||||
Reference in new issue
Block a user