diff --git a/slickcmd/res/resource.h b/slickcmd/res/resource.h index 6f5c1ef..7f41d86 100644 --- a/slickcmd/res/resource.h +++ b/slickcmd/res/resource.h @@ -23,6 +23,7 @@ #define IDC_CHK_RUN_ON_START 1006 #define IDC_CHK_RUN_ON_STARTUP 1006 #define IDC_SYSLINK1 1007 +#define IDC_CHK_SHOW_CLOCK 1008 #define IDC_STATIC -1 // Next default values for new objects @@ -32,7 +33,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 133 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_CONTROL_VALUE 1009 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/slickcmd/res/slickcmd.rc b/slickcmd/res/slickcmd.rc index 2403645..83010f5 100644 --- a/slickcmd/res/slickcmd.rc +++ b/slickcmd/res/slickcmd.rc @@ -97,7 +97,8 @@ BEGIN CONTROL "",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,49,21,11,14 CONTROL "&Path auto-complete for 'cd'",IDC_CHK_CD_COMPLETION, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,49,102,10 - CONTROL "&Run on Start Up",IDC_CHK_RUN_ON_STARTUP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,70,67,10 + CONTROL "&Run on Start Up",IDC_CHK_RUN_ON_STARTUP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,66,67,10 + CONTROL "Console &Clock",IDC_CHK_SHOW_CLOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,83,60,10 END diff --git a/slickcmd/res/slickcmd.res b/slickcmd/res/slickcmd.res index 36fa14f..6084ccb 100644 Binary files a/slickcmd/res/slickcmd.res and b/slickcmd/res/slickcmd.res differ diff --git a/slickcmd/src/clock_win.rs b/slickcmd/src/clock_win.rs new file mode 100644 index 0000000..f52949e --- /dev/null +++ b/slickcmd/src/clock_win.rs @@ -0,0 +1,213 @@ +use crate::global::GLOBAL; +use crate::win_man::WinMan; +use crate::wt_focus_man::WtFocusMan; +use slickcmd_common::consts::{WM_SYSTEM_MOVESIZEEND, WM_SYSTEM_MOVESIZESTART, WM_WT_FOCUS_CHANGE}; +use slickcmd_common::font_info::FontInfo; +use slickcmd_common::winproc::{wndproc, WinProc}; +use slickcmd_common::{logd, win32}; +use std::ffi::c_void; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use windows::Win32::Foundation::*; +use windows::Win32::Graphics::Gdi::*; +use windows::Win32::UI::WindowsAndMessaging::*; + +static WC_REGISTERED: AtomicBool = AtomicBool::new(false); + +pub struct ClockWin { + hwnd: HWND, + hwnd_term: HWND, + + hfont: HFONT, + + last_console_bounds: RECT, + + width: i32, + height: i32, + + is_wt: bool, +} + +impl ClockWin { + pub fn new(hwnd_term: HWND) -> ClockWin { + ClockWin { + hwnd: HWND::default(), + hwnd_term, + hfont: HFONT::default(), + last_console_bounds: RECT::default(), + width: 0, + height: 0, + is_wt: hwnd_term.0 as usize == WtFocusMan::hwnd_wt(), + } + } + + fn register_class(window_class: &str) -> bool { + let wsz_class = win32::wsz_from_str(window_class); + + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + hInstance: GLOBAL.hinstance(), + hCursor: win32::load_cursor(IDC_ARROW), + hbrBackground: HBRUSH((COLOR_WINDOW.0 + 1) as *mut c_void), + lpszClassName: win32::pwsz(&wsz_class), + lpfnWndProc: Some(wndproc::), + ..Default::default() + }; + + let atom = win32::register_class_ex(&wc); + if atom == 0 { + return false; + } + true + } + + pub fn create(&mut self, fi: FontInfo, cell_size: (i32, i32)) { + let window_class = "slck_cmd_clock"; + if !WC_REGISTERED.load(Relaxed) { + if !Self::register_class(&window_class) { + debug_assert!(false); + } + WC_REGISTERED.store(true, Relaxed); + } + + let mut lf = LOGFONTW::default(); + lf.lfWidth = fi.width; + lf.lfHeight = fi.height; + lf.lfPitchAndFamily = fi.pitch_and_family; + lf.lfCharSet = DEFAULT_CHARSET; + + let wsz_facename = win32::wsz_from_str(&fi.name); + lf.lfFaceName[..wsz_facename.len()].copy_from_slice(wsz_facename.as_slice()); + + self.hfont = win32::create_font_indirect(&lf); + + // + self.width = cell_size.0 * "23:45:59".len() as i32; + self.height = cell_size.1; + let style = if self.is_wt { WS_POPUP } else { WS_CHILD }; + let hwnd = win32::create_window_ex( + WINDOW_EX_STYLE::default(), + &window_class, + "", + style, + 0, + 0, + self.width, + self.height, + self.hwnd_term, + HMENU::default(), + GLOBAL.hinstance(), + Some(self as *const _ as *const c_void), + ); + self.hwnd = hwnd; + if self.is_wt { + WtFocusMan::add_wt_listener_hwnd(self.hwnd); + } else { + let style = win32::get_window_long(self.hwnd_term, GWL_STYLE); + let style = WINDOW_STYLE(style as u32); + if !style.contains(WS_CLIPCHILDREN) { + let style = style | WS_CLIPCHILDREN; + win32::set_window_long(self.hwnd_term, GWL_STYLE, style.0 as i32); + } + } + + self.on_timer(); + win32::set_timer(self.hwnd, 1, 1000, None); + } + + pub fn destroy(&mut self) { + if self.is_wt { + WtFocusMan::remove_wt_listener_hwnd(self.hwnd); + } + if self.hwnd.is_invalid() { + logd!("??"); + return; + } + win32::destroy_window(self.hwnd); + win32::delete_object(self.hfont.into()); + self.hwnd = HWND::default(); + self.hfont = HFONT::default(); + } + + fn on_timer(&mut self) { + let console_bounds = WinMan::get_console_bounds(self.hwnd_term); + let hwnd = self.hwnd; + if self.last_console_bounds != console_bounds { + self.last_console_bounds = console_bounds; + let mut pt = POINT { + x: console_bounds.right - self.width, + y: console_bounds.top, + }; + if self.is_wt { + win32::client_to_screen(self.hwnd_term, &mut pt); + } + + win32::move_window(hwnd, pt.x, pt.y, self.width, self.height, false); + win32::show_window(hwnd, SW_SHOWNOACTIVATE); + + if !self.is_wt { + win32::invalidate_rect(self.hwnd_term, None, true); + win32::update_window(self.hwnd_term); + } + } + + win32::invalidate_rect(hwnd, None, false); + win32::update_window(hwnd); + } + + fn on_paint(&mut self) { + let mut ps = PAINTSTRUCT::default(); + win32::begin_paint(self.hwnd, &mut ps); + let hfont_old = win32::select_object(ps.hdc, self.hfont.into()); + + let bg_color = win32::rgb(0, 0, 168); + let hbr = win32::create_solid_brush(bg_color); + win32::fill_rect(ps.hdc, &ps.rcPaint, hbr); + win32::delete_object(hbr.into()); + + let st = win32::get_local_time(); + let s_time = format!("{:02}:{:02}:{:02}", st.wHour, st.wMinute, st.wSecond); + win32::set_bk_color(ps.hdc, bg_color); + win32::set_text_color(ps.hdc, win32::rgb(240, 240, 240)); + + let mut rc = RECT { + left: 0, + top: 0, + right: self.width, + bottom: self.height, + }; + win32::draw_text( + ps.hdc, + &s_time, + &mut rc, + DT_SINGLELINE | DT_CENTER | DT_VCENTER, + ); + + win32::select_object(ps.hdc, hfont_old); + win32::end_paint(self.hwnd, &ps); + } +} + +impl WinProc for ClockWin { + fn wndproc(&mut self, window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + match message { + WM_TIMER => { + self.on_timer(); + return LRESULT(0); + } + WM_PAINT => { + self.on_paint(); + return LRESULT(0); + } + WM_SYSTEM_MOVESIZESTART => { + win32::show_window(self.hwnd, SW_HIDE); + } + WM_SYSTEM_MOVESIZEEND | WM_WT_FOCUS_CHANGE => { + self.last_console_bounds = RECT::default(); + self.on_timer(); + } + _ => {} + } + unsafe { DefWindowProcW(window, message, wparam, lparam) } + } +} diff --git a/slickcmd/src/console.rs b/slickcmd/src/console.rs index ccf8c01..ad21524 100644 --- a/slickcmd/src/console.rs +++ b/slickcmd/src/console.rs @@ -19,6 +19,7 @@ use windows::Win32::System::Threading::*; use windows::Win32::UI::Input::KeyboardAndMouse::*; use windows::Win32::UI::WindowsAndMessaging::*; use crate::app::App; +use crate::clock_win::ClockWin; use crate::win_man::WinMan; #[derive(Default)] @@ -48,6 +49,7 @@ pub struct Console { update_cur_dir_on_key_up: bool, manual_cd_completing: bool, + clock_win: Option>, } #[derive(Default)] @@ -207,6 +209,19 @@ impl Console { self.update_cur_dir(); } + if GLOBAL.options.show_clock() { + let clock_win = ClockWin::new(self.hwnd_term); + + let bounds = self.get_console_bounds(); + let size = (bounds.right - bounds.left, bounds.bottom - bounds.top); + let dim_info = self.read_dimension_info(size); + + let fi = self.get_font_info(&dim_info); + + let mut clock_win = Box::new(clock_win); + clock_win.create(fi, (dim_info.cell_width, dim_info.cell_height)); + self.clock_win = Some(clock_win); + } } pub fn on_deactivate(&mut self) { @@ -214,6 +229,10 @@ impl Console { if self.showing_ac_list { self.hide_ac_list(); } + if let Some(clock_win) = &mut self.clock_win { + clock_win.destroy(); + self.clock_win = None; + } win32::unregister_hotkey(self.hwnd_msg, 1); self.command_hist.save(); } diff --git a/slickcmd/src/lib.rs b/slickcmd/src/lib.rs index d3a59ca..277200b 100644 --- a/slickcmd/src/lib.rs +++ b/slickcmd/src/lib.rs @@ -20,3 +20,4 @@ pub mod win_man; pub mod app_state; pub mod wt_focus_man; pub mod console_man; +pub mod clock_win; diff --git a/slickcmd/src/options.rs b/slickcmd/src/options.rs index 1e9363e..f5ef784 100644 --- a/slickcmd/src/options.rs +++ b/slickcmd/src/options.rs @@ -8,7 +8,7 @@ pub struct Options { max_recent_dirs: Cell, cd_completion: Cell, run_on_startup: Cell, - + show_clock: Cell, } impl Options { @@ -23,6 +23,7 @@ impl Options { ini.write("General", "max_recent_dirs", self.max_recent_dirs()); ini.write("General", "cd_completion", self.cd_completion()); ini.write("General", "run_on_startup", self.run_on_startup()); + ini.write("General", "show_clock", self.show_clock()); } pub fn init(&self) { @@ -30,6 +31,7 @@ impl Options { self.set_max_recent_dirs(ini.read_or("General", "max_recent_dirs", 15)); self.set_cd_completion(ini.read_or("General", "cd_completion", true)); self.set_run_on_startup(ini.read_or("General", "run_on_startup", true)); + self.set_show_clock(ini.read_or("General", "show_clock", false)); } pub fn max_recent_dirs(&self) -> u32 { @@ -56,4 +58,11 @@ impl Options { self.run_on_startup.set(value); } + pub fn show_clock(&self) -> bool { + self.show_clock.get() + } + + pub fn set_show_clock(&self, value: bool) { + self.show_clock.set(value); + } } \ No newline at end of file diff --git a/slickcmd/src/options_dlg.rs b/slickcmd/src/options_dlg.rs index 18a9449..434d531 100644 --- a/slickcmd/src/options_dlg.rs +++ b/slickcmd/src/options_dlg.rs @@ -1,8 +1,5 @@ use crate::startup_link::StartupLink; -use slickcmd_common::consts::{ - IDC_CHK_CD_COMPLETION, IDC_CHK_RUN_ON_STARTUP, IDC_MAX_RECENT_DIRS, IDC_MAX_RECENT_DIRS_SPIN, - IDD_OPTIONS, -}; +use slickcmd_common::consts::{IDC_CHK_CD_COMPLETION, IDC_CHK_RUN_ON_STARTUP, IDC_CHK_SHOW_CLOCK, IDC_MAX_RECENT_DIRS, IDC_MAX_RECENT_DIRS_SPIN, IDD_OPTIONS}; use slickcmd_common::dlg::{dlg_proc, Dlg}; use slickcmd_common::{dlg, utils, win32}; use windows::Win32::Foundation::*; @@ -17,6 +14,7 @@ pub struct OptionsDlg { hwnd_max_recent_dirs: HWND, hwnd_chk_cd_completion: HWND, hwnd_chk_run_on_startup: HWND, + hwnd_chk_show_clock: HWND, } impl OptionsDlg { @@ -48,6 +46,7 @@ impl OptionsDlg { self.hwnd_chk_cd_completion = win32::get_dlg_item(self.hwnd, IDC_CHK_CD_COMPLETION); self.hwnd_chk_run_on_startup = win32::get_dlg_item(self.hwnd, IDC_CHK_RUN_ON_STARTUP); + self.hwnd_chk_show_clock = win32::get_dlg_item(self.hwnd, IDC_CHK_SHOW_CLOCK); let options = &GLOBAL.options; let text = &format!("{}", options.max_recent_dirs()); @@ -55,6 +54,7 @@ impl OptionsDlg { self.set_check(self.hwnd_chk_cd_completion, options.cd_completion()); self.set_check(self.hwnd_chk_run_on_startup, options.run_on_startup()); + self.set_check(self.hwnd_chk_show_clock, options.show_clock()); 1 } @@ -78,11 +78,13 @@ impl OptionsDlg { let enable_cd_completion = self.get_check(self.hwnd_chk_cd_completion); let run_on_startup = self.get_check(self.hwnd_chk_run_on_startup); + let show_clock = self.get_check(self.hwnd_chk_show_clock); let options = &GLOBAL.options; options.set_max_recent_dirs(max_recent_dirs); options.set_cd_completion(enable_cd_completion); options.set_run_on_startup(run_on_startup); + options.set_show_clock(show_clock); options.save(); // diff --git a/slickcmd/src/wt_focus_man.rs b/slickcmd/src/wt_focus_man.rs index 3ea747f..dc6925f 100644 --- a/slickcmd/src/wt_focus_man.rs +++ b/slickcmd/src/wt_focus_man.rs @@ -1,5 +1,5 @@ use crate::global::GLOBAL; -use slickcmd_common::consts::{WM_UIA_FOCUS_CHANGE, WM_WT_CONSOLE_ACTIVATE}; +use slickcmd_common::consts::{WM_SYSTEM_MOVESIZEEND, WM_SYSTEM_MOVESIZESTART, WM_UIA_FOCUS_CHANGE, WM_WT_CONSOLE_ACTIVATE, WM_WT_FOCUS_CHANGE}; use slickcmd_common::{logd, win32}; use std::collections::{HashMap, HashSet}; use std::ffi::c_void; @@ -35,11 +35,14 @@ static HWND_WT: AtomicUsize = AtomicUsize::new(0); static HWND_CUR_CONSOLE: AtomicUsize = AtomicUsize::new(0); static THREAD_ID: AtomicU32 = AtomicU32::new(0); +static WT_LISTENER_HWNDS: Mutex> = Mutex::new(Vec::new()); + pub struct WtFocusMan<'a> { uia: &'a IUIAutomation, hwnd_wt: usize, focused_console_index: isize, console_infos: Vec, + h_event_hook: HWINEVENTHOOK, } impl<'a> WtFocusMan<'a> { @@ -66,6 +69,17 @@ impl<'a> WtFocusMan<'a> { *CUR_CONSOLE_BOUNDS.lock().unwrap() } + pub fn add_wt_listener_hwnd(hwnd: HWND) { + WT_LISTENER_HWNDS.lock().unwrap().push(hwnd.0 as _); + } + + pub fn remove_wt_listener_hwnd(hwnd: HWND) { + let mut hwnds = WT_LISTENER_HWNDS.lock().unwrap(); + if let Some(index) = hwnds.iter().position(|&x| x == hwnd.0 as usize) { + hwnds.remove(index); + } + } + pub fn new(uia: &'a IUIAutomation, hwnd_wt: usize) -> WtFocusMan { let mut console_infos_map = CONSOLE_INFOS_MAP.lock().unwrap(); let console_infos = console_infos_map.remove(&hwnd_wt).unwrap_or_default(); @@ -75,10 +89,22 @@ impl<'a> WtFocusMan<'a> { hwnd_wt, focused_console_index: -1, console_infos, + h_event_hook: HWINEVENTHOOK::default(), } } pub fn init(&mut self) { + let (pid, tid) = win32::get_window_thread_process_id(HWND(self.hwnd_wt as _)); + self.h_event_hook = win32::set_win_event_hook( + EVENT_SYSTEM_MOVESIZESTART, + EVENT_SYSTEM_MOVESIZEEND, + HMODULE::default(), + Some(winevent_proc), + pid, + tid, + WINEVENT_OUTOFCONTEXT, + ); + self.check_focus(); } @@ -105,6 +131,9 @@ impl<'a> WtFocusMan<'a> { let info = &self.console_infos[self.focused_console_index as usize]; self.notify_console_activate(info.hwnd); } + for &hwnd in WT_LISTENER_HWNDS.lock().unwrap().iter() { + win32::post_message(HWND(hwnd as _), WM_WT_FOCUS_CHANGE, WPARAM(0), LPARAM(0)); + } } } } @@ -277,6 +306,8 @@ impl<'a> WtFocusMan<'a> { } fn dispose(&mut self) { + win32::unhook_win_event(self.h_event_hook); + let mut console_infos = mem::replace(&mut self.console_infos, Vec::new()); let mut invalid_console_indexes: Vec = Vec::new(); for n in (0..console_infos.len()).rev() { @@ -387,6 +418,15 @@ extern "system" fn wt_thread_proc(lp_param: *mut c_void) -> u32 { wt_focus_man.check_focus(); } else if msg.message == WM_UIA_FOCUS_CHANGE { wt_focus_man.check_focus(); + } else if msg.message == WM_SYSTEM_MOVESIZESTART { + for &hwnd in WT_LISTENER_HWNDS.lock().unwrap().iter() { + win32::post_message(HWND(hwnd as _), WM_SYSTEM_MOVESIZESTART, WPARAM(0), LPARAM(0)); + } + } else if msg.message == WM_SYSTEM_MOVESIZEEND { + wt_focus_man.check_focus(); + for &hwnd in WT_LISTENER_HWNDS.lock().unwrap().iter() { + win32::post_message(HWND(hwnd as _), WM_SYSTEM_MOVESIZEEND, WPARAM(0), LPARAM(0)); + } } } wt_focus_man.dispose(); @@ -394,3 +434,21 @@ extern "system" fn wt_thread_proc(lp_param: *mut c_void) -> u32 { win32::co_uninitialize(); 0u32 } + +unsafe extern "system" fn winevent_proc( + _hwineventhook: HWINEVENTHOOK, + event: u32, + _hwnd: HWND, + _idobject: i32, + _idchild: i32, + _ideventthread: u32, + _dwmseventtime: u32, +) { + let tid_wt = THREAD_ID.load(Relaxed); + if event == EVENT_SYSTEM_MOVESIZESTART { + win32::post_thread_message(tid_wt, WM_SYSTEM_MOVESIZESTART, WPARAM(0), LPARAM(0)); + } else if event == EVENT_SYSTEM_MOVESIZEEND { + win32::post_thread_message(tid_wt, WM_SYSTEM_MOVESIZEEND, WPARAM(0), LPARAM(0)); + + } +} diff --git a/slickcmd_common/src/consts.rs b/slickcmd_common/src/consts.rs index 90391cb..7873441 100644 --- a/slickcmd_common/src/consts.rs +++ b/slickcmd_common/src/consts.rs @@ -14,6 +14,7 @@ pub const IDC_MAX_RECENT_DIRS_SPIN: u16 = 1003; pub const IDC_CHK_CD_COMPLETION: u16 = 1005; pub const IDC_CHK_RUN_ON_STARTUP: u16 = 1006; pub const IDC_SYSLINK_SITE: u16 = 1007; +pub const IDC_CHK_SHOW_CLOCK: u16 = 1008; // pub const WM_USER: u32 = 0x0400; @@ -51,6 +52,9 @@ pub const WM_UIA_FOCUS_CHANGE: u32 = WM_USER + 4009; pub const WM_WT_CONSOLE_ACTIVATE: u32 = WM_USER + 4010; pub const WM_CLEAN_CONSOLES: u32 = WM_USER + 4011; +pub const WM_SYSTEM_MOVESIZESTART: u32 = WM_USER + 4012; +pub const WM_SYSTEM_MOVESIZEEND: u32 = WM_USER + 4013; +pub const WM_WT_FOCUS_CHANGE: u32 = WM_USER + 4014; // pub const APP_TITLE: &str = "Slick Cmd"; diff --git a/slickcmd_common/src/win32.rs b/slickcmd_common/src/win32.rs index 32404ae..834914c 100644 --- a/slickcmd_common/src/win32.rs +++ b/slickcmd_common/src/win32.rs @@ -19,6 +19,9 @@ use windows::Win32::System::ProcessStatus::*; use windows::Win32::System::SystemInformation::*; use windows::Win32::System::Threading::*; use windows::Win32::System::WindowsProgramming::*; +use windows::Win32::UI::Accessibility::{ + SetWinEventHook, UnhookWinEvent, HWINEVENTHOOK, WINEVENTPROC, +}; use windows::Win32::UI::Controls::*; use windows::Win32::UI::HiDpi::{ GetDpiForWindow, SetProcessDpiAwarenessContext, DPI_AWARENESS_CONTEXT, @@ -1239,6 +1242,79 @@ pub fn find_close(hfind: HANDLE) { unsafe { _ = FindClose(hfind) }; } +pub fn get_window_long(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> i32 { + unsafe { GetWindowLongW(hwnd, index) } +} + +pub fn set_window_long(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX, value: i32) { + unsafe { + SetWindowLongW(hwnd, index, value); + } +} + +pub fn create_solid_brush(color: COLORREF) -> HBRUSH { + unsafe { CreateSolidBrush(color) } +} + +pub fn rgb(r: u8, g: u8, b: u8) -> COLORREF { + let value: u32 = r as u32 | ((g as u16) << 8) as u32 | (b as u32) << 16; + COLORREF(value) +} + +pub fn get_window(hwnd: HWND, cmd: GET_WINDOW_CMD) -> HWND { + unsafe { GetWindow(hwnd, cmd).unwrap_or_default() } +} + +pub fn set_parent(hwnd_child: HWND, hwnd_new_parent: HWND) { + unsafe { + _ = SetParent(hwnd_child, hwnd_new_parent); + } +} + +pub fn set_win_event_hook( + event_min: u32, + event_max: u32, + hmod: HMODULE, + proc: WINEVENTPROC, + pid: u32, + tid: u32, + flags: u32, +) -> HWINEVENTHOOK { + unsafe { SetWinEventHook(event_min, event_max, hmod, proc, pid, tid, flags) } +} + +pub fn unhook_win_event(h_win_event_hook: HWINEVENTHOOK) -> bool { + unsafe { UnhookWinEvent(h_win_event_hook).as_bool() } +} + +pub fn ext_text_out( + hdc: HDC, + x: i32, + y: i32, + options: ETO_OPTIONS, + rect: Option<&RECT>, + text: String, +) -> bool { + let wsz_text = wsz_from_str(&text); + unsafe { + ExtTextOutW( + hdc, + x, + y, + options, + rect.map(|x| x as _), + pwsz(&wsz_text), + wsz_text.len() as u32, + None, + ) + .as_bool() + } +} + +pub fn set_text_align(hdc: HDC, align: TEXT_ALIGN_OPTIONS) -> TEXT_ALIGN_OPTIONS { + unsafe { TEXT_ALIGN_OPTIONS(SetTextAlign(hdc, align)) } +} + pub fn set_process_dpi_awareness_context(context: DPI_AWARENESS_CONTEXT) -> bool { unsafe { SetProcessDpiAwarenessContext(context).is_ok() } }