Mercurial > minori
view dep/animia/src/win/win32.cc @ 198:bc1ae1810855
dep/animia: switch from using classes to global functions
the old idea was ok, but sort of hackish; this method doesn't use classes
at all, and this way (especially important!) we can do wayland stuff AND x11
at the same time, which wasn't really possible without stupid workarounds in
the other method
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 24 Dec 2023 02:59:42 -0500 |
parents | 0fc126d52de4 |
children |
line wrap: on
line source
/* * win/win32.cc: support for Windows * * Surprisingly, this is the one time where Microsoft actually * does it fairly OK. Everything has a pretty simple API, despite * the stupid wide string stuff. */ #include "animia/win/win32.h" #include "animia.h" #include "animia/util/win32.h" #include "animia/win.h" #include <set> #include <string> #include <windows.h> namespace animia::internal::win32 { static std::wstring GetWindowClassName(HWND hwnd) { // The maximum size for lpszClassName, according to the documentation of // WNDCLASSEX structure constexpr int kMaxSize = 256; std::wstring buffer(kMaxSize, L'\0'); const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length()); /* for some reason GetClassName returns the actual size of the buffer *with* the terminating NULL byte */ buffer.resize(size); return buffer; } static std::wstring GetWindowText(HWND hwnd) { const auto estimated_size = ::GetWindowTextLengthW(hwnd); std::wstring buffer(estimated_size + 1, L'\0'); const auto size = ::GetWindowTextW(hwnd, &buffer.front(), buffer.length()); /* GetWindowTextLength docs: "Under certain conditions, the GetWindowTextLength function may return a value that is larger than the actual length of the text." */ buffer.resize(size); return buffer; } static DWORD GetWindowProcessId(HWND hwnd) { DWORD process_id = 0; ::GetWindowThreadProcessId(hwnd, &process_id); return process_id; } static std::wstring GetProcessPath(DWORD process_id) { // If we try to open a SYSTEM process, this function fails and the last error // code is ERROR_ACCESS_DENIED. // // Note that if we requested PROCESS_QUERY_INFORMATION access right instead // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used // to open an elevated process. Handle process_handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id)); if (!process_handle) return std::wstring(); std::wstring buffer(MAX_PATH, L'\0'); DWORD buf_size = buffer.length(); // Note that this function requires Windows Vista or above. You may use // GetProcessImageFileName or GetModuleFileNameEx on earlier versions. if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size)) return std::wstring(); buffer.resize(buf_size); return buffer; } //////////////////////////////////////////////////////////////////////////////// static bool VerifyWindowStyle(HWND hwnd) { const auto window_style = ::GetWindowLong(hwnd, GWL_STYLE); const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); auto has_style = [&window_style](DWORD style) { return (window_style & style) != 0; }; auto has_ex_style = [&window_ex_style](DWORD ex_style) { return (window_ex_style & ex_style) != 0; }; // Toolbars, tooltips and similar topmost windows if (has_style(WS_POPUP) && has_ex_style(WS_EX_TOOLWINDOW)) return false; if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW)) return false; return true; } static bool VerifyClassName(const std::wstring& name) { static const std::set<std::wstring> invalid_names = { // System classes L"#32770", // Dialog box L"CabinetWClass", // Windows Explorer L"ComboLBox", L"DDEMLEvent", L"DDEMLMom", L"DirectUIHWND", L"GDI+ Hook Window Class", L"IME", L"Internet Explorer_Hidden", L"MSCTFIME UI", L"tooltips_class32", }; return !name.empty() && !invalid_names.count(name); } static bool VerifyProcessPath(const std::wstring& path) { return !path.empty() && !IsSystemDirectory(path); } static bool VerifyProcessFileName(const std::wstring& name) { static const std::set<std::wstring> invalid_names = { // System files L"explorer", // Windows Explorer L"taskeng", // Task Scheduler Engine L"taskhost", // Host Process for Windows Tasks L"taskhostex", // Host Process for Windows Tasks L"Taskmgr", // Task Manager }; return !name.empty() && !invalid_names.count(name); } //////////////////////////////////////////////////////////////////////////////// static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) { if (!::IsWindowVisible(hwnd)) return TRUE; if (!VerifyWindowStyle(hwnd)) return TRUE; Window window; window.id = static_cast<unsigned int>(reinterpret_cast<ULONG_PTR>(hwnd)); window.text = ToUtf8String(GetWindowText(hwnd)); { std::wstring class_name = GetWindowClassName(hwnd); window.class_name = ToUtf8String(class_name); if (!VerifyClassName(class_name)) return TRUE; } Process process; process.pid = GetWindowProcessId(hwnd); const auto path = GetProcessPath(process.pid); if (!VerifyProcessPath(path)) return TRUE; { std::wstring name = GetFileNameWithoutExtension(GetFileNameFromPath(path)); process.name = ToUtf8String(name); if (!VerifyProcessFileName(name)) return TRUE; } auto& window_proc = *reinterpret_cast<window_proc_t*>(param); if (!window_proc(process, window)) return FALSE; return TRUE; } bool EnumerateWindows(window_proc_t window_proc) { if (!window_proc) return false; const auto param = reinterpret_cast<LPARAM>(&window_proc); // Note that EnumWindows enumerates only top-level windows of desktop apps // (as opposed to UWP apps) on Windows 8 and above. return ::EnumWindows(EnumWindowsProc, param) != FALSE; } } // namespace animia::internal::win32