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