view dep/animone/src/win/win32.cc @ 337:a7d4e5107531

dep/animone: REFACTOR ALL THE THINGS 1: animone now has its own syntax divergent from anisthesia, making different platforms actually have their own sections 2: process names in animone are now called `comm' (this will probably break things). this is what its called in bsd/linux so I'm just going to use it everywhere 3: the X11 code now checks for the existence of a UTF-8 window title and passes it if available 4: ANYTHING THATS NOT LINUX IS 100% UNTESTED AND CAN AND WILL BREAK! I still actually need to test the bsd code. to be honest I'm probably going to move all of the bsds into separate files because they're all essentially different operating systems at this point
author Paper <paper@paper.us.eu.org>
date Wed, 19 Jun 2024 12:51:15 -0400
parents b1f625b0227c
children adb79bdde329
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 "animone/win/win32.h"
#include "animone.h"
#include "animone/util/win32.h"
#include "animone/win.h"
#include "animone/fd.h"

#include <set>
#include <string>

#include <windows.h>

namespace animone::internal::win32 {

static std::wstring GetWindowClassName(HWND hwnd) {
	static constexpr int kMaxSize = 256;

	std::wstring buffer(kMaxSize, L'\0');
	const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length());
	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 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
	    "#32770",        // Dialog box
	    "CabinetWClass", // Windows Explorer
	    "ComboLBox",
	    "DDEMLEvent",
	    "DDEMLMom",
	    "DirectUIHWND",
	    "GDI+ Hook Window Class",
	    "IME",
	    "Internet Explorer_Hidden",
	    "MSCTFIME UI",
	    "tooltips_class32",
	};

	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.platform = WindowPlatform::Win32;
	window.id = static_cast<unsigned int>(reinterpret_cast<ULONG_PTR>(hwnd));
	window.text = ToUtf8String(GetWindowText(hwnd));
	window.class_name = ToUtf8String(GetWindowClassName(hwnd));
	if (!VerifyClassName(window.class_name))
		return TRUE;

	Process process;
	process.platform = ExecutablePlatform::Win32;
	process.pid = GetWindowProcessId(hwnd);
	GetProcessName(process.pid, process.comm);

	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 animone::internal::win32