diff --git a/thindx/src/_lib.rs b/thindx/src/_lib.rs
index b1c4ee54..c5860c53 100644
--- a/thindx/src/_lib.rs
+++ b/thindx/src/_lib.rs
@@ -33,6 +33,7 @@ mods! {
pub mod d3d11;
pub mod wkpdid;
pub mod xinput;
+ pub mod win32;
}
#[path="headers/d3d9.h/d3d9.rs"] mod d3d9_h; // d3d9 mod
@@ -42,7 +43,9 @@ mods! {
#[path="headers/d3dcommon.h/d3dcommon.rs"] mod d3dcommon_h; // d3d mod
#[path="headers/d3dcompiler.h/d3dcompiler.rs"] mod d3dcompiler_h; // d3d mod
#[path="headers/guiddef.h/guiddef.rs"] mod guiddef_h;
+ #[path="headers/processthreadsapi.h/processthreadsapi.rs"] mod processthreadsapi_h;
#[path="headers/unknwn.h/unknwn.rs"] mod unknwn_h;
+ #[path="headers/winuser.h/winuser.rs"] mod winuser_h;
#[path="headers/xinput.h/xinput.rs"] mod xinput_h;
#[path="traits/_traits.rs"] mod traits;
diff --git a/thindx/src/error_kind.rs b/thindx/src/error_kind.rs
index 5cb1b9d2..70bec0cd 100644
--- a/thindx/src/error_kind.rs
+++ b/thindx/src/error_kind.rs
@@ -122,6 +122,7 @@ impl ErrorKind {
ERROR::FILE_EXISTS => Some(("ERROR_FILE_EXISTS", "The file exists.")),
ERROR::BAD_ARGUMENTS => Some(("ERROR_BAD_ARGUMENTS", "One or more arguments are not correct.")),
ERROR::DEVICE_NOT_CONNECTED => Some(("ERROR_DEVICE_NOT_CONNECTED", "The device is not connected.")),
+ ERROR::INVALID_WINDOW_HANDLE => Some(("ERROR_INVALID_WINDOW_HANDLE", "Invalid window handle.")),
THINERR::NONSPECIFIC => Some(("THINERR_NONSPECIFIC", "A nonspecific error of some sort occured.")),
THINERR::DEVICE_MISMATCH => Some(("THINERR_DEVICE_MISMATCH", "Resource belonging to one Device was passed to a different Device. To avoid undefined behavior, DirectX was not called.")),
diff --git a/thindx/src/errors.rs b/thindx/src/errors.rs
index 124ae94f..16de190f 100644
--- a/thindx/src/errors.rs
+++ b/thindx/src/errors.rs
@@ -344,6 +344,9 @@ pub mod ERROR {
/// The device is not connected.
pub const DEVICE_NOT_CONNECTED : ErrorKind = ErrorKind(ERROR_DEVICE_NOT_CONNECTED as _);
+
+ /// Invalid window handle.
+ pub const INVALID_WINDOW_HANDLE : ErrorKind = ErrorKind(ERROR_INVALID_WINDOW_HANDLE as _);
}
/// `0x0000....` • \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/learnwin32/error-handling-in-com)\] • Win32/COM success "[ErrorKind]"s
diff --git a/thindx/src/headers/_misc/cpp2url.md b/thindx/src/headers/_misc/cpp2url.md
index 7bae88ac..98c56da8 100644
--- a/thindx/src/headers/_misc/cpp2url.md
+++ b/thindx/src/headers/_misc/cpp2url.md
@@ -2,6 +2,5 @@
[BOOL]: https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#BOOL
[GUID]: https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
[HRESULT]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
-[HWND]: https://docs.microsoft.com/en-us/windows/win32/learnwin32/what-is-a-window-
[LUID]: https://docs.microsoft.com/en-us/previous-versions/windows/hardware/drivers/ff549708(v=vs.85)
[RECT]: https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect
diff --git a/thindx/src/headers/processthreadsapi.h/cpp2url.md b/thindx/src/headers/processthreadsapi.h/cpp2url.md
new file mode 100644
index 00000000..639811be
--- /dev/null
+++ b/thindx/src/headers/processthreadsapi.h/cpp2url.md
@@ -0,0 +1,4 @@
+
+
+[GetCurrentProcessId]: https://docs.microsoft.com/en-gb/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid
+[GetCurrentThreadId]: https://docs.microsoft.com/en-gb/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid
diff --git a/thindx/src/headers/processthreadsapi.h/functions/get_current.rs b/thindx/src/headers/processthreadsapi.h/functions/get_current.rs
new file mode 100644
index 00000000..c08dbbbb
--- /dev/null
+++ b/thindx/src/headers/processthreadsapi.h/functions/get_current.rs
@@ -0,0 +1,37 @@
+use winapi::um::processthreadsapi::*;
+
+
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-gb/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid)\]
+/// GetCurrentProcessId
+///
+/// ### Example
+/// ```rust
+/// # use thindx::*;
+/// let process_id = win32::get_current_process_id();
+/// assert!(process_id != 0);
+/// ```
+pub fn get_current_process_id() -> u32 {
+ fn_context!(win32::get_current_process_id => GetCurrentThreadId);
+ unsafe { GetCurrentProcessId() }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-gb/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid)\]
+/// GetCurrentThreadId
+///
+/// ### Example
+/// ```rust
+/// # use thindx::*;
+/// let thread_id = win32::get_current_thread_id();
+/// assert!(thread_id != 0);
+/// ```
+pub fn get_current_thread_id() -> u32 {
+ // XXX: We could make this return NonZeroU32:
+ // > Note that no thread identifier will ever be 0.
+ // > https://docs.microsoft.com/en-us/windows/win32/procthread/thread-handles-and-identifiers
+ // However, that might be awkward to work with.
+ // We could have an alternative function...?
+ // Also, what if we run on a hacked up emulator that violates this invariant?
+ fn_context!(win32::get_current_thread_id => GetCurrentThreadId);
+ unsafe { GetCurrentThreadId() }
+}
diff --git a/thindx/src/headers/processthreadsapi.h/processthreadsapi.rs b/thindx/src/headers/processthreadsapi.h/processthreadsapi.rs
new file mode 100644
index 00000000..69646787
--- /dev/null
+++ b/thindx/src/headers/processthreadsapi.h/processthreadsapi.rs
@@ -0,0 +1,5 @@
+mods! {
+ inl mod functions {
+ inl mod get_current;
+ }
+}
diff --git a/thindx/src/headers/winuser.h/cpp2url.md b/thindx/src/headers/winuser.h/cpp2url.md
new file mode 100644
index 00000000..061ec205
--- /dev/null
+++ b/thindx/src/headers/winuser.h/cpp2url.md
@@ -0,0 +1,18 @@
+
+
+[DestroyWindow]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow
+
+[GetClientRect]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect
+[GetDesktopWindow]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow
+
+[GetWindowThreadProcessId]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
+
+[IsWindow]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindow
+[IsWindowUnicode]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowunicode
+[IsWindowVisible]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowvisible
+
+
+
+
+[HWND]: https://docs.microsoft.com/en-us/windows/win32/learnwin32/what-is-a-window-
+
diff --git a/thindx/src/headers/winuser.h/functions/window.rs b/thindx/src/headers/winuser.h/functions/window.rs
new file mode 100644
index 00000000..6d89a0df
--- /dev/null
+++ b/thindx/src/headers/winuser.h/functions/window.rs
@@ -0,0 +1,228 @@
+//! ### Safety
+//!
+//! `HWND`s are pretty funky.
+//! On the one hand, they're supposedly pointers, and in the single threaded days of 16-bit windows, they perhaps were.
+//! Modern 32-bit windows has turned these into generational indicies of sorts, and has sunken a lot of time into making Win32 safe/sound despite userspace's abuse.
+//! Windows may be owned by another thread, and thus destroyed at any time.
+//! Windows may be owned by another process, and thus destroyed at any time.
+//! Windows may be owned by another user, probably?
+//! HWNDs should generally be treated as weak references without proper validity checking.
+
+use crate::{Error, ErrorKind, ERROR};
+use crate::d3d::Rect; // ...?
+
+use winapi::um::winuser::*;
+
+
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/learnwin32/what-is-a-window-)\]
+/// HWND
+///
+/// A window handle.
+/// Note that Window's definition of a "window" is [pretty expansive](https://docs.microsoft.com/en-us/windows/win32/learnwin32/what-is-a-window-).
+/// There's a reason it's named "Windows"!
+///
+/// Windows are "windows".
+/// Buttons are "windows".
+/// The desktop is a "window".
+/// ~~You're a "window".~~
+/// Windows belonging to other threads are windows.
+/// Windows belonging to other processes are windows.
+/// Windows belonging to other users... *maybe* you're isolated from those? But probably not.
+pub type HWND = winapi::shared::windef::HWND;
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow)\]
+/// DestroyWindow
+///
+/// Destroys the specified window.
+///
+/// ### ⚠️ Safety ⚠️
+/// * Destroying a window out from underneath a 3D rendering API such as Direct3D is generally unsound.
+/// * Destroying a "destroyed" window is unlikely to be sound. While windows itself can handle it,
+/// it can result in multiple WM_DESTROY and WM_NCDESTROY events, which the underlying wndprocs likely can't handle.
+/// * Destroying a window belonging to another thread or process is an incredibly bad idea, if it even works.
+/// * Honestly, you should probably only destroy windows you created, in your own process, on your own thread, and even then be careful!
+///
+/// ### Errors
+/// * [ERROR::INVALID_WINDOW_HANDLE]
+///
+/// ### Example
+/// ```rust
+/// # use thindx::*;
+/// # use std::ptr::*;
+/// assert_eq!(ERROR::INVALID_WINDOW_HANDLE, unsafe { win32::destroy_window(null_mut()) });
+/// ```
+pub unsafe fn destroy_window(hwnd: impl Into) -> Result<(), Error> {
+ fn_context!(win32::destroy_window => DestroyWindow);
+ let hwnd = hwnd.into();
+ let succeeded = unsafe { DestroyWindow(hwnd) != 0 };
+ if succeeded { Ok(()) } else { fn_err!(get_last_error()) }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect)\]
+/// GetClientRect
+///
+/// Retrieves the coordinates of a window's client area.
+/// The client coordinates specify the upper-left and lower-right corners of the client area.
+/// Because client coordinates are relative to the upper-left corner of a window's client area, the coordinates of the upper-left corner are (0,0).
+///
+/// ### Errors
+/// * [ERROR::INVALID_WINDOW_HANDLE]
+///
+/// ### Example
+/// ```rust
+/// # use thindx::*;
+/// # use std::ptr::*;
+/// assert_eq!(ERROR::INVALID_WINDOW_HANDLE, win32::get_client_rect(null_mut()));
+/// let rect = win32::get_client_rect(win32::get_desktop_window()).unwrap();
+/// assert_eq!(0, rect.left());
+/// assert_eq!(0, rect.top());
+/// assert!(0 != rect.width());
+/// assert!(0 != rect.height());
+/// # for p in 0 .. 8 * std::mem::size_of::() {
+/// # let e = win32::get_client_rect((1usize << p) as win32::HWND); // shouldn't crash
+/// # if e.is_err() { assert_eq!(ERROR::INVALID_WINDOW_HANDLE, e) }
+/// # }
+/// ```
+pub fn get_client_rect(hwnd: impl TryInto) -> Result {
+ fn_context!(win32::get_client_rect => GetClientRect);
+ let hwnd = hwnd.try_into().map_err(|_| fn_param_error!(hwnd, ERROR::INVALID_WINDOW_HANDLE))?;
+ let mut rect = Rect::zeroed();
+ let succeeded = unsafe { GetClientRect(hwnd, rect.as_mut()) != 0 };
+ if succeeded { Ok(rect) } else { fn_err!(get_last_error()) }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow)\]
+/// GetDesktopWindow
+///
+/// Retrieves a handle to the desktop window.
+/// The desktop window covers the entire screen.
+/// The desktop window is the area on top of which other windows are painted.
+///
+/// ### Example
+/// ```rust
+/// # use thindx::win32;
+/// let hwnd = win32::get_desktop_window();
+/// # assert!(win32::is_window(hwnd));
+/// ```
+#[must_use] pub fn get_desktop_window() -> HWND {
+ fn_context!(win32::get_desktop_window => GetDesktopWindow);
+ unsafe { GetDesktopWindow() }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid)\]
+/// GetWindowThreadProcessId
+///
+/// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.
+///
+/// ### Returns
+/// * Ok((thread_id, process_id))
+/// * [ERROR::INVALID_WINDOW_HANDLE]
+///
+/// ### Example
+/// ```rust
+/// # use thindx::*;
+/// # let hwnd = win32::get_desktop_window();
+/// let (thread, process) = win32::get_window_thread_process_id(hwnd).unwrap();
+/// let hwnd_belongs_to_this_thread = thread == win32::get_current_thread_id();
+/// # assert!(!hwnd_belongs_to_this_thread, "desktop doesn't belong to us!");
+/// #
+/// # for p in 0 .. 8 * std::mem::size_of::() {
+/// # let e = win32::get_window_thread_process_id((1usize << p) as win32::HWND); // shouldn't crash
+/// # if e.is_err() { assert_eq!(ERROR::INVALID_WINDOW_HANDLE, e) }
+/// # }
+/// ```
+#[must_use] pub fn get_window_thread_process_id(hwnd: impl Into) -> Result<(u32, u32), Error> {
+ fn_context!(win32::get_window_thread_process_id => GetWindowThreadProcessId);
+ let hwnd = hwnd.into();
+ let mut pid = 0;
+ let tid = unsafe { GetWindowThreadProcessId(hwnd, &mut pid) };
+ if tid != 0 { Ok((tid, pid)) } else { fn_err!(get_last_error()) }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindow)\]
+/// IsWindow
+///
+/// Determines whether the specified window handle identifies an existing window.
+///
+/// Valid uses of this function are few and far between - windows belonging to another thread
+/// or process could be destroyed immediately after this returns true, and invalidated handles
+/// might suddenly spring back to life as the handle value is reused.
+///
+/// ### Example
+/// ```rust
+/// # use thindx::win32;
+/// # let valid_hwnd = win32::get_desktop_window();
+/// # let invalid_hwnd : win32::HWND = !42 as _;
+/// assert!( win32::is_window( valid_hwnd));
+/// assert!(!win32::is_window(invalid_hwnd));
+/// assert!(!win32::is_window(std::ptr::null_mut()));
+/// # for p in 0 .. 8 * std::mem::size_of::() {
+/// # let _ = win32::is_window((1usize << p) as win32::HWND); // shouldn't crash
+/// # }
+/// ```
+#[must_use] pub fn is_window(hwnd: impl TryInto) -> bool {
+ fn_context!(win32::is_window => IsWindow);
+ match hwnd.try_into() {
+ Ok(hwnd) => unsafe { IsWindow(hwnd) != 0 },
+ Err(_) => false,
+ }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindow)\]
+/// IsWindowUnicode
+///
+/// Determines whether the specified window is a native Unicode window.
+///
+/// ### Returns
+/// * `true` if the window's class was registered with `RegisterClassW`
+/// * `false` if the window's class was registered with `RegisterClassA`
+/// * `false` if the window isn't valid, probably? (TryInto failed, HWND null/dangling/destroyed, ...)
+///
+/// ### Example
+/// ```rust
+/// # use thindx::win32;
+/// # let unicode_hwnd = win32::get_desktop_window(); // TODO: replace with an explicitly created unicode hwnd
+/// assert!( win32::is_window_unicode(unicode_hwnd));
+/// assert!(!win32::is_window_unicode(std::ptr::null_mut()));
+/// # for p in 0 .. 8 * std::mem::size_of::() {
+/// # let _ = win32::is_window_unicode((1usize << p) as win32::HWND); // shouldn't crash
+/// # }
+/// ```
+#[must_use] pub fn is_window_unicode(hwnd: impl TryInto) -> bool {
+ fn_context!(win32::is_window_unicode => IsWindowUnicode);
+ match hwnd.try_into() {
+ Ok(hwnd) => unsafe { IsWindowUnicode(hwnd) != 0 },
+ Err(_) => false,
+ }
+}
+
+/// \[[docs.microsoft.com](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindow)\]
+/// IsWindowVisible
+///
+/// Determines whether the specified window is WS_VISIBLE.
+/// May return `true` even if the window is totally obscured by other windows, clipped out-of-bounds, etc.
+///
+/// ### Example
+/// ```rust
+/// # use thindx::win32;
+/// # let hwnd = win32::get_desktop_window(); // TODO: replace with an explicitly created unicode hwnd
+/// assert!( win32::is_window_visible(hwnd));
+/// assert!(!win32::is_window_visible(std::ptr::null_mut()));
+/// # for p in 0 .. 8 * std::mem::size_of::() {
+/// # let _ = win32::is_window_visible((1usize << p) as win32::HWND); // shouldn't crash
+/// # }
+/// ```
+#[must_use] pub fn is_window_visible(hwnd: impl TryInto) -> bool {
+ fn_context!(win32::is_window_unicode => IsWindowVisible);
+ match hwnd.try_into() {
+ Ok(hwnd) => unsafe { IsWindowVisible(hwnd) != 0 },
+ Err(_) => false,
+ }
+}
+
+
+
+#[must_use] fn get_last_error() -> ErrorKind {
+ ErrorKind(unsafe { winapi::um::errhandlingapi::GetLastError() } as _)
+}
diff --git a/thindx/src/headers/winuser.h/winuser.rs b/thindx/src/headers/winuser.h/winuser.rs
new file mode 100644
index 00000000..2f50ff22
--- /dev/null
+++ b/thindx/src/headers/winuser.h/winuser.rs
@@ -0,0 +1,5 @@
+mods! {
+ inl mod functions {
+ inl mod window;
+ }
+}
diff --git a/thindx/src/namespaces/win32.rs b/thindx/src/namespaces/win32.rs
new file mode 100644
index 00000000..d857b48d
--- /dev/null
+++ b/thindx/src/namespaces/win32.rs
@@ -0,0 +1,4 @@
+//! General windows APIs
+
+pub use crate::processthreadsapi_h::*;
+pub use crate::winuser_h::*;
diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock
index 99fd1b41..26fe9232 100644
--- a/xtask/Cargo.lock
+++ b/xtask/Cargo.lock
@@ -44,7 +44,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "maulingmonkey-windows-sdk-scanner"
version = "0.0.0-git"
-source = "git+https://github.com/MaulingMonkey/windows-sdk-scanner#d81e08e9df6a7fc7de2fea3a2a020717f1bddd68"
+source = "git+https://github.com/MaulingMonkey/windows-sdk-scanner#b58a05e9515f664b1a014c8cc158846c82b03484"
dependencies = [
"abibool",
"abistr",