diff foosdk/sdk/libPPUI/win32_utility.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/libPPUI/win32_utility.cpp	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,390 @@
+#include "stdafx.h"
+#include "win32_utility.h"
+#include "win32_op.h"
+#include <list>
+
+SIZE QueryContextDPI(HDC dc) {
+	return {GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY)};
+}
+unsigned QueryScreenDPI(HWND wnd) {
+	HDC dc = GetDC(wnd);
+	unsigned ret = GetDeviceCaps(dc, LOGPIXELSY);
+	ReleaseDC(wnd, dc);
+	return ret;
+}
+unsigned QueryScreenDPI_X(HWND wnd) {
+	HDC dc = GetDC(wnd);
+	unsigned ret = GetDeviceCaps(dc, LOGPIXELSX);
+	ReleaseDC(wnd, dc);
+	return ret;
+}
+unsigned QueryScreenDPI_Y(HWND wnd) {
+	HDC dc = GetDC(wnd);
+	unsigned ret = GetDeviceCaps(dc, LOGPIXELSY);
+	ReleaseDC(wnd, dc);
+	return ret;
+}
+
+SIZE QueryScreenDPIEx(HWND wnd) {
+	HDC dc = GetDC(wnd);
+	SIZE ret = { GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY) };
+	ReleaseDC(wnd, dc);
+	return ret;
+}
+
+void HeaderControl_SetSortIndicator(HWND header_, int column, bool isUp) {
+	CHeaderCtrl header(header_);
+	const int total = header.GetItemCount();
+	for (int walk = 0; walk < total; ++walk) {
+		HDITEM item = {}; item.mask = HDI_FORMAT;
+		if (header.GetItem(walk, &item)) {
+			auto newFormat = item.fmt;
+			newFormat &= ~(HDF_SORTUP | HDF_SORTDOWN);
+			if (walk == column) {
+				newFormat |= isUp ? HDF_SORTUP : HDF_SORTDOWN;
+			}
+			if (newFormat != item.fmt) {
+				item.fmt = newFormat;
+				header.SetItem(walk, &item);
+			}
+		}
+	}
+}
+
+HINSTANCE GetThisModuleHandle() {
+	return (HINSTANCE)_AtlBaseModule.m_hInst;
+}
+
+WinResourceRef_t WinLoadResource(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) {
+	SetLastError(0);
+	HRSRC res = wLang ? FindResourceEx(hMod, type, name, wLang) : FindResource(hMod, name, type);
+	if ( res == NULL ) WIN32_OP_FAIL();
+	SetLastError(0);
+	HGLOBAL hglob = LoadResource(hMod, res);
+	if ( hglob == NULL ) WIN32_OP_FAIL();
+	SetLastError(0);
+	void * ptr = LockResource(hglob);
+	if ( ptr == nullptr ) WIN32_OP_FAIL();
+	WinResourceRef_t ref;
+	ref.ptr = ptr;
+	ref.bytes = SizeofResource(hMod, res);
+	return ref;
+}
+
+CComPtr<IStream> WinLoadResourceAsStream(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) {
+	auto res = WinLoadResource(hMod, name, type, wLang );
+	auto str = SHCreateMemStream( (const BYTE*) res.ptr, (UINT) res.bytes );
+	if ( str == nullptr ) throw std::bad_alloc();
+	CComPtr<IStream> ret;
+	ret.Attach( str );
+	return ret;
+}
+
+UINT GetFontHeight(HFONT font)
+{
+	UINT ret;
+	HDC dc = CreateCompatibleDC(0);
+	SelectObject(dc, font);
+	ret = GetTextHeight(dc);
+	DeleteDC(dc);
+	return ret;
+}
+
+UINT GetTextHeight(HDC dc)
+{
+	TEXTMETRIC tm;
+	POINT pt[2];
+	GetTextMetrics(dc, &tm);
+	pt[0].x = 0;
+	pt[0].y = tm.tmHeight;
+	pt[1].x = 0;
+	pt[1].y = 0;
+	LPtoDP(dc, pt, 2);
+
+	int ret = pt[0].y - pt[1].y;
+	return ret > 1 ? (unsigned)ret : 1;
+}
+
+
+LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc) {
+
+	CDCHandle dc(p_dc);
+	DCStateScope scope(dc);
+	CRect client;
+	if (GetClientRect(p_from, client)) {
+		dc.IntersectClipRect(client);
+	}
+	
+	LRESULT status;
+	POINT pt = { 0, 0 }, pt_old = { 0,0 };
+	MapWindowPoints(p_from, p_to, &pt, 1);
+	OffsetWindowOrgEx(p_dc, pt.x, pt.y, &pt_old);
+	status = SendMessage(p_to, WM_ERASEBKGND, (WPARAM)p_dc, 0);
+	SetWindowOrgEx(p_dc, pt_old.x, pt_old.y, 0);
+	return status;
+}
+
+static LRESULT CALLBACK EraseHandlerProc(
+	HWND hWnd,
+	UINT uMsg,
+	WPARAM wParam,
+	LPARAM lParam,
+	UINT_PTR uIdSubclass,
+	DWORD_PTR dwRefData
+) {
+	if (uMsg == WM_ERASEBKGND) {
+		HWND wndTarget = reinterpret_cast<HWND>(dwRefData);
+		PFC_ASSERT(wndTarget != NULL);
+		return RelayEraseBkgnd(hWnd, wndTarget, (HDC)wParam);
+	}
+	return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+static LRESULT CALLBACK CtlColorProc(
+	HWND hWnd,
+	UINT uMsg,
+	WPARAM wParam,
+	LPARAM lParam,
+	UINT_PTR uIdSubclass,
+	DWORD_PTR dwRefData
+) {
+	switch (uMsg) {
+	case WM_CTLCOLORMSGBOX:
+	case WM_CTLCOLOREDIT:
+	case WM_CTLCOLORLISTBOX:
+	case WM_CTLCOLORBTN:
+	case WM_CTLCOLORDLG:
+	case WM_CTLCOLORSCROLLBAR:
+	case WM_CTLCOLORSTATIC:
+		return SendMessage(GetParent(hWnd), uMsg, wParam, lParam);
+	default:
+		return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+	}
+}
+
+
+void InjectEraseHandler(HWND wnd, HWND sendTo) {
+	PFC_ASSERT(sendTo != NULL);
+	WIN32_OP_D(SetWindowSubclass(wnd, EraseHandlerProc, 0, reinterpret_cast<DWORD_PTR>(sendTo)));
+}
+void InjectParentEraseHandler(HWND wnd) {
+	InjectEraseHandler(wnd, GetParent(wnd));
+}
+void InjectParentCtlColorHandler(HWND wnd) {
+	WIN32_OP_D(SetWindowSubclass(wnd, CtlColorProc, 0, 0));
+}
+static LRESULT CALLBACK BounceNextDlgCtlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
+	if (uMsg == WM_NEXTDLGCTL) {
+		return ::SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
+	}
+	return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+void BounceNextDlgCtl(HWND wnd, HWND wndTo) {
+	::SetWindowSubclass(wnd, BounceNextDlgCtlProc, 0, (DWORD_PTR)wndTo);
+}
+
+
+pfc::string8 EscapeTooltipText(const char * src)
+{
+	pfc::string8 out;
+	while (*src)
+	{
+		if (*src == '&')
+		{
+			out.add_string("&&&");
+			src++;
+			while (*src == '&')
+			{
+				out.add_string("&&");
+				src++;
+			}
+		} else out.add_byte(*(src++));
+	}
+	return out;
+}
+
+bool IsMenuNonEmpty(HMENU menu) {
+	unsigned n, m = GetMenuItemCount(menu);
+	for (n = 0; n < m; n++) {
+		if (GetSubMenu(menu, n)) return true;
+		if (!(GetMenuState(menu, n, MF_BYPOSITION)&MF_SEPARATOR)) return true;
+	}
+	return false;
+}
+
+void SetDefaultMenuItem(HMENU p_menu, unsigned p_id) {
+	MENUITEMINFO info = { sizeof(info) };
+	info.fMask = MIIM_STATE;
+	GetMenuItemInfo(p_menu, p_id, FALSE, &info);
+	info.fState |= MFS_DEFAULT;
+	SetMenuItemInfo(p_menu, p_id, FALSE, &info);
+}
+
+static bool FetchWineInfoAppend(pfc::string_base & out) {
+	typedef const char *(__cdecl *t_wine_get_build_id)(void);
+	typedef void(__cdecl *t_wine_get_host_version)(const char **sysname, const char **release);
+	const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll"));
+	if (ntdll == NULL) return false;
+	t_wine_get_build_id wine_get_build_id;
+	t_wine_get_host_version wine_get_host_version;
+	wine_get_build_id = (t_wine_get_build_id)GetProcAddress(ntdll, "wine_get_build_id");
+	wine_get_host_version = (t_wine_get_host_version)GetProcAddress(ntdll, "wine_get_host_version");
+	if (wine_get_build_id == NULL || wine_get_host_version == NULL) {
+		if (GetProcAddress(ntdll, "wine_server_call") != NULL) {
+			out << "wine (unknown version)";
+			return true;
+		}
+		return false;
+	}
+	const char * sysname = NULL; const char * release = NULL;
+	wine_get_host_version(&sysname, &release);
+	out << wine_get_build_id() << ", on: " << sysname << " / " << release;
+	return true;
+}
+
+static void GetOSVersionStringAppend(pfc::string_base & out) {
+
+	if (FetchWineInfoAppend(out)) return;
+
+	OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver);
+	WIN32_OP_D(GetVersionEx(&ver));
+	SYSTEM_INFO info = {};
+	GetNativeSystemInfo(&info);
+
+	out << "Windows " << (int)ver.dwMajorVersion << "." << (int)ver.dwMinorVersion << "." << (int)ver.dwBuildNumber;
+	if (ver.szCSDVersion[0] != 0) out << " " << pfc::stringcvt::string_utf8_from_os(ver.szCSDVersion, PFC_TABSIZE(ver.szCSDVersion));
+
+	switch (info.wProcessorArchitecture) {
+	case PROCESSOR_ARCHITECTURE_AMD64:
+		out << " x64"; break;
+	case PROCESSOR_ARCHITECTURE_IA64:
+		out << " IA64"; break;
+	case PROCESSOR_ARCHITECTURE_INTEL:
+		out << " x86"; break;
+	case PROCESSOR_ARCHITECTURE_ARM64:
+		out << " ARM64"; break;
+	}
+}
+
+void GetOSVersionString(pfc::string_base & out) {
+	out.reset(); GetOSVersionStringAppend(out);
+}
+WORD GetOSVersionCode() {
+	OSVERSIONINFO ver = {sizeof(ver)};
+	WIN32_OP_D(GetVersionEx(&ver));
+	
+	DWORD ret = ver.dwMinorVersion;
+	ret += ver.dwMajorVersion << 8;
+
+	return (WORD)ret;
+}
+
+bool IsWine() {
+	static bool ret = [] {	
+		HMODULE module = GetModuleHandle(_T("ntdll.dll"));
+		if (!module) return false;
+		return GetProcAddress(module, "wine_server_call") != NULL;
+	} ();
+	return ret;
+}
+
+static BOOL CALLBACK EnumChildWindowsProc(HWND w, LPARAM p) {
+	auto f = reinterpret_cast<std::function<void(HWND)>*>(p);
+	(*f)(w);
+	return TRUE;
+}
+void EnumChildWindows(HWND w, std::function<void(HWND)> f) {
+	::EnumChildWindows(w, EnumChildWindowsProc, reinterpret_cast<LPARAM>(&f));
+}
+void EnumChildWindowsHere(HWND parent, std::function<void(HWND)> f) {
+	for (HWND walk = GetWindow(parent, GW_CHILD); walk != NULL; walk = GetWindow(walk, GW_HWNDNEXT)) {
+		f(walk);
+	}
+}
+static DWORD Win10BuildNumber_() {
+	OSVERSIONINFO ver = { sizeof(ver) };
+	WIN32_OP_D(GetVersionEx(&ver));
+	return ver.dwMajorVersion == 10 ? ver.dwBuildNumber : 0;
+}
+DWORD Win10BuildNumber() {
+	static DWORD b = Win10BuildNumber_();
+	return b;
+}
+
+#include "hookWindowMessages.h"
+#include <algorithm>
+
+namespace {
+
+	class CWindowHook_Map : public CWindowImpl<CWindowHook_Map, CWindow> {
+	public:
+		CMessageMap* m_target = nullptr;
+		DWORD m_targetID = 0;
+
+		std::vector< DWORD > m_messages;
+
+		void setup(std::initializer_list<DWORD>&& arg) {
+			m_messages = std::move(arg);
+			std::sort(m_messages.begin(), m_messages.end());
+		}
+
+		bool isMsgWanted(DWORD msg) const {
+			return std::binary_search(m_messages.begin(), m_messages.end(), msg);
+		}
+		BEGIN_MSG_MAP(CWindowHook)
+			if (isMsgWanted(uMsg)) {
+				CHAIN_MSG_MAP_ALT_MEMBER((*m_target), m_targetID);
+			}
+		END_MSG_MAP()
+	};
+
+	class CWindowHook_Proc : public CWindowImpl<CWindowHook_Proc, CWindow> {
+	public:
+		CWindowHook_Proc(PP::messageHook_t proc) : m_proc(proc) {}
+		const PP::messageHook_t m_proc;
+
+		BEGIN_MSG_MAP(CWindowHook)
+			if (m_proc(hWnd, uMsg, wParam, lParam, lResult)) return TRUE;
+		END_MSG_MAP()
+	};
+
+}
+
+void PP::hookWindowMessages(HWND wnd, CMessageMap* target, DWORD targetID, std::initializer_list<DWORD>&& msgs) {
+	auto obj = PP::subclassThisWindow< CWindowHook_Map >(wnd);
+	obj->m_target = target; obj->m_targetID = targetID;
+	obj->setup(std::move(msgs));
+}
+void PP::hookWindowMessages(HWND wnd, messageHook_t h) {
+	PP::subclassThisWindow< CWindowHook_Proc >(wnd, h);
+}
+
+namespace PP {
+	static LONG regReadHelper(HKEY root, const wchar_t* path, const wchar_t* value, LONG def) {
+		wchar_t buf[64] = {};
+		DWORD cb = (DWORD)((std::size(buf) - 1) * sizeof(buf[0]));
+		if (RegGetValue(root, path, value, RRF_RT_REG_SZ, NULL, buf, &cb) == 0) {
+			return _wtol(buf);
+		}
+		return def;
+	}
+
+	static SIZE querySystemDragThreshold() {
+		constexpr DWORD def = 1;
+		static constexpr wchar_t path[] = L"Control Panel\\Desktop";
+		return {
+			(LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragWidth", def),
+			(LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragHeight", def)
+		};
+	}
+	SIZE queryDragThresholdForDPI(SIZE dpi) {
+		PFC_ASSERT(dpi.cx > 0 && dpi.cy > 0);
+		static SIZE sys = {};
+		if ( sys.cx == 0 || sys.cy == 0 ) sys = querySystemDragThreshold();
+		return { MulDiv(sys.cx, dpi.cx, 96), MulDiv(sys.cy, dpi.cy, 96) };
+	}
+	SIZE queryDragThreshold(HWND wndFor) {
+		return queryDragThresholdForDPI(QueryScreenDPIEx(wndFor));
+	}
+}