Mercurial > minori
view dep/animia/src/win/win32.cc @ 216:8a482049b968
dep/animia: win/wayland: add UNTESTED support for wlroots protocols
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 07 Jan 2024 12:42:10 -0500 |
parents | bc1ae1810855 |
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