Mercurial > foo_out_sdl
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/pfc/win-objects.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,563 @@ +#include "pfc-lite.h" + +#ifdef _WIN32 +#include "win-objects.h" +#include "array.h" +#include "pp-winapi.h" +#include "string_conv.h" +#include "string_base.h" +#include "debug.h" +#include "string-conv-lite.h" + +#include "pfc-fb2k-hooks.h" + +#include "sortstring.h" + +// StrCmpLogicalW() +#include <Shlwapi.h> +#pragma comment(lib, "Shlwapi.lib") + +namespace pfc { + +BOOL winFormatSystemErrorMessageImpl(pfc::string_base & p_out,DWORD p_code) { + switch(p_code) { + case ERROR_CHILD_NOT_COMPLETE: + p_out = "Application cannot be run in Win32 mode."; + return TRUE; + case ERROR_INVALID_ORDINAL: + p_out = "Invalid ordinal."; + return TRUE; + case ERROR_INVALID_STARTING_CODESEG: + p_out = "Invalid code segment."; + return TRUE; + case ERROR_INVALID_STACKSEG: + p_out = "Invalid stack segment."; + return TRUE; + case ERROR_INVALID_MODULETYPE: + p_out = "Invalid module type."; + return TRUE; + case ERROR_INVALID_EXE_SIGNATURE: + p_out = "Invalid executable signature."; + return TRUE; + case ERROR_BAD_EXE_FORMAT: + p_out = "Not a valid Win32 application."; + return TRUE; + case ERROR_EXE_MACHINE_TYPE_MISMATCH: + p_out = "Machine type mismatch."; + return TRUE; + case ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY: + case ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY: + p_out = "Unable to modify a signed binary."; + return TRUE; + default: + { +#ifdef PFC_WINDOWS_DESKTOP_APP + TCHAR temp[512]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,p_code,0,temp,_countof(temp),0) == 0) return FALSE; + for(t_size n=0;n<_countof(temp);n++) { + switch(temp[n]) { + case '\n': + case '\r': + temp[n] = ' '; + break; + } + } + p_out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; +#else + return FALSE; +#endif + } + } +} +void winPrefixPath(pfc::string_base & out, const char * p_path) { + if (pfc::string_has_prefix(p_path, "..\\") || strstr(p_path, "\\..\\") ) { + // do not touch relative paths if we somehow got them here + out = p_path; + return; + } + const char * prepend_header = "\\\\?\\"; + const char * prepend_header_net = "\\\\?\\UNC\\"; + if (pfc::strcmp_partial( p_path, prepend_header ) == 0) { out = p_path; return; } + out.reset(); + if (pfc::strcmp_partial(p_path,"\\\\") != 0) { + out << prepend_header << p_path; + } else { + out << prepend_header_net << (p_path+2); + } +}; + +BOOL winFormatSystemErrorMessage(pfc::string_base & p_out, DWORD p_code) { + return winFormatSystemErrorMessageHook( p_out, p_code ); +} +void winUnPrefixPath(pfc::string_base & out, const char * p_path) { + const char * prepend_header = "\\\\?\\"; + const char * prepend_header_net = "\\\\?\\UNC\\"; + if (pfc::strcmp_partial(p_path, prepend_header_net) == 0) { + out = PFC_string_formatter() << "\\\\" << (p_path + strlen(prepend_header_net) ); + return; + } + if (pfc::strcmp_partial(p_path, prepend_header) == 0) { + out = (p_path + strlen(prepend_header)); + return; + } + out = p_path; +} + +string8 winPrefixPath(const char * in) { + string8 temp; winPrefixPath(temp, in); return temp; +} +string8 winUnPrefixPath(const char * in) { + string8 temp; winUnPrefixPath(temp, in); return temp; +} + +} // namespace pfc + +pfc::string8 format_win32_error(DWORD p_code) { + pfc::LastErrorRevertScope revert; + pfc::string8 buffer; + if (p_code == 0) buffer = "Undefined error"; + else if (!pfc::winFormatSystemErrorMessage(buffer,p_code)) buffer << "Unknown error code (" << (unsigned)p_code << ")"; + return buffer; +} + +static void format_hresult_stamp_hex(pfc::string8 & buffer, HRESULT p_code) { + buffer << " (0x" << pfc::format_hex((t_uint32)p_code, 8) << ")"; +} + +pfc::string8 format_hresult(HRESULT p_code) { + pfc::string8 buffer; + if (!pfc::winFormatSystemErrorMessage(buffer,(DWORD)p_code)) buffer = "Unknown error code"; + format_hresult_stamp_hex(buffer, p_code); + return buffer; +} +pfc::string8 format_hresult(HRESULT p_code, const char * msgOverride) { + pfc::string8 buffer = msgOverride; + format_hresult_stamp_hex(buffer, p_code); + return buffer; +} + + +#ifdef PFC_WINDOWS_DESKTOP_APP + +namespace pfc { + HWND findOwningPopup(HWND p_wnd) + { + HWND walk = p_wnd; + while (walk != 0 && (GetWindowLong(walk, GWL_STYLE) & WS_CHILD) != 0) + walk = GetParent(walk); + return walk ? walk : p_wnd; + } + string8 getWindowClassName(HWND wnd) { + TCHAR temp[1024] = {}; + if (GetClassName(wnd, temp, PFC_TABSIZE(temp)) == 0) { + PFC_ASSERT(!"Should not get here"); + return ""; + } + return pfc::stringcvt::string_utf8_from_os(temp).get_ptr(); + } + void setWindowText(HWND wnd, const char * txt) { + SetWindowText(wnd, stringcvt::string_os_from_utf8(txt)); + } + string8 getWindowText(HWND wnd) { + PFC_ASSERT(wnd != NULL); + int len = GetWindowTextLength(wnd); + if (len >= 0) + { + len++; + pfc::array_t<TCHAR> temp; + temp.set_size(len); + temp[0] = 0; + if (GetWindowText(wnd, temp.get_ptr(), len) > 0) + { + return stringcvt::string_utf8_from_os(temp.get_ptr(), len).get_ptr(); + } + } + return ""; + } +} + +void uAddWindowStyle(HWND p_wnd,LONG p_style) { + SetWindowLong(p_wnd,GWL_STYLE, GetWindowLong(p_wnd,GWL_STYLE) | p_style); +} + +void uRemoveWindowStyle(HWND p_wnd,LONG p_style) { + SetWindowLong(p_wnd,GWL_STYLE, GetWindowLong(p_wnd,GWL_STYLE) & ~p_style); +} + +void uAddWindowExStyle(HWND p_wnd,LONG p_style) { + SetWindowLong(p_wnd,GWL_EXSTYLE, GetWindowLong(p_wnd,GWL_EXSTYLE) | p_style); +} + +void uRemoveWindowExStyle(HWND p_wnd,LONG p_style) { + SetWindowLong(p_wnd,GWL_EXSTYLE, GetWindowLong(p_wnd,GWL_EXSTYLE) & ~p_style); +} + +unsigned MapDialogWidth(HWND p_dialog,unsigned p_value) { + RECT temp; + temp.left = 0; temp.right = p_value; temp.top = temp.bottom = 0; + if (!MapDialogRect(p_dialog,&temp)) return 0; + return temp.right; +} + +bool IsKeyPressed(unsigned vk) { + return (GetKeyState(vk) & 0x8000) ? true : false; +} + +//! Returns current modifier keys pressed, using win32 MOD_* flags. +unsigned GetHotkeyModifierFlags() { + unsigned ret = 0; + if (IsKeyPressed(VK_CONTROL)) ret |= MOD_CONTROL; + if (IsKeyPressed(VK_SHIFT)) ret |= MOD_SHIFT; + if (IsKeyPressed(VK_MENU)) ret |= MOD_ALT; + if (IsKeyPressed(VK_LWIN) || IsKeyPressed(VK_RWIN)) ret |= MOD_WIN; + return ret; +} + + + +bool CClipboardOpenScope::Open(HWND p_owner) { + Close(); + if (OpenClipboard(p_owner)) { + m_open = true; + return true; + } else { + return false; + } +} +void CClipboardOpenScope::Close() { + if (m_open) { + m_open = false; + CloseClipboard(); + } +} + + +CGlobalLockScope::CGlobalLockScope(HGLOBAL p_handle) : m_ptr(GlobalLock(p_handle)), m_handle(p_handle) { + if (m_ptr == NULL) throw std::bad_alloc(); +} +CGlobalLockScope::~CGlobalLockScope() { + if (m_ptr != NULL) GlobalUnlock(m_handle); +} + +bool IsPointInsideControl(const POINT& pt, HWND wnd) { + HWND walk = WindowFromPoint(pt); + for(;;) { + if (walk == NULL) return false; + if (walk == wnd) return true; + if (GetWindowLong(walk,GWL_STYLE) & WS_POPUP) return false; + walk = GetParent(walk); + } +} +bool IsPopupWindowChildOf(HWND child, HWND parent) { + HWND walk = child; + while (walk != parent && walk != NULL) { + walk = GetParent(walk); + } + return walk == parent; +} +bool IsWindowChildOf(HWND child, HWND parent) { + HWND walk = child; + while(walk != parent && walk != NULL && (GetWindowLong(walk,GWL_STYLE) & WS_CHILD) != 0) { + walk = GetParent(walk); + } + return walk == parent; +} +void ResignActiveWindow(HWND wnd) { + if (IsPopupWindowChildOf(GetActiveWindow(), wnd)) { + HWND parent = GetParent(wnd); + if ( parent != NULL ) SetActiveWindow(parent); + } +} +void win32_menu::release() { + if (m_menu != NULL) { + DestroyMenu(m_menu); + m_menu = NULL; + } +} + +void win32_menu::create_popup() { + release(); + SetLastError(NO_ERROR); + m_menu = CreatePopupMenu(); + if (m_menu == NULL) throw exception_win32(GetLastError()); +} + +#endif // #ifdef PFC_WINDOWS_DESKTOP_APP + +void win32_event::create(bool p_manualreset,bool p_initialstate) { + release(); + SetLastError(NO_ERROR); + m_handle = CreateEvent(NULL,p_manualreset ? TRUE : FALSE, p_initialstate ? TRUE : FALSE,NULL); + if (m_handle == NULL) throw exception_win32(GetLastError()); +} + +void win32_event::release() { + HANDLE temp = detach(); + if (temp != NULL) CloseHandle(temp); +} + + +DWORD win32_event::g_calculate_wait_time(double p_seconds) { + DWORD time = 0; + if (p_seconds> 0) { + time = pfc::rint32(p_seconds * 1000.0); + if (time == 0) time = 1; + } else if (p_seconds < 0) { + time = INFINITE; + } + return time; +} + +//! Returns true when signaled, false on timeout +bool win32_event::g_wait_for(HANDLE p_event,double p_timeout_seconds) { + SetLastError(NO_ERROR); + DWORD status = WaitForSingleObject(p_event,g_calculate_wait_time(p_timeout_seconds)); + switch(status) { + case WAIT_FAILED: + throw exception_win32(GetLastError()); + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + default: + pfc::crash(); + } +} + +void win32_event::set_state(bool p_state) { + PFC_ASSERT(m_handle != NULL); + if (p_state) SetEvent(m_handle); + else ResetEvent(m_handle); +} + +size_t win32_event::g_multiWait(const HANDLE* events, size_t count, double timeout) { + auto status = WaitForMultipleObjects((DWORD)count, events, FALSE, g_calculate_wait_time(timeout)); + size_t idx = (size_t)(status - WAIT_OBJECT_0); + if (idx < count) { + return idx; + } + if (status == WAIT_TIMEOUT) return SIZE_MAX; + pfc::crash(); +} +size_t win32_event::g_multiWait( std::initializer_list<HANDLE> const & arg, double timeout ) { + return g_multiWait(arg.begin(), arg.size(), timeout); +} + +int win32_event::g_twoEventWait( HANDLE ev1, HANDLE ev2, double timeout ) { + HANDLE h[2] = {ev1, ev2}; + switch(WaitForMultipleObjects(2, h, FALSE, g_calculate_wait_time( timeout ) )) { + default: + pfc::crash(); + case WAIT_TIMEOUT: + return 0; + case WAIT_OBJECT_0: + return 1; + case WAIT_OBJECT_0 + 1: + return 2; + } +} + +int win32_event::g_twoEventWait( win32_event & ev1, win32_event & ev2, double timeout ) { + return g_twoEventWait( ev1.get_handle(), ev2.get_handle(), timeout ); +} + +#ifdef PFC_WINDOWS_DESKTOP_APP + +void win32_icon::release() { + HICON temp = detach(); + if (temp != NULL) DestroyIcon(temp); +} + + +void win32_accelerator::load(HINSTANCE p_inst,const TCHAR * p_id) { + release(); + SetLastError(NO_ERROR); + m_accel = LoadAccelerators(p_inst,p_id); + if (m_accel == NULL) { + throw exception_win32(GetLastError()); + } +} + +void win32_accelerator::release() { + if (m_accel != NULL) { + DestroyAcceleratorTable(m_accel); + m_accel = NULL; + } +} + +#endif // #ifdef PFC_WINDOWS_DESKTOP_APP + +void uSleepSeconds(double p_time,bool p_alertable) { + SleepEx(win32_event::g_calculate_wait_time(p_time),p_alertable ? TRUE : FALSE); +} + + +#ifdef PFC_WINDOWS_DESKTOP_APP + +WORD GetWindowsVersionCode() throw() { + const DWORD ver = GetVersion(); + return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8); +} + + +namespace pfc { + bool isShiftKeyPressed() { + return IsKeyPressed(VK_SHIFT); + } + bool isCtrlKeyPressed() { + return IsKeyPressed(VK_CONTROL); + } + bool isAltKeyPressed() { + return IsKeyPressed(VK_MENU); + } + + void winSetThreadDescription(HANDLE hThread, const wchar_t * desc) { +#if _WIN32_WINNT >= 0xA00 + SetThreadDescription(hThread, desc); +#else + auto proc = GetProcAddress(GetModuleHandle(L"KernelBase.dll"), "SetThreadDescription"); + if (proc == nullptr) { + proc = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "SetThreadDescription"); + } + if (proc != nullptr) { + typedef HRESULT(__stdcall * pSetThreadDescription_t)(HANDLE hThread, PCWSTR lpThreadDescription); + auto proc2 = reinterpret_cast<pSetThreadDescription_t>(proc); + proc2(hThread, desc); + } +#endif + } + pfc::string8 format_window(HWND wnd) { + pfc::string_formatter ret; + ret << "0x" << format_hex( (size_t)wnd ); + auto title = getWindowText(wnd); + if (title.length() > 0) { + ret << " [" << title << "]"; + } + return ret; + } + +#define _(X) {X, #X} + struct winStyle_t { + DWORD v; const char * n; + }; + static const winStyle_t winStyles[] = { + _(WS_POPUP), _(WS_CHILD), _(WS_MINIMIZE), _(WS_VISIBLE), + _(WS_DISABLED), _(WS_CLIPSIBLINGS), _(WS_CLIPCHILDREN), _(WS_MAXIMIZE), + _(WS_BORDER), _(WS_DLGFRAME), _(WS_VSCROLL), _(WS_HSCROLL), + _(WS_SYSMENU), _(WS_THICKFRAME), _(WS_GROUP), _(WS_TABSTOP), + _(WS_MINIMIZEBOX), _(WS_MAXIMIZEBOX) + }; + + pfc::string8 format_windowStyle(DWORD style) { + pfc::string_formatter ret; + ret << "0x" << format_hex( style, 8 ); + if (style != 0) { + pfc::string_formatter label; + for (auto& s : winStyles) if (style & s.v) { + if ( label.length() > 0 ) label << "|"; + label << s.n; + } + if (label.length() > 0) { + ret << " [" << label << "]"; + } + } + return ret; + } +} + +#else +// If unknown / not available on this architecture, return false always +namespace pfc { + bool isShiftKeyPressed() { + return false; + } + bool isCtrlKeyPressed() { + return false; + } + bool isAltKeyPressed() { + return false; + } +} + +#endif // #ifdef PFC_WINDOWS_DESKTOP_APP + +namespace pfc { + void winSleep( double seconds ) { + DWORD ms = INFINITE; + if (seconds > 0) { + ms = rint32(seconds * 1000); + if (ms < 1) ms = 1; + } else if (seconds == 0) { + ms = 0; + } + Sleep(ms); + } + void sleepSeconds(double seconds) { + winSleep(seconds); + } + void yield() { + Sleep(1); + } + + static pfc::string8 winUnicodeNormalize(const char* str, NORM_FORM form) { + pfc::string8 ret; + if (str != nullptr && *str != 0) { + auto w = wideFromUTF8(str); + int needed = NormalizeString(form, w, -1, nullptr, 0); + if (needed > 0) { + pfc::array_t<wchar_t> buf; buf.resize(needed); + int status = NormalizeString(form, w, -1, buf.get_ptr(), needed); + if (status > 0) { + ret = utf8FromWide(buf.get_ptr()); + } + } + } + return ret; + } + pfc::string8 unicodeNormalizeD(const char* str) { + return winUnicodeNormalize(str, NormalizationD); + } + pfc::string8 unicodeNormalizeC(const char* str) { + return winUnicodeNormalize(str, NormalizationC); + } + int winNaturalSortCompare(const char* s1, const char* s2) { + int ret = winNaturalSortCompareI(s1, s2); + if (ret == 0) ret = strcmp(s1, s2); + return ret; + } + int winNaturalSortCompare(const wchar_t* s1, const wchar_t* s2) { + int ret = winNaturalSortCompareI(s1, s2); + if (ret == 0) ret = wcscmp(s1, s2); + return ret; + } + int winNaturalSortCompareI(const char* s1, const char* s2) { + return winNaturalSortCompareI(wideFromUTF8(s1), wideFromUTF8(s2)); + } + int winNaturalSortCompareI(const wchar_t* s1, const wchar_t* s2) { + return StrCmpLogicalW(s1, s2); + } + +#ifndef PFC_SORTSTRING_GENERIC + sortString_t makeSortString(const char* in) { + auto out = std::make_unique<wchar_t[]>(pfc::stringcvt::estimate_utf8_to_wide(in)); + pfc::stringcvt::convert_utf8_to_wide_unchecked(out.get(), in); + return out; + } + sortString_t makeSortString(const wchar_t* in) { + size_t l = wcslen(in) + 1; + auto out = std::make_unique<wchar_t[]>(l+1); + memcpy(out.get(), in, sizeof(wchar_t) * l); + return out; + } + int sortStringCompare(sortString_t const& s1, sortString_t const& s2) { + return winNaturalSortCompare(s1.get(), s2.get()); + } + int sortStringCompareI(sortString_t const& s1, sortString_t const& s2) { + return winNaturalSortCompareI(s1.get(), s2.get()); + } +#endif +} + + +#endif // _WIN32
