comparison foosdk/sdk/pfc/win-objects.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #include "pfc-lite.h"
2
3 #ifdef _WIN32
4 #include "win-objects.h"
5 #include "array.h"
6 #include "pp-winapi.h"
7 #include "string_conv.h"
8 #include "string_base.h"
9 #include "debug.h"
10 #include "string-conv-lite.h"
11
12 #include "pfc-fb2k-hooks.h"
13
14 #include "sortstring.h"
15
16 // StrCmpLogicalW()
17 #include <Shlwapi.h>
18 #pragma comment(lib, "Shlwapi.lib")
19
20 namespace pfc {
21
22 BOOL winFormatSystemErrorMessageImpl(pfc::string_base & p_out,DWORD p_code) {
23 switch(p_code) {
24 case ERROR_CHILD_NOT_COMPLETE:
25 p_out = "Application cannot be run in Win32 mode.";
26 return TRUE;
27 case ERROR_INVALID_ORDINAL:
28 p_out = "Invalid ordinal.";
29 return TRUE;
30 case ERROR_INVALID_STARTING_CODESEG:
31 p_out = "Invalid code segment.";
32 return TRUE;
33 case ERROR_INVALID_STACKSEG:
34 p_out = "Invalid stack segment.";
35 return TRUE;
36 case ERROR_INVALID_MODULETYPE:
37 p_out = "Invalid module type.";
38 return TRUE;
39 case ERROR_INVALID_EXE_SIGNATURE:
40 p_out = "Invalid executable signature.";
41 return TRUE;
42 case ERROR_BAD_EXE_FORMAT:
43 p_out = "Not a valid Win32 application.";
44 return TRUE;
45 case ERROR_EXE_MACHINE_TYPE_MISMATCH:
46 p_out = "Machine type mismatch.";
47 return TRUE;
48 case ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY:
49 case ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY:
50 p_out = "Unable to modify a signed binary.";
51 return TRUE;
52 default:
53 {
54 #ifdef PFC_WINDOWS_DESKTOP_APP
55 TCHAR temp[512];
56 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,p_code,0,temp,_countof(temp),0) == 0) return FALSE;
57 for(t_size n=0;n<_countof(temp);n++) {
58 switch(temp[n]) {
59 case '\n':
60 case '\r':
61 temp[n] = ' ';
62 break;
63 }
64 }
65 p_out = stringcvt::string_utf8_from_os(temp,_countof(temp));
66 return TRUE;
67 #else
68 return FALSE;
69 #endif
70 }
71 }
72 }
73 void winPrefixPath(pfc::string_base & out, const char * p_path) {
74 if (pfc::string_has_prefix(p_path, "..\\") || strstr(p_path, "\\..\\") ) {
75 // do not touch relative paths if we somehow got them here
76 out = p_path;
77 return;
78 }
79 const char * prepend_header = "\\\\?\\";
80 const char * prepend_header_net = "\\\\?\\UNC\\";
81 if (pfc::strcmp_partial( p_path, prepend_header ) == 0) { out = p_path; return; }
82 out.reset();
83 if (pfc::strcmp_partial(p_path,"\\\\") != 0) {
84 out << prepend_header << p_path;
85 } else {
86 out << prepend_header_net << (p_path+2);
87 }
88 };
89
90 BOOL winFormatSystemErrorMessage(pfc::string_base & p_out, DWORD p_code) {
91 return winFormatSystemErrorMessageHook( p_out, p_code );
92 }
93 void winUnPrefixPath(pfc::string_base & out, const char * p_path) {
94 const char * prepend_header = "\\\\?\\";
95 const char * prepend_header_net = "\\\\?\\UNC\\";
96 if (pfc::strcmp_partial(p_path, prepend_header_net) == 0) {
97 out = PFC_string_formatter() << "\\\\" << (p_path + strlen(prepend_header_net) );
98 return;
99 }
100 if (pfc::strcmp_partial(p_path, prepend_header) == 0) {
101 out = (p_path + strlen(prepend_header));
102 return;
103 }
104 out = p_path;
105 }
106
107 string8 winPrefixPath(const char * in) {
108 string8 temp; winPrefixPath(temp, in); return temp;
109 }
110 string8 winUnPrefixPath(const char * in) {
111 string8 temp; winUnPrefixPath(temp, in); return temp;
112 }
113
114 } // namespace pfc
115
116 pfc::string8 format_win32_error(DWORD p_code) {
117 pfc::LastErrorRevertScope revert;
118 pfc::string8 buffer;
119 if (p_code == 0) buffer = "Undefined error";
120 else if (!pfc::winFormatSystemErrorMessage(buffer,p_code)) buffer << "Unknown error code (" << (unsigned)p_code << ")";
121 return buffer;
122 }
123
124 static void format_hresult_stamp_hex(pfc::string8 & buffer, HRESULT p_code) {
125 buffer << " (0x" << pfc::format_hex((t_uint32)p_code, 8) << ")";
126 }
127
128 pfc::string8 format_hresult(HRESULT p_code) {
129 pfc::string8 buffer;
130 if (!pfc::winFormatSystemErrorMessage(buffer,(DWORD)p_code)) buffer = "Unknown error code";
131 format_hresult_stamp_hex(buffer, p_code);
132 return buffer;
133 }
134 pfc::string8 format_hresult(HRESULT p_code, const char * msgOverride) {
135 pfc::string8 buffer = msgOverride;
136 format_hresult_stamp_hex(buffer, p_code);
137 return buffer;
138 }
139
140
141 #ifdef PFC_WINDOWS_DESKTOP_APP
142
143 namespace pfc {
144 HWND findOwningPopup(HWND p_wnd)
145 {
146 HWND walk = p_wnd;
147 while (walk != 0 && (GetWindowLong(walk, GWL_STYLE) & WS_CHILD) != 0)
148 walk = GetParent(walk);
149 return walk ? walk : p_wnd;
150 }
151 string8 getWindowClassName(HWND wnd) {
152 TCHAR temp[1024] = {};
153 if (GetClassName(wnd, temp, PFC_TABSIZE(temp)) == 0) {
154 PFC_ASSERT(!"Should not get here");
155 return "";
156 }
157 return pfc::stringcvt::string_utf8_from_os(temp).get_ptr();
158 }
159 void setWindowText(HWND wnd, const char * txt) {
160 SetWindowText(wnd, stringcvt::string_os_from_utf8(txt));
161 }
162 string8 getWindowText(HWND wnd) {
163 PFC_ASSERT(wnd != NULL);
164 int len = GetWindowTextLength(wnd);
165 if (len >= 0)
166 {
167 len++;
168 pfc::array_t<TCHAR> temp;
169 temp.set_size(len);
170 temp[0] = 0;
171 if (GetWindowText(wnd, temp.get_ptr(), len) > 0)
172 {
173 return stringcvt::string_utf8_from_os(temp.get_ptr(), len).get_ptr();
174 }
175 }
176 return "";
177 }
178 }
179
180 void uAddWindowStyle(HWND p_wnd,LONG p_style) {
181 SetWindowLong(p_wnd,GWL_STYLE, GetWindowLong(p_wnd,GWL_STYLE) | p_style);
182 }
183
184 void uRemoveWindowStyle(HWND p_wnd,LONG p_style) {
185 SetWindowLong(p_wnd,GWL_STYLE, GetWindowLong(p_wnd,GWL_STYLE) & ~p_style);
186 }
187
188 void uAddWindowExStyle(HWND p_wnd,LONG p_style) {
189 SetWindowLong(p_wnd,GWL_EXSTYLE, GetWindowLong(p_wnd,GWL_EXSTYLE) | p_style);
190 }
191
192 void uRemoveWindowExStyle(HWND p_wnd,LONG p_style) {
193 SetWindowLong(p_wnd,GWL_EXSTYLE, GetWindowLong(p_wnd,GWL_EXSTYLE) & ~p_style);
194 }
195
196 unsigned MapDialogWidth(HWND p_dialog,unsigned p_value) {
197 RECT temp;
198 temp.left = 0; temp.right = p_value; temp.top = temp.bottom = 0;
199 if (!MapDialogRect(p_dialog,&temp)) return 0;
200 return temp.right;
201 }
202
203 bool IsKeyPressed(unsigned vk) {
204 return (GetKeyState(vk) & 0x8000) ? true : false;
205 }
206
207 //! Returns current modifier keys pressed, using win32 MOD_* flags.
208 unsigned GetHotkeyModifierFlags() {
209 unsigned ret = 0;
210 if (IsKeyPressed(VK_CONTROL)) ret |= MOD_CONTROL;
211 if (IsKeyPressed(VK_SHIFT)) ret |= MOD_SHIFT;
212 if (IsKeyPressed(VK_MENU)) ret |= MOD_ALT;
213 if (IsKeyPressed(VK_LWIN) || IsKeyPressed(VK_RWIN)) ret |= MOD_WIN;
214 return ret;
215 }
216
217
218
219 bool CClipboardOpenScope::Open(HWND p_owner) {
220 Close();
221 if (OpenClipboard(p_owner)) {
222 m_open = true;
223 return true;
224 } else {
225 return false;
226 }
227 }
228 void CClipboardOpenScope::Close() {
229 if (m_open) {
230 m_open = false;
231 CloseClipboard();
232 }
233 }
234
235
236 CGlobalLockScope::CGlobalLockScope(HGLOBAL p_handle) : m_ptr(GlobalLock(p_handle)), m_handle(p_handle) {
237 if (m_ptr == NULL) throw std::bad_alloc();
238 }
239 CGlobalLockScope::~CGlobalLockScope() {
240 if (m_ptr != NULL) GlobalUnlock(m_handle);
241 }
242
243 bool IsPointInsideControl(const POINT& pt, HWND wnd) {
244 HWND walk = WindowFromPoint(pt);
245 for(;;) {
246 if (walk == NULL) return false;
247 if (walk == wnd) return true;
248 if (GetWindowLong(walk,GWL_STYLE) & WS_POPUP) return false;
249 walk = GetParent(walk);
250 }
251 }
252 bool IsPopupWindowChildOf(HWND child, HWND parent) {
253 HWND walk = child;
254 while (walk != parent && walk != NULL) {
255 walk = GetParent(walk);
256 }
257 return walk == parent;
258 }
259 bool IsWindowChildOf(HWND child, HWND parent) {
260 HWND walk = child;
261 while(walk != parent && walk != NULL && (GetWindowLong(walk,GWL_STYLE) & WS_CHILD) != 0) {
262 walk = GetParent(walk);
263 }
264 return walk == parent;
265 }
266 void ResignActiveWindow(HWND wnd) {
267 if (IsPopupWindowChildOf(GetActiveWindow(), wnd)) {
268 HWND parent = GetParent(wnd);
269 if ( parent != NULL ) SetActiveWindow(parent);
270 }
271 }
272 void win32_menu::release() {
273 if (m_menu != NULL) {
274 DestroyMenu(m_menu);
275 m_menu = NULL;
276 }
277 }
278
279 void win32_menu::create_popup() {
280 release();
281 SetLastError(NO_ERROR);
282 m_menu = CreatePopupMenu();
283 if (m_menu == NULL) throw exception_win32(GetLastError());
284 }
285
286 #endif // #ifdef PFC_WINDOWS_DESKTOP_APP
287
288 void win32_event::create(bool p_manualreset,bool p_initialstate) {
289 release();
290 SetLastError(NO_ERROR);
291 m_handle = CreateEvent(NULL,p_manualreset ? TRUE : FALSE, p_initialstate ? TRUE : FALSE,NULL);
292 if (m_handle == NULL) throw exception_win32(GetLastError());
293 }
294
295 void win32_event::release() {
296 HANDLE temp = detach();
297 if (temp != NULL) CloseHandle(temp);
298 }
299
300
301 DWORD win32_event::g_calculate_wait_time(double p_seconds) {
302 DWORD time = 0;
303 if (p_seconds> 0) {
304 time = pfc::rint32(p_seconds * 1000.0);
305 if (time == 0) time = 1;
306 } else if (p_seconds < 0) {
307 time = INFINITE;
308 }
309 return time;
310 }
311
312 //! Returns true when signaled, false on timeout
313 bool win32_event::g_wait_for(HANDLE p_event,double p_timeout_seconds) {
314 SetLastError(NO_ERROR);
315 DWORD status = WaitForSingleObject(p_event,g_calculate_wait_time(p_timeout_seconds));
316 switch(status) {
317 case WAIT_FAILED:
318 throw exception_win32(GetLastError());
319 case WAIT_OBJECT_0:
320 return true;
321 case WAIT_TIMEOUT:
322 return false;
323 default:
324 pfc::crash();
325 }
326 }
327
328 void win32_event::set_state(bool p_state) {
329 PFC_ASSERT(m_handle != NULL);
330 if (p_state) SetEvent(m_handle);
331 else ResetEvent(m_handle);
332 }
333
334 size_t win32_event::g_multiWait(const HANDLE* events, size_t count, double timeout) {
335 auto status = WaitForMultipleObjects((DWORD)count, events, FALSE, g_calculate_wait_time(timeout));
336 size_t idx = (size_t)(status - WAIT_OBJECT_0);
337 if (idx < count) {
338 return idx;
339 }
340 if (status == WAIT_TIMEOUT) return SIZE_MAX;
341 pfc::crash();
342 }
343 size_t win32_event::g_multiWait( std::initializer_list<HANDLE> const & arg, double timeout ) {
344 return g_multiWait(arg.begin(), arg.size(), timeout);
345 }
346
347 int win32_event::g_twoEventWait( HANDLE ev1, HANDLE ev2, double timeout ) {
348 HANDLE h[2] = {ev1, ev2};
349 switch(WaitForMultipleObjects(2, h, FALSE, g_calculate_wait_time( timeout ) )) {
350 default:
351 pfc::crash();
352 case WAIT_TIMEOUT:
353 return 0;
354 case WAIT_OBJECT_0:
355 return 1;
356 case WAIT_OBJECT_0 + 1:
357 return 2;
358 }
359 }
360
361 int win32_event::g_twoEventWait( win32_event & ev1, win32_event & ev2, double timeout ) {
362 return g_twoEventWait( ev1.get_handle(), ev2.get_handle(), timeout );
363 }
364
365 #ifdef PFC_WINDOWS_DESKTOP_APP
366
367 void win32_icon::release() {
368 HICON temp = detach();
369 if (temp != NULL) DestroyIcon(temp);
370 }
371
372
373 void win32_accelerator::load(HINSTANCE p_inst,const TCHAR * p_id) {
374 release();
375 SetLastError(NO_ERROR);
376 m_accel = LoadAccelerators(p_inst,p_id);
377 if (m_accel == NULL) {
378 throw exception_win32(GetLastError());
379 }
380 }
381
382 void win32_accelerator::release() {
383 if (m_accel != NULL) {
384 DestroyAcceleratorTable(m_accel);
385 m_accel = NULL;
386 }
387 }
388
389 #endif // #ifdef PFC_WINDOWS_DESKTOP_APP
390
391 void uSleepSeconds(double p_time,bool p_alertable) {
392 SleepEx(win32_event::g_calculate_wait_time(p_time),p_alertable ? TRUE : FALSE);
393 }
394
395
396 #ifdef PFC_WINDOWS_DESKTOP_APP
397
398 WORD GetWindowsVersionCode() throw() {
399 const DWORD ver = GetVersion();
400 return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8);
401 }
402
403
404 namespace pfc {
405 bool isShiftKeyPressed() {
406 return IsKeyPressed(VK_SHIFT);
407 }
408 bool isCtrlKeyPressed() {
409 return IsKeyPressed(VK_CONTROL);
410 }
411 bool isAltKeyPressed() {
412 return IsKeyPressed(VK_MENU);
413 }
414
415 void winSetThreadDescription(HANDLE hThread, const wchar_t * desc) {
416 #if _WIN32_WINNT >= 0xA00
417 SetThreadDescription(hThread, desc);
418 #else
419 auto proc = GetProcAddress(GetModuleHandle(L"KernelBase.dll"), "SetThreadDescription");
420 if (proc == nullptr) {
421 proc = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "SetThreadDescription");
422 }
423 if (proc != nullptr) {
424 typedef HRESULT(__stdcall * pSetThreadDescription_t)(HANDLE hThread, PCWSTR lpThreadDescription);
425 auto proc2 = reinterpret_cast<pSetThreadDescription_t>(proc);
426 proc2(hThread, desc);
427 }
428 #endif
429 }
430 pfc::string8 format_window(HWND wnd) {
431 pfc::string_formatter ret;
432 ret << "0x" << format_hex( (size_t)wnd );
433 auto title = getWindowText(wnd);
434 if (title.length() > 0) {
435 ret << " [" << title << "]";
436 }
437 return ret;
438 }
439
440 #define _(X) {X, #X}
441 struct winStyle_t {
442 DWORD v; const char * n;
443 };
444 static const winStyle_t winStyles[] = {
445 _(WS_POPUP), _(WS_CHILD), _(WS_MINIMIZE), _(WS_VISIBLE),
446 _(WS_DISABLED), _(WS_CLIPSIBLINGS), _(WS_CLIPCHILDREN), _(WS_MAXIMIZE),
447 _(WS_BORDER), _(WS_DLGFRAME), _(WS_VSCROLL), _(WS_HSCROLL),
448 _(WS_SYSMENU), _(WS_THICKFRAME), _(WS_GROUP), _(WS_TABSTOP),
449 _(WS_MINIMIZEBOX), _(WS_MAXIMIZEBOX)
450 };
451
452 pfc::string8 format_windowStyle(DWORD style) {
453 pfc::string_formatter ret;
454 ret << "0x" << format_hex( style, 8 );
455 if (style != 0) {
456 pfc::string_formatter label;
457 for (auto& s : winStyles) if (style & s.v) {
458 if ( label.length() > 0 ) label << "|";
459 label << s.n;
460 }
461 if (label.length() > 0) {
462 ret << " [" << label << "]";
463 }
464 }
465 return ret;
466 }
467 }
468
469 #else
470 // If unknown / not available on this architecture, return false always
471 namespace pfc {
472 bool isShiftKeyPressed() {
473 return false;
474 }
475 bool isCtrlKeyPressed() {
476 return false;
477 }
478 bool isAltKeyPressed() {
479 return false;
480 }
481 }
482
483 #endif // #ifdef PFC_WINDOWS_DESKTOP_APP
484
485 namespace pfc {
486 void winSleep( double seconds ) {
487 DWORD ms = INFINITE;
488 if (seconds > 0) {
489 ms = rint32(seconds * 1000);
490 if (ms < 1) ms = 1;
491 } else if (seconds == 0) {
492 ms = 0;
493 }
494 Sleep(ms);
495 }
496 void sleepSeconds(double seconds) {
497 winSleep(seconds);
498 }
499 void yield() {
500 Sleep(1);
501 }
502
503 static pfc::string8 winUnicodeNormalize(const char* str, NORM_FORM form) {
504 pfc::string8 ret;
505 if (str != nullptr && *str != 0) {
506 auto w = wideFromUTF8(str);
507 int needed = NormalizeString(form, w, -1, nullptr, 0);
508 if (needed > 0) {
509 pfc::array_t<wchar_t> buf; buf.resize(needed);
510 int status = NormalizeString(form, w, -1, buf.get_ptr(), needed);
511 if (status > 0) {
512 ret = utf8FromWide(buf.get_ptr());
513 }
514 }
515 }
516 return ret;
517 }
518 pfc::string8 unicodeNormalizeD(const char* str) {
519 return winUnicodeNormalize(str, NormalizationD);
520 }
521 pfc::string8 unicodeNormalizeC(const char* str) {
522 return winUnicodeNormalize(str, NormalizationC);
523 }
524 int winNaturalSortCompare(const char* s1, const char* s2) {
525 int ret = winNaturalSortCompareI(s1, s2);
526 if (ret == 0) ret = strcmp(s1, s2);
527 return ret;
528 }
529 int winNaturalSortCompare(const wchar_t* s1, const wchar_t* s2) {
530 int ret = winNaturalSortCompareI(s1, s2);
531 if (ret == 0) ret = wcscmp(s1, s2);
532 return ret;
533 }
534 int winNaturalSortCompareI(const char* s1, const char* s2) {
535 return winNaturalSortCompareI(wideFromUTF8(s1), wideFromUTF8(s2));
536 }
537 int winNaturalSortCompareI(const wchar_t* s1, const wchar_t* s2) {
538 return StrCmpLogicalW(s1, s2);
539 }
540
541 #ifndef PFC_SORTSTRING_GENERIC
542 sortString_t makeSortString(const char* in) {
543 auto out = std::make_unique<wchar_t[]>(pfc::stringcvt::estimate_utf8_to_wide(in));
544 pfc::stringcvt::convert_utf8_to_wide_unchecked(out.get(), in);
545 return out;
546 }
547 sortString_t makeSortString(const wchar_t* in) {
548 size_t l = wcslen(in) + 1;
549 auto out = std::make_unique<wchar_t[]>(l+1);
550 memcpy(out.get(), in, sizeof(wchar_t) * l);
551 return out;
552 }
553 int sortStringCompare(sortString_t const& s1, sortString_t const& s2) {
554 return winNaturalSortCompare(s1.get(), s2.get());
555 }
556 int sortStringCompareI(sortString_t const& s1, sortString_t const& s2) {
557 return winNaturalSortCompareI(s1.get(), s2.get());
558 }
559 #endif
560 }
561
562
563 #endif // _WIN32