changeset 152:8700806c2cc2

dep/animia: awesome new breaking changes! I'm so tired
author Paper <mrpapersonic@gmail.com>
date Wed, 15 Nov 2023 02:34:59 -0500
parents 54744a48a7d7
children bd439dd6ffc5
files dep/animia/CMakeLists.txt dep/animia/README.md dep/animia/include/animia.h dep/animia/include/animia/fd.h dep/animia/include/animia/fd/bsd.h dep/animia/include/animia/fd/linux.h dep/animia/include/animia/fd/win32.h dep/animia/include/animia/os.h dep/animia/include/animia/util.h dep/animia/include/animia/util/win32.h dep/animia/include/animia/win.h dep/animia/include/animia/win/win32.h dep/animia/src/animia.cc dep/animia/src/fd.cc dep/animia/src/fd/bsd.cc dep/animia/src/fd/linux.cc dep/animia/src/fd/win32.cc dep/animia/src/strategist.cc dep/animia/src/util.cc dep/animia/src/util/win32.cc dep/animia/src/win.cc dep/animia/src/win/win32.cc src/track/media.cc
diffstat 23 files changed, 651 insertions(+), 237 deletions(-) [+]
line wrap: on
line diff
--- a/dep/animia/CMakeLists.txt	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/CMakeLists.txt	Wed Nov 15 02:34:59 2023 -0500
@@ -7,6 +7,7 @@
 	src/util.cc
 	src/strategist.cc
 	src/fd.cc
+	src/win.cc
 )
 
 if(LINUX)
@@ -26,6 +27,15 @@
 	)
 endif()
 
+if(WIN32)
+	list(APPEND SRC_FILES
+		src/win/win32.cc
+		src/util/win32.cc
+	)
+else()
+	# soon x11 and apple stuff will be here...
+endif()
+
 add_library(animia SHARED ${SRC_FILES})
 set_target_properties(animia PROPERTIES
 	PUBLIC_HEADER include/animia.h
@@ -37,6 +47,9 @@
 elseif(LINUX)
 	target_compile_definitions(animia PUBLIC LINUX)
 elseif(UNIX)
+	if(APPLE)
+		target_compile_definitions(animia PUBLIC MACOSX)
+	endif()
 	target_compile_definitions(animia PUBLIC UNIX)
 endif()
 
--- a/dep/animia/README.md	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/README.md	Wed Nov 15 02:34:59 2023 -0500
@@ -1,4 +1,4 @@
 # Animia
-Animia is a work-in-progress cross-platform clone of Anisthesia and part of Minori.
+Animia is a work-in-progress cross-platform hard fork of Anisthesia and part of Minori.
 
-Most (if not all) Anisthesia configs should also work in this library as well.
+Most (if not all) Anisthesia configs should also work in this library as well (at least on Windows).
--- a/dep/animia/include/animia.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia.h	Wed Nov 15 02:34:59 2023 -0500
@@ -7,6 +7,11 @@
 
 namespace animia {
 
+enum class ResultType {
+	Process,
+	Window
+};
+
 struct Process {
 	internal::pid_t pid = 0;
 	std::string name;
@@ -19,14 +24,15 @@
 };
 
 struct Result {
+	ResultType type;
 	Player player;
-	Process process;
+	Process process; // unused when using window_title. it's dumb, blame X11
 	Window window; // unused with file descriptors
 	std::vector<Media> media;
 };
 
 bool GetResults(const std::vector<Player>& players, std::vector<Result>& results);
 
-} // namespace Animia
+} // namespace animia
 
 #endif // __animia__animia_h
--- a/dep/animia/include/animia/fd.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia/fd.h	Wed Nov 15 02:34:59 2023 -0500
@@ -3,28 +3,35 @@
 
 #include <set>
 #include <string>
-#include <utility>
-#include <string>
-#include <vector>
+#include <functional>
 
 #include "animia/types.h"
 
-namespace animia::internal {
+namespace animia {
+
+struct Process;
+
+namespace internal {
 
 struct OpenFile {
 	pid_t pid = 0;
 	std::string path;
 };
 
+using process_proc_t = std::function<bool(const Process&)>;
+
+using open_file_proc_t = std::function<bool(const OpenFile&)>;
+
 class BaseFdTools {
 	public:
-		virtual bool GetAllPids(std::set<pid_t>& pids) { return false; }
-		virtual bool GetProcessName(pid_t pid, std::string& result) { return false; }
-		virtual bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) { return false; }
+		virtual bool EnumerateOpenProcesses(process_proc_t process_proc) { return false; }
+		virtual bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) { return false; }
 };
 
 extern BaseFdTools& fd; // defined in fd.cc
 
-}
+} // namespace internal
+
+} // namespace animia
 
 #endif // __animia__animia__fd_h
--- a/dep/animia/include/animia/fd/bsd.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia/fd/bsd.h	Wed Nov 15 02:34:59 2023 -0500
@@ -1,8 +1,6 @@
 #ifndef __animia__animia__fd__linux_h
 #define __animia__animia__fd__linux_h
 
-#include <vector>
-#include <utility>
 #include <string>
 #include <set>
 
@@ -17,9 +15,8 @@
 
 class UnixFdTools final : public BaseFdTools {
 	public:
-		bool GetAllPids(std::set<pid_t>& pids) override;
-		bool GetProcessName(pid_t pid, std::string& result) override;
-		bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) override;
+		bool EnumerateOpenProcesses(process_proc_t process_proc) override;
+		bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) override;
 };
 
 }
--- a/dep/animia/include/animia/fd/linux.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia/fd/linux.h	Wed Nov 15 02:34:59 2023 -0500
@@ -1,8 +1,6 @@
 #ifndef __animia__animia__fd__linux_h
 #define __animia__animia__fd__linux_h
 
-#include <vector>
-#include <utility>
 #include <string>
 #include <set>
 
@@ -18,9 +16,8 @@
 
 class LinuxFdTools final : public BaseFdTools {
 	public:
-		bool GetAllPids(std::set<pid_t>& pids) override;
-		bool GetProcessName(pid_t pid, std::string& result) override;
-		bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) override;
+		bool EnumerateOpenProcesses(process_proc_t process_proc) override;
+		bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) override;
 };
 
 }
--- a/dep/animia/include/animia/fd/win32.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia/fd/win32.h	Wed Nov 15 02:34:59 2023 -0500
@@ -1,11 +1,8 @@
 #ifndef __animia__animia__fd__win32_h
 #define __animia__animia__fd__win32_h
 
-#include <vector>
-#include <utility>
 #include <string>
 #include <set>
-#include <memory>
 
 #include <windows.h>
 
@@ -14,18 +11,10 @@
 
 namespace animia::internal::win32 {
 
-struct HandleDeconstructor {
-	using pointer = HANDLE;
-	void operator()(pointer t) const { ::CloseHandle(t); };
-};
-
-using Handle = std::unique_ptr<HANDLE, HandleDeconstructor>;
-
 class Win32FdTools final : public BaseFdTools {
 	public:
-		bool GetAllPids(std::set<pid_t>& pids) override;
-		bool GetProcessName(pid_t pid, std::string& result) override;
-		bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) override;
+		bool EnumerateOpenProcesses(process_proc_t process_proc) override;
+		bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) override;
 };
 
 }
--- a/dep/animia/include/animia/os.h	Tue Nov 14 16:31:21 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-#ifndef __animia__animia__os_h
-#define __animia__animia__os_h
-
-#ifdef __linux__
-#	define ANIMIA_ON_LINUX
-#elif (defined(unix) || defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
-#	if (defined(__APPLE__) && defined(__MACH__))
-#		define ANIMIA_ON_OSX
-#	endif
-#	define ANIMIA_ON_UNIX
-#elif defined(_WIN32)
-#	define ANIMIA_ON_WIN32
-#endif
-
-#endif // __animia__animia__os_h
--- a/dep/animia/include/animia/util.h	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/include/animia/util.h	Wed Nov 15 02:34:59 2023 -0500
@@ -7,6 +7,8 @@
 
 bool ReadFile(const std::string& path, std::string& data);
 bool EqualStrings(const std::string& str1, const std::string& str2);
+bool Stem(const std::string& filename, std::string& stem);
+bool CheckPattern(const std::string& pattern, const std::string& str);
 bool TrimLeft(std::string& str, const char* chars);
 bool TrimRight(std::string& str, const char* chars);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/util/win32.h	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,28 @@
+#include <windows.h>
+#include <subauth.h>
+
+#include <string>
+#include <memory>
+
+namespace animia::internal::win32 {
+
+struct HandleDeconstructor {
+	using pointer = HANDLE;
+	void operator()(pointer t) const { ::CloseHandle(t); };
+};
+
+using Handle = std::unique_ptr<HANDLE, HandleDeconstructor>;
+
+/* ----------------------------------------------- */
+
+std::string ToUtf8String(const std::wstring& string);
+std::string ToUtf8String(const UNICODE_STRING& string);
+std::wstring ToWstring(const std::string& string);
+
+std::wstring GetFileNameFromPath(const std::wstring& path);
+std::wstring GetFileNameWithoutExtension(const std::wstring& filename);
+
+bool IsSystemDirectory(const std::string& path);
+bool IsSystemDirectory(std::wstring path);
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/win.h	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,27 @@
+#ifndef __animia__animia__win_h
+#define __animia__animia__win_h
+
+#include <string>
+#include <functional>
+
+namespace animia {
+
+struct Process;
+struct Window;
+
+namespace internal {
+
+using window_proc_t = std::function<bool(const Process&, const Window&)>;
+
+class BaseWinTools {
+	public:
+		virtual bool EnumerateWindows(window_proc_t window_proc) { return false; (void)window_proc; }
+};
+
+extern BaseWinTools& win;
+
+} // namespace internal
+
+} // namespace animia
+
+#endif // __animia__animia__win_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/win/win32.h	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,15 @@
+#ifndef __animia__animia__fd__win32_h
+#define __animia__animia__fd__win32_h
+
+#include "animia/win.h"
+
+namespace animia::internal::win32 {
+
+class Win32WinTools final : public BaseWinTools {
+	public:
+		bool EnumerateWindows(window_proc_t window_proc) override;
+};
+
+}
+
+#endif // __animia__animia__fd__win32_h
--- a/dep/animia/src/animia.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/animia.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -3,51 +3,90 @@
 #include <set>
 
 #include "animia.h"
+#include "animia/util.h"
 #include "animia/strategies.h"
 #include "animia/types.h"
 #include "animia/fd.h"
+#include "animia/win.h"
+
+#include <iostream>
 
 namespace animia {
 
-static bool ProcessInPlayers(const std::vector<Player>& players, std::string name, Player& player_) {
-	for (const auto& player : players) {
-		for (const auto& exe : player.executables) {
-			/* this is only really relevant on Windows, and even then
-			   executables can end in any number of extensions, so we should
-			   really remove them all... */
-			auto pos = name.rfind(".exe");
-			if (pos != std::string::npos)
-				name = name.substr(0, pos);
+namespace internal {
+
+static bool IsExecutableInList(const Player& player, const std::string& name) {
+	std::string stem;
+#ifdef WIN32
+	if (!util::Stem(name, stem))
+#endif
+		stem = name;
+
+	for (const auto& pattern : player.executables)
+		if (util::CheckPattern(pattern, stem))
+			return true;
 
-			if (exe == name) {
-				player_ = player;
-				return true;
-			}
-		}
+	return false;
+}
+
+static bool IsWindowInList(const Player& player, const std::string& name) {
+	for (const auto& pattern : player.windows)
+		if (util::CheckPattern(pattern, name))
+			return true;
+
+	return false;
+}
+
+static bool PlayerHasStrategy(const Player& player, const Strategy& strategy) {
+	for (const auto& pstrategy : player.strategies) {
+		if (pstrategy == strategy)
+			return true;
 	}
 	return false;
 }
 
+} // namespace internal
+
 bool GetResults(const std::vector<Player>& players, std::vector<Result>& results) {
-	std::set<internal::pid_t> pids;
+	/* Start out with file descriptors. */
+	auto process_proc = [&](const Process& process) -> bool {
+		for (const auto& player : players) {
+			if (!internal::PlayerHasStrategy(player, Strategy::OpenFiles))
+				continue;
 
-	if (!internal::fd.GetAllPids(pids))
+			if (!internal::IsExecutableInList(player, process.name))
+				continue;
+
+			results.push_back({ResultType::Process, player, process, {}, {}});
+			break;
+		}
+		return true;
+	};
+
+	if (!internal::fd.EnumerateOpenProcesses(process_proc))
 		return false;
 
-	for (const auto& pid : pids) {
-		std::string name;
-		internal::fd.GetProcessName(pid, name);
+	/* Then add our cool windows.
+	   Note: X11 is stupid and there's no reliable way to get a PID from a given window.
+	         This is because some windows might not even have a process attached to them.
+	         We should set the PID of the process if we can get it, but that'll be for when
+	         I can actually be arsed to implement the X11 backend. */
+	auto window_proc = [&](const Process& process, const Window& window) -> bool {
+		for (const auto& player : players) {
+			if (!internal::PlayerHasStrategy(player, Strategy::WindowTitle))
+				continue;
 
-		Player player;
-		if (!ProcessInPlayers(players, name, player))
-			continue;
+			if (!internal::IsWindowInList(player, window.class_name))
+				continue;
 
-		Result result;
-		result.process.pid = pid;
-		result.process.name = name;
-		result.player = player;
-		results.push_back(result);
-	}
+			results.push_back({ResultType::Window, player, process, window, {}});
+			break;
+		}
+		return true;
+	};
+
+	if (!internal::win.EnumerateWindows(window_proc))
+		return false;
 
 	return internal::ApplyStrategies(results);
 }
--- a/dep/animia/src/fd.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/fd.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -1,23 +1,20 @@
-#include "animia/os.h"
 #include "animia/fd.h"
 
-#ifdef ANIMIA_ON_WIN32
+#ifdef WIN32
 #	include "animia/fd/win32.h"
-#elif defined(ANIMIA_ON_LINUX)
+#elif defined(LINUX)
 #	include "animia/fd/linux.h"
-#elif defined(ANIMIA_ON_UNIX)
+#elif defined(UNIX)
 #	include "animia/fd/bsd.h"
 #endif
 
 namespace animia::internal {
 
-/* really stupid hack to get fd to point to the right
-   thing */
-#ifdef ANIMIA_ON_WIN32
+#ifdef WIN32
 win32::Win32FdTools os_fd;
-#elif defined(ANIMIA_ON_LINUX)
+#elif defined(LINUX)
 linux::LinuxFdTools os_fd;
-#elif defined(ANIMIA_ON_UNIX)
+#elif defined(UNIX)
 unix::UnixFdTools os_fd;
 #else
 BaseFdTools os_fd;
--- a/dep/animia/src/fd/bsd.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/fd/bsd.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -1,13 +1,13 @@
-/**
- * fd/bsd.cpp
- *  - this ONLY* supports OS X as of now
- *     (*there is some FreeBSD support code)
- **/
+/*
+** fd/bsd.cpp
+**  - this ONLY* supports OS X as of now
+**     (*there is some FreeBSD support code)
+*/
 #include "animia/fd/bsd.h"
+#include "animia.h"
 
 #include <unordered_map>
 #include <vector>
-#include <iostream>
 #include <string>
 
 #include <fcntl.h>
@@ -23,40 +23,7 @@
 
 namespace animia::internal::unix {
 
-/* this is a cleaned up version of a function from... Apple?
-   ...anyway, what it essentially does is gets the size and stuff from
-   sysctl() and reserves the space in a vector to store the PIDs
-
-	TODO: https://kaashif.co.uk/2015/06/18/how-to-get-a-list-of-processes-on-openbsd-in-c/ */
-bool UnixFdTools::GetAllPids(std::set<pid_t>& pids) {
-	struct kinfo_proc* result = NULL;
-	size_t length = 0;
-	static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
-
-	/* get appropriate length from sysctl()
-	   note: the reason this isn't checked is actually because this will
-	   *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */
-	sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0);
-
-	result = (struct kinfo_proc*)malloc(length);
-	if (result == NULL)
-		return std::vector<int>();
-
-	/* TODO: this might actually return ENOMEM if the amount of file handles changes between the
-	   original sysctl() call and this one, which is technically possible */
-	if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) {
-		assert(result != NULL);
-		free(result);
-		throw std::bad_alloc();
-	}
-
-	/* add pids to our vector */
-	pids.reserve(length / sizeof(*result));
-	for (int i = 0; i < length / sizeof(*result); i++)
-		pids.insert(result[i].kp_proc.p_pid);
-}
-
-bool UnixFdTools::GetProcessName(pid_t pid, std::string& result) {
+static bool GetProcessName(pid_t pid, std::string& result) {
 #ifdef __FreeBSD__
 	struct kinfo_proc* proc = kinfo_getproc(pid);
 	if (!proc)
@@ -83,8 +50,45 @@
 #endif
 }
 
+/* this is a cleaned up version of a function from... Apple?
+   ...anyway, what it essentially does is gets the size and stuff from
+   sysctl() and reserves the space in a vector to store the PIDs
+
+	TODO: https://kaashif.co.uk/2015/06/18/how-to-get-a-list-of-processes-on-openbsd-in-c/ */
+bool UnixFdTools::EnumerateOpenProcesses(process_proc_t process_proc) {
+	struct kinfo_proc* result = NULL;
+	size_t length = 0;
+	static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+
+	/* get appropriate length from sysctl()
+	   note: the reason this isn't checked is actually because this will
+	   *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */
+	sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0);
+
+	result = (struct kinfo_proc*)malloc(length);
+	if (result == NULL)
+		return std::vector<int>();
+
+	/* TODO: this might actually return ENOMEM if the amount of file handles changes between the
+	   original sysctl() call and this one, which is technically possible */
+	if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) {
+		assert(result != NULL);
+		free(result);
+		throw std::bad_alloc();
+	}
+
+	for (int i = 0; i < length / sizeof(*result); i++) {
+		const pid_t pid = result[i].kp_proc.p_pid;
+		if (!process_proc({pid, GetProcessName(pid)}))
+			return false;
+	}
+}
+
 /* this only works on OS X :( */
-bool UnixFdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::pair<pid_t, std::string>>& files) {
+bool UnixFdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
+		return false;
+
 	for (const auto& pid : pids) {
 		int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
 		if (bufsz == -1)
@@ -109,7 +113,8 @@
 					continue;
 				*/
 
-				files.push_back({pid, vnodeInfo.pvip.vip_path});
+				if (!open_file_proc({pid, vnodeInfo.pvip.vip_path}))
+					return false;
 			}
 		}
 	}
--- a/dep/animia/src/fd/linux.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/fd/linux.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -1,10 +1,10 @@
 #include "animia/util.h"
 #include "animia/fd/linux.h"
+#include "animia.h"
 
 #include <algorithm>
 #include <filesystem>
 #include <fstream>
-#include <iostream>
 #include <sstream>
 #include <string>
 #include <unordered_map>
@@ -99,7 +99,17 @@
 	return ret.c_str();
 }
 
-bool LinuxFdTools::GetAllPids(std::set<pid_t>& pids) {
+static bool GetProcessName(pid_t pid, std::string& result) {
+	const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm";
+
+	if (!util::ReadFile(path, result))
+		return false;
+
+	result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
+	return true;
+}
+
+bool LinuxFdTools::EnumerateOpenProcesses(process_proc_t process_proc) {
 	bool success = false;
 	for (const auto& dir : GetAllFilesInDir(PROC_LOCATION)) {
 		pid_t pid;
@@ -109,22 +119,16 @@
 		} catch (std::invalid_argument) {
 			continue;
 		}
-		pids.insert(pid);
+		if (!process_proc({pid, GetProcessName(pid)}))
+			return false;
 	}
 	return success;
 }
 
-bool LinuxFdTools::GetProcessName(pid_t pid, std::string& result) {
-	const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm";
-
-	if (!util::ReadFile(path, result))
+bool LinuxFdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
 		return false;
 
-	result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
-	return true;
-}
-
-bool LinuxFdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) {
 	for (const auto& pid : pids) {
 		const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd";
 
@@ -137,7 +141,8 @@
 			if (!IsRegularFile(name))
 				continue;
 
-			files.push_back({pid, name});
+			if (!open_file_proc({pid, name}))
+				return false;
 		}
 	}
 	return true;
--- a/dep/animia/src/fd/win32.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/fd/win32.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -1,9 +1,11 @@
-/**
- * win32.cpp
- *  - provides support for Windows clients
- *
- **/
+/*
+** win32.cpp
+**  - provides support for Windows clients
+**
+*/
 #include "animia/fd/win32.h"
+#include "animia/util/win32.h"
+#include "animia.h"
 
 #include <stdexcept>
 #include <string>
@@ -26,10 +28,15 @@
    you to use *internal functions* that can't even be linked to, hence why we have to
    use GetProcAddress and such. What a mess. */
 
-#define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40)
+/* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
+   32-bit PIDs, unlike SystemHandleInformation */
+constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
+
+/* more constants not in winternl.h */
 constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
-constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL;
 
+/* this is filled in at runtime because it's not guaranteed to be (and isn't)
+   constant between different versions of Windows */
 static unsigned short file_type_index = 0;
 
 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
@@ -77,19 +84,23 @@
 	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
 	/* we should really put a cap on this */
 	ULONG cb = 1 << 19;
-	NTSTATUS status = STATUS_SUCCESS;
-	SYSTEM_HANDLE_INFORMATION_EX* info;
 
-	do {
+	for (NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; status == STATUS_INFO_LENGTH_MISMATCH; ) {
+		/* why are we doing this? */
 		status = STATUS_NO_MEMORY;
 
-		if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2)))
+		SYSTEM_HANDLE_INFORMATION_EX* info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2);
+		if (!info)
 			continue;
 
 		res.reserve(cb);
 
-		if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) {
-			if (ULONG_PTR handles = info->NumberOfHandles) {
+		status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb);
+		if (0 <= status) {
+			ULONG_PTR handles = info->NumberOfHandles;
+			if (handles) {
+				res.reserve(res.size() + handles);
+
 				SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
 				do {
 					if (entry)
@@ -97,8 +108,9 @@
 				} while (entry++, --handles);
 			}
 		}
+
 		free(info);
-	} while (status == STATUS_INFO_LENGTH_MISMATCH);
+	}
 
 	return res;
 }
@@ -109,32 +121,9 @@
 	return info;
 }
 
-/* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that
-   sucks massive dick. this way we get unicode in the way every single other OS does it */
-static std::string UnicodeStringToUtf8(std::wstring string) {
-	unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL);
-	std::string ret = std::string(size, '\0');
-	::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL);
-	return ret;
-}
-
-static std::string UnicodeStringToUtf8(UNICODE_STRING string) {
-	unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL);
-	std::string ret = std::string(size, '\0');
-	::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL);
-	return ret;
-}
-
-static std::wstring Utf8StringToUnicode(std::string string) {
-	unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0);
-	std::wstring ret = std::wstring(size, L'\0');
-	::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length());
-	return ret;
-}
-
 static std::string GetHandleType(HANDLE handle) {
 	OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle);
-	return UnicodeStringToUtf8(info.TypeName);
+	return ToUtf8String(info.TypeName);
 }
 
 static std::string GetFinalPathNameByHandle(HANDLE handle) {
@@ -145,7 +134,7 @@
 	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
 	buffer.resize(buffer.find('\0'));
 
-	return UnicodeStringToUtf8(buffer);
+	return ToUtf8String(buffer);
 }
 
 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
@@ -170,27 +159,11 @@
 	return true;
 }
 
-static std::string GetSystemDirectory() {
-	PWSTR path_wch;
-	SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch);
-	std::wstring path_wstr(path_wch);
-	CoTaskMemFree(path_wch);
-	return UnicodeStringToUtf8(path_wstr);
-}
-
-static bool IsSystemFile(const std::string& path) {
-	std::wstring path_w = Utf8StringToUnicode(path);
-	CharUpperBuffW(&path_w.front(), path_w.length());
-	std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory());
-	CharUpperBuffW(&windir_w.front(), windir_w.length());
-	return path_w.find(windir_w) == 4;
-}
-
 static bool IsFilePathOk(const std::string& path) {
 	if (path.empty())
 		return false;
 
-	if (IsSystemFile(path))
+	if (IsSystemDirectory(path))
 		return false;
 
 	const auto file_attributes = GetFileAttributesA(path.c_str());
@@ -200,7 +173,7 @@
 	return true;
 }
 
-bool Win32FdTools::GetAllPids(std::set<pid_t>& pids) {
+bool Win32FdTools::EnumerateOpenProcesses(process_proc_t process_proc) {
 	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 	if (hProcessSnap == INVALID_HANDLE_VALUE)
 		return false;
@@ -211,39 +184,23 @@
 	if (!::Process32First(hProcessSnap, &pe32))
 		return false;
 
-	pids.insert(pe32.th32ProcessID);
+	if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+		return false;
 
 	while (::Process32Next(hProcessSnap, &pe32))
-		pids.insert(pe32.th32ProcessID);
+		if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+			return false;
 
 	::CloseHandle(hProcessSnap);
 
 	return true;
 }
 
-bool Win32FdTools::GetProcessName(pid_t pid, std::string& result) {
-	unsigned long ret_size = 0; // size given by GetModuleBaseNameW
-	Handle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
-	if (handle.get() == INVALID_HANDLE_VALUE)
+/* this could be changed to being a callback, but... I'm too lazy right now :) */
+bool Win32FdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
 		return false;
 
-	/* agh... */
-	std::wstring ret(256, L'\0');
-	for (; ret.length() < 32768; ret.resize(ret.length() * 2)) {
-		if (!(ret_size = ::GetModuleBaseNameW(handle.get(), 0, &ret.front(), ret.length()))) {
-			return false;
-		} else if (ret.length() > ret_size) {
-			ret.resize(ret.find(L'\0'));
-			result = UnicodeStringToUtf8(ret);
-			break;
-		}
-	}
-
-	return true;
-}
-
-/* this could be changed to being a callback, but... I'm too lazy right now :) */
-bool Win32FdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) {
 	std::unordered_map<pid_t, Handle> proc_handles;
 
 	for (const pid_t& pid : pids) {
@@ -279,7 +236,8 @@
 		if (!IsFilePathOk(path))
 			continue;
 
-		files.push_back({pid, path});
+		if (!open_file_proc({pid, path}))
+			return false;
 	}
 
 	return true;
--- a/dep/animia/src/strategist.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/strategist.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -3,6 +3,10 @@
 #include "animia/fd.h"
 #include "animia.h"
 
+#include <iostream>
+
+#include <regex>
+
 namespace animia::internal {
 
 class Strategist {
@@ -15,6 +19,7 @@
 		bool AddMedia(const MediaInfo media_information);
 
 		bool ApplyOpenFilesStrategy();
+		bool ApplyWindowTitleStrategy();
 
 		Result& result_;
 };
@@ -22,12 +27,14 @@
 bool Strategist::ApplyStrategies() {
 	bool success = false;
 
-	for (const auto strategy : result_.player.strategies) {
-		switch (strategy) {
-			case Strategy::OpenFiles:
-				success |= ApplyOpenFilesStrategy();
-				break;
-		}
+	switch (result_.type) {
+		case ResultType::Process:
+			success |= ApplyOpenFilesStrategy();
+			break;
+		case ResultType::Window:
+			std::cout << "Wat" << std::endl;
+			success |= ApplyWindowTitleStrategy();
+			break;
 	}
 
 	return success;
@@ -46,17 +53,56 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+static bool ApplyWindowTitleFormat(const std::string& format, std::string& title) {
+	if (format.empty())
+		return false;
+
+	const std::regex pattern(format);
+	std::smatch match;
+	std::regex_match(title, match, pattern);
+
+	// Use the first non-empty match result, because the regular expression may
+	// contain multiple sub-expressions.
+	for (size_t i = 1; i < match.size(); ++i) {
+		if (!match.str(i).empty()) {
+			title = match.str(i);
+			return true;
+		}
+	}
+
+	// Results are empty, but the match was successful
+	if (!match.empty()) {
+		title.clear();
+		return true;
+	}
+
+	return true;
+}
+
+static MediaInfoType InferMediaInformationType(const std::string& str) {
+	const std::regex path_pattern(R"(^(?:[A-Za-z]:[/\\]|\\\\)[^<>:"/\\|?*]+)");
+	return (std::regex_search(str, path_pattern))
+		? MediaInfoType::File : MediaInfoType::Unknown;
+}
+
+bool Strategist::ApplyWindowTitleStrategy() {
+	auto title = result_.window.text;
+	ApplyWindowTitleFormat(result_.player.window_title_format, title);
+
+	return AddMedia({InferMediaInformationType(title), title});
+}
+
 bool Strategist::ApplyOpenFilesStrategy() {
 	bool success = false;
 
 	const std::set<pid_t> pids{result_.process.pid};
-	std::vector<OpenFile> files;
-
-	fd.EnumerateOpenFiles(pids, files);
 
-	for (const auto& [pid, path] : files) {
-		success |= AddMedia({MediaInfoType::File, path});
-	}
+	auto open_file_proc = [&](const OpenFile& file) -> bool {
+		success |= AddMedia({MediaInfoType::File, file.path});
+		return true;
+	};
+
+	fd.EnumerateOpenFiles(pids, open_file_proc);
 
 	return success;
 }
--- a/dep/animia/src/util.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/dep/animia/src/util.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -3,6 +3,9 @@
 #include <sstream>
 #include <string>
 #include <cctype>
+#include <regex>
+
+#include <iostream>
 
 #include "animia/util.h"
 
@@ -32,6 +35,23 @@
 	return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), equal_chars);
 }
 
+bool Stem(const std::string& filename, std::string& stem) {
+	unsigned long long pos = filename.find_last_of(".");
+	if (pos != std::string::npos)
+		return false;
+
+	stem = filename.substr(0, pos);
+	return true;
+}
+
+bool CheckPattern(const std::string& pattern, const std::string& str) {
+	if (pattern.empty())
+		return false;
+	if (pattern.front() == '^' && std::regex_match(str, std::regex(pattern)))
+		return true;
+	return util::EqualStrings(pattern, str);
+}
+
 bool TrimLeft(std::string& str, const char* chars) {
 	if (str.empty())
 		return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/util/win32.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,90 @@
+#include "animia/util/win32.h"
+
+#include <windows.h>
+#include <shlobj.h> /* SHGetKnownFolderPath */
+#include <subauth.h> /* UNICODE_STRING */
+
+namespace animia::internal::win32 {
+
+std::string ToUtf8String(const std::wstring& string) {
+	const auto wctomb = [&string](LPSTR out, int size) -> int {
+		return ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), out, size, nullptr, nullptr);
+	};
+
+	if (string.empty())
+		return std::string();
+
+
+	long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), nullptr, 0, nullptr, nullptr);
+	std::string ret = std::string(size, '\0');
+	::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), nullptr, nullptr);
+	return ret;
+}
+
+std::string ToUtf8String(const UNICODE_STRING& string) {
+	const auto wctomb = [&string](LPSTR out, int size) -> int {
+		return ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, out, size, nullptr, nullptr);
+	};
+
+	if (string.Length <= 0)
+		return std::string();
+
+	long size = wctomb(nullptr, 0);
+	std::string ret = std::string(size, '\0');
+	wctomb(&ret.front(), ret.length());
+	return ret;
+}
+
+std::wstring ToWstring(const std::string& string) {
+	const auto mbtowc = [&string](LPWSTR out, int size) -> int {
+		return ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), out, size);
+	};
+
+	if (string.empty())
+		return std::wstring();
+
+	long size = mbtowc(nullptr, 0);
+	std::wstring ret = std::wstring(size, L'\0');
+	mbtowc(&ret.front(), ret.length());
+	return ret;
+}
+
+std::wstring GetFileNameFromPath(const std::wstring& path) {
+	const auto pos = path.find_last_of(L"/\\");
+	return pos != std::wstring::npos ? path.substr(pos + 1) : path;
+}
+
+std::wstring GetFileNameWithoutExtension(const std::wstring& filename) {
+	const auto pos = filename.find_last_of(L".");
+	return pos != std::wstring::npos ? filename.substr(0, pos) : filename;
+}
+
+static std::wstring GetSystemDirectory() {
+	PWSTR path_wch;
+	SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch);
+	std::wstring path_wstr(path_wch);
+	CoTaskMemFree(path_wch);
+	return path_wstr;
+}
+
+bool IsSystemDirectory(const std::string& path) {
+	std::wstring path_w = ToWstring(path);
+	::CharUpperBuffW(&path_w.front(), path_w.length());
+
+	std::wstring windir = GetSystemDirectory();
+	::CharUpperBuffW(&windir.front(), windir.length());
+
+	/* wtf is 4? */
+	return path_w.find(windir) == 4;
+}
+
+bool IsSystemDirectory(std::wstring path) {
+	::CharUpperBuffW(&path.front(), path.length());
+
+	std::wstring windir = GetSystemDirectory();
+	::CharUpperBuffW(&windir.front(), windir.length());
+
+	return path.find(windir) == 4;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/win.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,17 @@
+#include "animia/fd.h"
+
+#ifdef WIN32
+#	include "animia/win/win32.h"
+#endif
+
+namespace animia::internal {
+
+#ifdef WIN32
+win32::Win32WinTools os_win;
+#else
+BaseWinTools os_win;
+#endif
+
+BaseWinTools& win = os_win;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/win/win32.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -0,0 +1,171 @@
+#include "animia/win/win32.h"
+#include "animia/util/win32.h"
+#include "animia/win.h"
+#include "animia.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());
+	return buffer;
+}
+
+static std::wstring GetWindowText(HWND hwnd) {
+	const int size = ::GetWindowTextLengthW(hwnd);
+
+	std::wstring buffer(size, L'\0');
+	::GetWindowTextW(hwnd, &buffer.front(), buffer.length());
+	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();
+
+	return buffer.substr(0, buf_size + 1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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 Win32WinTools::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::win::detail
\ No newline at end of file
--- a/src/track/media.cc	Tue Nov 14 16:31:21 2023 -0500
+++ b/src/track/media.cc	Wed Nov 15 02:34:59 2023 -0500
@@ -41,6 +41,7 @@
 	for (const auto& result : results) {
 		for (const auto& media : result.media) {
 			for (const auto& info : media.information) {
+				std::cout << static_cast<int>(info.type) << ": " << info.value << std::endl;
 				vec.push_back(info.value);
 				success |= true;
 			}
@@ -50,9 +51,8 @@
 	return success;
 }
 
+/* this sucks. use anitomy directly like a real man */
 std::unordered_map<std::string, std::string> GetMapFromElements(const anitomy::Elements& elements) {
-	/* there are way more than this in anitomy, but we only need basic information
-	   I also just prefer using maps than using the ".get()" stuff which is why I'm doing this */
 	std::unordered_map<std::string, std::string> ret;
 
 	ret["title"] = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));