1use std::ffi::{OsStr, OsString, CString};
21use std::fmt;
22use std::mem;
23use std::os::windows::ffi::{OsStrExt, OsStringExt};
24use std::ptr;
25use std::slice;
26use winapi::ctypes::c_void;
27use winapi::shared::guiddef::REFIID;
28use winapi::shared::minwindef::*;
29use winapi::shared::ntdef::*;
30use winapi::shared::windef::*;
31use winapi::shared::winerror::SUCCEEDED;
32use winapi::um::fileapi::*;
33use winapi::um::handleapi::*;
34use winapi::um::libloaderapi::*;
35use winapi::um::processenv::*;
36use winapi::um::shellscalingapi::*;
37use winapi::um::unknwnbase::IUnknown;
38use winapi::um::winbase::*;
39use winapi::um::wincon::*;
40use winapi::um::winnt::{GENERIC_READ, GENERIC_WRITE, FILE_SHARE_WRITE};
42
43use direct2d::enums::DrawTextOptions;
44
45pub enum Error {
48 Null,
49 Hr(HRESULT),
50 D2Error,
52 OldWindows,
54}
55
56impl fmt::Debug for Error {
57 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
58 match *self {
59 Error::Null => write!(f, "Null error"),
60 Error::Hr(hr) => write!(f, "HRESULT 0x{:x}", hr),
61 Error::D2Error => write!(f, "Direct2D error"),
62 Error::OldWindows => write!(f, "Attempted newer API on older Windows"),
63 }
64 }
65}
66
67pub fn as_result(hr: HRESULT) -> Result<(), Error> {
68 if SUCCEEDED(hr) {
69 Ok(())
70 } else {
71 Err(Error::Hr(hr))
72 }
73}
74
75impl From<HRESULT> for Error {
76 fn from(hr: HRESULT) -> Error {
77 Error::Hr(hr)
78 }
79}
80
81pub trait ToWide {
82 fn to_wide_sized(&self) -> Vec<u16>;
83 fn to_wide(&self) -> Vec<u16>;
84}
85
86impl<T> ToWide for T where T: AsRef<OsStr> {
87 fn to_wide_sized(&self) -> Vec<u16> {
88 self.as_ref().encode_wide().collect()
89 }
90 fn to_wide(&self) -> Vec<u16> {
91 self.as_ref().encode_wide().chain(Some(0)).collect()
92 }
93}
94
95pub trait FromWide {
96 fn to_u16_slice(&self) -> &[u16];
97
98 fn to_os_string(&self) -> OsString {
99 OsStringExt::from_wide(self.to_u16_slice())
100 }
101
102 fn from_wide(&self) -> Option<String> {
103 String::from_utf16(self.to_u16_slice()).ok()
104 }
105}
106
107impl FromWide for LPWSTR {
108 fn to_u16_slice(&self) -> &[u16] {
109 unsafe {
110 let mut len = 0;
111 while *self.offset(len) != 0 {
112 len += 1;
113 }
114 slice::from_raw_parts(*self, len as usize)
115 }
116 }
117}
118
119impl FromWide for [u16] {
120 fn to_u16_slice(&self) -> &[u16] { self }
121}
122
123type GetDpiForSystem = unsafe extern "system" fn() -> UINT;
126type GetDpiForMonitor = unsafe extern "system" fn(HMONITOR, MONITOR_DPI_TYPE, *mut UINT, *mut UINT);
127type SetProcessDpiAwareness = unsafe extern "system" fn(PROCESS_DPI_AWARENESS) -> HRESULT;
129type DCompositionCreateDevice2 = unsafe extern "system" fn(
130 renderingDevice: *const IUnknown,
131 iid: REFIID,
132 dcompositionDevice: *mut *mut c_void,
133) -> HRESULT;
134type CreateDXGIFactory2 = unsafe extern "system" fn(
135 Flags: UINT,
136 riid: REFIID,
137 ppFactory: *mut *mut c_void,
138) -> HRESULT;
139
140#[allow(non_snake_case)] pub struct OptionalFunctions {
142 pub GetDpiForSystem: Option<GetDpiForSystem>,
143 pub GetDpiForMonitor: Option<GetDpiForMonitor>,
144 pub SetProcessDpiAwareness: Option<SetProcessDpiAwareness>,
145 pub DCompositionCreateDevice2: Option<DCompositionCreateDevice2>,
146 pub CreateDXGIFactory2: Option<CreateDXGIFactory2>,
147}
148
149#[allow(non_snake_case)] fn load_optional_functions() -> OptionalFunctions {
151 macro_rules! load_function {
155 ($lib: expr, $function: ident, $min_windows_version: expr) => {{
156 let name = stringify!($function);
157
158 let cstr = CString::new(name).unwrap();
161
162 let function_ptr = unsafe { GetProcAddress($lib, cstr.as_ptr()) };
163
164 if function_ptr.is_null() {
165 println!(
166 "Could not load `{}`. Windows {} or later is needed",
167 name, $min_windows_version
168 );
169 } else {
170 let function = unsafe { mem::transmute::<_, $function>(function_ptr) };
171 $function = Some(function);
172 }
173 }};
174 }
175
176 fn load_library(name: &str) -> HMODULE {
177 let encoded_name = name.to_wide();
178
179 let library = unsafe { GetModuleHandleW(encoded_name.as_ptr()) };
182 if !library.is_null() {
183 return library;
184 }
185
186 let library = unsafe { LoadLibraryW(encoded_name.as_ptr()) };
187 return library;
188 }
189
190 let shcore = load_library("shcore.dll");
191 let user32 = load_library("user32.dll");
192 let dcomp = load_library("dcomp.dll");
193 let dxgi = load_library("dxgi.dll");
194
195 let mut GetDpiForSystem = None;
196 let mut GetDpiForMonitor = None;
197 let mut SetProcessDpiAwareness = None;
198 let mut DCompositionCreateDevice2 = None;
199 let mut CreateDXGIFactory2 = None;
200
201 if shcore.is_null() {
202 println!("No shcore.dll");
203 } else {
204 load_function!(shcore, SetProcessDpiAwareness, "8.1");
205 load_function!(shcore, GetDpiForMonitor, "8.1");
206 }
207
208 if user32.is_null() {
209 println!("No user32.dll");
210 } else {
211 load_function!(user32, GetDpiForSystem, "10");
212 }
213
214 if !dcomp.is_null() {
215 load_function!(dcomp, DCompositionCreateDevice2, "8.1");
216 }
217
218 if !dxgi.is_null() {
219 load_function!(dxgi, CreateDXGIFactory2, "8.1");
220 }
221
222 OptionalFunctions {
223 GetDpiForSystem,
224 GetDpiForMonitor,
225 SetProcessDpiAwareness,
226 DCompositionCreateDevice2,
227 CreateDXGIFactory2,
228 }
229}
230
231lazy_static! {
232 pub static ref OPTIONAL_FUNCTIONS: OptionalFunctions = load_optional_functions();
233}
234
235pub fn init() {
237 attach_console();
238 if let Some(func) = OPTIONAL_FUNCTIONS.SetProcessDpiAwareness {
239 unsafe {
241 func(PROCESS_SYSTEM_DPI_AWARE); }
243 }
244}
245
246pub fn default_text_options() -> DrawTextOptions {
249 if OPTIONAL_FUNCTIONS.SetProcessDpiAwareness.is_some() {
251 DrawTextOptions::ENABLE_COLOR_FONT
252 } else {
253 DrawTextOptions::NONE
254 }
255}
256
257#[macro_export]
259macro_rules! accel {
260 ( $( $fVirt:expr, $key:expr, $cmd:expr, )* ) => {
261 [
262 $(
263 ACCEL { fVirt: $fVirt | FVIRTKEY, key: $key as WORD, cmd: $cmd as WORD },
264 )*
265 ]
266 }
267}
268
269fn attach_console() {
273 unsafe {
274 let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
275 if stdout != INVALID_HANDLE_VALUE && GetFileType(stdout) != FILE_TYPE_UNKNOWN {
276 return;
279 }
280
281 if AttachConsole(ATTACH_PARENT_PROCESS) > 0 {
282 let chnd = CreateFileA(
283 CString::new("CONOUT$").unwrap().as_ptr(),
284 GENERIC_READ | GENERIC_WRITE,
285 FILE_SHARE_WRITE,
286 ptr::null_mut(),
287 OPEN_EXISTING,
288 0,
289 ptr::null_mut());
290
291 if chnd == INVALID_HANDLE_VALUE {
292 return;
294 }
295
296 SetStdHandle(STD_OUTPUT_HANDLE, chnd);
297 SetStdHandle(STD_ERROR_HANDLE, chnd);
298 }
299 }
300}
301