Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "stdafx.h" | |
| 2 #include "win32_utility.h" | |
| 3 #include "win32_op.h" | |
| 4 #include <list> | |
| 5 | |
| 6 SIZE QueryContextDPI(HDC dc) { | |
| 7 return {GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY)}; | |
| 8 } | |
| 9 unsigned QueryScreenDPI(HWND wnd) { | |
| 10 HDC dc = GetDC(wnd); | |
| 11 unsigned ret = GetDeviceCaps(dc, LOGPIXELSY); | |
| 12 ReleaseDC(wnd, dc); | |
| 13 return ret; | |
| 14 } | |
| 15 unsigned QueryScreenDPI_X(HWND wnd) { | |
| 16 HDC dc = GetDC(wnd); | |
| 17 unsigned ret = GetDeviceCaps(dc, LOGPIXELSX); | |
| 18 ReleaseDC(wnd, dc); | |
| 19 return ret; | |
| 20 } | |
| 21 unsigned QueryScreenDPI_Y(HWND wnd) { | |
| 22 HDC dc = GetDC(wnd); | |
| 23 unsigned ret = GetDeviceCaps(dc, LOGPIXELSY); | |
| 24 ReleaseDC(wnd, dc); | |
| 25 return ret; | |
| 26 } | |
| 27 | |
| 28 SIZE QueryScreenDPIEx(HWND wnd) { | |
| 29 HDC dc = GetDC(wnd); | |
| 30 SIZE ret = { GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY) }; | |
| 31 ReleaseDC(wnd, dc); | |
| 32 return ret; | |
| 33 } | |
| 34 | |
| 35 void HeaderControl_SetSortIndicator(HWND header_, int column, bool isUp) { | |
| 36 CHeaderCtrl header(header_); | |
| 37 const int total = header.GetItemCount(); | |
| 38 for (int walk = 0; walk < total; ++walk) { | |
| 39 HDITEM item = {}; item.mask = HDI_FORMAT; | |
| 40 if (header.GetItem(walk, &item)) { | |
| 41 auto newFormat = item.fmt; | |
| 42 newFormat &= ~(HDF_SORTUP | HDF_SORTDOWN); | |
| 43 if (walk == column) { | |
| 44 newFormat |= isUp ? HDF_SORTUP : HDF_SORTDOWN; | |
| 45 } | |
| 46 if (newFormat != item.fmt) { | |
| 47 item.fmt = newFormat; | |
| 48 header.SetItem(walk, &item); | |
| 49 } | |
| 50 } | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 HINSTANCE GetThisModuleHandle() { | |
| 55 return (HINSTANCE)_AtlBaseModule.m_hInst; | |
| 56 } | |
| 57 | |
| 58 WinResourceRef_t WinLoadResource(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) { | |
| 59 SetLastError(0); | |
| 60 HRSRC res = wLang ? FindResourceEx(hMod, type, name, wLang) : FindResource(hMod, name, type); | |
| 61 if ( res == NULL ) WIN32_OP_FAIL(); | |
| 62 SetLastError(0); | |
| 63 HGLOBAL hglob = LoadResource(hMod, res); | |
| 64 if ( hglob == NULL ) WIN32_OP_FAIL(); | |
| 65 SetLastError(0); | |
| 66 void * ptr = LockResource(hglob); | |
| 67 if ( ptr == nullptr ) WIN32_OP_FAIL(); | |
| 68 WinResourceRef_t ref; | |
| 69 ref.ptr = ptr; | |
| 70 ref.bytes = SizeofResource(hMod, res); | |
| 71 return ref; | |
| 72 } | |
| 73 | |
| 74 CComPtr<IStream> WinLoadResourceAsStream(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) { | |
| 75 auto res = WinLoadResource(hMod, name, type, wLang ); | |
| 76 auto str = SHCreateMemStream( (const BYTE*) res.ptr, (UINT) res.bytes ); | |
| 77 if ( str == nullptr ) throw std::bad_alloc(); | |
| 78 CComPtr<IStream> ret; | |
| 79 ret.Attach( str ); | |
| 80 return ret; | |
| 81 } | |
| 82 | |
| 83 UINT GetFontHeight(HFONT font) | |
| 84 { | |
| 85 UINT ret; | |
| 86 HDC dc = CreateCompatibleDC(0); | |
| 87 SelectObject(dc, font); | |
| 88 ret = GetTextHeight(dc); | |
| 89 DeleteDC(dc); | |
| 90 return ret; | |
| 91 } | |
| 92 | |
| 93 UINT GetTextHeight(HDC dc) | |
| 94 { | |
| 95 TEXTMETRIC tm; | |
| 96 POINT pt[2]; | |
| 97 GetTextMetrics(dc, &tm); | |
| 98 pt[0].x = 0; | |
| 99 pt[0].y = tm.tmHeight; | |
| 100 pt[1].x = 0; | |
| 101 pt[1].y = 0; | |
| 102 LPtoDP(dc, pt, 2); | |
| 103 | |
| 104 int ret = pt[0].y - pt[1].y; | |
| 105 return ret > 1 ? (unsigned)ret : 1; | |
| 106 } | |
| 107 | |
| 108 | |
| 109 LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc) { | |
| 110 | |
| 111 CDCHandle dc(p_dc); | |
| 112 DCStateScope scope(dc); | |
| 113 CRect client; | |
| 114 if (GetClientRect(p_from, client)) { | |
| 115 dc.IntersectClipRect(client); | |
| 116 } | |
| 117 | |
| 118 LRESULT status; | |
| 119 POINT pt = { 0, 0 }, pt_old = { 0,0 }; | |
| 120 MapWindowPoints(p_from, p_to, &pt, 1); | |
| 121 OffsetWindowOrgEx(p_dc, pt.x, pt.y, &pt_old); | |
| 122 status = SendMessage(p_to, WM_ERASEBKGND, (WPARAM)p_dc, 0); | |
| 123 SetWindowOrgEx(p_dc, pt_old.x, pt_old.y, 0); | |
| 124 return status; | |
| 125 } | |
| 126 | |
| 127 static LRESULT CALLBACK EraseHandlerProc( | |
| 128 HWND hWnd, | |
| 129 UINT uMsg, | |
| 130 WPARAM wParam, | |
| 131 LPARAM lParam, | |
| 132 UINT_PTR uIdSubclass, | |
| 133 DWORD_PTR dwRefData | |
| 134 ) { | |
| 135 if (uMsg == WM_ERASEBKGND) { | |
| 136 HWND wndTarget = reinterpret_cast<HWND>(dwRefData); | |
| 137 PFC_ASSERT(wndTarget != NULL); | |
| 138 return RelayEraseBkgnd(hWnd, wndTarget, (HDC)wParam); | |
| 139 } | |
| 140 return DefSubclassProc(hWnd, uMsg, wParam, lParam); | |
| 141 } | |
| 142 | |
| 143 static LRESULT CALLBACK CtlColorProc( | |
| 144 HWND hWnd, | |
| 145 UINT uMsg, | |
| 146 WPARAM wParam, | |
| 147 LPARAM lParam, | |
| 148 UINT_PTR uIdSubclass, | |
| 149 DWORD_PTR dwRefData | |
| 150 ) { | |
| 151 switch (uMsg) { | |
| 152 case WM_CTLCOLORMSGBOX: | |
| 153 case WM_CTLCOLOREDIT: | |
| 154 case WM_CTLCOLORLISTBOX: | |
| 155 case WM_CTLCOLORBTN: | |
| 156 case WM_CTLCOLORDLG: | |
| 157 case WM_CTLCOLORSCROLLBAR: | |
| 158 case WM_CTLCOLORSTATIC: | |
| 159 return SendMessage(GetParent(hWnd), uMsg, wParam, lParam); | |
| 160 default: | |
| 161 return DefSubclassProc(hWnd, uMsg, wParam, lParam); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 | |
| 166 void InjectEraseHandler(HWND wnd, HWND sendTo) { | |
| 167 PFC_ASSERT(sendTo != NULL); | |
| 168 WIN32_OP_D(SetWindowSubclass(wnd, EraseHandlerProc, 0, reinterpret_cast<DWORD_PTR>(sendTo))); | |
| 169 } | |
| 170 void InjectParentEraseHandler(HWND wnd) { | |
| 171 InjectEraseHandler(wnd, GetParent(wnd)); | |
| 172 } | |
| 173 void InjectParentCtlColorHandler(HWND wnd) { | |
| 174 WIN32_OP_D(SetWindowSubclass(wnd, CtlColorProc, 0, 0)); | |
| 175 } | |
| 176 static LRESULT CALLBACK BounceNextDlgCtlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { | |
| 177 if (uMsg == WM_NEXTDLGCTL) { | |
| 178 return ::SendMessage((HWND)dwRefData, uMsg, wParam, lParam); | |
| 179 } | |
| 180 return DefSubclassProc(hWnd, uMsg, wParam, lParam); | |
| 181 } | |
| 182 | |
| 183 void BounceNextDlgCtl(HWND wnd, HWND wndTo) { | |
| 184 ::SetWindowSubclass(wnd, BounceNextDlgCtlProc, 0, (DWORD_PTR)wndTo); | |
| 185 } | |
| 186 | |
| 187 | |
| 188 pfc::string8 EscapeTooltipText(const char * src) | |
| 189 { | |
| 190 pfc::string8 out; | |
| 191 while (*src) | |
| 192 { | |
| 193 if (*src == '&') | |
| 194 { | |
| 195 out.add_string("&&&"); | |
| 196 src++; | |
| 197 while (*src == '&') | |
| 198 { | |
| 199 out.add_string("&&"); | |
| 200 src++; | |
| 201 } | |
| 202 } else out.add_byte(*(src++)); | |
| 203 } | |
| 204 return out; | |
| 205 } | |
| 206 | |
| 207 bool IsMenuNonEmpty(HMENU menu) { | |
| 208 unsigned n, m = GetMenuItemCount(menu); | |
| 209 for (n = 0; n < m; n++) { | |
| 210 if (GetSubMenu(menu, n)) return true; | |
| 211 if (!(GetMenuState(menu, n, MF_BYPOSITION)&MF_SEPARATOR)) return true; | |
| 212 } | |
| 213 return false; | |
| 214 } | |
| 215 | |
| 216 void SetDefaultMenuItem(HMENU p_menu, unsigned p_id) { | |
| 217 MENUITEMINFO info = { sizeof(info) }; | |
| 218 info.fMask = MIIM_STATE; | |
| 219 GetMenuItemInfo(p_menu, p_id, FALSE, &info); | |
| 220 info.fState |= MFS_DEFAULT; | |
| 221 SetMenuItemInfo(p_menu, p_id, FALSE, &info); | |
| 222 } | |
| 223 | |
| 224 static bool FetchWineInfoAppend(pfc::string_base & out) { | |
| 225 typedef const char *(__cdecl *t_wine_get_build_id)(void); | |
| 226 typedef void(__cdecl *t_wine_get_host_version)(const char **sysname, const char **release); | |
| 227 const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); | |
| 228 if (ntdll == NULL) return false; | |
| 229 t_wine_get_build_id wine_get_build_id; | |
| 230 t_wine_get_host_version wine_get_host_version; | |
| 231 wine_get_build_id = (t_wine_get_build_id)GetProcAddress(ntdll, "wine_get_build_id"); | |
| 232 wine_get_host_version = (t_wine_get_host_version)GetProcAddress(ntdll, "wine_get_host_version"); | |
| 233 if (wine_get_build_id == NULL || wine_get_host_version == NULL) { | |
| 234 if (GetProcAddress(ntdll, "wine_server_call") != NULL) { | |
| 235 out << "wine (unknown version)"; | |
| 236 return true; | |
| 237 } | |
| 238 return false; | |
| 239 } | |
| 240 const char * sysname = NULL; const char * release = NULL; | |
| 241 wine_get_host_version(&sysname, &release); | |
| 242 out << wine_get_build_id() << ", on: " << sysname << " / " << release; | |
| 243 return true; | |
| 244 } | |
| 245 | |
| 246 static void GetOSVersionStringAppend(pfc::string_base & out) { | |
| 247 | |
| 248 if (FetchWineInfoAppend(out)) return; | |
| 249 | |
| 250 OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver); | |
| 251 WIN32_OP_D(GetVersionEx(&ver)); | |
| 252 SYSTEM_INFO info = {}; | |
| 253 GetNativeSystemInfo(&info); | |
| 254 | |
| 255 out << "Windows " << (int)ver.dwMajorVersion << "." << (int)ver.dwMinorVersion << "." << (int)ver.dwBuildNumber; | |
| 256 if (ver.szCSDVersion[0] != 0) out << " " << pfc::stringcvt::string_utf8_from_os(ver.szCSDVersion, PFC_TABSIZE(ver.szCSDVersion)); | |
| 257 | |
| 258 switch (info.wProcessorArchitecture) { | |
| 259 case PROCESSOR_ARCHITECTURE_AMD64: | |
| 260 out << " x64"; break; | |
| 261 case PROCESSOR_ARCHITECTURE_IA64: | |
| 262 out << " IA64"; break; | |
| 263 case PROCESSOR_ARCHITECTURE_INTEL: | |
| 264 out << " x86"; break; | |
| 265 case PROCESSOR_ARCHITECTURE_ARM64: | |
| 266 out << " ARM64"; break; | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 void GetOSVersionString(pfc::string_base & out) { | |
| 271 out.reset(); GetOSVersionStringAppend(out); | |
| 272 } | |
| 273 WORD GetOSVersionCode() { | |
| 274 OSVERSIONINFO ver = {sizeof(ver)}; | |
| 275 WIN32_OP_D(GetVersionEx(&ver)); | |
| 276 | |
| 277 DWORD ret = ver.dwMinorVersion; | |
| 278 ret += ver.dwMajorVersion << 8; | |
| 279 | |
| 280 return (WORD)ret; | |
| 281 } | |
| 282 | |
| 283 bool IsWine() { | |
| 284 static bool ret = [] { | |
| 285 HMODULE module = GetModuleHandle(_T("ntdll.dll")); | |
| 286 if (!module) return false; | |
| 287 return GetProcAddress(module, "wine_server_call") != NULL; | |
| 288 } (); | |
| 289 return ret; | |
| 290 } | |
| 291 | |
| 292 static BOOL CALLBACK EnumChildWindowsProc(HWND w, LPARAM p) { | |
| 293 auto f = reinterpret_cast<std::function<void(HWND)>*>(p); | |
| 294 (*f)(w); | |
| 295 return TRUE; | |
| 296 } | |
| 297 void EnumChildWindows(HWND w, std::function<void(HWND)> f) { | |
| 298 ::EnumChildWindows(w, EnumChildWindowsProc, reinterpret_cast<LPARAM>(&f)); | |
| 299 } | |
| 300 void EnumChildWindowsHere(HWND parent, std::function<void(HWND)> f) { | |
| 301 for (HWND walk = GetWindow(parent, GW_CHILD); walk != NULL; walk = GetWindow(walk, GW_HWNDNEXT)) { | |
| 302 f(walk); | |
| 303 } | |
| 304 } | |
| 305 static DWORD Win10BuildNumber_() { | |
| 306 OSVERSIONINFO ver = { sizeof(ver) }; | |
| 307 WIN32_OP_D(GetVersionEx(&ver)); | |
| 308 return ver.dwMajorVersion == 10 ? ver.dwBuildNumber : 0; | |
| 309 } | |
| 310 DWORD Win10BuildNumber() { | |
| 311 static DWORD b = Win10BuildNumber_(); | |
| 312 return b; | |
| 313 } | |
| 314 | |
| 315 #include "hookWindowMessages.h" | |
| 316 #include <algorithm> | |
| 317 | |
| 318 namespace { | |
| 319 | |
| 320 class CWindowHook_Map : public CWindowImpl<CWindowHook_Map, CWindow> { | |
| 321 public: | |
| 322 CMessageMap* m_target = nullptr; | |
| 323 DWORD m_targetID = 0; | |
| 324 | |
| 325 std::vector< DWORD > m_messages; | |
| 326 | |
| 327 void setup(std::initializer_list<DWORD>&& arg) { | |
| 328 m_messages = std::move(arg); | |
| 329 std::sort(m_messages.begin(), m_messages.end()); | |
| 330 } | |
| 331 | |
| 332 bool isMsgWanted(DWORD msg) const { | |
| 333 return std::binary_search(m_messages.begin(), m_messages.end(), msg); | |
| 334 } | |
| 335 BEGIN_MSG_MAP(CWindowHook) | |
| 336 if (isMsgWanted(uMsg)) { | |
| 337 CHAIN_MSG_MAP_ALT_MEMBER((*m_target), m_targetID); | |
| 338 } | |
| 339 END_MSG_MAP() | |
| 340 }; | |
| 341 | |
| 342 class CWindowHook_Proc : public CWindowImpl<CWindowHook_Proc, CWindow> { | |
| 343 public: | |
| 344 CWindowHook_Proc(PP::messageHook_t proc) : m_proc(proc) {} | |
| 345 const PP::messageHook_t m_proc; | |
| 346 | |
| 347 BEGIN_MSG_MAP(CWindowHook) | |
| 348 if (m_proc(hWnd, uMsg, wParam, lParam, lResult)) return TRUE; | |
| 349 END_MSG_MAP() | |
| 350 }; | |
| 351 | |
| 352 } | |
| 353 | |
| 354 void PP::hookWindowMessages(HWND wnd, CMessageMap* target, DWORD targetID, std::initializer_list<DWORD>&& msgs) { | |
| 355 auto obj = PP::subclassThisWindow< CWindowHook_Map >(wnd); | |
| 356 obj->m_target = target; obj->m_targetID = targetID; | |
| 357 obj->setup(std::move(msgs)); | |
| 358 } | |
| 359 void PP::hookWindowMessages(HWND wnd, messageHook_t h) { | |
| 360 PP::subclassThisWindow< CWindowHook_Proc >(wnd, h); | |
| 361 } | |
| 362 | |
| 363 namespace PP { | |
| 364 static LONG regReadHelper(HKEY root, const wchar_t* path, const wchar_t* value, LONG def) { | |
| 365 wchar_t buf[64] = {}; | |
| 366 DWORD cb = (DWORD)((std::size(buf) - 1) * sizeof(buf[0])); | |
| 367 if (RegGetValue(root, path, value, RRF_RT_REG_SZ, NULL, buf, &cb) == 0) { | |
| 368 return _wtol(buf); | |
| 369 } | |
| 370 return def; | |
| 371 } | |
| 372 | |
| 373 static SIZE querySystemDragThreshold() { | |
| 374 constexpr DWORD def = 1; | |
| 375 static constexpr wchar_t path[] = L"Control Panel\\Desktop"; | |
| 376 return { | |
| 377 (LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragWidth", def), | |
| 378 (LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragHeight", def) | |
| 379 }; | |
| 380 } | |
| 381 SIZE queryDragThresholdForDPI(SIZE dpi) { | |
| 382 PFC_ASSERT(dpi.cx > 0 && dpi.cy > 0); | |
| 383 static SIZE sys = {}; | |
| 384 if ( sys.cx == 0 || sys.cy == 0 ) sys = querySystemDragThreshold(); | |
| 385 return { MulDiv(sys.cx, dpi.cx, 96), MulDiv(sys.cy, dpi.cy, 96) }; | |
| 386 } | |
| 387 SIZE queryDragThreshold(HWND wndFor) { | |
| 388 return queryDragThresholdForDPI(QueryScreenDPIEx(wndFor)); | |
| 389 } | |
| 390 } |
