winsafe 0.0.27

Windows API and GUI in safe, idiomatic Rust.
Documentation
use std::ops::{Deref, DerefMut};

use crate::co;
use crate::decl::*;
use crate::gdi::ffi;
use crate::guard::*;
use crate::prelude::*;

handle_guard! { DeleteDCGuard: HDC;
	ffi::DeleteDC;
	/// RAII implementation for [`HDC`](crate::HDC) which automatically calls
	/// [`DeleteDC`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deletedc)
	/// when the object goes out of scope.
}

/// RAII implementation for a [`GdiObject`](crate::prelude::GdiObject) which
/// automatically calls
/// [`DeleteObject`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject)
/// when the object goes out of scope.
pub struct DeleteObjectGuard<T>
where
	T: GdiObject,
{
	handle: T,
}

impl<T> Drop for DeleteObjectGuard<T>
where
	T: GdiObject,
{
	fn drop(&mut self) {
		if let Some(h) = self.handle.as_opt() {
			unsafe {
				ffi::DeleteObject(h.ptr());
			} // ignore errors
		}
	}
}

impl<T> Deref for DeleteObjectGuard<T>
where
	T: GdiObject,
{
	type Target = T;

	fn deref(&self) -> &Self::Target {
		&self.handle
	}
}

impl<T> DerefMut for DeleteObjectGuard<T>
where
	T: GdiObject,
{
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.handle
	}
}

impl<T> DeleteObjectGuard<T>
where
	T: GdiObject,
{
	/// Constructs the guard by taking ownership of the handle.
	///
	/// # Safety
	///
	/// Be sure the handle must be freed with
	/// [`DeleteObject`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject)
	/// at the end of scope.
	#[must_use]
	pub const unsafe fn new(handle: T) -> Self {
		Self { handle }
	}

	/// Ejects the underlying handle, leaving a
	/// [`Handle::INVALID`](crate::prelude::Handle::INVALID) in its place.
	///
	/// Since the internal handle will be invalidated, the destructor will not
	/// run. It's your responsability to run it, otherwise you'll cause a
	/// resource leak.
	#[must_use]
	pub fn leak(&mut self) -> T {
		std::mem::replace(&mut self.handle, T::INVALID)
	}
}

handle_guard! { DeleteObjectPaletteGuard: HPALETTE;
	ffi::DeleteObject;
	/// RAII implementation for [`HPALETTE`](crate::HPALETTE) which
	/// automatically calls
	/// [`DeleteObject`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject)
	/// when the object goes out of scope.
}

/// RAII implementation for [`LOGPALETTE`](crate::LOGPALETTE) which manages the
/// allocated memory.
pub struct LogpaletteGuard {
	ptr: GlobalFreeGuard,
}

impl Deref for LogpaletteGuard {
	type Target = LOGPALETTE;

	fn deref(&self) -> &Self::Target {
		unsafe { &*(self.ptr.ptr() as *const _) }
	}
}

impl DerefMut for LogpaletteGuard {
	fn deref_mut(&mut self) -> &mut Self::Target {
		unsafe { &mut *(self.ptr.ptr() as *mut _) }
	}
}

impl LogpaletteGuard {
	#[must_use]
	pub(in crate::gdi) fn new(pal_version: u16, entries: &[PALETTEENTRY]) -> SysResult<Self> {
		let sz = std::mem::size_of::<LOGPALETTE>() // size in bytes of the allocated struct
			- std::mem::size_of::<PALETTEENTRY>()
			+ (entries.len() * std::mem::size_of::<PALETTEENTRY>());
		let mut new_self = Self {
			ptr: HGLOBAL::GlobalAlloc(co::GMEM::FIXED | co::GMEM::ZEROINIT, sz)?,
		};
		new_self.palVersion = pal_version;
		new_self.palNumEntries = entries.len() as _;
		entries
			.iter()
			.zip(new_self.palPalEntry_mut())
			.for_each(|(src, dest)| *dest = *src); // copy all PALETTEENTRY into struct room
		Ok(new_self)
	}
}

/// RAII implementation for [`HDC::SelectObject`](crate::HDC::SelectObject)
/// calls, which automatically selects the previous GDI object at the end of the
/// scope.
pub struct SelectObjectGuard<'a, G>
where
	G: GdiObject,
{
	hdc: &'a HDC,
	prev_hgdi: G,
	region: Option<co::REGION>,
}

impl<'a, G> Drop for SelectObjectGuard<'a, G>
where
	G: GdiObject,
{
	fn drop(&mut self) {
		if let Some(h) = self.hdc.as_opt() {
			if let Some(g) = self.prev_hgdi.as_opt() {
				unsafe {
					ffi::SelectObject(h.ptr(), g.ptr());
				} // ignore errors
			}
		}
	}
}

impl<'a, G> SelectObjectGuard<'a, G>
where
	G: GdiObject,
{
	/// Constructs the guard by taking ownership of the handle.
	///
	/// # Safety
	///
	/// Be sure the handle must be again passed to
	/// [`HDC::SelectObject`](crate::HDC::SelectObject) at the end of scope.
	#[must_use]
	pub const unsafe fn new(hdc: &'a HDC, prev_hgdi: G, region: Option<co::REGION>) -> Self {
		Self { hdc, prev_hgdi, region }
	}

	/// Returns a handle to the object that has been replaced.
	#[must_use]
	pub const fn prev_object(&self) -> &G {
		&self.prev_hgdi
	}

	/// Returns the region information returned by the source
	/// [`HDC::SelectObject`](crate::HDC::SelectObject) call, if the
	/// [`GdiObject`](crate::prelude::GdiObject) was an [`HRGN`](crate::HRGN);
	/// otherwise returns `None`.
	#[must_use]
	pub const fn region(&self) -> Option<co::REGION> {
		self.region
	}

	/// Ejects the underlying handle, leaving a
	/// [`Handle::INVALID`](crate::prelude::Handle::INVALID) in its place.
	///
	/// Since the internal handle will be invalidated, the destructor will not
	/// run. It's your responsability to run it, otherwise you'll cause a
	/// resource leak.
	#[must_use]
	pub fn leak(&mut self) -> G {
		std::mem::replace(&mut self.prev_hgdi, G::INVALID)
	}
}