changeset 258:862d0d8619f6

*: HUUUGE changes animia has been renamed to animone, so instead of thinking of a health condition, you think of a beautiful flower :) I've also edited some of the code for animone, but I have no idea if it even works or not because I don't have a mac or windows machine lying around. whoops! ... anyway, all of the changes divergent from Anisthesia are now licensed under BSD. it's possible that I could even rewrite most of the code to where I don't even have to keep the MIT license, but that's thinking too far into the future I've been slacking off on implementing the anime seasons page, mostly out of laziness. I think I'd have to create another db file specifically for the seasons anyway, this code is being pushed *primarily* because the hard drive it's on is failing! yay :)
author Paper <paper@paper.us.eu.org>
date Mon, 01 Apr 2024 02:43:44 -0400
parents 699a20c57dc8
children 0362f3c4534c
files .builds/linux.yml .clang-format .hgignore INSTALL LICENSE Makefile.am README configure.ac dep/animia/.clang-format dep/animia/.hgignore dep/animia/LICENSE dep/animia/Makefile.am dep/animia/README.md dep/animia/configure.ac dep/animia/data/ext-foreign-toplevel-list-v1.xml dep/animia/data/players.anisthesia dep/animia/data/wlr-foreign-toplevel-management-unstable-v1.xml dep/animia/include/animia.h dep/animia/include/animia/fd.h dep/animia/include/animia/fd/kvm.h dep/animia/include/animia/fd/libutil.h dep/animia/include/animia/fd/proc.h dep/animia/include/animia/fd/win32.h dep/animia/include/animia/fd/xnu.h dep/animia/include/animia/media.h dep/animia/include/animia/player.h dep/animia/include/animia/strategies.h dep/animia/include/animia/types.h dep/animia/include/animia/util.h dep/animia/include/animia/util/osx.h dep/animia/include/animia/util/win32.h dep/animia/include/animia/win.h dep/animia/include/animia/win/quartz.h dep/animia/include/animia/win/wayland.h dep/animia/include/animia/win/wayland/ext-foreign-toplevel-list-v1.h dep/animia/include/animia/win/wayland/wlr-foreign-toplevel-management-unstable-v1.h dep/animia/include/animia/win/win32.h dep/animia/include/animia/win/x11.h dep/animia/src/animia.cc dep/animia/src/fd.cc dep/animia/src/fd/kvm.cc dep/animia/src/fd/libutil.cc dep/animia/src/fd/proc.cc dep/animia/src/fd/win32.cc dep/animia/src/fd/xnu.cc dep/animia/src/player.cc dep/animia/src/strategist.cc dep/animia/src/util.cc dep/animia/src/util/osx.cc dep/animia/src/util/win32.cc dep/animia/src/win.cc dep/animia/src/win/quartz.cc dep/animia/src/win/wayland.cc dep/animia/src/win/wayland/ext-foreign-toplevel-list-v1.c dep/animia/src/win/wayland/wlr-foreign-toplevel-management-unstable-v1.c dep/animia/src/win/win32.cc dep/animia/src/win/x11.cc dep/animone/.clang-format dep/animone/.hgignore dep/animone/LICENSE.BSD dep/animone/LICENSE.MIT dep/animone/Makefile.am dep/animone/README.md dep/animone/configure.ac dep/animone/data/players.anisthesia dep/animone/include/animone.h dep/animone/include/animone/fd.h dep/animone/include/animone/fd/kvm.h dep/animone/include/animone/fd/proc.h dep/animone/include/animone/fd/win32.h dep/animone/include/animone/fd/xnu.h dep/animone/include/animone/media.h dep/animone/include/animone/player.h dep/animone/include/animone/strategies.h dep/animone/include/animone/types.h dep/animone/include/animone/util.h dep/animone/include/animone/util/osx.h dep/animone/include/animone/util/win32.h dep/animone/include/animone/win.h dep/animone/include/animone/win/quartz.h dep/animone/include/animone/win/win32.h dep/animone/include/animone/win/x11.h dep/animone/src/animone.cc dep/animone/src/fd.cc dep/animone/src/fd/kvm.cc dep/animone/src/fd/proc.cc dep/animone/src/fd/win32.cc dep/animone/src/fd/xnu.cc dep/animone/src/player.cc dep/animone/src/strategist.cc dep/animone/src/util.cc dep/animone/src/util/osx.cc dep/animone/src/util/win32.cc dep/animone/src/win.cc dep/animone/src/win/quartz.cc dep/animone/src/win/win32.cc dep/animone/src/win/x11.cc dep/anitomy/LICENSE dep/anitomy/Makefile.am dep/anitomy/README.md dep/anitomy/anitomy/anitomy.cpp dep/anitomy/anitomy/anitomy.h dep/anitomy/anitomy/element.cpp dep/anitomy/anitomy/element.h dep/anitomy/anitomy/keyword.cpp dep/anitomy/anitomy/keyword.h dep/anitomy/anitomy/options.h dep/anitomy/anitomy/parser.cpp dep/anitomy/anitomy/parser.h dep/anitomy/anitomy/parser_helper.cpp dep/anitomy/anitomy/parser_number.cpp dep/anitomy/anitomy/string.cpp dep/anitomy/anitomy/string.h dep/anitomy/anitomy/token.cpp dep/anitomy/anitomy/token.h dep/anitomy/anitomy/tokenizer.cpp dep/anitomy/anitomy/tokenizer.h dep/anitomy/configure.ac dep/anitomy/test/data.json dep/json/json.hpp dep/json/json_fwd.hpp dep/mini/ini.h dep/pugixml/LICENSE.md dep/pugixml/Makefile.am dep/pugixml/configure.ac dep/pugixml/docs/images/dom_tree.png dep/pugixml/docs/images/vs2005_link1.png dep/pugixml/docs/images/vs2005_link2.png dep/pugixml/docs/images/vs2005_pch1.png dep/pugixml/docs/images/vs2005_pch2.png dep/pugixml/docs/images/vs2005_pch3.png dep/pugixml/docs/images/vs2005_pch4.png dep/pugixml/docs/images/vs2010_link1.png dep/pugixml/docs/images/vs2010_link2.png dep/pugixml/docs/manual.html dep/pugixml/docs/quickstart.html dep/pugixml/docs/samples/character.xml dep/pugixml/docs/samples/custom_memory_management.cpp dep/pugixml/docs/samples/include.cpp dep/pugixml/docs/samples/load_error_handling.cpp dep/pugixml/docs/samples/load_file.cpp dep/pugixml/docs/samples/load_memory.cpp dep/pugixml/docs/samples/load_options.cpp dep/pugixml/docs/samples/load_stream.cpp dep/pugixml/docs/samples/modify_add.cpp dep/pugixml/docs/samples/modify_base.cpp dep/pugixml/docs/samples/modify_remove.cpp dep/pugixml/docs/samples/save_custom_writer.cpp dep/pugixml/docs/samples/save_declaration.cpp dep/pugixml/docs/samples/save_file.cpp dep/pugixml/docs/samples/save_options.cpp dep/pugixml/docs/samples/save_stream.cpp dep/pugixml/docs/samples/save_subtree.cpp dep/pugixml/docs/samples/text.cpp dep/pugixml/docs/samples/transitions.xml dep/pugixml/docs/samples/traverse_base.cpp dep/pugixml/docs/samples/traverse_iter.cpp dep/pugixml/docs/samples/traverse_predicate.cpp dep/pugixml/docs/samples/traverse_rangefor.cpp dep/pugixml/docs/samples/traverse_walker.cpp dep/pugixml/docs/samples/tree.xml dep/pugixml/docs/samples/weekly-shift_jis.xml dep/pugixml/docs/samples/weekly-utf-16.xml dep/pugixml/docs/samples/weekly-utf-8.xml dep/pugixml/docs/samples/xgconsole.xml dep/pugixml/docs/samples/xpath_error.cpp dep/pugixml/docs/samples/xpath_query.cpp dep/pugixml/docs/samples/xpath_select.cpp dep/pugixml/docs/samples/xpath_variables.cpp dep/pugixml/readme.txt dep/pugixml/scripts/cocoapods_push.sh dep/pugixml/scripts/natvis/pugixml.natvis dep/pugixml/scripts/natvis/pugixml_compact.natvis dep/pugixml/scripts/nuget/build/native/pugixml-propertiesui.xml dep/pugixml/scripts/nuget/build/native/pugixml.targets dep/pugixml/scripts/nuget/pugixml.nuspec dep/pugixml/scripts/nuget_build.ps1 dep/pugixml/scripts/premake4.lua dep/pugixml/scripts/pugixml-config.cmake.in dep/pugixml/scripts/pugixml.pc.in dep/pugixml/scripts/pugixml.podspec dep/pugixml/scripts/pugixml.xcodeproj/project.pbxproj dep/pugixml/scripts/pugixml_airplay.mkf dep/pugixml/scripts/pugixml_codeblocks.cbp dep/pugixml/scripts/pugixml_codelite.project dep/pugixml/scripts/pugixml_dll.rc dep/pugixml/scripts/pugixml_vs2005.vcproj dep/pugixml/scripts/pugixml_vs2005_static.vcproj dep/pugixml/scripts/pugixml_vs2008.vcproj dep/pugixml/scripts/pugixml_vs2008_static.vcproj dep/pugixml/scripts/pugixml_vs2010.vcxproj dep/pugixml/scripts/pugixml_vs2010_static.vcxproj dep/pugixml/scripts/pugixml_vs2013.vcxproj dep/pugixml/scripts/pugixml_vs2013_static.vcxproj dep/pugixml/scripts/pugixml_vs2015.vcxproj dep/pugixml/scripts/pugixml_vs2015_static.vcxproj dep/pugixml/scripts/pugixml_vs2017.vcxproj dep/pugixml/scripts/pugixml_vs2017_static.vcxproj dep/pugixml/scripts/pugixml_vs2019.vcxproj dep/pugixml/scripts/pugixml_vs2019_static.vcxproj dep/pugixml/scripts/pugixml_vs2022.vcxproj dep/pugixml/scripts/pugixml_vs2022_static.vcxproj dep/pugixml/src/pugiconfig.hpp dep/pugixml/src/pugixml.cpp dep/pugixml/src/pugixml.hpp dep/semver/semver.hpp include/core/anime.h include/core/anime_db.h include/core/config.h include/core/date.h include/core/filesystem.h include/core/http.h include/core/ini.h include/core/json.h include/core/session.h include/core/strings.h include/core/time.h include/core/torrent.h include/gui/dialog/about.h include/gui/dialog/information.h include/gui/dialog/settings.h include/gui/layouts/flow_layout.h include/gui/locale.h include/gui/pages/anime_list.h include/gui/pages/history.h include/gui/pages/now_playing.h include/gui/pages/search.h include/gui/pages/seasons.h include/gui/pages/statistics.h include/gui/pages/torrents.h include/gui/theme.h include/gui/translate/anilist.h include/gui/translate/anime.h include/gui/translate/config.h include/gui/widgets/anime_button.h include/gui/widgets/anime_info.h include/gui/widgets/clickable_label.h include/gui/widgets/elided_label.h include/gui/widgets/graph.h include/gui/widgets/optional_date.h include/gui/widgets/poster.h include/gui/widgets/sidebar.h include/gui/widgets/text.h include/gui/window.h include/library/library.h include/services/anilist.h include/services/services.h include/sys/glib/dark_theme.h include/sys/osx/dark_theme.h include/sys/osx/filesystem.h include/sys/osx/permissions.h include/sys/win32/dark_theme.h include/track/media.h m4/autotroll.m4 m4/m4_ax_cxx_compile_stdcxx.m4 m4/m4_ax_have_qt.m4 rc/animone.qrc rc/icons/16x16/arrow-circle-315.png rc/icons/16x16/calendar-next.png rc/icons/16x16/calendar-previous.png rc/icons/16x16/calendar.png rc/icons/16x16/category.png rc/icons/16x16/chart.png rc/icons/16x16/clock-history-frame.png rc/icons/16x16/cross-button.png rc/icons/16x16/document-list.png rc/icons/16x16/feed.png rc/icons/16x16/film.png rc/icons/16x16/gear.png rc/icons/16x16/magnifier.png rc/icons/16x16/navigation-270-button.png rc/icons/16x16/plus-button.png rc/icons/16x16/sort-quantity-descending.png rc/icons/16x16/ui-scroll-pane-detail.png rc/icons/24x24/application-export.png rc/icons/24x24/application-sidebar-list.png rc/icons/24x24/arrow-circle-double-135.png rc/icons/24x24/feed.png rc/icons/24x24/folder-open.png rc/icons/24x24/gear.png rc/icons/24x24/globe.png rc/icons/24x24/inbox-film.png rc/icons/24x24/megaphone.png rc/icons/24x24/question.png rc/icons/README.md rc/icons/icons.qrc rc/linux/Minori.desktop rc/linux/Minori.png rc/locale/en_GB.ts rc/locale/es.ts rc/osx/Minori.app/Contents/Info.plist rc/osx/Minori.app/Contents/PkgInfo rc/osx/Minori.app/Contents/Resources/Minori.icns rc/player_data.qrc rc/sys/linux/Minori.desktop rc/sys/linux/Minori.png rc/sys/osx/Minori.app/Contents/Info.plist rc/sys/osx/Minori.app/Contents/PkgInfo rc/sys/osx/Minori.app/Contents/Resources/Minori.icns rc/sys/win32/dark/dark.qrc rc/sys/win32/dark/dark.qss rc/sys/win32/favicon.ico rc/sys/win32/resource.rc rc/sys/win32/version.rc rc/win32/dark/dark.qrc rc/win32/dark/dark.qss rc/win32/favicon.ico rc/win32/resource.rc rc/win32/version.rc scripts/osx/deploy_build.sh src/core/anime.cc src/core/anime_db.cc src/core/config.cc src/core/date.cc src/core/filesystem.cc src/core/http.cc src/core/json.cc src/core/strings.cc src/core/time.cc src/gui/dialog/about.cc src/gui/dialog/information.cc src/gui/dialog/settings.cc src/gui/dialog/settings/application.cc src/gui/dialog/settings/library.cc src/gui/dialog/settings/recognition.cc src/gui/dialog/settings/services.cc src/gui/dialog/settings/torrents.cc src/gui/layouts/flow_layout.cc src/gui/locale.cc src/gui/pages/anime_list.cc src/gui/pages/history.cc src/gui/pages/now_playing.cc src/gui/pages/search.cc src/gui/pages/seasons.cc src/gui/pages/statistics.cc src/gui/pages/torrents.cc src/gui/theme.cc src/gui/translate/anilist.cc src/gui/translate/anime.cc src/gui/translate/config.cc src/gui/widgets/anime_button.cc src/gui/widgets/anime_info.cc src/gui/widgets/clickable_label.cc src/gui/widgets/elided_label.cc src/gui/widgets/optional_date.cc src/gui/widgets/poster.cc src/gui/widgets/sidebar.cc src/gui/widgets/text.cc src/gui/window.cc src/library/library.cc src/main.cc src/services/anilist.cc src/services/services.cc src/sys/glib/dark_theme.cc src/sys/osx/dark_theme.cc src/sys/osx/permissions.cc src/sys/win32/dark_theme.cc src/track/media.cc
diffstat 195 files changed, 5666 insertions(+), 8267 deletions(-) [+]
line wrap: on
line diff
--- a/.builds/linux.yml	Sun Feb 18 16:02:14 2024 -0500
+++ b/.builds/linux.yml	Mon Apr 01 02:43:44 2024 -0400
@@ -29,8 +29,8 @@
 
       # resources
       mkdir -p rc
-      cp ../rc/linux/Minori.desktop rc/Minori.desktop
-      cp ../rc/linux/Minori.png rc/Minori.png
+      cp ../rc/sys/linux/Minori.desktop rc/Minori.desktop
+      cp ../rc/sys/linux/Minori.png rc/Minori.png
 
       # use linuxdeploy to make an appimage
       wget -O linuxdeploy "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20231026-1/linuxdeploy-x86_64.AppImage"
--- a/.clang-format	Sun Feb 18 16:02:14 2024 -0500
+++ b/.clang-format	Mon Apr 01 02:43:44 2024 -0400
@@ -5,15 +5,21 @@
 ColumnLimit: 120
 IndentWidth: 4
 TabWidth: 4
-AccessModifierOffset: 4
+
+# hack!!!
+AccessModifierOffset: -4
 
 IndentCaseLabels: true
-IndentAccessModifiers: true
+IndentAccessModifiers: false
 IndentPPDirectives: AfterHash
 
 BreakBeforeBraces: Attach
 BreakStringLiterals: true
 
+AlwaysBreakTemplateDeclarations: true
+
+SpaceAfterTemplateKeyword: false
+
 AlignAfterOpenBracket: Align
 AlignArrayOfStructures: Left
 AlignEscapedNewlines: DontAlign
@@ -29,4 +35,4 @@
 
 ---
 Language: Cpp
-Standard: Cpp17
+Standard: c++17
--- a/Makefile.am	Sun Feb 18 16:02:14 2024 -0500
+++ b/Makefile.am	Mon Apr 01 02:43:44 2024 -0400
@@ -20,7 +20,7 @@
 
 minori_qtrc = \
 	$(top_srcdir)/rc/icons/icons.qrc	\
-	$(top_srcdir)/rc/player_data.qrc
+	$(top_srcdir)/rc/animone.qrc
 
 # various things we want to distribute
 
@@ -55,20 +55,20 @@
 	$(top_srcdir)/rc/icons/favicon.png
 
 minori_linux_rc = \
-	$(top_srcdir)/rc/linux/Minori.desktop \
-	$(top_srcdir)/rc/linux/Minori.png
+	$(top_srcdir)/rc/sys/linux/Minori.desktop \
+	$(top_srcdir)/rc/sys/linux/Minori.png
 
 minori_osx_rc = \
-	$(top_srcdir)/rc/osx/Minori.app/Contents/Resources/Minori.icns \
-	$(top_srcdir)/rc/osx/Minori.app/Contents/Info.plist \
-	$(top_srcdir)/rc/osx/Minori.app/Contents/PkgInfo
+	$(top_srcdir)/rc/sys/osx/Minori.app/Contents/Resources/Minori.icns \
+	$(top_srcdir)/rc/sys/osx/Minori.app/Contents/Info.plist \
+	$(top_srcdir)/rc/sys/osx/Minori.app/Contents/PkgInfo
 
 minori_win32_rc = \
-	$(top_srcdir)/rc/win32/dark/dark.qrc \
-	$(top_srcdir)/rc/win32/dark/dark.qss \
-	$(top_srcdir)/rc/win32/favicon.ico \
-	$(top_srcdir)/rc/win32/resource.rc \
-	$(top_srcdir)/rc/win32/version.rc
+	$(top_srcdir)/rc/sys/win32/dark/dark.qrc \
+	$(top_srcdir)/rc/sys/win32/dark/dark.qss \
+	$(top_srcdir)/rc/sys/win32/favicon.ico \
+	$(top_srcdir)/rc/sys/win32/resource.rc \
+	$(top_srcdir)/rc/sys/win32/version.rc
 
 minori_scripts = \
 	$(top_srcdir)/scripts/osx/deploy_build.sh \
@@ -108,7 +108,7 @@
 WRCFLAGS = --use-temp-file -I. -I$(srcdir) $(wrcflags_version) $(CPPFLAGS)
 .rc.$(OBJEXT):
 	$(WINDRES) $(WRCFLAGS) -i $< -o $@
-files_windres=rc/win32/version.rc rc/win32/resource.rc
+files_windres=rc/sys/win32/version.rc rc/sys/win32/resource.rc
 
 endif # BUILD_WINDRES
 
@@ -147,7 +147,6 @@
 	include/gui/widgets/sidebar.h			\
 	include/gui/widgets/text.h		\
 	include/gui/widgets/elided_label.h	\
-	include/gui/layouts/flow_layout.h	\
 	include/gui/locale.h	\
 	include/gui/theme.h			\
 	include/gui/window.h
@@ -196,7 +195,6 @@
 	src/gui/dialog/about.cc		\
 	src/gui/dialog/information.cc		\
 	src/gui/dialog/settings.cc		\
-	src/gui/layouts/flow_layout.cc	\
 	src/gui/pages/anime_list.cc		\
 	src/gui/pages/history.cc		\
 	src/gui/pages/now_playing.cc		\
@@ -238,7 +236,7 @@
 
 minori_includes = \
 	-I$(top_srcdir)/include \
-	-I$(top_srcdir)/dep/animia/include \
+	-I$(top_srcdir)/dep/animone/include \
 	-I$(top_srcdir)/dep/pugixml/src \
 	-I$(top_srcdir)/dep/anitomy \
 	-I$(top_srcdir)/dep
@@ -247,21 +245,21 @@
 minori_CXXFLAGS = $(QT_CXXFLAGS) $(cflags_osx) $(cflags_glib) $(cflags_win)
 minori_LDFLAGS = $(QT_LDFLAGS) $(ldflags_osx) $(ldflags_win)
 
-minori_DEPENDENCIES = dep/pugixml/libpugixml.la dep/animia/libanimia.la dep/anitomy/libanitomy.la
+minori_DEPENDENCIES = dep/pugixml/libpugixml.la dep/animone/libanimone.la dep/anitomy/libanitomy.la
 minori_LDADD = $(minori_DEPENDENCIES) $(libs_glib) $(LIBCURL) $(QT_LIBS) $(libs_osx) $(libs_win)
 
 # Build only one qrc, otherwise we get a ton of
 # weird linking errors
 rc/final_qrc.cc: $(minori_qtrc)
-	$(RCC) -o $@ $(minori_qtrc)
+	$(QT_RCC) -o $@ $(minori_qtrc)
 
 .h_moc.cc:
 	$(MKDIR_P) -- $$(dirname $@)
-	$(MOC) -o $@ $(minori_includes) $<
+	$(QT_MOC) -o $@ $(minori_includes) $<
 
 .ts.qm:
 	$(MKDIR_P) $$(dirname $@); \
-	$(LRELEASE) $< -qm $@
+	$(QT_LRELEASE) $< -qm $@
 
 SUFFIXES = .h _moc.cc .ts .qm
 SUBDIRS = $(subdirs)
--- a/configure.ac	Sun Feb 18 16:02:14 2024 -0500
+++ b/configure.ac	Mon Apr 01 02:43:44 2024 -0400
@@ -2,7 +2,7 @@
 
 AC_CANONICAL_HOST
 
-AC_CONFIG_SUBDIRS([dep/pugixml dep/animia dep/anitomy])
+AC_CONFIG_SUBDIRS([dep/pugixml dep/animone dep/anitomy])
 AC_CONFIG_SRCDIR([src/main.cc])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIRS([m4])
@@ -19,9 +19,14 @@
 LT_INIT
 
 dnl Qt?
-AT_WITH_QT([widgets gui core], [], [], [have_qt=no], [have_qt=yes])
+have_qt=no
+AX_HAVE_QT
 
 AS_IF([test "x$have_qt" = "xno"], [AC_MSG_ERROR([*** Qt not found.])])
+AC_SUBST([QT_LRELEASE])
+AC_SUBST([QT_LUPDATE])
+AC_SUBST([QT_MOC])
+AC_SUBST([QT_RCC])
 
 dnl need this for moc
 AC_PROG_MKDIR_P
--- a/dep/animia/.clang-format	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
----
-BasedOnStyle: LLVM
-UseTab: ForIndentation
-PointerAlignment: Left
-ColumnLimit: 120
-IndentWidth: 4
-TabWidth: 4
-
-IndentCaseLabels: true
-IndentPPDirectives: AfterHash
-
-BreakBeforeBraces: Attach
-BreakStringLiterals: true
-
-AlignAfterOpenBracket: Align
-AlignArrayOfStructures: Left
-AlignEscapedNewlines: DontAlign
-AlignConsecutiveMacros: true
-
-AllowShortIfStatementsOnASingleLine: false
-AllowShortBlocksOnASingleLine: Empty
-AllowShortEnumsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: InlineOnly
-AllowShortCaseLabelsOnASingleLine: true
-
----
-Language: Cpp
-Standard: Cpp11
--- a/dep/animia/.hgignore	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-syntax: glob
-
-# Prerequisites
-*.d
-
-# Compiled Object files
-*.slo
-*.lo
-*.o
-*.obj
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Compiled Dynamic libraries
-*.so
-*.dylib
-*.dll
-
-# Fortran module files
-*.mod
-*.smod
-
-# Compiled Static libraries
-*.lai
-*.la
-*.a
-*.lib
-
-# Executables
-*.exe
-*.out
-*.app
-
-syntax: regexp
-
-# Build dir
-^build/
-^test/build/
\ No newline at end of file
--- a/dep/animia/LICENSE	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-MIT License
-
-Copyright (c) 2017 Eren Okka
-Copyright (c) 2023 Paper
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
--- a/dep/animia/Makefile.am	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-lib_LTLIBRARIES = libanimia.la
-
-include_HEADERS = \
-	include/animia.h
-
-animiadir = $(includedir)/animia
-nobase_animia_HEADERS = \
-	include/animia/media.h \
-	include/animia/player.h \
-	include/animia/types.h
-
-noinst_HEADERS = \
-	include/animia/fd/kvm.h \
-	include/animia/fd/libutil.h \
-	include/animia/fd/proc.h \
-	include/animia/fd/win32.h \
-	include/animia/fd/xnu.h \
-	include/animia/util/osx.h \
-	include/animia/util/win32.h \
-	include/animia/win/wayland/ext-foreign-toplevel-list-v1.h \
-	include/animia/win/wayland/wlr-foreign-toplevel-management-unstable-v1.h \
-	include/animia/win/quartz.h \
-	include/animia/win/wayland.h \
-	include/animia/win/win32.h \
-	include/animia/win/x11.h \
-	include/animia/fd.h \
-	include/animia/strategies.h \
-	include/animia/util.h \
-	include/animia/win.h
-
-if BUILD_WIN
-files_win = src/fd/win32.cc src/win/win32.cc src/util/win32.cc
-libs_win = -lole32 -luuid
-endif
-
-if BUILD_OSX
-files_osx = src/fd/xnu.cc src/win/quartz.cc src/util/osx.cc
-libs_osx = -lobjc
-ldflags_osx = -framework Foundation -framework CoreGraphics -framework ApplicationServices
-endif
-
-if BUILD_LINUX
-files_linux = src/fd/proc.cc
-endif
-
-if BUILD_LIBUTIL
-files_libutil = src/fd/libutil.cc
-libs_libutil = -lutil
-endif
-
-if BUILD_LIBKVM
-files_libkvm = src/fd/kvm.cc
-libs_libkvm = -lkvm
-endif
-
-if BUILD_XCB
-files_x11 = src/win/x11.cc
-cflags_x11 = @XCB_CFLAGS@
-libs_x11 = @XCB_LIBS@
-endif
-
-if BUILD_WAYLAND
-files_wayland = \
-	src/win/wayland.cc \
-	src/win/wayland/ext-foreign-toplevel-list-v1.c \
-	src/win/wayland/wlr-foreign-toplevel-management-unstable-v1.c
-cflags_wayland = @WAYLAND_CFLAGS@
-libs_wayland = @WAYLAND_LIBS@
-endif
-
-EXTRA_DIST = \
-	$(top_srcdir)/data/players.anisthesia
-
-libanimia_la_SOURCES = \
-	src/animia.cc \
-	src/fd.cc \
-	src/player.cc \
-	src/strategist.cc \
-	src/util.cc \
-	src/win.cc \
-	$(files_win) \
-	$(files_osx) \
-	$(files_linux) \
-	$(files_libutil) \
-	$(files_libkvm) \
-	$(files_x11) \
-	$(files_wayland)
-
-libanimia_la_CPPFLAGS = -I$(top_srcdir)/include @DEFS@
-
-libanimia_la_CXXFLAGS = -std=c++17 $(cflags_osx) $(cflags_x11) $(cflags_wayland)
-libanimia_la_LDFLAGS = -version-info 0:0:0 $(ldflags_osx)
-
-libanimia_la_LIBADD = $(libs_win) $(libs_wayland) $(libs_x11) $(libs_osx) $(libs_libutil) $(libs_libkvm)
-
-ACLOCAL_AMFLAGS = -I m4
--- a/dep/animia/README.md	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-# Animia
-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
-(at least on Windows).
-
-## Support
-Animia supports Windows, macOS, and Linux when dealing with file descriptors.
-When enumerating windows, it supports Windows, macOS (Quartz), X11, and
-Wayland (only via the `ext_foreign_toplevel_handle_v1` and
-`wlr_foreign_toplevel_management_unstable_v1` interfaces).
-
-Unlike Anisthesia, Animia currently does not support UI automation, i.e., most
-web browsers will not work properly, if at all.
-
-## Platform-specific quirks
-
-### Windows
-To get the currently opened file handles on Windows, Animia has to use internal
-kernel functions. However, these functions aren't likely to change anytime soon.
-
-### macOS
-The code to retrieve executable names on macOS uses internal functions. However,
-if these functions cannot be found for whatever reason, it falls back to parsing
-the arguments, and then to calling the kernel.
-
-Additionally, macOS does not have the concept of class names, rather, it has
-bundle identifiers, which are a suitable replacement in most use cases, and are
-what Animia will try to grab before falling back to the Quartz window name.
-
-### X11
-If your X server has the XRes extension installed, Animia will use it to get
-PIDs. Otherwise, X11 has no idea what PID started your window. As a result,
-what Animia will give you is from the `_NET_WM_PID` resource, which is
-[very](https://stackoverflow.com/a/49970490)
-[unreliable](https://stackoverflow.com/a/49970271).
-
-### Wayland
-Only Wayland servers that implement the `ext_foreign_toplevel_handle_v1` or
-`wlr_foreign_toplevel_management_unstable_v1` interfaces will work with Animia.
-Currently, both Sway and Mir support the latter. Maybe some day we'll have
-major compositor support for `ext_foreign_toplevel_handle_v1`.
--- a/dep/animia/configure.ac	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-AC_INIT([animia], [0.1.0-alpha.1])
-
-AC_CANONICAL_HOST
-
-AC_CONFIG_SRCDIR([src/animia.cc])
-AC_CONFIG_AUX_DIR([build-aux])
-AC_CONFIG_MACRO_DIRS([m4])
-
-AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
-
-# Do we have a C++17 compiler
-AC_PROG_CXX
-
-AM_PROG_AR
-LT_INIT
-
-build_win32=no
-build_osx=no
-build_linux=no
-build_libutil=no
-build_kvm=no
-
-build_x11=no
-build_wayland=no
-
-case "${host_os}" in
-	cygwin*|mingw*)
-		# Windows
-		build_windows=yes
-		AC_CHECK_TOOL([WINDRES], [windres])
-		AC_SUBST(WINDRES)
-		AC_DEFINE([WIN32])
-		;;
-	darwin*)
-		# Mac OS X
-		build_osx=yes
-		AC_DEFINE([MACOSX])
-		;;
-	linux*)
-		build_linux=yes
-		AC_DEFINE([LINUX])
-		;;
-	*)
-		# FreeBSD
-		AC_CHECK_LIB([util], [kinfo_getfile], [build_libutil=yes], [build_libutil=no])
-		if test "x$build_libutil" = "xyes"; then
-			AC_DEFINE([LIBUTIL])
-		else
-			# OpenBSD
-			AC_CHECK_LIB([kvm], [kvm_getfiles], [build_kvm=yes], [build_kvm=no])
-			if test "x$build_kvm" = "xyes"; then
-				AC_DEFINE([LIBKVM])
-			fi
-		fi
-		;;
-esac
-
-if ! test "x$build_osx" = "xyes" && ! test "x$build_windows" = "xyes"; then
-	PKG_CHECK_MODULES(XCB, [xcb xcb-res], [build_x11=yes], [build_x11=no])
-	if test "x$build_x11" = "xyes"; then
-		AC_DEFINE([X11])
-	fi
-	PKG_CHECK_MODULES(WAYLAND, [wayland-client], [build_wayland=yes], [build_wayland=no])
-	if test "x$build_wayland" = "xyes"; then
-		AC_DEFINE([WAYLAND])
-	fi
-fi
-
-AM_CONDITIONAL([BUILD_WIN], [test "x$build_windows" = "xyes"])
-AM_CONDITIONAL([BUILD_OSX], [test "x$build_osx" = "xyes"])
-AM_CONDITIONAL([BUILD_LINUX], [test "x$build_linux" = "xyes"])
-AM_CONDITIONAL([BUILD_LIBUTIL], [test "x$build_libutil" = "xyes"])
-AM_CONDITIONAL([BUILD_LIBKVM], [test "x$build_kvm" = "xyes"])
-
-AM_CONDITIONAL([BUILD_XCB], [test "x$build_x11" = "xyes"])
-AM_CONDITIONAL([BUILD_WAYLAND], [test "x$build_wayland" = "xyes"])
-
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
--- a/dep/animia/data/ext-foreign-toplevel-list-v1.xml	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,219 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="ext_foreign_toplevel_list_v1">
-  <copyright>
-    Copyright © 2018 Ilia Bozhinov
-    Copyright © 2020 Isaac Freund
-    Copyright © 2022 wb9688
-    Copyright © 2023 i509VCB
-
-    Permission to use, copy, modify, distribute, and sell this
-    software and its documentation for any purpose is hereby granted
-    without fee, provided that the above copyright notice appear in
-    all copies and that both that copyright notice and this permission
-    notice appear in supporting documentation, and that the name of
-    the copyright holders not be used in advertising or publicity
-    pertaining to distribution of the software without specific,
-    written prior permission.  The copyright holders make no
-    representations about the suitability of this software for any
-    purpose.  It is provided "as is" without express or implied
-    warranty.
-
-    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
-    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
-    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-    THIS SOFTWARE.
-  </copyright>
-
-  <description summary="list toplevels">
-    The purpose of this protocol is to provide protocol object handles for
-    toplevels, possibly originating from another client.
-
-    This protocol is intentionally minimalistic and expects additional
-    functionality (e.g. creating a screencopy source from a toplevel handle,
-    getting information about the state of the toplevel) to be implemented
-    in extension protocols.
-
-    The compositor may choose to restrict this protocol to a special client
-    launched by the compositor itself or expose it to all clients,
-    this is compositor policy.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended",  "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-
-    Warning! The protocol described in this file is currently in the testing
-    phase. Backward compatible changes may be added together with the
-    corresponding interface version bump. Backward incompatible changes can
-    only be done by creating a new major version of the extension.
-  </description>
-
-  <interface name="ext_foreign_toplevel_list_v1" version="1">
-    <description summary="list toplevels">
-      A toplevel is defined as a surface with a role similar to xdg_toplevel.
-      XWayland surfaces may be treated like toplevels in this protocol.
-
-      After a client binds the ext_foreign_toplevel_list_v1, each mapped
-      toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
-      event.
-
-      Clients which only care about the current state can perform a roundtrip after
-      binding this global.
-
-      For each instance of ext_foreign_toplevel_list_v1, the compositor must
-      create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
-
-      If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
-      event after the global is bound, the compositor must not send any
-      ext_foreign_toplevel_list_v1.toplevel events.
-    </description>
-
-    <event name="toplevel">
-      <description summary="a toplevel has been created">
-        This event is emitted whenever a new toplevel window is created. It is
-        emitted for all toplevels, regardless of the app that has created them.
-
-        All initial properties of the toplevel (identifier, title, app_id) will be sent
-        immediately after this event using the corresponding events for
-        ext_foreign_toplevel_handle_v1. The compositor will use the
-        ext_foreign_toplevel_handle_v1.done event to indicate when all data has
-        been sent.
-      </description>
-      <arg name="toplevel" type="new_id" interface="ext_foreign_toplevel_handle_v1"/>
-    </event>
-
-    <event name="finished">
-      <description summary="the compositor has finished with the toplevel manager">
-        This event indicates that the compositor is done sending events
-        to this object. The client should should destroy the object.
-        See ext_foreign_toplevel_list_v1.destroy for more information.
-
-        The compositor must not send any more toplevel events after this event.
-      </description>
-    </event>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        This request indicates that the client no longer wishes to receive
-        events for new toplevels.
-
-        The Wayland protocol is asynchronous, meaning the compositor may send
-        further toplevel events until the stop request is processed.
-        The client should wait for a ext_foreign_toplevel_list_v1.finished
-        event before destroying this object.
-      </description>
-    </request>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the ext_foreign_toplevel_list_v1 object">
-        This request should be called either when the client will no longer
-        use the ext_foreign_toplevel_list_v1 or after the finished event
-        has been received to allow destruction of the object.
-
-        If a client wishes to destroy this object it should send a
-        ext_foreign_toplevel_list_v1.stop request and wait for a ext_foreign_toplevel_list_v1.finished
-        event, then destroy the handles and then this object.
-      </description>
-    </request>
-  </interface>
-
-  <interface name="ext_foreign_toplevel_handle_v1" version="1">
-    <description summary="a mapped toplevel">
-      A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
-      window. A single app may have multiple mapped toplevels.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the ext_foreign_toplevel_handle_v1 object">
-        This request should be used when the client will no longer use the handle
-        or after the closed event has been received to allow destruction of the
-        object.
-
-        When a handle is destroyed, a new handle may not be created by the server
-        until the toplevel is unmapped and then remapped. Destroying a toplevel handle
-        is not recommended unless the client is cleaning up child objects
-        before destroying the ext_foreign_toplevel_list_v1 object, the toplevel
-        was closed or the toplevel handle will not be used in the future.
-
-        Other protocols which extend the ext_foreign_toplevel_handle_v1
-        interface should require destructors for extension interfaces be
-        called before allowing the toplevel handle to be destroyed.
-      </description>
-    </request>
-
-    <event name="closed">
-      <description summary="the toplevel has been closed">
-        The server will emit no further events on the ext_foreign_toplevel_handle_v1
-        after this event. Any requests received aside from the destroy request must
-        be ignored. Upon receiving this event, the client should destroy the handle.
-
-        Other protocols which extend the ext_foreign_toplevel_handle_v1
-        interface must also ignore requests other than destructors.
-      </description>
-    </event>
-
-    <event name="done">
-      <description summary="all information about the toplevel has been sent">
-        This event is sent after all changes in the toplevel state have
-        been sent.
-
-        This allows changes to the ext_foreign_toplevel_handle_v1 properties
-        to be atomically applied. Other protocols which extend the
-        ext_foreign_toplevel_handle_v1 interface may use this event to also
-        atomically apply any pending state.
-
-        This event must not be sent after the ext_foreign_toplevel_handle_v1.closed
-        event.
-      </description>
-    </event>
-
-    <event name="title">
-      <description summary="title change">
-        The title of the toplevel has changed.
-
-        The configured state must not be applied immediately. See
-        ext_foreign_toplevel_handle_v1.done for details.
-      </description>
-      <arg name="title" type="string"/>
-    </event>
-
-    <event name="app_id">
-      <description summary="app_id change">
-        The app id of the toplevel has changed.
-
-        The configured state must not be applied immediately. See
-        ext_foreign_toplevel_handle_v1.done for details.
-      </description>
-      <arg name="app_id" type="string"/>
-    </event>
-
-    <event name="identifier">
-      <description summary="a stable identifier for a toplevel">
-        This identifier is used to check if two or more toplevel handles belong
-        to the same toplevel.
-
-        The identifier is useful for command line tools or privileged clients
-        which may need to reference an exact toplevel across processes or
-        instances of the ext_foreign_toplevel_list_v1 global.
-
-        The compositor must only send this event when the handle is created.
-
-        The identifier must be unique per toplevel and it's handles. Two different
-        toplevels must not have the same identifier. The identifier is only valid
-        as long as the toplevel is mapped. If the toplevel is unmapped the identifier
-        must not be reused. An identifier must not be reused by the compositor to
-        ensure there are no races when sharing identifiers between processes.
-
-        An identifier is a string that contains up to 32 printable ASCII bytes.
-        An identifier must not be an empty string. It is recommended that a
-        compositor includes an opaque generation value in identifiers. How the
-        generation value is used when generating the identifier is implementation
-        dependent.
-      </description>
-      <arg name="identifier" type="string"/>
-    </event>
-  </interface>
-</protocol>
--- a/dep/animia/data/players.anisthesia	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,496 +0,0 @@
-# This file includes media player data for Anisthesia. It is used to detect
-# running players and retrieve information about current media.
-#
-# Please read before editing this file:
-# - Indentation is significant. You must use tabs rather than spaces.
-# - Regular expressions begin with a '^' character. ECMAScript grammar is used.
-#
-# The latest version of this file can be found at:
-# <https://github.com/erengy/anisthesia>
-#
-# This file is in the public domain.
-
-5KPlayer
-	windows:
-		Qt5QWindowIcon
-	executables:
-		5KPlayer
-	strategies:
-		open_files
-
-Ace Player HD
-	windows:
-		QWidget
-	executables:
-		ace_player
-	strategies:
-		# Must be enabled from: Advanced Preferences -> Interface -> Main
-		# interfaces -> Qt -> Show playing item name in window title
-		#
-		# We use the last alternative to avoid detecting other windows such as
-		# Preferences dialog, which has the same generic class name.
-		window_title:
-			^Ace Player HD.*|(.+) - Ace Player HD.*|.+
-
-ALLPlayer
-	windows:
-		TApplication
-	executables:
-		ALLPlayer
-	strategies:
-		open_files
-
-Baka MPlayer
-	windows:
-		Qt5QWindowIcon
-	executables:
-		Baka MPlayer
-	strategies:
-		open_files
-		# We cannot avoid detecting other windows such as Preferences dialog, which
-		# has the same generic class name.
-		window_title:
-			^Baka MPlayer|(.+)
-
-BESTplayer
-	windows:
-		TBESTplayerApp.UnicodeClass
-	executables:
-		BESTplayer
-	strategies:
-		open_files
-		window_title:
-			^BESTplayer.*|(.+) - BESTplayer.*
-
-bomi
-	windows:
-		Qt5QWindowGLOwnDCIcon
-	executables:
-		bomi
-	strategies:
-		open_files
-		window_title:
-			^bomi|(.+) - bomi
-
-BS.Player
-	windows:
-		BSPlayer
-	executables:
-		bsplayer
-	strategies:
-		open_files
-
-DivX Player
-	windows:
-		Qt5QWindowIcon
-		QWidget
-	executables:
-		DivX Player
-		DivX Plus Player
-	strategies:
-		open_files
-
-GOM Player
-	windows:
-		GomPlayer1.x
-		GomPlayerPlus32_2.x
-		GomPlayerPlus64_2.x
-	executables:
-		GOM
-		GOM64
-	strategies:
-		open_files
-		window_title:
-			^GOM Player(?: Plus)|(.+)(?:\[Subtitle\]) - GOM Player(?: Plus)
-
-Kantaris
-	windows:
-		^WindowsForms10\.Window\.20008\.app\..+
-	executables:
-		Kantaris
-		KantarisMain
-	strategies:
-		open_files
-		window_title:
-			^Kantaris.*|(.+) \d{2}:\d{2}:\d{2} - \d{2}:\d{2}:\d{2}
-
-KMPlayer
-	windows:
-		KMPlayer 64X
-		TApplication
-	executables:
-		KMPlayer
-		KMPlayer64
-	strategies:
-		open_files
-		window_title:
-			^(?:The )?KMPlayer|(?:\[\d+/\d+\] )?(.+) - (?:The )?KMPlayer|(.+)
-
-Kodi
-	windows:
-		Kodi
-		XBMC
-	executables:
-		kodi
-		XBMC
-	strategies:
-		open_files
-
-Light Alloy
-	windows:
-		TApplication
-	executables:
-		LA
-	strategies:
-		open_files
-		window_title:
-			^Light Alloy.*|(.+) - Light Alloy.*
-
-Media Player Classic
-	windows:
-		MediaPlayerClassicW
-	executables:
-		mplayerc
-		mplayerc64
-	strategies:
-		open_files
-		# Depends on: Options -> Player -> Title bar
-		window_title:
-			^Media Player Classic|(.+) - Media Player Classic
-
-Media Player Classic Qute Theater
-	windows:
-		^Qt.+QWindowIcon
-	executables:
-		mpc-qt
-	strategies:
-		open_files
-		# Depends on: Options -> Player -> Title bar
-		#
-		# We use the last alternative to avoid detecting other windows such as
-		# Options dialog, which has the same generic class name.
-		window_title:
-			^Media Player Classic Qute Theater|Media Player Classic Qute Theater - (.+)|.+
-
-Memento
-	windows:
-		^Qt.+QWindowIcon
-	executables:
-		memento
-	strategies:
-		open_files
-		window_title:
-			^Memento|(.+) - Memento
-
-Miro
-	windows:
-		gdkWindowToplevel
-	executables:
-		Miro
-	strategies:
-		open_files
-
-MPC-BE
-	windows:
-		MediaPlayerClassicW
-		MPC-BE
-	executables:
-		mpc-be
-		mpc-be64
-	strategies:
-		open_files
-		# Depends on: Options -> Player -> Title bar
-		window_title:
-			^MPC-BE.*|(.+) - MPC-BE.*
-
-MPC-HC
-	windows:
-		MediaPlayerClassicW
-	executables:
-		mpc-hc
-		mpc-hc64
-		# Some codec installers append "_nvo" to the filename, if NVIDIA Optimus
-		# is present on the system. Similarly, various guides recommend
-		# appending "-gpu", etc. in order to fix some GPU-related issues.
-		^mpc-hc.+
-		# LAV Filters Megamix
-		iris
-		shoukaku
-	strategies:
-		open_files
-		# Depends on: Options -> Player -> Title bar
-		window_title:
-			^Media Player Classic Home Cinema|MPC-HC|(.+)
-
-MPCSTAR
-	windows:
-		^wxWindow@.*
-		wxWindowClassNR
-	executables:
-		mpcstar
-	strategies:
-		open_files
-		window_title:
-			^MPCSTAR.*|(.+) - MPCSTAR.*
-
-MPDN
-	windows:
-		^WindowsForms10\.Window\.8\.app\..+
-	executables:
-		MediaPlayerDotNet
-	strategies:
-		open_files
-		window_title:
-			^MPDN - Media Player .NET \((?:32|64)-bit Edition\)|(.*) - MPDN \((?:32|64)-bit Edition\)
-
-mpv
-	windows:
-		mpv
-	executables:
-		mpv
-	strategies:
-		open_files
-		# May be in an unexpected format if "--title" option is used. Ideally, it
-		# should return only "${filename}", "${path}" or "${media-title}".
-		window_title:
-			^No file - mpv|(.+) - mpv|mpv - (.+)
-
-mpv.net
-	windows:
-		^WindowsForms10\.Window\.8\.app\..+
-	executables:
-		mpvnet
-	strategies:
-		open_files
-		window_title:
-			^mpv\.net.*|(.+) - mpv\.net.*
-
-MV2Player
-	windows:
-		TApplication
-	executables:
-		Mv2Player
-		Mv2PlayerPlus
-	strategies:
-		open_files
-		# Depends on: Options -> Player -> Constant app. title
-		window_title:
-			^MV2 Player|(.+)
-
-PotPlayer
-	windows:
-		PotPlayer
-		PotPlayer64
-	executables:
-		PotPlayer
-		PotPlayer64
-		PotPlayerMini
-		PotPlayerMini64
-		# LAV Filters Megamix
-		sumire
-		zuikaku
-	strategies:
-		open_files
-		window_title:
-			^PotPlayer|(.+) - PotPlayer
-
-SMPlayer
-	windows:
-		# Qt5QWindowIcon, Qt5152QWindowIcon, etc.
-		^Qt.+QWindowIcon
-		# Older versions
-		QWidget
-	executables:
-		smplayer
-		smplayer2
-	strategies:
-		# "open_files" strategy does not work here, because files are loaded by
-		# a child process of SMPlayer (mplayer or mpv, depending on the selected
-		# multimedia engine).
-		#
-		# We use the last alternative to avoid detecting other windows such as
-		# Preferences dialog, which has the same generic class name.
-		window_title:
-			^SMPlayer|(.+) - SMPlayer|.+
-
-Splash
-	windows:
-		DX_DISPLAY0
-	executables:
-		Splash
-		SplashLite
-	strategies:
-		open_files
-
-SPlayer
-	windows:
-		MediaPlayerClassicW
-	executables:
-		splayer
-	strategies:
-		open_files
-		# Does not work in theater mode.
-		window_title:
-			^SPlayer|(?:\[(?:GPU Accel\+)?EVR\] )?(.+) - SPlayer
-
-UMPlayer
-	windows:
-		QWidget
-	executables:
-		umplayer
-	strategies:
-		# "open_files" strategy does not work here, because files are loaded by
-		# a child process of UMPlayer (mplayer).
-		#
-		# We use the last alternative to avoid detecting other windows such as
-		# Preferences dialog, which has the same generic class name.
-		window_title:
-			^UMPlayer|(.+) - UMPlayer|.+
-
-VLC media player
-	windows:
-		# Qt5QWindowIcon, Qt5151QWindowIcon, etc.
-		^Qt.+QWindowIcon
-		# Older versions
-		QWidget
-		# Skinnable interface
-		SkinWindowClass
-		# X11
-		vlc
-	executables:
-		vlc
-	strategies:
-		open_files
-		# Must be enabled from: Advanced Preferences -> Interface -> Main
-		# interfaces -> Qt -> Show playing item name in window title
-		#
-		# We use the last alternative to avoid detecting other windows such as
-		# Preferences dialog, which has the same generic class name.
-		window_title:
-			^VLC media player|(.+) - VLC media player|.+
-
-WebTorrent Desktop
-	windows:
-		Chrome_WidgetWin_1
-	executables:
-		WebTorrent
-	strategies:
-		window_title:
-			^WebTorrent(?: \(BETA\))?|Main Window|Preferences|About WebTorrent.*|(.+)
-
-Winamp
-	windows:
-		Winamp v1.x
-	executables:
-		winamp
-	strategies:
-		open_files
-		window_title:
-			^Winamp [\d.]+ Build \d+|\d+\. (.+) - Winamp(?: \[.+\])?
-
-Windows Media Player
-	windows:
-		WMPlayerApp
-		WMP Skin Host
-	executables:
-		wmplayer
-	strategies:
-		open_files
-
-Zoom Player
-	windows:
-		TApplication
-	executables:
-		zplayer
-	strategies:
-		open_files
-		window_title:
-			^Zoom Player|(.+) - Zoom Player (?:FREE|MAX)
-
-################################################################################
-# Web browsers
-
-Brave Browser
-	windows:
-		Chrome_WidgetWin_1
-	executables:
-		brave
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) \(Private\)(?: - Brave)?|(.+) - Brave|(.+)
-	type:
-		web_browser
-
-Google Chrome
-	windows:
-		Chrome_WidgetWin_1
-	executables:
-		chrome
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) \(Incognito\)(?: - Google Chrome)?|(.+) - Google Chrome|(.+)
-	type:
-		web_browser
-
-Internet Explorer
-	windows:
-		IEFrame
-	executables:
-		iexplore
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) - Internet Explorer(?: - \[InPrivate\])?
-	type:
-		web_browser
-
-Microsoft Edge
-	windows:
-		Chrome_WidgetWin_1
-	executables:
-		msedge
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) and \d+ more pages? - .+|(.+) - [^-]+ - Microsoft.*Edge|(.+)
-	type:
-		web_browser
-
-Mozilla Firefox
-	windows:
-		MozillaUIWindowClass
-		MozillaWindowClass
-	executables:
-		firefox
-	strategies:
-		ui_automation
-		window_title:
-			^(?:Mozilla Firefox|Firefox Developer Edition)|(.+) (?:-|—) (?:Mozilla Firefox|Firefox Developer Edition)(?: \(Private Browsing\))?
-	type:
-		web_browser
-
-Opera
-	windows:
-		Chrome_WidgetWin_1
-	executables:
-		opera
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) - Opera(?: \(Private\))?
-	type:
-		web_browser
-
-Waterfox
-	windows:
-		MozillaWindowClass
-	executables:
-		waterfox
-	strategies:
-		ui_automation
-		window_title:
-			^(.+) - Waterfox(?: \(Private Browsing\))?
-	type:
-		web_browser
--- a/dep/animia/data/wlr-foreign-toplevel-management-unstable-v1.xml	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="wlr_foreign_toplevel_management_unstable_v1">
-  <copyright>
-    Copyright © 2018 Ilia Bozhinov
-
-    Permission to use, copy, modify, distribute, and sell this
-    software and its documentation for any purpose is hereby granted
-    without fee, provided that the above copyright notice appear in
-    all copies and that both that copyright notice and this permission
-    notice appear in supporting documentation, and that the name of
-    the copyright holders not be used in advertising or publicity
-    pertaining to distribution of the software without specific,
-    written prior permission.  The copyright holders make no
-    representations about the suitability of this software for any
-    purpose.  It is provided "as is" without express or implied
-    warranty.
-
-    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
-    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
-    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-    THIS SOFTWARE.
-  </copyright>
-
-  <interface name="zwlr_foreign_toplevel_manager_v1" version="3">
-    <description summary="list and control opened apps">
-      The purpose of this protocol is to enable the creation of taskbars
-      and docks by providing them with a list of opened applications and
-      letting them request certain actions on them, like maximizing, etc.
-
-      After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
-      toplevel window will be sent via the toplevel event
-    </description>
-
-    <event name="toplevel">
-      <description summary="a toplevel has been created">
-        This event is emitted whenever a new toplevel window is created. It
-        is emitted for all toplevels, regardless of the app that has created
-        them.
-
-        All initial details of the toplevel(title, app_id, states, etc.) will
-        be sent immediately after this event via the corresponding events in
-        zwlr_foreign_toplevel_handle_v1.
-      </description>
-      <arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
-    </event>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        Indicates the client no longer wishes to receive events for new toplevels.
-        However the compositor may emit further toplevel_created events, until
-        the finished event is emitted.
-
-        The client must not send any more requests after this one.
-      </description>
-    </request>
-
-    <event name="finished" type="destructor">
-      <description summary="the compositor has finished with the toplevel manager">
-        This event indicates that the compositor is done sending events to the
-        zwlr_foreign_toplevel_manager_v1. The server will destroy the object
-        immediately after sending this request, so it will become invalid and
-        the client should free any resources associated with it.
-      </description>
-    </event>
-  </interface>
-
-  <interface name="zwlr_foreign_toplevel_handle_v1" version="3">
-    <description summary="an opened toplevel">
-      A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
-      window. Each app may have multiple opened toplevels.
-
-      Each toplevel has a list of outputs it is visible on, conveyed to the
-      client with the output_enter and output_leave events.
-    </description>
-
-    <event name="title">
-      <description summary="title change">
-        This event is emitted whenever the title of the toplevel changes.
-      </description>
-      <arg name="title" type="string"/>
-    </event>
-
-    <event name="app_id">
-      <description summary="app-id change">
-        This event is emitted whenever the app-id of the toplevel changes.
-      </description>
-      <arg name="app_id" type="string"/>
-    </event>
-
-    <event name="output_enter">
-      <description summary="toplevel entered an output">
-        This event is emitted whenever the toplevel becomes visible on
-        the given output. A toplevel may be visible on multiple outputs.
-      </description>
-      <arg name="output" type="object" interface="wl_output"/>
-    </event>
-
-    <event name="output_leave">
-      <description summary="toplevel left an output">
-        This event is emitted whenever the toplevel stops being visible on
-        the given output. It is guaranteed that an entered-output event
-        with the same output has been emitted before this event.
-      </description>
-      <arg name="output" type="object" interface="wl_output"/>
-    </event>
-
-    <request name="set_maximized">
-      <description summary="requests that the toplevel be maximized">
-        Requests that the toplevel be maximized. If the maximized state actually
-        changes, this will be indicated by the state event.
-      </description>
-    </request>
-
-    <request name="unset_maximized">
-      <description summary="requests that the toplevel be unmaximized">
-        Requests that the toplevel be unmaximized. If the maximized state actually
-        changes, this will be indicated by the state event.
-      </description>
-    </request>
-
-    <request name="set_minimized">
-      <description summary="requests that the toplevel be minimized">
-        Requests that the toplevel be minimized. If the minimized state actually
-        changes, this will be indicated by the state event.
-      </description>
-    </request>
-
-    <request name="unset_minimized">
-      <description summary="requests that the toplevel be unminimized">
-        Requests that the toplevel be unminimized. If the minimized state actually
-        changes, this will be indicated by the state event.
-      </description>
-    </request>
-
-    <request name="activate">
-      <description summary="activate the toplevel">
-        Request that this toplevel be activated on the given seat.
-        There is no guarantee the toplevel will be actually activated.
-      </description>
-      <arg name="seat" type="object" interface="wl_seat"/>
-    </request>
-
-    <enum name="state">
-      <description summary="types of states on the toplevel">
-        The different states that a toplevel can have. These have the same meaning
-        as the states with the same names defined in xdg-toplevel
-      </description>
-
-      <entry name="maximized"  value="0" summary="the toplevel is maximized"/>
-      <entry name="minimized"  value="1" summary="the toplevel is minimized"/>
-      <entry name="activated"  value="2" summary="the toplevel is active"/>
-      <entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
-    </enum>
-
-    <event name="state">
-      <description summary="the toplevel state changed">
-        This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
-        is created and each time the toplevel state changes, either because of a
-        compositor action or because of a request in this protocol.
-      </description>
-
-      <arg name="state" type="array"/>
-    </event>
-
-    <event name="done">
-      <description summary="all information about the toplevel has been sent">
-        This event is sent after all changes in the toplevel state have been
-        sent.
-
-        This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
-        to be seen as atomic, even if they happen via multiple events.
-      </description>
-    </event>
-
-    <request name="close">
-      <description summary="request that the toplevel be closed">
-        Send a request to the toplevel to close itself. The compositor would
-        typically use a shell-specific method to carry out this request, for
-        example by sending the xdg_toplevel.close event. However, this gives
-        no guarantees the toplevel will actually be destroyed. If and when
-        this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
-        be emitted.
-      </description>
-    </request>
-
-    <request name="set_rectangle">
-      <description summary="the rectangle which represents the toplevel">
-        The rectangle of the surface specified in this request corresponds to
-        the place where the app using this protocol represents the given toplevel.
-        It can be used by the compositor as a hint for some operations, e.g
-        minimizing. The client is however not required to set this, in which
-        case the compositor is free to decide some default value.
-
-        If the client specifies more than one rectangle, only the last one is
-        considered.
-
-        The dimensions are given in surface-local coordinates.
-        Setting width=height=0 removes the already-set rectangle.
-      </description>
-
-      <arg name="surface" type="object" interface="wl_surface"/>
-      <arg name="x" type="int"/>
-      <arg name="y" type="int"/>
-      <arg name="width" type="int"/>
-      <arg name="height" type="int"/>
-    </request>
-
-    <enum name="error">
-      <entry name="invalid_rectangle" value="0"
-        summary="the provided rectangle is invalid"/>
-    </enum>
-
-    <event name="closed">
-      <description summary="this toplevel has been destroyed">
-        This event means the toplevel has been destroyed. It is guaranteed there
-        won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
-        toplevel itself becomes inert so any requests will be ignored except the
-        destroy request.
-      </description>
-    </event>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
-        Destroys the zwlr_foreign_toplevel_handle_v1 object.
-
-        This request should be called either when the client does not want to
-        use the toplevel anymore or after the closed event to finalize the
-        destruction of the object.
-      </description>
-    </request>
-
-    <!-- Version 2 additions -->
-
-    <request name="set_fullscreen" since="2">
-      <description summary="request that the toplevel be fullscreened">
-        Requests that the toplevel be fullscreened on the given output. If the
-        fullscreen state and/or the outputs the toplevel is visible on actually
-        change, this will be indicated by the state and output_enter/leave
-        events.
-
-        The output parameter is only a hint to the compositor. Also, if output
-        is NULL, the compositor should decide which output the toplevel will be
-        fullscreened on, if at all.
-      </description>
-      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
-    </request>
-
-    <request name="unset_fullscreen" since="2">
-      <description summary="request that the toplevel be unfullscreened">
-        Requests that the toplevel be unfullscreened. If the fullscreen state
-        actually changes, this will be indicated by the state event.
-      </description>
-    </request>
-
-    <!-- Version 3 additions -->
-
-    <event name="parent" since="3">
-      <description summary="parent change">
-        This event is emitted whenever the parent of the toplevel changes.
-
-        No event is emitted when the parent handle is destroyed by the client.
-      </description>
-      <arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
-    </event>
-  </interface>
-</protocol>
--- a/dep/animia/include/animia.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-#ifndef __animia__animia_h
-#define __animia__animia_h
-
-#include "animia/media.h"
-#include "animia/player.h"
-#include "animia/types.h"
-
-namespace animia {
-
-enum class ResultType {
-	Process,
-	Window
-};
-
-struct Process {
-		internal::pid_t pid = 0;
-		std::string name;
-};
-
-struct Window {
-		unsigned int id = 0;
-		std::string class_name;
-		std::string text; // title bar text
-};
-
-struct Result {
-		ResultType type;
-		Player player;
-		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
-
-#endif // __animia__animia_h
--- a/dep/animia/include/animia/fd.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#ifndef __animia__animia__fd_h
-#define __animia__animia__fd_h
-
-#include <functional>
-#include <set>
-#include <string>
-
-#include "animia/types.h"
-
-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&)>;
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace internal
-
-} // namespace animia
-
-#endif // __animia__animia__fd_h
--- a/dep/animia/include/animia/fd/kvm.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#ifndef __animia__animia__fd__kvm_h
-#define __animia__animia__fd__kvm_h
-
-#include <set>
-#include <string>
-
-#include "animia/fd.h"
-#include "animia/types.h"
-
-namespace animia::internal::kvm {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animia::internal::kvm
-
-#endif // __animia__animia__fd__kvm_h
--- a/dep/animia/include/animia/fd/libutil.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#ifndef __animia__animia__fd__libutil_h
-#define __animia__animia__fd__libutil_h
-
-#include <set>
-#include <string>
-
-#include "animia/fd.h"
-#include "animia/types.h"
-
-namespace animia::internal::libutil {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animia::internal::libutil
-
-#endif // __animia__animia__fd__libutil_h
--- a/dep/animia/include/animia/fd/proc.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#ifndef __animia__animia__fd__proc_h
-#define __animia__animia__fd__proc_h
-
-#include <set>
-#include <string>
-
-#include "animia/fd.h"
-#include "animia/types.h"
-
-namespace animia::internal::proc {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animia::internal::proc
-
-#endif // __animia__animia__fd__proc_h
--- a/dep/animia/include/animia/fd/win32.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#ifndef __animia__animia__fd__win32_h
-#define __animia__animia__fd__win32_h
-
-#include <set>
-#include <string>
-
-#include <windows.h>
-
-#include "animia/fd.h"
-#include "animia/types.h"
-
-namespace animia::internal::win32 {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animia::internal::win32
-
-#endif // __animia__animia__fd__win32_h
--- a/dep/animia/include/animia/fd/xnu.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#ifndef __animia__animia__fd__xnu_h
-#define __animia__animia__fd__xnu_h
-
-#include <set>
-#include <string>
-
-#include "animia/fd.h"
-#include "animia/types.h"
-
-namespace animia::internal::xnu {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animia::internal::xnu
-
-#endif // __animia__animia__fd__xnu_h
--- a/dep/animia/include/animia/media.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#ifndef __animia__animia__media_h
-#define __animia__animia__media_h
-
-#include <chrono>
-#include <functional>
-#include <string>
-#include <vector>
-
-namespace animia {
-
-using media_time_t = std::chrono::milliseconds;
-
-enum class MediaInfoType {
-	Unknown,
-	File,
-	Tab,
-	Title,
-	Url
-};
-
-struct MediaInfo {
-		MediaInfoType type = MediaInfoType::Unknown;
-		std::string value;
-};
-
-struct Media {
-		std::vector<MediaInfo> information;
-};
-
-} // namespace animia
-
-#endif // __animia__animia__media_h
--- a/dep/animia/include/animia/player.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#ifndef __animia__animia__player_h
-#define __animia__animia__player_h
-
-#include <string>
-#include <vector>
-
-namespace animia {
-
-enum class Strategy {
-	WindowTitle,
-	OpenFiles,
-	UiAutomation // unused
-};
-
-enum class PlayerType {
-	Default,
-	WebBrowser // unused
-};
-
-struct Player {
-		PlayerType type = PlayerType::Default;
-		std::string name;
-		std::string window_title_format;
-		std::vector<std::string> windows;
-		std::vector<std::string> executables;
-		std::vector<Strategy> strategies;
-};
-
-bool ParsePlayersData(const std::string& data, std::vector<Player>& players);
-bool ParsePlayersFile(const std::string& path, std::vector<Player>& players);
-
-} // namespace animia
-
-#endif // __animia__animia__player_h
\ No newline at end of file
--- a/dep/animia/include/animia/strategies.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef __animia__animia__strategies_h
-#define __animia__animia__strategies_h
-
-#include "animia.h"
-#include <vector>
-
-namespace animia::internal {
-
-bool ApplyStrategies(std::vector<Result>& results);
-
-}
-
-#endif // __animia__animia__strategies_h
\ No newline at end of file
--- a/dep/animia/include/animia/types.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#ifndef __animia__animia__types_h
-#define __animia__animia__types_h
-
-/* define this as unsigned long (DWORD) on win32 so we
-   don't force the user to include <windows.h> or <IntBase.h> */
-#ifdef _WIN32
-namespace animia::internal {
-typedef unsigned long pid_t;
-}
-#else
-/* <sys/types.h> shouldn't be that big, right? */
-#	include <sys/types.h>
-namespace animia::internal {
-typedef ::pid_t pid_t;
-}
-#endif
-
-#endif // __animia__animia__types_h
--- a/dep/animia/include/animia/util.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-#ifndef __animia__animia__util_h
-#define __animia__animia__util_h
-
-#include <string>
-#include <sstream>
-
-namespace animia::internal::util {
-
-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);
-
-template<typename T = int,
-         std::enable_if_t<std::is_integral<T>::value, bool> = true>
-T StringToInt(const std::string& str, T def = 0) {
-	std::istringstream s(str);
-	s >> std::noboolalpha >> def;
-	return def;
-}
-
-} // namespace animia::internal::util
-
-#endif // __animia__animia__util_h
--- a/dep/animia/include/animia/util/osx.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#ifndef __animia__animia__util__osx_h
-#define __animia__animia__util__osx_h
-
-#include "animia/types.h"
-#include <string>
-#include <cstdint>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-namespace animia::internal::osx::util {
-
-template<typename T>
-bool GetCFNumber(CFNumberRef num, T& result) {
-	if (!num)
-		return false;
-
-	int64_t res;
-	if (!CFNumberGetValue(num, static_cast<CFNumberType>(4), &res))
-		return false;
-
-	result = static_cast<T>(res);
-	return true;
-}
-
-bool StringFromCFString(CFStringRef string, std::string& result);
-
-bool GetProcessName(pid_t pid, std::string& result);
-
-}
-
-#endif // __animia__animia__util__osx_h
--- a/dep/animia/include/animia/util/win32.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#ifndef __animia__animia__util__win32_h
-#define __animia__animia__util__win32_h
-
-#include <windows.h>
-#include <subauth.h>
-
-#include <memory>
-#include <string>
-
-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);
-
-} // namespace animia::internal::win32
-
-#endif // __animia__animia__util__win32_h
\ No newline at end of file
--- a/dep/animia/include/animia/win.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#ifndef __animia__animia__win_h
-#define __animia__animia__win_h
-
-#include <functional>
-#include <string>
-
-namespace animia {
-
-struct Process;
-struct Window;
-
-namespace internal {
-
-using window_proc_t = std::function<bool(const Process&, const Window&)>;
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace internal
-
-} // namespace animia
-
-#endif // __animia__animia__win_h
--- a/dep/animia/include/animia/win/quartz.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#ifndef __animia__animia__win__quartz_h
-#define __animia__animia__win__quartz_h
-
-#include "animia/win.h"
-
-namespace animia::internal::quartz {
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace animia::internal::quartz
-
-#endif // __animia__animia__win__quartz_h
--- a/dep/animia/include/animia/win/wayland.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#ifndef __animia__animia__win__wayland_h
-#define __animia__animia__win__wayland_h
-
-#include "animia/win.h"
-
-namespace animia::internal::wayland {
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace animia::internal::wayland
-
-#endif // __animia__animia__win__wayland_h
--- a/dep/animia/include/animia/win/wayland/ext-foreign-toplevel-list-v1.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,443 +0,0 @@
-/* Generated by wayland-scanner 1.21.0 */
-
-#ifndef EXT_FOREIGN_TOPLEVEL_LIST_V1_CLIENT_PROTOCOL_H
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_CLIENT_PROTOCOL_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include "wayland-client.h"
-
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
-/**
- * @page page_ext_foreign_toplevel_list_v1 The ext_foreign_toplevel_list_v1 protocol
- * list toplevels
- *
- * @section page_desc_ext_foreign_toplevel_list_v1 Description
- *
- * The purpose of this protocol is to provide protocol object handles for
- * toplevels, possibly originating from another client.
- *
- * This protocol is intentionally minimalistic and expects additional
- * functionality (e.g. creating a screencopy source from a toplevel handle,
- * getting information about the state of the toplevel) to be implemented
- * in extension protocols.
- *
- * The compositor may choose to restrict this protocol to a special client
- * launched by the compositor itself or expose it to all clients,
- * this is compositor policy.
- *
- * The key words "must", "must not", "required", "shall", "shall not",
- * "should", "should not", "recommended",  "may", and "optional" in this
- * document are to be interpreted as described in IETF RFC 2119.
- *
- * Warning! The protocol described in this file is currently in the testing
- * phase. Backward compatible changes may be added together with the
- * corresponding interface version bump. Backward incompatible changes can
- * only be done by creating a new major version of the extension.
- *
- * @section page_ifaces_ext_foreign_toplevel_list_v1 Interfaces
- * - @subpage page_iface_ext_foreign_toplevel_list_v1 - list toplevels
- * - @subpage page_iface_ext_foreign_toplevel_handle_v1 - a mapped toplevel
- * @section page_copyright_ext_foreign_toplevel_list_v1 Copyright
- * <pre>
- *
- * Copyright © 2018 Ilia Bozhinov
- * Copyright © 2020 Isaac Freund
- * Copyright © 2022 wb9688
- * Copyright © 2023 i509VCB
- *
- * Permission to use, copy, modify, distribute, and sell this
- * software and its documentation for any purpose is hereby granted
- * without fee, provided that the above copyright notice appear in
- * all copies and that both that copyright notice and this permission
- * notice appear in supporting documentation, and that the name of
- * the copyright holders not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no
- * representations about the suitability of this software for any
- * purpose.  It is provided "as is" without express or implied
- * warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- * </pre>
- */
-struct ext_foreign_toplevel_handle_v1;
-struct ext_foreign_toplevel_list_v1;
-
-#ifndef EXT_FOREIGN_TOPLEVEL_LIST_V1_INTERFACE
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_INTERFACE
-/**
- * @page page_iface_ext_foreign_toplevel_list_v1 ext_foreign_toplevel_list_v1
- * @section page_iface_ext_foreign_toplevel_list_v1_desc Description
- *
- * A toplevel is defined as a surface with a role similar to xdg_toplevel.
- * XWayland surfaces may be treated like toplevels in this protocol.
- *
- * After a client binds the ext_foreign_toplevel_list_v1, each mapped
- * toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
- * event.
- *
- * Clients which only care about the current state can perform a roundtrip after
- * binding this global.
- *
- * For each instance of ext_foreign_toplevel_list_v1, the compositor must
- * create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
- *
- * If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
- * event after the global is bound, the compositor must not send any
- * ext_foreign_toplevel_list_v1.toplevel events.
- * @section page_iface_ext_foreign_toplevel_list_v1_api API
- * See @ref iface_ext_foreign_toplevel_list_v1.
- */
-/**
- * @defgroup iface_ext_foreign_toplevel_list_v1 The ext_foreign_toplevel_list_v1 interface
- *
- * A toplevel is defined as a surface with a role similar to xdg_toplevel.
- * XWayland surfaces may be treated like toplevels in this protocol.
- *
- * After a client binds the ext_foreign_toplevel_list_v1, each mapped
- * toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
- * event.
- *
- * Clients which only care about the current state can perform a roundtrip after
- * binding this global.
- *
- * For each instance of ext_foreign_toplevel_list_v1, the compositor must
- * create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
- *
- * If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
- * event after the global is bound, the compositor must not send any
- * ext_foreign_toplevel_list_v1.toplevel events.
- */
-extern const struct wl_interface ext_foreign_toplevel_list_v1_interface;
-#endif
-#ifndef EXT_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE
-/**
- * @page page_iface_ext_foreign_toplevel_handle_v1 ext_foreign_toplevel_handle_v1
- * @section page_iface_ext_foreign_toplevel_handle_v1_desc Description
- *
- * A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
- * window. A single app may have multiple mapped toplevels.
- * @section page_iface_ext_foreign_toplevel_handle_v1_api API
- * See @ref iface_ext_foreign_toplevel_handle_v1.
- */
-/**
- * @defgroup iface_ext_foreign_toplevel_handle_v1 The ext_foreign_toplevel_handle_v1 interface
- *
- * A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
- * window. A single app may have multiple mapped toplevels.
- */
-extern const struct wl_interface ext_foreign_toplevel_handle_v1_interface;
-#endif
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- * @struct ext_foreign_toplevel_list_v1_listener
- */
-struct ext_foreign_toplevel_list_v1_listener {
-	/**
-	 * a toplevel has been created
-	 *
-	 * This event is emitted whenever a new toplevel window is
-	 * created. It is emitted for all toplevels, regardless of the app
-	 * that has created them.
-	 *
-	 * All initial properties of the toplevel (identifier, title,
-	 * app_id) will be sent immediately after this event using the
-	 * corresponding events for ext_foreign_toplevel_handle_v1. The
-	 * compositor will use the ext_foreign_toplevel_handle_v1.done
-	 * event to indicate when all data has been sent.
-	 */
-	void (*toplevel)(void *data,
-			 struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1,
-			 struct ext_foreign_toplevel_handle_v1 *toplevel);
-	/**
-	 * the compositor has finished with the toplevel manager
-	 *
-	 * This event indicates that the compositor is done sending
-	 * events to this object. The client should should destroy the
-	 * object. See ext_foreign_toplevel_list_v1.destroy for more
-	 * information.
-	 *
-	 * The compositor must not send any more toplevel events after this
-	 * event.
-	 */
-	void (*finished)(void *data,
-			 struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1);
-};
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- */
-static inline int
-ext_foreign_toplevel_list_v1_add_listener(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1,
-					  const struct ext_foreign_toplevel_list_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) ext_foreign_toplevel_list_v1,
-				     (void (**)(void)) listener, data);
-}
-
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP 0
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY 1
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_TOPLEVEL_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_FINISHED_SINCE_VERSION 1
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY_SINCE_VERSION 1
-
-/** @ingroup iface_ext_foreign_toplevel_list_v1 */
-static inline void
-ext_foreign_toplevel_list_v1_set_user_data(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) ext_foreign_toplevel_list_v1, user_data);
-}
-
-/** @ingroup iface_ext_foreign_toplevel_list_v1 */
-static inline void *
-ext_foreign_toplevel_list_v1_get_user_data(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) ext_foreign_toplevel_list_v1);
-}
-
-static inline uint32_t
-ext_foreign_toplevel_list_v1_get_version(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1);
-}
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- *
- * This request indicates that the client no longer wishes to receive
- * events for new toplevels.
- *
- * The Wayland protocol is asynchronous, meaning the compositor may send
- * further toplevel events until the stop request is processed.
- * The client should wait for a ext_foreign_toplevel_list_v1.finished
- * event before destroying this object.
- */
-static inline void
-ext_foreign_toplevel_list_v1_stop(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_list_v1,
-			 EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1), 0);
-}
-
-/**
- * @ingroup iface_ext_foreign_toplevel_list_v1
- *
- * This request should be called either when the client will no longer
- * use the ext_foreign_toplevel_list_v1 or after the finished event
- * has been received to allow destruction of the object.
- *
- * If a client wishes to destroy this object it should send a
- * ext_foreign_toplevel_list_v1.stop request and wait for a ext_foreign_toplevel_list_v1.finished
- * event, then destroy the handles and then this object.
- */
-static inline void
-ext_foreign_toplevel_list_v1_destroy(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_list_v1,
-			 EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1), WL_MARSHAL_FLAG_DESTROY);
-}
-
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- * @struct ext_foreign_toplevel_handle_v1_listener
- */
-struct ext_foreign_toplevel_handle_v1_listener {
-	/**
-	 * the toplevel has been closed
-	 *
-	 * The server will emit no further events on the
-	 * ext_foreign_toplevel_handle_v1 after this event. Any requests
-	 * received aside from the destroy request must be ignored. Upon
-	 * receiving this event, the client should destroy the handle.
-	 *
-	 * Other protocols which extend the ext_foreign_toplevel_handle_v1
-	 * interface must also ignore requests other than destructors.
-	 */
-	void (*closed)(void *data,
-		       struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1);
-	/**
-	 * all information about the toplevel has been sent
-	 *
-	 * This event is sent after all changes in the toplevel state
-	 * have been sent.
-	 *
-	 * This allows changes to the ext_foreign_toplevel_handle_v1
-	 * properties to be atomically applied. Other protocols which
-	 * extend the ext_foreign_toplevel_handle_v1 interface may use this
-	 * event to also atomically apply any pending state.
-	 *
-	 * This event must not be sent after the
-	 * ext_foreign_toplevel_handle_v1.closed event.
-	 */
-	void (*done)(void *data,
-		     struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1);
-	/**
-	 * title change
-	 *
-	 * The title of the toplevel has changed.
-	 *
-	 * The configured state must not be applied immediately. See
-	 * ext_foreign_toplevel_handle_v1.done for details.
-	 */
-	void (*title)(void *data,
-		      struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
-		      const char *title);
-	/**
-	 * app_id change
-	 *
-	 * The app id of the toplevel has changed.
-	 *
-	 * The configured state must not be applied immediately. See
-	 * ext_foreign_toplevel_handle_v1.done for details.
-	 */
-	void (*app_id)(void *data,
-		       struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
-		       const char *app_id);
-	/**
-	 * a stable identifier for a toplevel
-	 *
-	 * This identifier is used to check if two or more toplevel
-	 * handles belong to the same toplevel.
-	 *
-	 * The identifier is useful for command line tools or privileged
-	 * clients which may need to reference an exact toplevel across
-	 * processes or instances of the ext_foreign_toplevel_list_v1
-	 * global.
-	 *
-	 * The compositor must only send this event when the handle is
-	 * created.
-	 *
-	 * The identifier must be unique per toplevel and it's handles. Two
-	 * different toplevels must not have the same identifier. The
-	 * identifier is only valid as long as the toplevel is mapped. If
-	 * the toplevel is unmapped the identifier must not be reused. An
-	 * identifier must not be reused by the compositor to ensure there
-	 * are no races when sharing identifiers between processes.
-	 *
-	 * An identifier is a string that contains up to 32 printable ASCII
-	 * bytes. An identifier must not be an empty string. It is
-	 * recommended that a compositor includes an opaque generation
-	 * value in identifiers. How the generation value is used when
-	 * generating the identifier is implementation dependent.
-	 */
-	void (*identifier)(void *data,
-			   struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
-			   const char *identifier);
-};
-
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-static inline int
-ext_foreign_toplevel_handle_v1_add_listener(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
-					    const struct ext_foreign_toplevel_handle_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) ext_foreign_toplevel_handle_v1,
-				     (void (**)(void)) listener, data);
-}
-
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 0
-
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_IDENTIFIER_SINCE_VERSION 1
-
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- */
-#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1
-
-/** @ingroup iface_ext_foreign_toplevel_handle_v1 */
-static inline void
-ext_foreign_toplevel_handle_v1_set_user_data(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) ext_foreign_toplevel_handle_v1, user_data);
-}
-
-/** @ingroup iface_ext_foreign_toplevel_handle_v1 */
-static inline void *
-ext_foreign_toplevel_handle_v1_get_user_data(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) ext_foreign_toplevel_handle_v1);
-}
-
-static inline uint32_t
-ext_foreign_toplevel_handle_v1_get_version(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_handle_v1);
-}
-
-/**
- * @ingroup iface_ext_foreign_toplevel_handle_v1
- *
- * This request should be used when the client will no longer use the handle
- * or after the closed event has been received to allow destruction of the
- * object.
- *
- * When a handle is destroyed, a new handle may not be created by the server
- * until the toplevel is unmapped and then remapped. Destroying a toplevel handle
- * is not recommended unless the client is cleaning up child objects
- * before destroying the ext_foreign_toplevel_list_v1 object, the toplevel
- * was closed or the toplevel handle will not be used in the future.
- *
- * Other protocols which extend the ext_foreign_toplevel_handle_v1
- * interface should require destructors for extension interfaces be
- * called before allowing the toplevel handle to be destroyed.
- */
-static inline void
-ext_foreign_toplevel_handle_v1_destroy(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_handle_v1,
-			 EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_handle_v1), WL_MARSHAL_FLAG_DESTROY);
-}
-
-#ifdef  __cplusplus
-}
-#endif
-
-#endif
--- a/dep/animia/include/animia/win/wayland/wlr-foreign-toplevel-management-unstable-v1.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,615 +0,0 @@
-/* Generated by wayland-scanner 1.21.0 */
-
-#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
-#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include "wayland-client.h"
-
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
-/**
- * @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol
- * @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces
- * - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps
- * - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel
- * @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright
- * <pre>
- *
- * Copyright © 2018 Ilia Bozhinov
- *
- * Permission to use, copy, modify, distribute, and sell this
- * software and its documentation for any purpose is hereby granted
- * without fee, provided that the above copyright notice appear in
- * all copies and that both that copyright notice and this permission
- * notice appear in supporting documentation, and that the name of
- * the copyright holders not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no
- * representations about the suitability of this software for any
- * purpose.  It is provided "as is" without express or implied
- * warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- * </pre>
- */
-struct wl_output;
-struct wl_seat;
-struct wl_surface;
-struct zwlr_foreign_toplevel_handle_v1;
-struct zwlr_foreign_toplevel_manager_v1;
-
-#ifndef ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_INTERFACE
-#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_INTERFACE
-/**
- * @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1
- * @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description
- *
- * The purpose of this protocol is to enable the creation of taskbars
- * and docks by providing them with a list of opened applications and
- * letting them request certain actions on them, like maximizing, etc.
- *
- * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
- * toplevel window will be sent via the toplevel event
- * @section page_iface_zwlr_foreign_toplevel_manager_v1_api API
- * See @ref iface_zwlr_foreign_toplevel_manager_v1.
- */
-/**
- * @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface
- *
- * The purpose of this protocol is to enable the creation of taskbars
- * and docks by providing them with a list of opened applications and
- * letting them request certain actions on them, like maximizing, etc.
- *
- * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
- * toplevel window will be sent via the toplevel event
- */
-extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface;
-#endif
-#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE
-/**
- * @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1
- * @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description
- *
- * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
- * window. Each app may have multiple opened toplevels.
- *
- * Each toplevel has a list of outputs it is visible on, conveyed to the
- * client with the output_enter and output_leave events.
- * @section page_iface_zwlr_foreign_toplevel_handle_v1_api API
- * See @ref iface_zwlr_foreign_toplevel_handle_v1.
- */
-/**
- * @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface
- *
- * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
- * window. Each app may have multiple opened toplevels.
- *
- * Each toplevel has a list of outputs it is visible on, conveyed to the
- * client with the output_enter and output_leave events.
- */
-extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
-#endif
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- * @struct zwlr_foreign_toplevel_manager_v1_listener
- */
-struct zwlr_foreign_toplevel_manager_v1_listener {
-	/**
-	 * a toplevel has been created
-	 *
-	 * This event is emitted whenever a new toplevel window is
-	 * created. It is emitted for all toplevels, regardless of the app
-	 * that has created them.
-	 *
-	 * All initial details of the toplevel(title, app_id, states, etc.)
-	 * will be sent immediately after this event via the corresponding
-	 * events in zwlr_foreign_toplevel_handle_v1.
-	 */
-	void (*toplevel)(void *data,
-			 struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
-			 struct zwlr_foreign_toplevel_handle_v1 *toplevel);
-	/**
-	 * the compositor has finished with the toplevel manager
-	 *
-	 * This event indicates that the compositor is done sending
-	 * events to the zwlr_foreign_toplevel_manager_v1. The server will
-	 * destroy the object immediately after sending this request, so it
-	 * will become invalid and the client should free any resources
-	 * associated with it.
-	 */
-	void (*finished)(void *data,
-			 struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1);
-};
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- */
-static inline int
-zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
-					      const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
-				     (void (**)(void)) listener, data);
-}
-
-#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1
-
-/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
-static inline void
-zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data);
-}
-
-/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
-static inline void *
-zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
-}
-
-static inline uint32_t
-zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
-}
-
-/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
-static inline void
-zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
-{
-	wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_manager_v1
- *
- * Indicates the client no longer wishes to receive events for new toplevels.
- * However the compositor may emit further toplevel_created events, until
- * the finished event is emitted.
- *
- * The client must not send any more requests after this one.
- */
-static inline void
-zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1), 0);
-}
-
-#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- * types of states on the toplevel
- *
- * The different states that a toplevel can have. These have the same meaning
- * as the states with the same names defined in xdg-toplevel
- */
-enum zwlr_foreign_toplevel_handle_v1_state {
-	/**
-	 * the toplevel is maximized
-	 */
-	ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0,
-	/**
-	 * the toplevel is minimized
-	 */
-	ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1,
-	/**
-	 * the toplevel is active
-	 */
-	ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2,
-	/**
-	 * the toplevel is fullscreen
-	 * @since 2
-	 */
-	ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3,
-};
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2
-#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */
-
-#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
-enum zwlr_foreign_toplevel_handle_v1_error {
-	/**
-	 * the provided rectangle is invalid
-	 */
-	ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0,
-};
-#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- * @struct zwlr_foreign_toplevel_handle_v1_listener
- */
-struct zwlr_foreign_toplevel_handle_v1_listener {
-	/**
-	 * title change
-	 *
-	 * This event is emitted whenever the title of the toplevel
-	 * changes.
-	 */
-	void (*title)(void *data,
-		      struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-		      const char *title);
-	/**
-	 * app-id change
-	 *
-	 * This event is emitted whenever the app-id of the toplevel
-	 * changes.
-	 */
-	void (*app_id)(void *data,
-		       struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-		       const char *app_id);
-	/**
-	 * toplevel entered an output
-	 *
-	 * This event is emitted whenever the toplevel becomes visible on
-	 * the given output. A toplevel may be visible on multiple outputs.
-	 */
-	void (*output_enter)(void *data,
-			     struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-			     struct wl_output *output);
-	/**
-	 * toplevel left an output
-	 *
-	 * This event is emitted whenever the toplevel stops being
-	 * visible on the given output. It is guaranteed that an
-	 * entered-output event with the same output has been emitted
-	 * before this event.
-	 */
-	void (*output_leave)(void *data,
-			     struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-			     struct wl_output *output);
-	/**
-	 * the toplevel state changed
-	 *
-	 * This event is emitted immediately after the
-	 * zlw_foreign_toplevel_handle_v1 is created and each time the
-	 * toplevel state changes, either because of a compositor action or
-	 * because of a request in this protocol.
-	 */
-	void (*state)(void *data,
-		      struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-		      struct wl_array *state);
-	/**
-	 * all information about the toplevel has been sent
-	 *
-	 * This event is sent after all changes in the toplevel state
-	 * have been sent.
-	 *
-	 * This allows changes to the zwlr_foreign_toplevel_handle_v1
-	 * properties to be seen as atomic, even if they happen via
-	 * multiple events.
-	 */
-	void (*done)(void *data,
-		     struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
-	/**
-	 * this toplevel has been destroyed
-	 *
-	 * This event means the toplevel has been destroyed. It is
-	 * guaranteed there won't be any more events for this
-	 * zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes
-	 * inert so any requests will be ignored except the destroy
-	 * request.
-	 */
-	void (*closed)(void *data,
-		       struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
-	/**
-	 * parent change
-	 *
-	 * This event is emitted whenever the parent of the toplevel
-	 * changes.
-	 *
-	 * No event is emitted when the parent handle is destroyed by the
-	 * client.
-	 * @since 3
-	 */
-	void (*parent)(void *data,
-		       struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-		       struct zwlr_foreign_toplevel_handle_v1 *parent);
-};
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-static inline int
-zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
-					     const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-				     (void (**)(void)) listener, data);
-}
-
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION 3
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- */
-#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2
-
-/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
-static inline void
-zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data);
-}
-
-/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
-static inline void *
-zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
-}
-
-static inline uint32_t
-zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be maximized. If the maximized state actually
- * changes, this will be indicated by the state event.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be unmaximized. If the maximized state actually
- * changes, this will be indicated by the state event.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be minimized. If the minimized state actually
- * changes, this will be indicated by the state event.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be unminimized. If the minimized state actually
- * changes, this will be indicated by the state event.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Request that this toplevel be activated on the given seat.
- * There is no guarantee the toplevel will be actually activated.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, seat);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Send a request to the toplevel to close itself. The compositor would
- * typically use a shell-specific method to carry out this request, for
- * example by sending the xdg_toplevel.close event. However, this gives
- * no guarantees the toplevel will actually be destroyed. If and when
- * this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
- * be emitted.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * The rectangle of the surface specified in this request corresponds to
- * the place where the app using this protocol represents the given toplevel.
- * It can be used by the compositor as a hint for some operations, e.g
- * minimizing. The client is however not required to set this, in which
- * case the compositor is free to decide some default value.
- *
- * If the client specifies more than one rectangle, only the last one is
- * considered.
- *
- * The dimensions are given in surface-local coordinates.
- * Setting width=height=0 removes the already-set rectangle.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, surface, x, y, width, height);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Destroys the zwlr_foreign_toplevel_handle_v1 object.
- *
- * This request should be called either when the client does not want to
- * use the toplevel anymore or after the closed event to finalize the
- * destruction of the object.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), WL_MARSHAL_FLAG_DESTROY);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be fullscreened on the given output. If the
- * fullscreen state and/or the outputs the toplevel is visible on actually
- * change, this will be indicated by the state and output_enter/leave
- * events.
- *
- * The output parameter is only a hint to the compositor. Also, if output
- * is NULL, the compositor should decide which output the toplevel will be
- * fullscreened on, if at all.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, output);
-}
-
-/**
- * @ingroup iface_zwlr_foreign_toplevel_handle_v1
- *
- * Requests that the toplevel be unfullscreened. If the fullscreen state
- * actually changes, this will be indicated by the state event.
- */
-static inline void
-zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
-			 ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0);
-}
-
-#ifdef  __cplusplus
-}
-#endif
-
-#endif
--- a/dep/animia/include/animia/win/win32.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#ifndef __animia__animia__win__win32_h
-#define __animia__animia__win__win32_h
-
-#include "animia/win.h"
-
-namespace animia::internal::win32 {
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace animia::internal::win32
-
-#endif // __animia__animia__win__win32_h
--- a/dep/animia/include/animia/win/x11.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#ifndef __animia__animia__win__x11_h
-#define __animia__animia__win__x11_h
-
-#include "animia/win.h"
-
-namespace animia::internal::x11 {
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace animia::internal::x11
-
-#endif // __animia__animia__win__x11_h
--- a/dep/animia/src/animia.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-#include "animia.h"
-#include "animia/fd.h"
-#include "animia/strategies.h"
-#include "animia/types.h"
-#include "animia/util.h"
-#include "animia/win.h"
-
-#include <set>
-#include <string>
-#include <vector>
-
-namespace animia {
-
-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;
-
-	return false;
-}
-
-static bool IsWindowInList(const Player& player, const Window& window) {
-//	if (util::CheckPattern(player.window_title_format, window.text))
-//		return true;
-
-	for (const auto& pattern : player.windows)
-		if (util::CheckPattern(pattern, window.class_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) {
-	/* 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::IsExecutableInList(player, process.name))
-				continue;
-
-			results.push_back({ResultType::Process, player, process, {}, {}});
-			break;
-		}
-
-		return true;
-	};
-
-	if (!internal::EnumerateOpenProcesses(process_proc))
-		return false;
-
-	auto window_proc = [&](const Process& process, const Window& window) -> bool {
-		for (const auto& player : players) {
-			if (internal::IsWindowInList(player, window))
-				results.push_back({ResultType::Window, player, process, window, {}});
-		}
-
-		return true;
-	};
-
-	if (!internal::EnumerateWindows(window_proc))
-		return false;
-
-	return internal::ApplyStrategies(results);
-}
-
-} // namespace animia
--- a/dep/animia/src/fd.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-#include "animia/fd.h"
-
-#ifdef WIN32
-#	include "animia/fd/win32.h"
-#endif
-
-#ifdef LINUX
-#	include "animia/fd/proc.h"
-#endif
-
-#ifdef MACOSX
-#	include "animia/fd/xnu.h"
-#endif
-
-#ifdef LIBUTIL
-#	include "animia/fd/libutil.h"
-#endif
-
-#ifdef LIBKVM
-#	include "animia/fd/kvm.h"
-#endif
-
-namespace animia::internal {
-
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
-	bool success = false;
-
-#ifdef WIN32
-	success ^= win32::EnumerateOpenFiles(pids, open_file_proc);
-#endif
-
-#ifdef LINUX
-	success ^= proc::EnumerateOpenFiles(pids, open_file_proc);
-#endif
-
-#ifdef MACOSX
-	success ^= xnu::EnumerateOpenFiles(pids, open_file_proc);
-#endif
-
-#ifdef LIBUTIL
-	success ^= libutil::EnumerateOpenFiles(pids, open_file_proc);
-#endif
-
-#ifdef LIBKVM
-	success ^= kvm::EnumerateOpenFiles(pids, open_file_proc);
-#endif
-
-	return success;
-}
-
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	bool success = false;
-
-#ifdef WIN32
-	success ^= win32::EnumerateOpenProcesses(process_proc);
-#endif
-
-#ifdef LINUX
-	success ^= proc::EnumerateOpenProcesses(process_proc);
-#endif
-
-#ifdef MACOSX
-	success ^= xnu::EnumerateOpenProcesses(process_proc);
-#endif
-
-#ifdef LIBUTIL
-	success ^= libutil::EnumerateOpenProcesses(process_proc);
-#endif
-
-#ifdef LIBKVM
-	success ^= kvm::EnumerateOpenProcesses(process_proc);
-#endif
-
-	return success;
-}
-
-} // namespace animia::internal
--- a/dep/animia/src/fd/kvm.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/* kvm.cc: provides support for OpenBSD's libkvm
-**
-** Theoretically, this code *should* work, but I haven't
-** even tested it.
-**
-** This also contains some code to support NetBSD, although
-** it calls the kernel instead of kvm.
-*/
-
-#include "animia/fd/kvm.h"
-#include "animia/fd.h"
-#include "animia.h"
-
-#include <sys/types.h>
-#include <sys/user.h>
-#include <sys/file.h>
-#include <sys/filedesc.h>
-#include <sys/param.h>
-#include <sys/vnode.h>
-#include <sys/queue.h>
-#include <sys/sysctl.h>
-
-#include <kvm.h>
-
-#include <string>
-
-namespace animia::internal::kvm {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
-	if (!kernel)
-		return false;
-
-	int entries = 0;
-	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, &entries);
-	if (!kinfo)
-		return false;
-
-	for (int i = 0; i < entries; i++)
-		if (!process_proc({kinfo[i].ki_paddr->p_pid, kinfo[i].ki_paddr->p_comm}))
-			return false;
-
-	kvm_close(kernel);
-
-	return true;
-}
-
-bool EnumerateOpenFiles(std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
-#ifdef HAVE_KVM_GETFILES
-	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
-	if (!kernel)
-		return false;
-
-	for (const auto& pid : pids) {
-		int cnt;
-		struct kinfo_file* kfile = kvm_getfiles(kernel, KERN_FILE_BYPID, pid, &cnt);
-		if (!kfile)
-			return false;
-
-		for (int i = 0; i < cnt; i++)
-			if (!open_file_proc({pid, kfile[i].kf_path}))
-				return false;
-	}
-
-	kvm_close(kernel);
-
-	return true;
-#else /* For NetBSD... I think */
-	for (const auto& pid : pids) {
-		int mib[6] = {
-			CTL_KERN,
-			KERN_FILE2,
-			KERN_FILE_BYPID,
-			pid,
-			sizeof(struct kinfo_file),
-			0
-		};
-
-		size_t len = 0;
-		if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) == -1)
-			return false;
-
-		mib[5] = len / sizeof(struct kinfo_file);
-
-		std::unique_ptr<struct kinfo_file[]> buf(new struct kinfo_file[mib[5]]);
-		if (!buf)
-			return false;
-
-		if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), buf.get(), &len, NULL, 0) == -1)
-			return false;
-
-		for (size_t i = 0; i < cnt; i++)
-			if (!open_file_proc({pid, kfile[i].kf_path}))
-				return false;
-	}
-
-	return true;
-#endif
-}
-
-}
--- a/dep/animia/src/fd/libutil.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/* This file uses the FreeBSD-specific libprocstat
-*/
-
-#include "animia/fd/libutil.h"
-#include "animia/fd.h"
-#include "animia.h"
-
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/user.h>
-#include <libutil.h>
-
-#include <memory>
-
-namespace animia::internal::libutil {
-
-static bool IsSystemFile(const std::string& file) {
-	return (!file.find("/usr") || !file.find("/lib") || !file.find("/dev") ||
-		    !file.find("/proc"));
-}
-
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
-	size_t length = 0;
-
-	sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, NULL, &length, NULL, 0);
-
-	std::unique_ptr<struct kinfo_proc[]> result;
-	result.reset(new struct kinfo_proc[length]);
-
-	if (!result.get())
-		return false;
-
-	/* actually get our results */
-	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM) {
-		result.reset();
-		throw std::bad_alloc();
-	}
-
-	if (length < sizeof(struct kinfo_proc))
-		return false;
-
-	for (int i = 0; i < length / sizeof(result[0]); i++)
-		if (!process_proc({result[i].ki_pid, result[i].ki_comm}))
-			return false;
-
-	return true;
-}
-
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
-	for (const auto& pid : pids) {
-		int cnt;
-		std::unique_ptr<struct kinfo_file[]> files;
-		files.reset(kinfo_getfile(pid, &cnt));
-		if (!files.get())
-			return false;
-
-		for (int i = 0; i < cnt; i++) {
-			const struct kinfo_file& current = files[i];
-			if (current.kf_vnode_type != KF_VTYPE_VREG)
-				continue;
-
-			if (!open_file_proc({pid, current.kf_path}))
-				return false;
-		}
-	}
-
-	return true;
-}
-
-}
--- a/dep/animia/src/fd/proc.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-#include "animia/fd/proc.h"
-#include "animia.h"
-#include "animia/util.h"
-
-#include <filesystem>
-#include <fstream>
-#include <sstream>
-#include <string>
-
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-static constexpr std::string_view PROC_LOCATION = "/proc";
-
-namespace animia::internal::proc {
-
-template<typename T = int>
-static T StringToInt(const std::string& str, T def = 0) {
-	std::istringstream s(str);
-	s >> def;
-	return def;
-}
-
-static bool IsRegularFile(std::string link) {
-	struct stat sb;
-	if (stat(link.c_str(), &sb) == -1)
-		return false;
-
-	return S_ISREG(sb.st_mode);
-}
-
-static bool AreFlagsOk(pid_t pid, int fd) {
-	const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fdinfo" / std::to_string(fd);
-
-	std::ifstream file(path);
-	if (!file)
-		return false;
-
-	int flags = 0;
-	for (std::string line; std::getline(file, line); )
-		if (line.find("flags:", 0) == 0)
-			flags = StringToInt(line.substr(line.find_last_not_of("0123456789") + 1));
-
-	if (flags & O_WRONLY || flags & O_RDWR)
-		return false;
-
-	return true;
-}
-
-static bool GetFilenameFromFd(std::string link, std::string& out) {
-	/* gets around stupid linux limitation where /proc doesn't
-	 * give actual size readings of the string
-	*/
-	constexpr size_t OUT_MAX = (1ul << 15); // 32KiB
-	out.resize(32);
-
-	ssize_t exe_used = 0;
-	do {
-		out.resize(out.length() * 2);
-
-		exe_used = readlink(link.c_str(), &out.front(), out.length());
-		if (exe_used == (ssize_t)-1 || exe_used < (ssize_t)1)
-			return false; // we got a bad result. SAD!
-	} while (out.length() < OUT_MAX && exe_used >= static_cast<ssize_t>(out.length()));
-
-	out.resize(out.find('\0'));
-
-	return true;
-}
-
-static bool GetProcessName(pid_t pid, std::string& result) {
-	const std::filesystem::path path = std::filesystem::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 EnumerateOpenProcesses(process_proc_t process_proc) {
-	bool success = false;
-
-	for (const auto& dir : std::filesystem::directory_iterator{PROC_LOCATION}) {
-		Process proc;
-
-		try {
-			proc.pid = StringToInt(dir.path().stem());
-			success = true;
-		} catch (std::invalid_argument const& ex) {
-			continue;
-		}
-
-		if (!GetProcessName(proc.pid, proc.name))
-			continue;
-
-		if (!process_proc(proc))
-			return false;
-	}
-
-	return success;
-}
-
-bool 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) {
-		const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fd";
-
-		for (const auto& dir : std::filesystem::directory_iterator{path}) {
-			if (!AreFlagsOk(pid, StringToInt(dir.path().stem())))
-				continue;
-
-			std::string name;
-			if (!GetFilenameFromFd(dir.path(), name))
-				continue;
-
-			if (!IsRegularFile(name))
-				continue;
-
-			if (!open_file_proc({pid, name}))
-				return false;
-		}
-	}
-	return true;
-}
-
-} // namespace animia::internal::linux
--- a/dep/animia/src/fd/win32.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-#include "animia/fd/win32.h"
-#include "animia/util/win32.h"
-#include "animia.h"
-
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include <fileapi.h>
-#include <handleapi.h>
-#include <libloaderapi.h>
-#include <ntdef.h>
-#include <psapi.h>
-#include <shlobj.h>
-#include <stringapiset.h>
-#include <tlhelp32.h>
-#include <windows.h>
-#include <winternl.h>
-
-/* This file is noticably more complex than Unix and Linux, and that's because
- * there is no "simple" way to get the paths of a file. In fact, this thing requires
- * you to use *internal functions* that can't even be linked to, hence why we have to
- * use GetProcAddress and such. What a mess.
- *
- * Speaking of which, because this file uses internal functions of the OS, it is not
- * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista)
- * at least, so it's unlikely to be changed much ever.
-*/
-
-/* 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);
-constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
-
-/* 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 {
-		PVOID Object;
-		ULONG_PTR UniqueProcessId;
-		HANDLE HandleValue;
-		ACCESS_MASK GrantedAccess;
-		USHORT CreatorBackTraceIndex;
-		USHORT ObjectTypeIndex;
-		ULONG HandleAttributes;
-		ULONG Reserved;
-};
-
-struct SYSTEM_HANDLE_INFORMATION_EX {
-		ULONG_PTR NumberOfHandles;
-		ULONG_PTR Reserved;
-		SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
-};
-
-namespace animia::internal::win32 {
-
-static HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
-	HANDLE dup_handle = nullptr;
-	const bool result =
-	    ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
-	return result ? dup_handle : nullptr;
-}
-
-static PVOID GetNTDLLAddress(LPCSTR proc_name) {
-	return reinterpret_cast<PVOID>(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name));
-}
-
-static NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) {
-	static const auto func =
-	    reinterpret_cast<decltype(::NtQuerySystemInformation)*>(GetNTDLLAddress("NtQuerySystemInformation"));
-	return func(cls, sysinfo, len, retlen);
-}
-
-static NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
-	static const auto func = reinterpret_cast<decltype(::NtQueryObject)*>(GetNTDLLAddress("NtQueryObject"));
-	return func(handle, cls, objinf, objinflen, retlen);
-}
-
-static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
-	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
-	/* we should really put a cap on this */
-	ULONG cb = 1 << 19;
-
-	for (NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; status == STATUS_INFO_LENGTH_MISMATCH;) {
-		/* why are we doing this? */
-		status = STATUS_NO_MEMORY;
-
-		SYSTEM_HANDLE_INFORMATION_EX* info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2);
-		if (!info)
-			continue;
-
-		res.reserve(cb);
-
-		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)
-						res.push_back(*entry);
-				} while (entry++, --handles);
-			}
-		}
-
-		free(info);
-	}
-
-	return res;
-}
-
-static OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) {
-	OBJECT_TYPE_INFORMATION info;
-	QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
-	return info;
-}
-
-static std::string GetHandleType(HANDLE handle) {
-	OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle);
-	return ToUtf8String(info.TypeName);
-}
-
-static std::string GetFinalPathNameByHandle(HANDLE handle) {
-	std::wstring buffer;
-
-	int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-	buffer.resize(result);
-	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-	buffer.resize(buffer.find('\0'));
-
-	return ToUtf8String(buffer);
-}
-
-static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
-	if (file_type_index)
-		return object_type_index == file_type_index;
-	else if (!handle)
-		return true;
-	else if (GetHandleType(handle) == "File") {
-		file_type_index = object_type_index;
-		return true;
-	}
-	return false;
-}
-
-static bool IsFileMaskOk(ACCESS_MASK access_mask) {
-	if (!(access_mask & FILE_READ_DATA))
-		return false;
-
-	if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
-		return false;
-
-	return true;
-}
-
-static bool IsFilePathOk(const std::string& path) {
-	if (path.empty())
-		return false;
-
-	if (IsSystemDirectory(path))
-		return false;
-
-	const auto file_attributes = GetFileAttributesA(path.c_str());
-	if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
-		return false;
-
-	return true;
-}
-
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
-	if (hProcessSnap == INVALID_HANDLE_VALUE)
-		return false;
-
-	PROCESSENTRY32 pe32;
-	pe32.dwSize = sizeof(PROCESSENTRY32);
-
-	if (!::Process32First(hProcessSnap, &pe32))
-		return false;
-
-	if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
-		return false;
-
-	while (::Process32Next(hProcessSnap, &pe32))
-		if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
-			return false;
-
-	::CloseHandle(hProcessSnap);
-
-	return true;
-}
-
-/* this could be changed to being a callback, but... I'm too lazy right now :) */
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
-	if (!open_file_proc)
-		return false;
-
-	std::unordered_map<pid_t, Handle> proc_handles;
-
-	for (const pid_t& pid : pids) {
-		const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
-		if (handle != INVALID_HANDLE_VALUE)
-			proc_handles[pid] = Handle(handle);
-	}
-
-	if (proc_handles.empty())
-		return false;
-
-	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
-
-	for (const auto& h : info) {
-		const pid_t pid = h.UniqueProcessId;
-		if (!pids.count(pid))
-			continue;
-
-		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
-			continue;
-
-		if (!IsFileMaskOk(h.GrantedAccess))
-			continue;
-
-		Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
-		if (handle.get() == INVALID_HANDLE_VALUE)
-			continue;
-
-		if (GetFileType(handle.get()) != FILE_TYPE_DISK)
-			continue;
-
-		const std::string path = GetFinalPathNameByHandle(handle.get());
-		if (!IsFilePathOk(path))
-			continue;
-
-		if (!open_file_proc({pid, path}))
-			return false;
-	}
-
-	return true;
-}
-
-} // namespace animia::internal::win32
--- a/dep/animia/src/fd/xnu.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-#include "animia/fd/xnu.h"
-#include "animia/util/osx.h"
-#include "animia.h"
-
-#include <cassert>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include <memory>
-
-#include <fcntl.h>
-#include <libproc.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/user.h>
-
-namespace animia::internal::xnu {
-
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	size_t pids_size = 256;
-	std::unique_ptr<pid_t[]> pids;
-
-	int returned_size = 0;
-	do {
-		pids.reset(new pid_t[pids_size *= 2]);
-		returned_size = proc_listpids(PROC_ALL_PIDS, 0, pids.get(), pids_size * sizeof(pid_t));
-		if (returned_size == -1)
-			return false;
-	} while ((pids_size * sizeof(size_t)) < returned_size);
-
-	for (int i = 0; i < pids_size; i++) {
-		std::string result;
-		osx::util::GetProcessName(pids[i], result);
-		if (!process_proc({pids[i], result}))
-			return false;
-	}
-
-	return true;
-}
-
-bool 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)
-			return false;
-
-		struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz);
-		if (!info)
-			return false;
-
-		proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
-
-		for (int i = 0; i < bufsz / sizeof(info[0]); i++) {
-			if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
-				struct vnode_fdinfowithpath vnodeInfo;
-
-				int sz = proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo,
-				                        PROC_PIDFDVNODEPATHINFO_SIZE);
-				if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
-					return false;
-
-				/* this doesn't work!
-				if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
-				    continue;
-				*/
-
-				if (!open_file_proc({pid, vnodeInfo.pvip.vip_path}))
-					return false;
-			}
-		}
-	}
-
-	return true;
-}
-
-} // namespace animia::internal::xnu
--- a/dep/animia/src/player.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-#include "animia/player.h"
-#include "animia/util.h"
-
-#include <map>
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace animia {
-
-namespace internal::parser {
-
-enum class State {
-	ExpectPlayerName,
-	ExpectSection,
-	ExpectWindow,
-	ExpectExecutable,
-	ExpectStrategy,
-	ExpectType,
-	ExpectWindowTitle,
-};
-
-size_t GetIndentation(const std::string& line) {
-	return line.find_first_not_of('\t');
-}
-
-bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) {
-	// Each state has a definitive expected indentation
-	const auto expected = [&state]() -> size_t {
-		switch (state) {
-			default:
-			case State::ExpectPlayerName: return 0;
-			case State::ExpectSection: return 1;
-			case State::ExpectWindow:
-			case State::ExpectExecutable:
-			case State::ExpectStrategy:
-			case State::ExpectType: return 2;
-			case State::ExpectWindowTitle: return 3;
-		}
-	}();
-
-	if (current > expected)
-		return false; // Disallow excessive indentation
-
-	if (current < expected) {
-		auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; };
-		switch (state) {
-			case State::ExpectWindow:
-				if (players.back().windows.empty())
-					return false;
-				fix_state();
-				break;
-			case State::ExpectExecutable:
-				if (players.back().executables.empty())
-					return false;
-				fix_state();
-				break;
-			case State::ExpectStrategy:
-				if (players.back().strategies.empty())
-					return false;
-				fix_state();
-				break;
-			case State::ExpectType: fix_state(); break;
-			case State::ExpectWindowTitle: return false;
-		}
-	}
-
-	return true;
-}
-
-bool HandleState(std::string& line, std::vector<Player>& players, State& state) {
-	switch (state) {
-		case State::ExpectPlayerName:
-			players.push_back(Player());
-			players.back().name = line;
-			state = State::ExpectSection;
-			break;
-
-		case State::ExpectSection: {
-			static const std::map<std::string, State> sections = {
-			    {"windows",     State::ExpectWindow    },
-			    {"executables", State::ExpectExecutable},
-			    {"strategies",  State::ExpectStrategy  },
-			    {"type",        State::ExpectType      },
-			};
-			util::TrimRight(line, ":");
-			const auto it = sections.find(line);
-			if (it == sections.end())
-				return false;
-			state = it->second;
-			break;
-		}
-
-		case State::ExpectWindow: players.back().windows.push_back(line); break;
-
-		case State::ExpectExecutable: players.back().executables.push_back(line); break;
-
-		case State::ExpectStrategy: {
-			static const std::map<std::string, Strategy> strategies = {
-			    {"window_title",  Strategy::WindowTitle },
-			    {"open_files",    Strategy::OpenFiles   },
-			    {"ui_automation", Strategy::UiAutomation},
-			};
-			util::TrimRight(line, ":");
-			const auto it = strategies.find(line);
-			if (it == strategies.end())
-				return false;
-			const auto strategy = it->second;
-			players.back().strategies.push_back(strategy);
-			switch (strategy) {
-				case Strategy::WindowTitle: state = State::ExpectWindowTitle; break;
-			}
-			break;
-		}
-
-		case State::ExpectType: {
-			static const std::map<std::string, PlayerType> types = {
-			    {"default",     PlayerType::Default   },
-			    {"web_browser", PlayerType::WebBrowser},
-			};
-			const auto it = types.find(line);
-			if (it == types.end())
-				return false;
-			players.back().type = it->second;
-			break;
-		}
-
-		case State::ExpectWindowTitle:
-			players.back().window_title_format = line;
-			state = State::ExpectStrategy;
-			break;
-	}
-
-	return true;
-}
-
-} // namespace internal::parser
-
-////////////////////////////////////////////////////////////////////////////////
-
-bool ParsePlayersData(const std::string& data, std::vector<Player>& players) {
-	if (data.empty())
-		return false;
-
-	std::istringstream stream(data);
-	std::string line;
-	size_t indentation = 0;
-	auto state = internal::parser::State::ExpectPlayerName;
-
-	while (std::getline(stream, line, '\n')) {
-		if (line.empty())
-			continue; // Ignore empty lines
-
-		indentation = internal::parser::GetIndentation(line);
-
-		internal::util::TrimLeft(line, "\t");
-		internal::util::TrimRight(line, "\n\r");
-
-		if (line.empty() || line.front() == '#')
-			continue; // Ignore empty lines and comments
-
-		if (!internal::parser::HandleIndentation(indentation, players, state))
-			return false;
-
-		if (!internal::parser::HandleState(line, players, state))
-			return false;
-	}
-
-	return !players.empty();
-}
-
-bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) {
-	std::string data;
-
-	if (!internal::util::ReadFile(path, data))
-		return false;
-
-	return ParsePlayersData(data, players);
-}
-
-} // namespace animia
--- a/dep/animia/src/strategist.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-#include <regex>
-
-#include "animia.h"
-#include "animia/fd.h"
-#include "animia/strategies.h"
-#include "animia/util.h"
-
-#include <iostream>
-
-namespace animia::internal {
-
-class Strategist {
-	public:
-		Strategist(Result& result) : result_(result) {}
-
-		bool ApplyStrategies();
-
-	private:
-		bool AddMedia(const MediaInfo media_information);
-
-		bool ApplyOpenFilesStrategy();
-		bool ApplyWindowTitleStrategy();
-
-		Result& result_;
-};
-
-bool Strategist::ApplyStrategies() {
-	bool success = false;
-
-	switch (result_.type) {
-		case ResultType::Process: success |= ApplyOpenFilesStrategy(); break;
-		case ResultType::Window: success |= ApplyWindowTitleStrategy(); break;
-		default: break;
-	}
-
-	return success;
-}
-
-bool ApplyStrategies(std::vector<Result>& results) {
-	bool success = false;
-
-	for (auto& result : results) {
-		Strategist strategist(result);
-		success |= strategist.ApplyStrategies();
-	}
-
-	return success;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-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};
-
-	auto open_file_proc = [&](const OpenFile& file) -> bool {
-		success |= AddMedia({MediaInfoType::File, file.path});
-		return true;
-	};
-
-	EnumerateOpenFiles(pids, open_file_proc);
-
-	return success;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-bool Strategist::AddMedia(const MediaInfo media_information) {
-	if (media_information.value.empty())
-		return false;
-
-	Media media;
-	media.information.push_back(media_information);
-	result_.media.push_back(std::move(media));
-
-	return true;
-}
-
-} // namespace animia::internal
--- a/dep/animia/src/util.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-#include <algorithm>
-#include <fstream>
-#include <regex>
-#include <sstream>
-#include <string>
-
-#include "animia/util.h"
-
-namespace animia::internal::util {
-
-bool ReadFile(const std::string& path, std::string& data) {
-	std::ifstream file(path.c_str(), std::ios::in | std::ios::binary);
-	if (!file)
-		return false;
-
-	std::ostringstream string;
-	string << file.rdbuf();
-	file.close();
-
-	data = string.str();
-
-	return true;
-}
-
-/* this assumes ASCII... which really should be the case for what we need, anyway */
-bool EqualStrings(const std::string& str1, const std::string& str2) {
-	auto tolower = [](const char c) -> char { return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c; };
-
-	auto equal_chars = [&tolower](const char c1, const char c2) -> bool { return tolower(c1) == tolower(c2); };
-
-	return str1.length() == str2.length() && 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;
-
-	const auto found = str.find_first_not_of(chars);
-
-	if (found == 0)
-		return false;
-
-	if (found == std::string::npos)
-		str.clear();
-	else
-		str.erase(0, found);
-
-	return true;
-}
-
-bool TrimRight(std::string& str, const char* chars) {
-	if (str.empty())
-		return false;
-
-	const auto found = str.find_last_not_of(chars);
-
-	if (found == str.size() - 1)
-		return false;
-
-	if (found == std::string::npos)
-		str.clear();
-	else
-		str.resize(found + 1);
-
-	return true;
-}
-
-} // namespace animia::internal::util
--- a/dep/animia/src/util/osx.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-#include "animia/util/osx.h"
-
-#include <string>
-#include <memory>
-
-#include <sys/sysctl.h>
-#include <libproc.h>
-
-namespace animia::internal::osx::util {
-
-/* all of these LaunchServices things use *internal functions* that are subject
- * to change. Granted, it's not very likely that these will change very much
- * because I'm fairly sure Apple uses them lots in their own internal code.
-*/
-typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
-typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
-
-static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr;
-static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr;
-
-/* retrieved from LaunchServicesSPI.h in WebKit */
-static constexpr int kLSDefaultSessionID = -2;
-
-static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
-
-/* retrieved dynamically */
-static CFStringRef kLSDisplayNameKey = nullptr;
-static CFStringRef kLSPIDKey = nullptr;
-
-static bool GetLaunchServicesPrivateSymbols() {
-	CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
-	if (!launch_services_bundle)
-		return false;
-
-	LSCopyApplicationInformation = (LSCopyApplicationInformationSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation"));
-	if (!LSCopyApplicationInformation)
-		return false;
-
-	LSASNCreateWithPid = (LSASNCreateWithPidSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid"));
-	if (!LSASNCreateWithPid)
-		return false;
-
-	kLSDisplayNameKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey"));
-	if (!kLSDisplayNameKey)
-		return false;
-
-	kLSPIDKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey"));
-	if (!kLSPIDKey)
-		return false;
-
-	return true;
-}
-
-static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) {
-	if (!LSCopyApplicationInformation || !LSASNCreateWithPid)
-		if (!GetLaunchServicesPrivateSymbols())
-			return false;
-
-	CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
-	if (!asn)
-		return false;
-
-	CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL);
-	if (!request_array) {
-		CFRelease(asn);
-		return false;
-	}
-
-	CFDictionaryRef dictionary = LSCopyApplicationInformation(kLSDefaultSessionID, asn, request_array);
-
-	CFRelease(request_array);
-	CFRelease(asn);
-
-	if (!dictionary)
-		return false;
-
-	{
-		CFStringRef rstr;
-
-		if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr)
-			return false;
-
-		if (!StringFromCFString(rstr, result)) {
-			CFRelease(rstr);
-			return false;
-		}
-
-		CFRelease(rstr);
-	}
-
-	result.resize(result.find('\0'));
-
-	return true;
-}
-
-bool StringFromCFString(CFStringRef string, std::string& result) {
-	if (!string)
-		return false;
-
-	result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1);
-	if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8))
-		return false;
-
-	return true;
-}
-
-static bool GetProcessArgs(pid_t pid, std::string& args) {
-	/* sysctl shouldn't touch these, so we define them as const */
-	const int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)};
-	const size_t mib_size = sizeof(mib)/sizeof(*mib);
-
-	/* Get the initial size of the array
-	 *
-	 * NOTE: it IS possible for this value to change inbetween calls to sysctl().
-	 * Unfortunately, I couldn't care less about handling this. :)
-	*/
-	size_t size;
-	{
-		int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
-		if (ret)
-			return false;
-	}
-
-	/* Reserve the space for it in args */
-	args.resize(size);
-
-	/* Get the contents of argc and argv */
-	{
-		int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0);
-		if (ret)
-			return false;
-	}
-
-	/* Is the size big enough to hold at least argc? */
-	if (size < sizeof(int))
-		return false;
-
-	args.resize(size);
-	return true;
-}
-
-static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
-	if (!GetProcessArgs(pid, result))
-		return false;
-
-	/* Get argc using memcpy */
-	int argc;
-	memcpy(&result, &result.front(), sizeof(argc));
-
-	/* Do we even have argv[0]? */
-	if (argc < 1)
-		return false;
-
-	/* Find the first null character */
-	size_t null_pos = result.find('\0', sizeof(argc));
-	if (null_pos == std::string::npos)
-		return false;
-
-	/* Find the last slash */
-	size_t last_slash = result.rfind('/', null_pos);
-	if (last_slash == std::string::npos)
-		return false;
-
-	/* Return our result */
-	result = result.substr(last_slash + 1, null_pos - last_slash - 1);
-	return true;
-}
-
-static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
-	result.resize(2 * MAXCOMLEN);
-
-	int size = proc_name(pid, &result.front(), result.length());
-	if (!size)
-		return false;
-
-	result.resize(size);
-	return true;
-}
-
-bool GetProcessName(pid_t pid, std::string& result) {
-#ifdef HAVE_COREFOUNDATION
-	if (LaunchServicesGetProcessName(pid, result))
-		return true;
-#endif // HAVE_COREFOUNDATION
-
-	/* Try parsing the arguments, this prevents the process name being
-	   cut off to 2*MAXCOMLEN (32 chars) */
-	if (GetProcessNameFromArgs(pid, result))
-		return true;
-
-	/* Then attempt getting it from the kernel, which results in the
-	   process name being cut to 32 chars (worse, 16 chars if p_name is
-	   unavailable) */
-	if (GetProcessNameFromKernel(pid, result))
-		return true;
-
-	return false;
-}
-
-}
--- a/dep/animia/src/util/win32.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-#include "animia/util/win32.h"
-
-#include <shlobj.h>  /* SHGetKnownFolderPath */
-#include <subauth.h> /* UNICODE_STRING */
-#include <windows.h>
-
-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;
-}
-
-} // namespace animia::internal::win32
--- a/dep/animia/src/win.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#include "animia/win.h"
-
-#ifdef WIN32
-#	include "animia/win/win32.h"
-#endif
-
-#ifdef MACOSX
-#	include "animia/win/quartz.h"
-#endif
-
-#ifdef X11
-#	include "animia/win/x11.h"
-#endif
-
-#ifdef WAYLAND
-#	include "animia/win/wayland.h"
-#endif
-
-namespace animia::internal {
-
-bool EnumerateWindows(window_proc_t window_proc) {
-	bool success = false;
-
-#ifdef WIN32
-	success |= win32::EnumerateWindows(window_proc);
-#endif
-
-#ifdef MACOSX
-	success |= quartz::EnumerateWindows(window_proc);
-#endif
-
-#ifdef X11
-	success |= x11::EnumerateWindows(window_proc);
-#endif
-
-#ifdef WAYLAND
-	success |= wayland::EnumerateWindows(window_proc);
-#endif
-
-	return success;
-}
-
-} // namespace animia::internal
--- a/dep/animia/src/win/quartz.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-/*
- * win/quartz.cc: support for macOS (the Quartz Compositor)
- *
- * This file does not require an Objective-C++ compiler,
- * but it *does* require an Objective-C runtime.
-*/
-#include "animia/win/quartz.h"
-#include "animia/util/osx.h"
-#include "animia.h"
-
-#include <objc/runtime.h>
-#include <objc/message.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <ApplicationServices/ApplicationServices.h>
-
-namespace animia::internal::quartz {
-
-/* all of these LaunchServices things use *internal functions* that are subject
- * to change. Granted, it's not very likely that these will change very much
- * because I'm fairly sure Apple uses them lots in their own internal code.
-*/
-#if __LP64__
-typedef long NSInteger;
-#else
-typedef int NSInteger;
-#endif
-typedef int CGSConnection;
-
-typedef CGSConnection (*CGSDefaultConnectionForThreadSpec)(void);
-typedef CGError (*CGSCopyWindowPropertySpec)(const CGSConnection, NSInteger, CFStringRef, CFStringRef*);
-
-static CGSDefaultConnectionForThreadSpec CGSDefaultConnectionForThread = nullptr;
-static CGSCopyWindowPropertySpec CGSCopyWindowProperty = nullptr;
-
-static const CFStringRef kCoreGraphicsBundleID = CFSTR("com.apple.CoreGraphics");
-
-/* Objective-C */
-typedef id (*object_message_send)(id, SEL, ...);
-typedef id (*class_message_send)(Class, SEL, ...);
-
-static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend);
-static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend);
-
-static bool GetCoreGraphicsPrivateSymbols() {
-	CFBundleRef core_graphics_bundle = CFBundleGetBundleWithIdentifier(kCoreGraphicsBundleID);
-	if (!core_graphics_bundle)
-		return false;
-
-	CGSDefaultConnectionForThread = (CGSDefaultConnectionForThreadSpec)CFBundleGetFunctionPointerForName(core_graphics_bundle, CFSTR("CGSDefaultConnectionForThread"));
-	if (!CGSDefaultConnectionForThread)
-		return false;
-
-	CGSCopyWindowProperty = (CGSCopyWindowPropertySpec)CFBundleGetFunctionPointerForName(core_graphics_bundle, CFSTR("CGSCopyWindowProperty"));
-	if (!CGSCopyWindowProperty)
-		return false;
-
-	return true;
-}
-
-template<typename T>
-static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) {
-	CFTypeRef data = nullptr;
-	if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data)
-		return false;
-
-	if constexpr (std::is_arithmetic<T>::value)
-		osx::util::GetCFNumber(reinterpret_cast<CFNumberRef>(data), out);
-	else if constexpr (std::is_same<T, std::string>::value)
-		osx::util::StringFromCFString(reinterpret_cast<CFStringRef>(data), out);
-	else
-		return false;
-
-	return true;
-}
-
-static bool GetWindowTitleAccessibility(unsigned int wid, pid_t pid, std::string& result) {
-	CGRect bounds = {0};
-	{
-		const CGWindowID wids[1] = {wid};
-		CFArrayRef arr = CFArrayCreate(kCFAllocatorDefault, (CFTypeRef*)wids, 1, NULL);
-
-		CFArrayRef dicts = CGWindowListCreateDescriptionFromArray(arr);
-
-		CFRelease(arr);
-
-		if (!dicts || CFArrayGetCount(dicts) < 1)
-			return false;
-
-		CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(dicts, 0);
-		if (!dict) {
-			CFRelease(dicts);
-			return false;
-		}
-
-		CFDictionaryRef bounds_dict = nullptr;
-		if (!CFDictionaryGetValueIfPresent(dict, kCGWindowBounds, reinterpret_cast<CFTypeRef*>(&bounds_dict)) || !bounds_dict) {
-			CFRelease(dicts);
-			return false;
-		}
-
-		if (!CGRectMakeWithDictionaryRepresentation(bounds_dict, &bounds)) {
-			CFRelease(dicts);
-			return false;
-		}
-
-		CFRelease(dicts);
-	}
-
-	/* now we can actually do stuff */
-	AXUIElementRef axapp = AXUIElementCreateApplication(pid);
-	CFArrayRef windows;
-	if ((AXUIElementCopyAttributeValue(axapp, kAXWindowsAttribute, (CFTypeRef*)&windows) != kAXErrorSuccess) || !windows)
-		return false;
-
-	const CFIndex count = CFArrayGetCount(windows);
-	for (CFIndex i = 0; i < count; i++) {
-		const AXUIElementRef window = (AXUIElementRef)CFArrayGetValueAtIndex(windows, i);
-
-		AXValueRef val;
-		if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, (CFTypeRef*)&val) == kAXErrorSuccess) {
-			CGPoint point;
-			if (!AXValueGetValue(val, kAXValueTypeCGPoint, (void*)&point) || (point.x != bounds.origin.x || point.y != bounds.origin.y))
-				continue;
-		} else continue;
-
-		if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, (CFTypeRef*)&val) == kAXErrorSuccess) {
-			CGSize size;
-			if (!AXValueGetValue(val, kAXValueTypeCGSize, (void*)&size) || (size.width != bounds.size.width || size.height != bounds.size.height))
-				continue;
-		} else continue;
-
-		CFStringRef title;
-		if (AXUIElementCopyAttributeValue(window, kAXTitleAttribute, (CFTypeRef*)&title) == kAXErrorSuccess) {
-			CFRelease(windows);
-			return osx::util::StringFromCFString(title, result);
-		}
-	}
-
-	CFRelease(windows);
-
-	return false;
-}
-
-static bool GetWindowTitle(unsigned int wid, pid_t pid, std::string& result) {
-	/* private internal OS X functions */
-	if ((CGSDefaultConnectionForThread && CGSCopyWindowProperty) || GetCoreGraphicsPrivateSymbols()) {
-		CFStringRef title = nullptr;
-
-		CGSCopyWindowProperty(CGSDefaultConnectionForThread(), wid, CFSTR("kCGSWindowTitle"), &title);
-		if (title && CFStringGetLength(title) && osx::util::StringFromCFString(title, result))
-			return true;
-	}
-
-	/* don't attempt to use accessibility if we aren't trusted */
-	return AXIsProcessTrusted() ? GetWindowTitleAccessibility(wid, pid, result) : false;
-}
-
-static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) {
-	/* The Bundle ID is essentially OS X's solution to Windows'
-	 * "class name"; theoretically, it should be different for
-	 * each program, although it requires an app bundle.
-	*/
-	const id app = cls_send(objc_getClass("NSRunningApplication"), sel_getUid("runningApplicationWithProcessIdentifier:"), pid);
-	if (!app)
-		return false;
-
-	CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier")));
-	if (!bundle_id)
-		return false;
-
-	result = osx::util::StringFromCFString(bundle_id, result);
-	return true;
-}
-
-bool EnumerateWindows(window_proc_t window_proc) {
-	if (!window_proc)
-		return false;
-
-	const CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
-	if (!windows)
-		return false;
-
-	const CFIndex count = CFArrayGetCount(windows);
-	for (CFIndex i = 0; i < count; i++) {
-		CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows, i));
-		if (!window)
-			continue;
-
-		Process proc;
-		{
-			CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid);
-			if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name))
-				osx::util::GetProcessName(proc.pid, proc.name);
-		}
-
-		Window win;
-		{
-			CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id);
-
-			if (!GetProcessBundleIdentifier(proc.pid, win.class_name))
-				// Fallback to the Quartz window name, which is unlikely to be filled, but it
-				// *could* be.
-				CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name);
-
-			GetWindowTitle(win.id, proc.pid, win.text);
-		}
-
-		if (!window_proc(proc, win)) {
-			CFRelease(windows);
-			return false;
-		}
-	}
-
-	CFRelease(windows);
-
-	return true;
-}
-
-} // namespace animia::win::detail
--- a/dep/animia/src/win/wayland.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,244 +0,0 @@
-#include "animia/win/wayland.h"
-#include "animia.h"
-#include "animia/win.h"
-#include "animia/util.h"
-
-#include <cstring>
-#include <iostream>
-
-#include "animia/win/wayland/ext-foreign-toplevel-list-v1.h"
-#include "animia/win/wayland/wlr-foreign-toplevel-management-unstable-v1.h"
-#include <wayland-client.h>
-
-namespace animia::internal::wayland {
-
-/* zwlr-foreign-toplevel-management-v1 implementation */
-static void zwlr_foreign_handle_handle_title(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                             const char* title) {
-	if (title)
-		reinterpret_cast<Window*>(data)->text = title;
-}
-
-static void zwlr_foreign_handle_handle_app_id(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                              const char* app_id) {
-	if (app_id)
-		reinterpret_cast<Window*>(data)->class_name = app_id;
-}
-
-static void zwlr_foreign_handle_handle_done(void* data, struct zwlr_foreign_toplevel_handle_v1* handle) {
-	if (handle)
-		zwlr_foreign_toplevel_handle_v1_destroy(handle);
-}
-
-static void zwlr_foreign_handle_handle_state(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                             struct wl_array*) {
-}
-
-static void zwlr_foreign_handle_handle_parent(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                              struct zwlr_foreign_toplevel_handle_v1*) {
-}
-
-static void zwlr_foreign_handle_handle_output_enter(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                                    struct wl_output*) {
-}
-
-static void zwlr_foreign_handle_handle_output_leave(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
-                                                    struct wl_output*) {
-}
-
-static void zwlr_foreign_handle_handle_closed(void* data, struct zwlr_foreign_toplevel_handle_v1* handle) {
-}
-
-static const struct zwlr_foreign_toplevel_handle_v1_listener zwlr_handle_listener = {
-    .title = zwlr_foreign_handle_handle_title,
-    .app_id = zwlr_foreign_handle_handle_app_id,
-    .output_enter = zwlr_foreign_handle_handle_output_enter,
-    .output_leave = zwlr_foreign_handle_handle_output_leave,
-    .state = zwlr_foreign_handle_handle_state,
-    .done = zwlr_foreign_handle_handle_done,
-    .closed = zwlr_foreign_handle_handle_closed,
-    .parent = zwlr_foreign_handle_handle_parent
-};
-
-static void zwlr_toplevel_manager_handle_toplevel(void* data, struct zwlr_foreign_toplevel_manager_v1* manager,
-                                                  struct zwlr_foreign_toplevel_handle_v1* handle) {
-	std::vector<Window>* windows = reinterpret_cast<std::vector<Window>*>(data);
-	if (!windows)
-		return;
-
-	windows->push_back({0});
-	zwlr_foreign_toplevel_handle_v1_add_listener(handle, &zwlr_handle_listener, &*windows->end());
-}
-
-static void zwlr_toplevel_manager_handle_finished(void*, struct zwlr_foreign_toplevel_manager_v1*) {
-}
-
-static const struct zwlr_foreign_toplevel_manager_v1_listener zwlr_toplevel_manager_listener = {
-    .toplevel = zwlr_toplevel_manager_handle_toplevel,
-    .finished = zwlr_toplevel_manager_handle_finished,
-};
-
-/* ext_foreign_handle stuff */
-static void ext_foreign_handle_handle_app_id(void* data, struct ext_foreign_toplevel_handle_v1* handle,
-                                             const char* app_id) {
-	if (app_id)
-		reinterpret_cast<Window*>(data)->class_name = app_id;
-}
-
-static void ext_foreign_handle_handle_title(void* data, struct ext_foreign_toplevel_handle_v1* handle,
-                                            const char* title) {
-	if (title)
-		reinterpret_cast<Window*>(data)->text = title;
-}
-
-static void ext_foreign_handle_handle_identifier(void* data, ext_foreign_toplevel_handle_v1* handle, const char* identifier) {
-	if (identifier) {
-		reinterpret_cast<Window*>(data)->id = util::StringToInt(identifier, 0);
-	}
-}
-
-static void ext_foreign_handle_handle_done(void* data, struct ext_foreign_toplevel_handle_v1* handle) {
-	if (handle)
-		ext_foreign_toplevel_handle_v1_destroy(handle);
-}
-
-static void ext_foreign_handle_handle_closed(void*, struct ext_foreign_toplevel_handle_v1*) {
-}
-
-static const struct ext_foreign_toplevel_handle_v1_listener ext_handle_listener = {
-    .closed = ext_foreign_handle_handle_closed,
-    .done = ext_foreign_handle_handle_done,
-    .title = ext_foreign_handle_handle_title,
-    .app_id = ext_foreign_handle_handle_app_id,
-    .identifier = ext_foreign_handle_handle_identifier // for now
-};
-
-static void ext_toplevel_list_handle_toplevel(void* data, struct ext_foreign_toplevel_list_v1* list,
-                                              struct ext_foreign_toplevel_handle_v1* handle) {
-	std::vector<Window>* windows = reinterpret_cast<std::vector<Window>*>(data);
-	if (!windows)
-		return;
-
-	windows->push_back({0});
-	ext_foreign_toplevel_handle_v1_add_listener(handle, &ext_handle_listener, &*windows->end());
-}
-
-static void ext_toplevel_list_handle_finished(void*, ext_foreign_toplevel_list_v1*) {
-}
-
-static const struct ext_foreign_toplevel_list_v1_listener ext_toplevel_list_listener = {
-    .toplevel = ext_toplevel_list_handle_toplevel, .finished = ext_toplevel_list_handle_finished};
-
-/* -- Global data -- */
-struct global_data {
-	struct ext_foreign_toplevel_list_v1* ext_toplevel_list = nullptr;
-	struct zwlr_foreign_toplevel_manager_v1* zwlr_toplevel_mgr = nullptr;
-	std::vector<Window> windows;
-};
-
-static void registry_handle_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface,
-                                   uint32_t version) {
-	struct global_data* global = reinterpret_cast<struct global_data*>(data);
-
-	if (!std::strcmp(interface, ext_foreign_toplevel_list_v1_interface.name)) {
-		if (global->zwlr_toplevel_mgr)
-			return; // we don't need this then
-
-		global->ext_toplevel_list = reinterpret_cast<struct ext_foreign_toplevel_list_v1*>(
-		    wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, 1));
-
-		ext_foreign_toplevel_list_v1_add_listener(global->ext_toplevel_list, &ext_toplevel_list_listener,
-		                                          &global->windows);
-	} else if (!std::strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name)) {
-		if (global->ext_toplevel_list || version < 3)
-			return; // we don't need this then
-
-		global->zwlr_toplevel_mgr = reinterpret_cast<struct zwlr_foreign_toplevel_manager_v1*>(
-		    wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 1));
-
-		zwlr_foreign_toplevel_manager_v1_add_listener(global->zwlr_toplevel_mgr, &zwlr_toplevel_manager_listener,
-		                                              &global->windows);
-	}
-}
-
-static void registry_handle_global_remove(void*, wl_registry*, uint32_t) {
-}
-
-static const struct wl_registry_listener registry_listener = {.global = registry_handle_global,
-                                                              .global_remove = registry_handle_global_remove};
-
-/* BAH! humbug... */
-struct sync_data {
-	bool loop = true;
-	int sync = 0;
-
-	struct global_data global = {0};
-
-	struct wl_callback* callback = nullptr;
-	struct wl_display* display = nullptr;
-};
-
-static void sync_handle_done(void* data, struct wl_callback* callback, uint32_t other_data);
-static const struct wl_callback_listener sync_callback_listener = {
-    .done = sync_handle_done,
-};
-
-static void sync_handle_done(void* data, struct wl_callback* callback, uint32_t other_data) {
-	struct sync_data* sync = reinterpret_cast<struct sync_data*>(data);
-
-	wl_callback_destroy(callback);
-	sync->callback = nullptr;
-
-	if (sync->sync == 0) {
-		if (!sync->global.ext_toplevel_list && !sync->global.zwlr_toplevel_mgr) {
-			std::cerr << "animia/wayland: Wayland server doesn't support ext-foreign-toplevel-list-v1 nor "
-			             "wlr-foreign-toplevel-management-unstable-v1!"
-			          << std::endl;
-			sync->loop = false;
-			return;
-		}
-
-		sync->sync++;
-		sync->callback = wl_display_sync(sync->display);
-		wl_callback_add_listener(sync->callback, &sync_callback_listener, sync);
-
-		/* we may need another sync here if there are protocol extensions for
-		 * ext_foreign_toplevel_list.
-		 *
-		 * more info: https://git.sr.ht/~leon_plickat/lswt/tree/toplevel-info/item/lswt.c
-		 */
-	} else {
-		/* we've received everything we need! */
-		sync->loop = false;
-	}
-}
-
-/* here comes the actual function we can use */
-bool EnumerateWindows(window_proc_t window_proc) {
-	struct sync_data sync = {.display = wl_display_connect(NULL)};
-
-	if (!sync.display) {
-		std::cerr << "animia/wayland: Can't connect to display" << std::endl;
-		return false;
-	}
-
-	struct global_data global;
-
-	struct wl_registry* registry = wl_display_get_registry(sync.display);
-	wl_registry_add_listener(registry, &registry_listener, &sync.global);
-
-	sync.callback = wl_display_sync(sync.display);
-	wl_callback_add_listener(sync.callback, &sync_callback_listener, &sync);
-
-	while (sync.loop && wl_display_dispatch(sync.display));
-
-	for (const auto& window : global.windows)
-		if (!window_proc({0}, window))
-			return false;
-
-	wl_display_disconnect(sync.display);
-
-	return true;
-}
-
-} // namespace animia::internal::wayland
--- a/dep/animia/src/win/wayland/ext-foreign-toplevel-list-v1.c	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/* Generated by wayland-scanner 1.21.0 */
-
-/*
- * Copyright © 2018 Ilia Bozhinov
- * Copyright © 2020 Isaac Freund
- * Copyright © 2022 wb9688
- * Copyright © 2023 i509VCB
- *
- * Permission to use, copy, modify, distribute, and sell this
- * software and its documentation for any purpose is hereby granted
- * without fee, provided that the above copyright notice appear in
- * all copies and that both that copyright notice and this permission
- * notice appear in supporting documentation, and that the name of
- * the copyright holders not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no
- * representations about the suitability of this software for any
- * purpose.  It is provided "as is" without express or implied
- * warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include "wayland-util.h"
-
-#ifndef __has_attribute
-# define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
-#endif
-
-#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
-#define WL_PRIVATE __attribute__ ((visibility("hidden")))
-#else
-#define WL_PRIVATE
-#endif
-
-extern const struct wl_interface ext_foreign_toplevel_handle_v1_interface;
-
-static const struct wl_interface *ext_foreign_toplevel_list_v1_types[] = {
-	NULL,
-	&ext_foreign_toplevel_handle_v1_interface,
-};
-
-static const struct wl_message ext_foreign_toplevel_list_v1_requests[] = {
-	{ "stop", "", ext_foreign_toplevel_list_v1_types + 0 },
-	{ "destroy", "", ext_foreign_toplevel_list_v1_types + 0 },
-};
-
-static const struct wl_message ext_foreign_toplevel_list_v1_events[] = {
-	{ "toplevel", "n", ext_foreign_toplevel_list_v1_types + 1 },
-	{ "finished", "", ext_foreign_toplevel_list_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface ext_foreign_toplevel_list_v1_interface = {
-	"ext_foreign_toplevel_list_v1", 1,
-	2, ext_foreign_toplevel_list_v1_requests,
-	2, ext_foreign_toplevel_list_v1_events,
-};
-
-static const struct wl_message ext_foreign_toplevel_handle_v1_requests[] = {
-	{ "destroy", "", ext_foreign_toplevel_list_v1_types + 0 },
-};
-
-static const struct wl_message ext_foreign_toplevel_handle_v1_events[] = {
-	{ "closed", "", ext_foreign_toplevel_list_v1_types + 0 },
-	{ "done", "", ext_foreign_toplevel_list_v1_types + 0 },
-	{ "title", "s", ext_foreign_toplevel_list_v1_types + 0 },
-	{ "app_id", "s", ext_foreign_toplevel_list_v1_types + 0 },
-	{ "identifier", "s", ext_foreign_toplevel_list_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface ext_foreign_toplevel_handle_v1_interface = {
-	"ext_foreign_toplevel_handle_v1", 1,
-	1, ext_foreign_toplevel_handle_v1_requests,
-	5, ext_foreign_toplevel_handle_v1_events,
-};
-
--- a/dep/animia/src/win/wayland/wlr-foreign-toplevel-management-unstable-v1.c	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/* Generated by wayland-scanner 1.21.0 */
-
-/*
- * Copyright © 2018 Ilia Bozhinov
- *
- * Permission to use, copy, modify, distribute, and sell this
- * software and its documentation for any purpose is hereby granted
- * without fee, provided that the above copyright notice appear in
- * all copies and that both that copyright notice and this permission
- * notice appear in supporting documentation, and that the name of
- * the copyright holders not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no
- * representations about the suitability of this software for any
- * purpose.  It is provided "as is" without express or implied
- * warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include "wayland-util.h"
-
-#ifndef __has_attribute
-# define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
-#endif
-
-#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
-#define WL_PRIVATE __attribute__ ((visibility("hidden")))
-#else
-#define WL_PRIVATE
-#endif
-
-extern const struct wl_interface wl_output_interface;
-extern const struct wl_interface wl_seat_interface;
-extern const struct wl_interface wl_surface_interface;
-extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
-
-static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = {
-	NULL,
-	&zwlr_foreign_toplevel_handle_v1_interface,
-	&wl_seat_interface,
-	&wl_surface_interface,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	&wl_output_interface,
-	&wl_output_interface,
-	&wl_output_interface,
-	&zwlr_foreign_toplevel_handle_v1_interface,
-};
-
-static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = {
-	{ "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-};
-
-static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = {
-	{ "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 },
-	{ "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = {
-	"zwlr_foreign_toplevel_manager_v1", 3,
-	1, zwlr_foreign_toplevel_manager_v1_requests,
-	2, zwlr_foreign_toplevel_manager_v1_events,
-};
-
-static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = {
-	{ "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 },
-	{ "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 },
-	{ "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 },
-	{ "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-};
-
-static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = {
-	{ "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 },
-	{ "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 },
-	{ "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
-	{ "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 },
-};
-
-WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = {
-	"zwlr_foreign_toplevel_handle_v1", 3,
-	10, zwlr_foreign_toplevel_handle_v1_requests,
-	8, zwlr_foreign_toplevel_handle_v1_events,
-};
-
--- a/dep/animia/src/win/win32.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/*
- * 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
--- a/dep/animia/src/win/x11.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,234 +0,0 @@
-#include "animia/win/x11.h"
-#include "animia/win.h"
-#include "animia.h"
-
-#include <xcb/xcb.h>
-#include <xcb/res.h>
-
-#include <cstdint>
-#include <climits>
-#include <cstring>
-#include <string>
-#include <set>
-
-#include <chrono>
-
-#include <iostream>
-
-static size_t str_nlen(const char* s, size_t len) {
-    size_t i = 0;
-    for (; i < len && s[i]; i++);
-    return i;
-}
-
-namespace animia::internal::x11 {
-
-static void GetWindowPID(xcb_connection_t* connection, const std::vector<xcb_window_t>& windows, std::unordered_map<xcb_window_t, pid_t>& result) {
-	std::vector<xcb_res_query_client_ids_cookie_t> cookies;
-	cookies.reserve(windows.size());
-
-	for (const auto& window : windows) {
-		xcb_res_client_id_spec_t spec = {
-			.client = window,
-			.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID
-		};
-
-		cookies.push_back(::xcb_res_query_client_ids(connection, 1, &spec));
-	}
-
-	for (size_t i = 0; i < cookies.size(); i++) {
-		xcb_res_query_client_ids_reply_t* reply = ::xcb_res_query_client_ids_reply(connection, cookies.at(i), NULL);
-
-		xcb_res_client_id_value_iterator_t it = xcb_res_query_client_ids_ids_iterator(reply);
-		for (; it.rem; xcb_res_client_id_value_next(&it)) {
-			if (it.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
-				result[windows.at(i)] = *::xcb_res_client_id_value_value(it.data);
-				continue;
-			}
-		}
-	}
-}
-
-static bool GetAllTopLevelWindowsEWMH(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots, std::set<xcb_window_t>& result) {
-	const xcb_atom_t Atom__NET_CLIENT_LIST = [connection]{
-		static constexpr std::string_view name = "_NET_CLIENT_LIST";
-		xcb_intern_atom_cookie_t cookie = ::xcb_intern_atom(connection, true, name.size(), name.data());
-		xcb_intern_atom_reply_t* reply = ::xcb_intern_atom_reply(connection, cookie, NULL);
-
-		xcb_atom_t atom = reply->atom;
-		free(reply);
-		return atom;
-	}();
-	if (Atom__NET_CLIENT_LIST == XCB_ATOM_NONE)
-		return false; // BTFO
-
-	bool success = false;
-
-	std::vector<xcb_get_property_cookie_t> cookies;
-	cookies.reserve(roots.size());
-
-	for (const auto& root : roots)
-		cookies.push_back(::xcb_get_property(connection, 0, root, Atom__NET_CLIENT_LIST, XCB_ATOM_ANY, 0L, UINT_MAX));
-
-	for (const auto& cookie : cookies) {
-		xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, cookie, NULL);
-		if (reply) {
-			xcb_window_t* value = reinterpret_cast<xcb_window_t*>(::xcb_get_property_value(reply));
-			int len = ::xcb_get_property_value_length(reply);
-
-			for (size_t i = 0; i < len; i++)
-				result.insert(value[i]);
-			success = true;
-		}
-		free(reply);
-	}
-
-	return success;
-}
-
-/* I have no idea why this works. */
-static bool WalkWindows(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots, std::set<xcb_window_t>& result) {
-	/* move this somewhere */
-	xcb_atom_t Atom_WM_STATE = [connection]{
-		static constexpr std::string_view name = "WM_STATE";
-		xcb_intern_atom_cookie_t cookie = ::xcb_intern_atom(connection, true, name.size(), name.data());
-		xcb_intern_atom_reply_t* reply = ::xcb_intern_atom_reply(connection, cookie, NULL);
-
-		xcb_atom_t atom = reply->atom;
-		free(reply);
-		return atom;
-	}();
-	if (Atom_WM_STATE == XCB_ATOM_NONE)
-		return false;
-
-	std::vector<xcb_query_tree_cookie_t> cookies;
-	cookies.reserve(roots.size());
-
-	for (const auto& root : roots)
-		cookies.push_back(::xcb_query_tree(connection, root));
-
-	for (const auto& cookie : cookies) {
-		xcb_query_tree_reply_t* reply = ::xcb_query_tree_reply(connection, cookie, NULL);
-
-		std::vector<xcb_window_t> windows = [reply]{
-			xcb_window_t* windows = ::xcb_query_tree_children(reply);
-			int len = ::xcb_query_tree_children_length(reply);
-
-			std::vector<xcb_window_t> w;
-			w.reserve(len);
-
-			for (int i = 0; i < len; i++)
-				w.push_back(windows[i]);
-
-			return w;
-		}();
-
-		std::vector<xcb_get_property_cookie_t> state_property_cookies;
-		state_property_cookies.reserve(windows.size());
-
-		for (int i = 0; i < windows.size(); i++)
-			state_property_cookies.push_back(::xcb_get_property(connection, 0, windows[i], Atom_WM_STATE, Atom_WM_STATE, 0, 0));
-
-		for (size_t i = 0; i < state_property_cookies.size(); i++) {
-			xcb_generic_error_t* err = NULL;
-			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, state_property_cookies.at(i), &err);
-
-			if (reply->format || reply->type || reply->length) {
-				result.insert(windows[i]);
-				free(reply);
-				continue;
-			}
-
-			free(reply);
-		}
-
-		if (WalkWindows(connection, windows, result))
-			continue;
-	}
-
-	return false;
-}
-
-bool EnumerateWindows(window_proc_t window_proc) {
-	if (!window_proc)
-		return false;
-
-	xcb_connection_t* connection = ::xcb_connect(NULL, NULL);
-	if (!connection)
-		return false;
-
-	std::set<xcb_window_t> windows;
-	{
-		std::vector<xcb_window_t> roots;
-		{
-			xcb_screen_iterator_t iter = ::xcb_setup_roots_iterator(xcb_get_setup(connection));
-			for (; iter.rem; ::xcb_screen_next(&iter))
-				roots.push_back(iter.data->root);
-		}
-
-		
-		if (!GetAllTopLevelWindowsEWMH(connection, roots, windows))
-			WalkWindows(connection, roots, windows);
-	}
-
-	std::vector<xcb_get_property_cookie_t> class_property_cookies;
-	std::vector<xcb_get_property_cookie_t> name_property_cookies;
-	std::vector<xcb_get_property_cookie_t> pid_property_cookies;
-	class_property_cookies.reserve(windows.size());
-	name_property_cookies.reserve(windows.size());
-	pid_property_cookies.reserve(windows.size());
-
-	for (const auto& window : windows) {
-		class_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0L, 2048L));
-		name_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0L, UINT_MAX));
-		pid_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_CARDINAL, 0L, 1L));
-	}
-
-	size_t i = 0;
-	for (const auto& window : windows) {
-		Window win = {0};
-		win.id = window;
-		{
-			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, class_property_cookies.at(i), NULL);
-			if (reply && reply->format == 8) {
-				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply));
-				const int data_len = ::xcb_get_property_value_length(reply);
-
-				int instance_len = str_nlen(data, data_len);
-				const char* class_name = data + instance_len + 1;
-
-				win.class_name = std::string(class_name, str_nlen(class_name, data_len - (instance_len + 1)));
-			}
-			free(reply);
-		}
-		{
-			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, name_property_cookies.at(i), NULL);
-			if (reply) {
-				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply));
-				int len = ::xcb_get_property_value_length(reply);
-
-				win.text = std::string((char*)data, len);
-			}
-			free(reply);
-		}
-		Process proc = {0};
-		{
-			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, pid_property_cookies.at(i), NULL);
-			if (reply)
-				proc.pid = *reinterpret_cast<uint32_t*>(::xcb_get_property_value(reply));
-
-			free(reply);
-		}
-		if (!window_proc(proc, win)) {
-			::xcb_disconnect(connection);
-			return false;
-		}
-		i++;
-	}
-
-	::xcb_disconnect(connection);
-
-	return true;
-}
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/.clang-format	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,38 @@
+---
+BasedOnStyle: LLVM
+UseTab: ForIndentation
+PointerAlignment: Left
+ColumnLimit: 120
+IndentWidth: 4
+TabWidth: 4
+
+# hack!!!
+AccessModifierOffset: -4
+
+IndentCaseLabels: true
+IndentAccessModifiers: false
+IndentPPDirectives: AfterHash
+
+BreakBeforeBraces: Attach
+BreakStringLiterals: true
+
+AlwaysBreakTemplateDeclarations: true
+
+SpaceAfterTemplateKeyword: false
+
+AlignAfterOpenBracket: Align
+AlignArrayOfStructures: Left
+AlignEscapedNewlines: DontAlign
+AlignConsecutiveMacros: true
+
+AllowShortIfStatementsOnASingleLine: false
+AllowShortBlocksOnASingleLine: Empty
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: InlineOnly
+AllowShortCaseLabelsOnASingleLine: true
+
+Cpp11BracedListStyle: true
+
+---
+Language: Cpp
+Standard: c++17
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/.hgignore	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,40 @@
+syntax: glob
+
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+syntax: regexp
+
+# Build dir
+^build/
+^test/build/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/LICENSE.BSD	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2023-2024, Paper
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/LICENSE.MIT	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2017 Eren Okka
+Copyright (c) 2023 Paper
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/Makefile.am	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,83 @@
+lib_LTLIBRARIES = libanimone.la
+
+include_HEADERS = \
+	include/animone.h
+
+animiadir = $(includedir)/animone
+nobase_animia_HEADERS = \
+	include/animone/media.h \
+	include/animone/player.h \
+	include/animone/types.h
+
+noinst_HEADERS = \
+	include/animone/fd/kvm.h \
+	include/animone/fd/proc.h \
+	include/animone/fd/win32.h \
+	include/animone/fd/xnu.h \
+	include/animone/util/osx.h \
+	include/animone/util/win32.h \
+	include/animone/win/quartz.h \
+	include/animone/win/win32.h \
+	include/animone/win/x11.h \
+	include/animone/fd.h \
+	include/animone/strategies.h \
+	include/animone/util.h \
+	include/animone/win.h
+
+if BUILD_WIN
+files_win = src/fd/win32.cc src/win/win32.cc src/util/win32.cc
+libs_win = -lole32 -luuid
+endif
+
+if BUILD_OSX
+files_osx = src/fd/xnu.cc src/win/quartz.cc src/util/osx.cc
+libs_osx = -lobjc
+ldflags_osx = -framework Foundation -framework CoreGraphics -framework ApplicationServices
+endif
+
+if BUILD_LINUX
+files_linux = src/fd/proc.cc
+endif
+
+# these should be in standard locations anyway
+if BUILD_LIBUTIL
+libs_libutil = -lutil
+endif
+
+if BUILD_LIBKVM
+files_libkvm = src/fd/kvm.cc
+libs_libkvm = -lkvm
+endif
+
+if BUILD_XCB
+files_x11 = src/win/x11.cc
+cflags_x11 = $(XCB_CFLAGS)
+libs_x11 = $(XCB_LIBS)
+endif
+
+EXTRA_DIST = \
+	$(top_srcdir)/data/players.anisthesia
+
+libanimone_la_SOURCES = \
+	src/animone.cc \
+	src/fd.cc \
+	src/player.cc \
+	src/strategist.cc \
+	src/util.cc \
+	src/win.cc \
+	$(files_win) \
+	$(files_osx) \
+	$(files_linux) \
+	$(files_libutil) \
+	$(files_libkvm) \
+	$(files_x11) \
+	$(files_wayland)
+
+libanimone_la_CPPFLAGS = -I$(top_srcdir)/include $(DEFS)
+
+libanimone_la_CXXFLAGS = -std=c++17 $(cflags_osx) $(cflags_x11) $(cflags_wayland)
+libanimone_la_LDFLAGS = -version-info 0:0:0 $(ldflags_osx)
+
+libanimone_la_LIBADD = $(libs_win) $(libs_wayland) $(libs_x11) $(libs_osx) $(libs_libutil) $(libs_libkvm)
+
+ACLOCAL_AMFLAGS = -I m4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/README.md	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,36 @@
+# Animia
+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
+(at least on Windows).
+
+## License
+Changes divergent from Anisthesia are under the BSD 3-clause license. You can
+find a copy of the original MIT license bundled with Anisthesia at `LICENSE.MIT`
+in the root folder.
+
+## Support
+Unlike Anisthesia, Animia currently does not support UI automation, i.e., most
+web browsers will not work properly, if at all.
+
+Animia will first attempt to connect to a windowing system. If that fails, it falls
+back to just enumerating over the open processes in the system.
+
+## Platform-specific quirks
+
+### Windows
+To get the currently opened file handles on Windows, Animia has to use internal
+kernel functions. However, these functions aren't likely to change anytime soon.
+
+### macOS
+The code to retrieve executable names on macOS uses internal functions. However,
+if these functions cannot be found for whatever reason, it falls back to parsing
+the arguments, and then to calling the kernel.
+
+Additionally, macOS does not have the concept of class names, rather, it has
+bundle identifiers, which are a suitable replacement in most use cases, and are
+what Animia will try to grab before falling back to the Quartz window name.
+
+### X11
+Animia requires that the XRes extension is installed to retrieve window PIDs.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/configure.ac	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,75 @@
+AC_INIT([animone], [0.1.0-alpha.1])
+
+AC_CANONICAL_HOST
+
+AC_CONFIG_SRCDIR([src/animone.cc])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIRS([m4])
+
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+
+# Do we have a C++17 compiler
+AC_PROG_CXX
+
+AM_PROG_AR
+LT_INIT
+
+build_win32=no
+build_osx=no
+build_linux=no
+build_libutil=no
+build_kvm=no
+
+build_x11=no
+
+case "${host_os}" in
+	cygwin*|mingw*)
+		# Windows
+		build_windows=yes
+		AC_CHECK_TOOL([WINDRES], [windres])
+		AC_SUBST(WINDRES)
+		AC_DEFINE([WIN32])
+		;;
+	darwin*)
+		# Mac OS X
+		build_osx=yes
+		AC_DEFINE([MACOSX])
+		;;
+	linux*)
+		build_linux=yes
+		AC_DEFINE([LINUX])
+		;;
+	*)
+		# FreeBSD
+		AC_CHECK_LIB([util], [kinfo_getfile], [build_libutil=yes], [build_libutil=no])
+		if test "x$build_libutil" = "xyes"; then
+			AC_DEFINE([LIBUTIL])
+		else
+			# OpenBSD
+			AC_CHECK_LIB([kvm], [kvm_getfiles], [build_kvm=yes], [build_kvm=no])
+			if test "x$build_kvm" = "xyes"; then
+				AC_DEFINE([LIBKVM])
+			fi
+		fi
+		;;
+esac
+
+if test "x$build_osx" = "xno" && test "x$build_windows" = "xno"; then
+	PKG_CHECK_MODULES(XCB, [xcb xcb-res], [build_x11=yes], [build_x11=no])
+	if test "x$build_x11" = "xyes"; then
+		AC_DEFINE([X11])
+		AC_SUBST([XCB_LIBS])
+		AC_SUBST([XCB_CFLAGS])
+	fi
+fi
+
+AM_CONDITIONAL([BUILD_WIN], [test "x$build_windows" = "xyes"])
+AM_CONDITIONAL([BUILD_OSX], [test "x$build_osx" = "xyes"])
+AM_CONDITIONAL([BUILD_LINUX], [test "x$build_linux" = "xyes"])
+AM_CONDITIONAL([BUILD_LIBUTIL], [test "x$build_libutil" = "xyes"])
+AM_CONDITIONAL([BUILD_LIBKVM], [test "x$build_kvm" = "xyes"])
+
+AM_CONDITIONAL([BUILD_XCB], [test "x$build_x11" = "xyes"])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/data/players.anisthesia	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,496 @@
+# This file includes media player data for Anisthesia. It is used to detect
+# running players and retrieve information about current media.
+#
+# Please read before editing this file:
+# - Indentation is significant. You must use tabs rather than spaces.
+# - Regular expressions begin with a '^' character. ECMAScript grammar is used.
+#
+# The latest version of this file can be found at:
+# <https://github.com/erengy/anisthesia>
+#
+# This file is in the public domain.
+
+5KPlayer
+	windows:
+		Qt5QWindowIcon
+	executables:
+		5KPlayer
+	strategies:
+		open_files
+
+Ace Player HD
+	windows:
+		QWidget
+	executables:
+		ace_player
+	strategies:
+		# Must be enabled from: Advanced Preferences -> Interface -> Main
+		# interfaces -> Qt -> Show playing item name in window title
+		#
+		# We use the last alternative to avoid detecting other windows such as
+		# Preferences dialog, which has the same generic class name.
+		window_title:
+			^Ace Player HD.*|(.+) - Ace Player HD.*|.+
+
+ALLPlayer
+	windows:
+		TApplication
+	executables:
+		ALLPlayer
+	strategies:
+		open_files
+
+Baka MPlayer
+	windows:
+		Qt5QWindowIcon
+	executables:
+		Baka MPlayer
+	strategies:
+		open_files
+		# We cannot avoid detecting other windows such as Preferences dialog, which
+		# has the same generic class name.
+		window_title:
+			^Baka MPlayer|(.+)
+
+BESTplayer
+	windows:
+		TBESTplayerApp.UnicodeClass
+	executables:
+		BESTplayer
+	strategies:
+		open_files
+		window_title:
+			^BESTplayer.*|(.+) - BESTplayer.*
+
+bomi
+	windows:
+		Qt5QWindowGLOwnDCIcon
+	executables:
+		bomi
+	strategies:
+		open_files
+		window_title:
+			^bomi|(.+) - bomi
+
+BS.Player
+	windows:
+		BSPlayer
+	executables:
+		bsplayer
+	strategies:
+		open_files
+
+DivX Player
+	windows:
+		Qt5QWindowIcon
+		QWidget
+	executables:
+		DivX Player
+		DivX Plus Player
+	strategies:
+		open_files
+
+GOM Player
+	windows:
+		GomPlayer1.x
+		GomPlayerPlus32_2.x
+		GomPlayerPlus64_2.x
+	executables:
+		GOM
+		GOM64
+	strategies:
+		open_files
+		window_title:
+			^GOM Player(?: Plus)|(.+)(?:\[Subtitle\]) - GOM Player(?: Plus)
+
+Kantaris
+	windows:
+		^WindowsForms10\.Window\.20008\.app\..+
+	executables:
+		Kantaris
+		KantarisMain
+	strategies:
+		open_files
+		window_title:
+			^Kantaris.*|(.+) \d{2}:\d{2}:\d{2} - \d{2}:\d{2}:\d{2}
+
+KMPlayer
+	windows:
+		KMPlayer 64X
+		TApplication
+	executables:
+		KMPlayer
+		KMPlayer64
+	strategies:
+		open_files
+		window_title:
+			^(?:The )?KMPlayer|(?:\[\d+/\d+\] )?(.+) - (?:The )?KMPlayer|(.+)
+
+Kodi
+	windows:
+		Kodi
+		XBMC
+	executables:
+		kodi
+		XBMC
+	strategies:
+		open_files
+
+Light Alloy
+	windows:
+		TApplication
+	executables:
+		LA
+	strategies:
+		open_files
+		window_title:
+			^Light Alloy.*|(.+) - Light Alloy.*
+
+Media Player Classic
+	windows:
+		MediaPlayerClassicW
+	executables:
+		mplayerc
+		mplayerc64
+	strategies:
+		open_files
+		# Depends on: Options -> Player -> Title bar
+		window_title:
+			^Media Player Classic|(.+) - Media Player Classic
+
+Media Player Classic Qute Theater
+	windows:
+		^Qt.+QWindowIcon
+	executables:
+		mpc-qt
+	strategies:
+		open_files
+		# Depends on: Options -> Player -> Title bar
+		#
+		# We use the last alternative to avoid detecting other windows such as
+		# Options dialog, which has the same generic class name.
+		window_title:
+			^Media Player Classic Qute Theater|Media Player Classic Qute Theater - (.+)|.+
+
+Memento
+	windows:
+		^Qt.+QWindowIcon
+	executables:
+		memento
+	strategies:
+		open_files
+		window_title:
+			^Memento|(.+) - Memento
+
+Miro
+	windows:
+		gdkWindowToplevel
+	executables:
+		Miro
+	strategies:
+		open_files
+
+MPC-BE
+	windows:
+		MediaPlayerClassicW
+		MPC-BE
+	executables:
+		mpc-be
+		mpc-be64
+	strategies:
+		open_files
+		# Depends on: Options -> Player -> Title bar
+		window_title:
+			^MPC-BE.*|(.+) - MPC-BE.*
+
+MPC-HC
+	windows:
+		MediaPlayerClassicW
+	executables:
+		mpc-hc
+		mpc-hc64
+		# Some codec installers append "_nvo" to the filename, if NVIDIA Optimus
+		# is present on the system. Similarly, various guides recommend
+		# appending "-gpu", etc. in order to fix some GPU-related issues.
+		^mpc-hc.+
+		# LAV Filters Megamix
+		iris
+		shoukaku
+	strategies:
+		open_files
+		# Depends on: Options -> Player -> Title bar
+		window_title:
+			^Media Player Classic Home Cinema|MPC-HC|(.+)
+
+MPCSTAR
+	windows:
+		^wxWindow@.*
+		wxWindowClassNR
+	executables:
+		mpcstar
+	strategies:
+		open_files
+		window_title:
+			^MPCSTAR.*|(.+) - MPCSTAR.*
+
+MPDN
+	windows:
+		^WindowsForms10\.Window\.8\.app\..+
+	executables:
+		MediaPlayerDotNet
+	strategies:
+		open_files
+		window_title:
+			^MPDN - Media Player .NET \((?:32|64)-bit Edition\)|(.*) - MPDN \((?:32|64)-bit Edition\)
+
+mpv
+	windows:
+		mpv
+	executables:
+		mpv
+	strategies:
+		open_files
+		# May be in an unexpected format if "--title" option is used. Ideally, it
+		# should return only "${filename}", "${path}" or "${media-title}".
+		window_title:
+			^No file - mpv|(.+) - mpv|mpv - (.+)
+
+mpv.net
+	windows:
+		^WindowsForms10\.Window\.8\.app\..+
+	executables:
+		mpvnet
+	strategies:
+		open_files
+		window_title:
+			^mpv\.net.*|(.+) - mpv\.net.*
+
+MV2Player
+	windows:
+		TApplication
+	executables:
+		Mv2Player
+		Mv2PlayerPlus
+	strategies:
+		open_files
+		# Depends on: Options -> Player -> Constant app. title
+		window_title:
+			^MV2 Player|(.+)
+
+PotPlayer
+	windows:
+		PotPlayer
+		PotPlayer64
+	executables:
+		PotPlayer
+		PotPlayer64
+		PotPlayerMini
+		PotPlayerMini64
+		# LAV Filters Megamix
+		sumire
+		zuikaku
+	strategies:
+		open_files
+		window_title:
+			^PotPlayer|(.+) - PotPlayer
+
+SMPlayer
+	windows:
+		# Qt5QWindowIcon, Qt5152QWindowIcon, etc.
+		^Qt.+QWindowIcon
+		# Older versions
+		QWidget
+	executables:
+		smplayer
+		smplayer2
+	strategies:
+		# "open_files" strategy does not work here, because files are loaded by
+		# a child process of SMPlayer (mplayer or mpv, depending on the selected
+		# multimedia engine).
+		#
+		# We use the last alternative to avoid detecting other windows such as
+		# Preferences dialog, which has the same generic class name.
+		window_title:
+			^SMPlayer|(.+) - SMPlayer|.+
+
+Splash
+	windows:
+		DX_DISPLAY0
+	executables:
+		Splash
+		SplashLite
+	strategies:
+		open_files
+
+SPlayer
+	windows:
+		MediaPlayerClassicW
+	executables:
+		splayer
+	strategies:
+		open_files
+		# Does not work in theater mode.
+		window_title:
+			^SPlayer|(?:\[(?:GPU Accel\+)?EVR\] )?(.+) - SPlayer
+
+UMPlayer
+	windows:
+		QWidget
+	executables:
+		umplayer
+	strategies:
+		# "open_files" strategy does not work here, because files are loaded by
+		# a child process of UMPlayer (mplayer).
+		#
+		# We use the last alternative to avoid detecting other windows such as
+		# Preferences dialog, which has the same generic class name.
+		window_title:
+			^UMPlayer|(.+) - UMPlayer|.+
+
+VLC media player
+	windows:
+		# Qt5QWindowIcon, Qt5151QWindowIcon, etc.
+		^Qt.+QWindowIcon
+		# Older versions
+		QWidget
+		# Skinnable interface
+		SkinWindowClass
+		# X11
+		vlc
+	executables:
+		vlc
+	strategies:
+		open_files
+		# Must be enabled from: Advanced Preferences -> Interface -> Main
+		# interfaces -> Qt -> Show playing item name in window title
+		#
+		# We use the last alternative to avoid detecting other windows such as
+		# Preferences dialog, which has the same generic class name.
+		window_title:
+			^VLC media player|(.+) - VLC media player|.+
+
+WebTorrent Desktop
+	windows:
+		Chrome_WidgetWin_1
+	executables:
+		WebTorrent
+	strategies:
+		window_title:
+			^WebTorrent(?: \(BETA\))?|Main Window|Preferences|About WebTorrent.*|(.+)
+
+Winamp
+	windows:
+		Winamp v1.x
+	executables:
+		winamp
+	strategies:
+		open_files
+		window_title:
+			^Winamp [\d.]+ Build \d+|\d+\. (.+) - Winamp(?: \[.+\])?
+
+Windows Media Player
+	windows:
+		WMPlayerApp
+		WMP Skin Host
+	executables:
+		wmplayer
+	strategies:
+		open_files
+
+Zoom Player
+	windows:
+		TApplication
+	executables:
+		zplayer
+	strategies:
+		open_files
+		window_title:
+			^Zoom Player|(.+) - Zoom Player (?:FREE|MAX)
+
+################################################################################
+# Web browsers
+
+Brave Browser
+	windows:
+		Chrome_WidgetWin_1
+	executables:
+		brave
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) \(Private\)(?: - Brave)?|(.+) - Brave|(.+)
+	type:
+		web_browser
+
+Google Chrome
+	windows:
+		Chrome_WidgetWin_1
+	executables:
+		chrome
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) \(Incognito\)(?: - Google Chrome)?|(.+) - Google Chrome|(.+)
+	type:
+		web_browser
+
+Internet Explorer
+	windows:
+		IEFrame
+	executables:
+		iexplore
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) - Internet Explorer(?: - \[InPrivate\])?
+	type:
+		web_browser
+
+Microsoft Edge
+	windows:
+		Chrome_WidgetWin_1
+	executables:
+		msedge
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) and \d+ more pages? - .+|(.+) - [^-]+ - Microsoft.*Edge|(.+)
+	type:
+		web_browser
+
+Mozilla Firefox
+	windows:
+		MozillaUIWindowClass
+		MozillaWindowClass
+	executables:
+		firefox
+	strategies:
+		ui_automation
+		window_title:
+			^(?:Mozilla Firefox|Firefox Developer Edition)|(.+) (?:-|—) (?:Mozilla Firefox|Firefox Developer Edition)(?: \(Private Browsing\))?
+	type:
+		web_browser
+
+Opera
+	windows:
+		Chrome_WidgetWin_1
+	executables:
+		opera
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) - Opera(?: \(Private\))?
+	type:
+		web_browser
+
+Waterfox
+	windows:
+		MozillaWindowClass
+	executables:
+		waterfox
+	strategies:
+		ui_automation
+		window_title:
+			^(.+) - Waterfox(?: \(Private Browsing\))?
+	type:
+		web_browser
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,37 @@
+#ifndef ANIMONE_ANIMONE_H_
+#define ANIMONE_ANIMONE_H_
+
+#include "animone/media.h"
+#include "animone/player.h"
+#include "animone/types.h"
+
+namespace animone {
+
+enum class ResultType {
+	Process,
+	Window
+};
+
+struct Process {
+	internal::pid_t pid = 0; /* pid_t == DWORD on Windows, from <sys/types.h> everywhere else */
+	std::string name;
+};
+
+struct Window {
+	unsigned int id = 0;
+	std::string class_name;
+	std::string text; /* title bar text */
+};
+
+struct Result {
+	Player player;
+	Process process;
+	Window window; /* has nothing under process mode */
+	std::vector<Media> media;
+};
+
+bool GetResults(const std::vector<Player>& players, std::vector<Result>& results);
+
+} // namespace animone
+
+#endif // ANIMONE_ANIMONE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,32 @@
+#ifndef ANIMONE_ANIMONE_FD_H_
+#define ANIMONE_ANIMONE_FD_H_
+
+#include <functional>
+#include <set>
+#include <string>
+
+#include "animone/types.h"
+
+namespace animone {
+
+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&)>;
+
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace internal
+
+} // namespace animone
+
+#endif // ANIMONE_ANIMONE_FD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/kvm.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,17 @@
+#ifndef ANIMONE_ANIMONE_FD_KVM_H_
+#define ANIMONE_ANIMONE_FD_KVM_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::kvm {
+
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::kvm
+
+#endif // ANIMONE_ANIMONE_FD_KVM_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/proc.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,18 @@
+#ifndef ANIMONE_ANIMONE_FD_PROC_H_
+#define ANIMONE_ANIMONE_FD_PROC_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::proc {
+
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+bool GetProcessName(pid_t pid, std::string& result);
+
+} // namespace animone::internal::proc
+
+#endif // ANIMONE_ANIMONE_FD_PROC_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/win32.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,19 @@
+#ifndef ANIMONE_ANIMONE_FD_WIN32_H_
+#define ANIMONE_ANIMONE_FD_WIN32_H_
+
+#include <set>
+#include <string>
+
+#include <windows.h>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::win32 {
+
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::win32
+
+#endif // ANIMONE_ANIMONE_FD_WIN32_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/xnu.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,17 @@
+#ifndef ANIMONE_ANIMONE_FD_XNU_H_
+#define ANIMONE_ANIMONE_FD_XNU_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::xnu {
+
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::xnu
+
+#endif // ANIMONE_ANIMONE_FD_XNU_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/media.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,32 @@
+#ifndef ANIMONE_ANIMONE_MEDIA_H_
+#define ANIMONE_ANIMONE_MEDIA_H_
+
+#include <chrono>
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace animone {
+
+using media_time_t = std::chrono::milliseconds;
+
+enum class MediaInfoType {
+	Unknown,
+	File,
+	Tab,
+	Title,
+	Url
+};
+
+struct MediaInfo {
+	MediaInfoType type = MediaInfoType::Unknown;
+	std::string value;
+};
+
+struct Media {
+	std::vector<MediaInfo> information;
+};
+
+} // namespace animone
+
+#endif // ANIMONE_ANIMONE_MEDIA_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/player.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,34 @@
+#ifndef ANIMONE_ANIMONE_PLAYER_H_
+#define ANIMONE_ANIMONE_PLAYER_H_
+
+#include <string>
+#include <vector>
+
+namespace animone {
+
+enum class Strategy {
+	WindowTitle,
+	OpenFiles,
+	UiAutomation // unused
+};
+
+enum class PlayerType {
+	Default,
+	WebBrowser // unused
+};
+
+struct Player {
+	PlayerType type = PlayerType::Default;
+	std::string name;
+	std::string window_title_format;
+	std::vector<std::string> windows;
+	std::vector<std::string> executables;
+	std::vector<Strategy> strategies;
+};
+
+bool ParsePlayersData(const std::string& data, std::vector<Player>& players);
+bool ParsePlayersFile(const std::string& path, std::vector<Player>& players);
+
+} // namespace animone
+
+#endif // ANIMONE_ANIMONE_PLAYER_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/strategies.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,13 @@
+#ifndef ANIMONE_ANIMONE_STRATEGIES_H_
+#define ANIMONE_ANIMONE_STRATEGIES_H_
+
+#include "animone.h"
+#include <vector>
+
+namespace animone::internal {
+
+bool ApplyStrategies(std::vector<Result>& results);
+
+}
+
+#endif // ANIMONE_ANIMONE_STRATEGIES_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/types.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,18 @@
+#ifndef ANIMONE_ANIMONE_TYPES_H_
+#define ANIMONE_ANIMONE_TYPES_H_
+
+/* define this as unsigned long (DWORD) on win32 so we
+   don't force the user to include <windows.h> or <IntBase.h> */
+#ifdef _WIN32
+namespace animone::internal {
+typedef unsigned long pid_t;
+}
+#else
+/* <sys/types.h> shouldn't be that big, right? */
+#	include <sys/types.h>
+namespace animone::internal {
+typedef ::pid_t pid_t;
+}
+#endif
+
+#endif // ANIMONE_ANIMONE_TYPES_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/util.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,25 @@
+#ifndef ANIMONE_ANIMONE_UTIL_H_
+#define ANIMONE_ANIMONE_UTIL_H_
+
+#include <sstream>
+#include <string>
+
+namespace animone::internal::util {
+
+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);
+
+template<typename T = int, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+T StringToInt(const std::string& str, T def = 0) {
+	std::istringstream s(str);
+	s >> std::noboolalpha >> def;
+	return def;
+}
+
+} // namespace animone::internal::util
+
+#endif // ANIMONE_ANIMONE_UTIL_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/util/osx.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,40 @@
+#ifndef ANIMONE_ANIMONE_UTIL_OSX_H_
+#define ANIMONE_ANIMONE_UTIL_OSX_H_
+
+#include "animone/types.h"
+#include <cstdint>
+#include <string>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace animone::internal::osx::util {
+
+template<typename T>
+bool GetCFNumber(CFNumberRef num, T& result) {
+	if (!num)
+		return false;
+
+	int64_t res;
+	if (!CFNumberGetValue(num, static_cast<CFNumberType>(4), &res))
+		return false;
+
+	result = static_cast<T>(res);
+	return true;
+}
+
+template<typename T>
+struct CFDeconstructor {
+	using pointer = T;
+	void operator()(pointer t) const { ::CFRelease(t); };
+};
+
+template<typename T>
+using CFPtr = vector<T, CFDecontructor<T>>; // type-id is vector<T, Alloc<T>>
+
+bool StringFromCFString(CFStringRef string, std::string& result);
+
+bool GetProcessName(pid_t pid, std::string& result);
+
+} // namespace animone::internal::osx::util
+
+#endif // ANIMONE_ANIMONE_UTIL_OSX_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/util/win32.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,33 @@
+#ifndef ANIMONE_ANIMONE_UTIL_WIN32_H_
+#define ANIMONE_ANIMONE_UTIL_WIN32_H_
+
+#include <subauth.h>
+#include <windows.h>
+
+#include <memory>
+#include <string>
+
+namespace animone::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);
+
+} // namespace animone::internal::win32
+
+#endif // ANIMONE_ANIMONE_UTIL_WIN32_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/win.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,22 @@
+#ifndef ANIMONE_ANIMONE_WIN_H_
+#define ANIMONE_ANIMONE_WIN_H_
+
+#include <functional>
+#include <string>
+
+namespace animone {
+
+struct Process;
+struct Window;
+
+namespace internal {
+
+using window_proc_t = std::function<bool(const Process&, const Window&)>;
+
+bool EnumerateWindows(window_proc_t window_proc);
+
+} // namespace internal
+
+} // namespace animone
+
+#endif // ANIMONE_ANIMONE_WIN_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/win/quartz.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,12 @@
+#ifndef ANIMONE_ANIMONE_WIN_QUARTZ_H_
+#define ANIMONE_ANIMONE_WIN_QUARTZ_H_
+
+#include "animone/win.h"
+
+namespace animone::internal::quartz {
+
+bool EnumerateWindows(window_proc_t window_proc);
+
+} // namespace animone::internal::quartz
+
+#endif // ANIMONE_ANIMONE_WIN_QUARTZ_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/win/win32.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,12 @@
+#ifndef ANIMONE_ANIMONE_WIN_WIN32_H_
+#define ANIMONE_ANIMONE_WIN_WIN32_H_
+
+#include "animone/win.h"
+
+namespace animone::internal::win32 {
+
+bool EnumerateWindows(window_proc_t window_proc);
+
+} // namespace animone::internal::win32
+
+#endif // ANIMONE_ANIMONE_WIN_WIN32_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/win/x11.h	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,12 @@
+#ifndef ANIMONE_ANIMONE_WIN_X11_H_
+#define ANIMONE_ANIMONE_WIN_X11_H_
+
+#include "animone/win.h"
+
+namespace animone::internal::x11 {
+
+bool EnumerateWindows(window_proc_t window_proc);
+
+} // namespace animone::internal::x11
+
+#endif // ANIMONE_ANIMONE_WIN_X11_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/animone.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,76 @@
+#include "animone.h"
+#include "animone/fd.h"
+#include "animone/strategies.h"
+#include "animone/types.h"
+#include "animone/util.h"
+#include "animone/win.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+namespace animone {
+
+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;
+
+	return false;
+}
+
+static bool IsWindowInList(const Player& player, const Window& window) {
+	for (const auto& pattern : player.windows)
+		if (util::CheckPattern(pattern, window.class_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) {
+	auto window_proc = [&](const Process& process, const Window& window) -> bool {
+		for (const auto& player : players) {
+			if (internal::IsWindowInList(player, window))
+				results.push_back({player, process, window, {}});
+		}
+
+		return true;
+	};
+
+	if (internal::EnumerateWindows(window_proc))
+		return internal::ApplyStrategies(results);
+
+	/* fallback, enumerate over open processes instead */
+	auto process_proc = [&](const Process& process) -> bool {
+		for (const auto& player : players)
+			if (internal::IsExecutableInList(player, process.name))
+				results.push_back({player, process, {}, {}});
+
+		return true;
+	};
+
+	if (internal::EnumerateOpenProcesses(process_proc))
+		return internal::ApplyStrategies(results);
+
+	return false;
+}
+
+} // namespace animone
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,88 @@
+#include "animone/fd.h"
+
+#ifdef WIN32
+#	include "animone/fd/win32.h"
+#endif
+
+#ifdef LINUX
+#	include "animone/fd/proc.h"
+#endif
+
+#ifdef MACOSX
+#	include "animone/fd/xnu.h"
+#	include "animone/util/osx.h"
+#endif
+
+#ifdef LIBKVM
+#	include "animone/fd/kvm.h"
+#endif
+
+namespace animone::internal {
+
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	bool success = false;
+
+#ifdef WIN32
+	success ^= win32::EnumerateOpenFiles(pids, open_file_proc);
+#endif
+
+#ifdef LINUX
+	success ^= proc::EnumerateOpenFiles(pids, open_file_proc);
+#endif
+
+#ifdef MACOSX
+	success ^= xnu::EnumerateOpenFiles(pids, open_file_proc);
+#endif
+
+#ifdef LIBKVM
+	success ^= kvm::EnumerateOpenFiles(pids, open_file_proc);
+#endif
+
+	return success;
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	bool success = false;
+
+#ifdef WIN32
+	success ^= win32::EnumerateOpenProcesses(process_proc);
+#endif
+
+#ifdef LINUX
+	success ^= proc::EnumerateOpenProcesses(process_proc);
+#endif
+
+#ifdef MACOSX
+	success ^= xnu::EnumerateOpenProcesses(process_proc);
+#endif
+
+#ifdef LIBKVM
+	success ^= kvm::EnumerateOpenProcesses(process_proc);
+#endif
+
+	return success;
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	bool success = false;
+
+#ifdef WIN32
+	success ^= win32::GetProcessName(pid, name);
+#endif
+
+#ifdef LINUX
+	success ^= proc::GetProcessName(pid, name);
+#endif
+
+#ifdef MACOSX
+	success ^= osx::util::GetProcessName(pid, name);
+#endif
+
+#ifdef LIBKVM
+	success ^= kvm::GetProcessName(pid, name);
+#endif
+
+	return success;
+}
+
+} // namespace animone::internal
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/kvm.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,151 @@
+/* kvm.cc: provides support for *BSD.
+ */
+
+#include "animone/fd/kvm.h"
+#include "animone.h"
+#include "animone/fd.h"
+
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+
+#include <kvm.h>
+#ifdef LIBUTIL
+#	include <libutil.h>
+#endif
+
+#include <string>
+
+namespace animone::internal::kvm {
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	int entries = 0;
+	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &entries);
+	if (!kinfo) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	if (entries < 1) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	name = kinfo[0].ki_paddr->p_comm;
+
+	return true;
+}
+
+/* Most of the BSDs share the common kvm library,
+ * so accessing this information can be trivial.
+ */
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	int entries = 0;
+	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, &entries);
+	if (!kinfo) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	for (int i = 0; i < entries; i++) {
+		if (!process_proc({kinfo[i].ki_paddr->p_pid, kinfo[i].ki_paddr->p_comm})) {
+			kvm_close(kernel);
+			return false;
+		}
+	}
+
+	kvm_close(kernel);
+
+	return true;
+}
+
+bool EnumerateOpenFiles(std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+#ifdef __OpenBSD__
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	for (const auto& pid : pids) {
+		int cnt;
+		struct kinfo_file* kfile = kvm_getfiles(kernel, KERN_FILE_BYPID, pid, &cnt);
+		if (!kfile) {
+			kvm_close(kernel);
+			return false;
+		}
+
+		for (int i = 0; i < cnt; i++) {
+			if (!open_file_proc({pid, kfile[i].kf_path})) {
+				kvm_close(kernel);
+				return false;
+			}
+		}
+	}
+
+	kvm_close(kernel);
+
+	return true;
+#elif defined(LIBUTIL)
+	/* does this code even work? */
+	for (const auto& pid : pids) {
+		int cnt;
+		std::unique_ptr<struct kinfo_file[]> files(kinfo_getfile(pid, &cnt));
+		if (!files)
+			return false;
+
+		for (int i = 0; i < cnt; i++) {
+			const struct kinfo_file& current = files[i];
+			if (current.kf_vnode_type != KF_VTYPE_VREG)
+				continue;
+
+			if (!open_file_proc({pid, current.kf_path}))
+				return false;
+		}
+	}
+
+	return true;
+#elif defined(__NetBSD__)
+	for (const auto& pid : pids) {
+		int mib[6] = {CTL_KERN, KERN_FILE2, KERN_FILE_BYPID, pid, sizeof(struct kinfo_file), 0};
+
+		size_t len = 0;
+		if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0) == -1)
+			return false;
+
+		mib[5] = len / sizeof(struct kinfo_file);
+
+		std::unique_ptr<struct kinfo_file[]> buf(new struct kinfo_file[mib[5]]);
+		if (!buf)
+			return false;
+
+		if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf.get(), &len, NULL, 0) == -1)
+			return false;
+
+		/* TODO: check kfile[i].ki_ofileflags */
+		for (size_t i = 0; i < mib[5]; i++)
+			if (!open_file_proc({pid, kfile[i].kf_path}))
+				return false;
+	}
+
+	return true;
+#else
+	return false;
+#endif
+}
+
+} // namespace animone::internal::kvm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/proc.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,138 @@
+#include "animone/fd/proc.h"
+#include "animone.h"
+#include "animone/util.h"
+
+#include <filesystem>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static constexpr std::string_view PROC_LOCATION = "/proc";
+
+namespace animone::internal::proc {
+
+static bool IsRegularFile(std::string link) {
+	struct stat sb;
+	if (stat(link.c_str(), &sb) == -1)
+		return false;
+
+	return S_ISREG(sb.st_mode);
+}
+
+static bool AreFlagsOk(pid_t pid, int fd) {
+	const std::filesystem::path path =
+	    std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fdinfo" / std::to_string(fd);
+
+	std::ifstream file(path);
+	if (!file)
+		return false;
+
+	int flags = 0;
+	for (std::string line; std::getline(file, line);)
+		if (line.find("flags:", 0) == 0)
+			flags = util::StringToInt(line.substr(line.find_last_not_of("0123456789") + 1));
+
+	if (flags & O_WRONLY || flags & O_RDWR)
+		return false;
+
+	return true;
+}
+
+static bool GetFilenameFromFd(std::string link, std::string& out) {
+	/* /proc is a "virtual filesystem", so we have to guess the path size. yippee! */
+	constexpr size_t OUT_MAX = (1ul << 15); // 32KiB
+	out.resize(32);
+
+	ssize_t exe_used = 0;
+	do {
+		out.resize(out.length() * 2);
+
+		exe_used = readlink(link.c_str(), &out.front(), out.length());
+		if (exe_used == (ssize_t)-1 || exe_used < (ssize_t)1)
+			return false; // we got a bad result. SAD!
+	} while (out.length() < OUT_MAX && exe_used >= static_cast<ssize_t>(out.length()));
+
+	out.resize(out.find('\0'));
+
+	return true;
+}
+
+static bool IsSystemFile(const std::string& path) {
+	static constexpr std::array<std::string_view, 9> invalid_paths = {"/boot", "/dev", "/bin", "/usr", "/opt",
+	                                                                  "/proc", "/var", "/etc", "/dev"};
+
+	for (const auto& invalid_path : invalid_paths) {
+		if (!path.rfind(invalid_path, 0)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+	const std::filesystem::path path = std::filesystem::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 EnumerateOpenProcesses(process_proc_t process_proc) {
+	bool success = false;
+
+	for (const auto& dir : std::filesystem::directory_iterator{PROC_LOCATION}) {
+		Process proc;
+
+		try {
+			proc.pid = util::StringToInt(dir.path().stem());
+			success = true;
+		} catch (std::invalid_argument const& ex) {
+			continue;
+		}
+
+		if (!GetProcessName(proc.pid, proc.name))
+			continue;
+
+		if (!process_proc(proc))
+			return false;
+	}
+
+	return success;
+}
+
+bool 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) {
+		const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fd";
+
+		for (const auto& dir : std::filesystem::directory_iterator{path}) {
+			if (!AreFlagsOk(pid, util::StringToInt(dir.path().stem())))
+				continue;
+
+			std::string name;
+			if (!GetFilenameFromFd(dir.path(), name))
+				continue;
+
+			if (!IsRegularFile(name))
+				continue;
+
+			if (IsSystemFile(name))
+				continue;
+
+			if (!open_file_proc({pid, name}))
+				return false;
+		}
+	}
+	return true;
+}
+
+} // namespace animia::internal::proc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/win32.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,259 @@
+#include "animone/fd/win32.h"
+#include "animone.h"
+#include "animone/util/win32.h"
+
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <fileapi.h>
+#include <handleapi.h>
+#include <libloaderapi.h>
+#include <ntdef.h>
+#include <psapi.h>
+#include <shlobj.h>
+#include <stringapiset.h>
+#include <tlhelp32.h>
+#include <windows.h>
+#include <winternl.h>
+
+/* This file is noticably more complex than Unix and Linux, and that's because
+ * there is no "simple" way to get the paths of a file. In fact, this thing requires
+ * you to use *internal functions* that can't even be linked to, hence why we have to
+ * use GetProcAddress and such. What a mess.
+ *
+ * Speaking of which, because this file uses internal functions of the OS, it is not
+ * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista)
+ * at least, so it's unlikely to be changed much ever.
+ */
+
+/* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
+ * 32-bit PIDs, unlike SystemHandleInformation
+ *
+ * TODO: implement SystemHandleInformation for systems older than XP
+ */
+static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
+static constexpr SYSTEM_INFORMATION_CLASS SystemHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x10);
+static constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
+
+/* 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 {
+	PVOID Object;
+	ULONG_PTR UniqueProcessId;
+	HANDLE HandleValue;
+	ACCESS_MASK GrantedAccess;
+	USHORT CreatorBackTraceIndex;
+	USHORT ObjectTypeIndex;
+	ULONG HandleAttributes;
+	ULONG Reserved;
+};
+
+struct SYSTEM_HANDLE_INFORMATION_EX {
+	ULONG_PTR NumberOfHandles;
+	ULONG_PTR Reserved;
+	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+};
+
+namespace animone::internal::win32 {
+
+class Ntdll {
+public:
+	Ntdll() {
+		ntdll = ::GetModuleHandleW(L"ntdll.dll");
+		nt_query_system_information = reinterpret_cast<decltype(::NtQuerySystemInformation)*>(
+		    ::GetProcAddress(ntdll, "NtQuerySystemInformation"));
+		nt_query_object = reinterpret_cast<decltype(::NtQueryObject)*>(::GetProcAddress(ntdll, "NtQueryObject"));
+	}
+
+	NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len,
+	                                PULONG retlen){return nt_query_system_information(cls, sysinfo, len, retlen)}
+
+	NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
+		return nt_query_object(handle, cls, objinf, objinflen, retlen);
+	}
+
+private:
+	HMODULE ntdll;
+	decltype(::NtQuerySystemInformation)* nt_query_system_information;
+	decltype(::NtQueryObject)* nt_query_object;
+
+}
+
+Ntdll ntdll;
+
+static HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
+	HANDLE dup_handle = nullptr;
+	const bool result =
+	    ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
+	return result ? dup_handle : nullptr;
+}
+
+static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
+	/* we should really put a cap on this */
+	ULONG cb = 1 << 19;
+	NTSTATUS status = STATUS_NO_MEMORY;
+	std::unique_ptr<SYSTEM_HANDLE_INFORMATION_EX> info(malloc(cb));
+
+	do {
+		info.reset(malloc(cb *= 2));
+		if (!info)
+			continue;
+
+		status = ntdll.QuerySystemInformation(SystemExtendedHandleInformation, info.get(), cb, &cb);
+	} while (status == STATUS_INFO_LENGTH_MISMATCH);
+
+	if (!NT_SUCCESS(status))
+		return {};
+
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
+
+	ULONG_PTR handles = info->NumberOfHandles;
+	if (!handles)
+		return {};
+
+	res.reserve(handles);
+
+	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
+	do {
+		if (entry)
+			res.push_back(*(entry++));
+	} while (--handles);
+
+	return res;
+}
+
+static std::wstring GetHandleType(HANDLE handle) {
+	OBJECT_TYPE_INFORMATION info = {0};
+	ntdll.QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
+	return std::wstring(info.TypeName.Buffer, info.TypeName.Length);
+}
+
+static std::wstring GetFinalPathNameByHandle(HANDLE handle) {
+	std::wstring buffer;
+
+	DWORD size = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+	buffer.resize(size);
+	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+
+	return buffer;
+}
+
+static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
+	if (file_type_index)
+		return object_type_index == file_type_index;
+	else if (!handle)
+		return true;
+	else if (GetHandleType(handle) == L"File") {
+		file_type_index = object_type_index;
+		return true;
+	}
+	return false;
+}
+
+static bool IsFileMaskOk(ACCESS_MASK access_mask) {
+	if (!(access_mask & FILE_READ_DATA))
+		return false;
+
+	if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
+		return false;
+
+	return true;
+}
+
+static bool IsFilePathOk(const std::wstring& path) {
+	if (path.empty())
+		return false;
+
+	if (IsSystemDirectory(path))
+		return false;
+
+	const auto file_attributes = GetFileAttributesW(path.c_str());
+	if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
+		return false;
+
+	return true;
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	std::wstring wname = GetProcessPath();
+	if (wname.empty())
+		return false;
+
+	return ToUtf8String(GetFileNameWithoutExtension(GetFileNameFromPath(wname)));
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+	if (hProcessSnap == INVALID_HANDLE_VALUE)
+		return false;
+
+	PROCESSENTRY32 pe32;
+	pe32.dwSize = sizeof(PROCESSENTRY32);
+
+	if (!::Process32First(hProcessSnap, &pe32))
+		return false;
+
+	if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+		return false;
+
+	while (::Process32Next(hProcessSnap, &pe32))
+		if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+			return false;
+
+	::CloseHandle(hProcessSnap);
+
+	return true;
+}
+
+/* this could be changed to being a callback, but... I'm too lazy right now :) */
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
+		return false;
+
+	std::unordered_map<pid_t, Handle> proc_handles;
+
+	for (const pid_t& pid : pids) {
+		const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
+		if (handle != INVALID_HANDLE_VALUE)
+			proc_handles[pid] = Handle(handle);
+	}
+
+	if (proc_handles.empty())
+		return false;
+
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
+
+	for (const auto& h : info) {
+		const pid_t pid = h.UniqueProcessId;
+		if (!pids.count(pid))
+			continue;
+
+		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
+			continue;
+
+		if (!IsFileMaskOk(h.GrantedAccess))
+			continue;
+
+		Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
+		if (handle.get() == INVALID_HANDLE_VALUE)
+			continue;
+
+		if (GetFileType(handle.get()) != FILE_TYPE_DISK)
+			continue;
+
+		const std::wstring path = GetFinalPathNameByHandle(handle.get());
+		if (!IsFilePathOk(path))
+			continue;
+
+		if (!open_file_proc({pid, ToUtf8String(path)}))
+			return false;
+	}
+
+	return true;
+}
+
+} // namespace animone::internal::win32
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/xnu.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,84 @@
+#include "animone/fd/xnu.h"
+#include "animone.h"
+#include "animone/util/osx.h"
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <fcntl.h>
+#include <libproc.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+namespace animone::internal::xnu {
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	size_t pids_size = 256;
+	std::unique_ptr<pid_t[]> pids;
+
+	int returned_size = 0;
+	do {
+		pids.reset(new pid_t[pids_size *= 2]);
+		returned_size = proc_listpids(PROC_ALL_PIDS, 0, pids.get(), pids_size * sizeof(pid_t));
+		if (returned_size == -1)
+			return false;
+	} while ((pids_size * sizeof(size_t)) < returned_size);
+
+	for (int i = 0; i < pids_size; i++) {
+		std::string result;
+		osx::util::GetProcessName(pids[i], result);
+		if (!process_proc({pids[i], result}))
+			return false;
+	}
+
+	return true;
+}
+
+bool 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) {
+		const int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+		if (bufsz < 0)
+			return false;
+
+		const size_t info_len = bufsz / sizeof(struct proc_fdinfo);
+		if (info_len < 1)
+			return false;
+
+		std::unique_ptr<struct proc_fdinfo[]> info(new struct proc_fdinfo[info_len]);
+		if (!info)
+			return false;
+
+		proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info.get(), bufsz);
+
+		for (size_t i = 0; i < info_len; i++) {
+			if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
+				struct vnode_fdinfowithpath vnodeInfo;
+
+				int sz = proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo,
+				                        PROC_PIDFDVNODEPATHINFO_SIZE);
+				if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
+					return false;
+
+				// This doesn't work (for unknown reasons). I assume somethings fucked up with
+				// my assumptions; I don't care enough to look into it tbh
+				//
+				// if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
+				//     continue;
+
+				if (!open_file_proc({pid, vnodeInfo.pvip.vip_path}))
+					return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+} // namespace animone::internal::xnu
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/player.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,181 @@
+#include "animone/player.h"
+#include "animone/util.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace animone {
+
+namespace internal::parser {
+
+enum class State {
+	ExpectPlayerName,
+	ExpectSection,
+	ExpectWindow,
+	ExpectExecutable,
+	ExpectStrategy,
+	ExpectType,
+	ExpectWindowTitle,
+};
+
+size_t GetIndentation(const std::string& line) {
+	return line.find_first_not_of('\t');
+}
+
+bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) {
+	// Each state has a definitive expected indentation
+	const auto expected = [&state]() -> size_t {
+		switch (state) {
+			default:
+			case State::ExpectPlayerName: return 0;
+			case State::ExpectSection: return 1;
+			case State::ExpectWindow:
+			case State::ExpectExecutable:
+			case State::ExpectStrategy:
+			case State::ExpectType: return 2;
+			case State::ExpectWindowTitle: return 3;
+		}
+	}();
+
+	if (current > expected)
+		return false; // Disallow excessive indentation
+
+	if (current < expected) {
+		auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; };
+		switch (state) {
+			case State::ExpectWindow:
+				if (players.back().windows.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectExecutable:
+				if (players.back().executables.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectStrategy:
+				if (players.back().strategies.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectType: fix_state(); break;
+			case State::ExpectWindowTitle: return false;
+		}
+	}
+
+	return true;
+}
+
+bool HandleState(std::string& line, std::vector<Player>& players, State& state) {
+	switch (state) {
+		case State::ExpectPlayerName:
+			players.push_back(Player());
+			players.back().name = line;
+			state = State::ExpectSection;
+			break;
+
+		case State::ExpectSection: {
+			static const std::map<std::string, State> sections = {
+			    {"windows",     State::ExpectWindow    },
+			    {"executables", State::ExpectExecutable},
+			    {"strategies",  State::ExpectStrategy  },
+			    {"type",        State::ExpectType      },
+			};
+			util::TrimRight(line, ":");
+			const auto it = sections.find(line);
+			if (it == sections.end())
+				return false;
+			state = it->second;
+			break;
+		}
+
+		case State::ExpectWindow: players.back().windows.push_back(line); break;
+
+		case State::ExpectExecutable: players.back().executables.push_back(line); break;
+
+		case State::ExpectStrategy: {
+			static const std::map<std::string, Strategy> strategies = {
+			    {"window_title",  Strategy::WindowTitle },
+			    {"open_files",    Strategy::OpenFiles   },
+			    {"ui_automation", Strategy::UiAutomation},
+			};
+			util::TrimRight(line, ":");
+			const auto it = strategies.find(line);
+			if (it == strategies.end())
+				return false;
+			const auto strategy = it->second;
+			players.back().strategies.push_back(strategy);
+			switch (strategy) {
+				case Strategy::WindowTitle: state = State::ExpectWindowTitle; break;
+			}
+			break;
+		}
+
+		case State::ExpectType: {
+			static const std::map<std::string, PlayerType> types = {
+			    {"default",     PlayerType::Default   },
+			    {"web_browser", PlayerType::WebBrowser},
+			};
+			const auto it = types.find(line);
+			if (it == types.end())
+				return false;
+			players.back().type = it->second;
+			break;
+		}
+
+		case State::ExpectWindowTitle:
+			players.back().window_title_format = line;
+			state = State::ExpectStrategy;
+			break;
+	}
+
+	return true;
+}
+
+} // namespace internal::parser
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool ParsePlayersData(const std::string& data, std::vector<Player>& players) {
+	if (data.empty())
+		return false;
+
+	std::istringstream stream(data);
+	std::string line;
+	size_t indentation = 0;
+	auto state = internal::parser::State::ExpectPlayerName;
+
+	while (std::getline(stream, line, '\n')) {
+		if (line.empty())
+			continue; // Ignore empty lines
+
+		indentation = internal::parser::GetIndentation(line);
+
+		internal::util::TrimLeft(line, "\t");
+		internal::util::TrimRight(line, "\n\r");
+
+		if (line.empty() || line.front() == '#')
+			continue; // Ignore empty lines and comments
+
+		if (!internal::parser::HandleIndentation(indentation, players, state))
+			return false;
+
+		if (!internal::parser::HandleState(line, players, state))
+			return false;
+	}
+
+	return !players.empty();
+}
+
+bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) {
+	std::string data;
+
+	if (!internal::util::ReadFile(path, data))
+		return false;
+
+	return ParsePlayersData(data, players);
+}
+
+} // namespace animone
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/strategist.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,111 @@
+#include <regex>
+
+#include "animone.h"
+#include "animone/fd.h"
+#include "animone/strategies.h"
+#include "animone/util.h"
+
+#include <iostream>
+
+/* this was STUPIDLY slow in Anisthesia, oops! */
+
+namespace animone::internal {
+
+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;
+}
+
+static bool AddMedia(Result& result, const MediaInfo media_information) {
+	if (media_information.value.empty())
+		return false;
+
+	Media media;
+	media.information.push_back(media_information);
+	result.media.push_back(std::move(media));
+
+	return true;
+}
+
+static bool ApplyWindowTitleStrategy(std::vector<Result>& results) {
+	bool success = false;
+
+	for (auto& result : results) {
+		auto title = result.window.text;
+		if (title.empty())
+			continue;
+
+		ApplyWindowTitleFormat(result.player.window_title_format, title);
+
+		success |= AddMedia(result, {InferMediaInformationType(title), title});
+	}
+
+	return success;
+}
+
+static bool ApplyOpenFilesStrategy(std::vector<Result>& results) {
+	bool success = false;
+
+	/* map pids to our results, saves time with open_file_proc */
+	std::unordered_map<pid_t, Result*> pid_map;
+	pid_map.reserve(results.size());
+
+	std::set<pid_t> pids;
+
+	for (Result& result : results) {
+		const pid_t pid = result.process.pid;
+		if (!pid)
+			continue;
+
+		pid_map.insert({pid, &result});
+		pids.insert(pid);
+	}
+
+	auto open_file_proc = [&](const OpenFile& file) -> bool {
+		success |= AddMedia(*pid_map[file.pid], {MediaInfoType::File, file.path});
+		return true;
+	};
+
+	EnumerateOpenFiles(pids, open_file_proc);
+
+	return success;
+}
+
+bool ApplyStrategies(std::vector<Result>& results) {
+	bool success = false;
+
+	success |= ApplyWindowTitleStrategy(results);
+	success |= ApplyOpenFilesStrategy(results);
+
+	return success;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace animone::internal
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/util.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,85 @@
+#include <algorithm>
+#include <fstream>
+#include <regex>
+#include <sstream>
+#include <string>
+
+#include "animone/util.h"
+
+namespace animone::internal::util {
+
+bool ReadFile(const std::string& path, std::string& data) {
+	std::ifstream file(path.c_str(), std::ios::in | std::ios::binary);
+	if (!file)
+		return false;
+
+	std::ostringstream string;
+	string << file.rdbuf();
+	file.close();
+
+	data = string.str();
+
+	return true;
+}
+
+/* this assumes ASCII... which really should be the case for what we need, anyway */
+bool EqualStrings(const std::string& str1, const std::string& str2) {
+	auto tolower = [](const char c) -> char { return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c; };
+
+	auto equal_chars = [&tolower](const char c1, const char c2) -> bool { return tolower(c1) == tolower(c2); };
+
+	return str1.length() == str2.length() && 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;
+
+	const auto found = str.find_first_not_of(chars);
+
+	if (found == 0)
+		return false;
+
+	if (found == std::string::npos)
+		str.clear();
+	else
+		str.erase(0, found);
+
+	return true;
+}
+
+bool TrimRight(std::string& str, const char* chars) {
+	if (str.empty())
+		return false;
+
+	const auto found = str.find_last_not_of(chars);
+
+	if (found == str.size() - 1)
+		return false;
+
+	if (found == std::string::npos)
+		str.clear();
+	else
+		str.resize(found + 1);
+
+	return true;
+}
+
+} // namespace animone::internal::util
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/util/osx.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,196 @@
+#include "animone/util/osx.h"
+
+#include <memory>
+#include <string>
+
+#include <libproc.h>
+#include <sys/sysctl.h>
+
+namespace animone::internal::osx::util {
+
+typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
+typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
+
+/* retrieved dynamically from launchservices */
+static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr;
+static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr;
+
+static CFStringRef kLSDisplayNameKey = nullptr;
+static CFStringRef kLSPIDKey = nullptr;
+
+/* retrieved from LaunchServicesSPI.h in WebKit */
+static constexpr int kLSDefaultSessionID = -2;
+
+static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
+
+static bool GetLaunchServicesPrivateSymbols() {
+	CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
+	if (!launch_services_bundle)
+		return false;
+
+	LSCopyApplicationInformation = reinterpret_cast<LSCopyApplicationInformationSpec>(
+	    CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation")));
+	if (!LSCopyApplicationInformation)
+		return false;
+
+	LSASNCreateWithPid = reinterpret_cast<LSASNCreateWithPidSpec>(
+	    CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid")));
+	if (!LSASNCreateWithPid)
+		return false;
+
+	CFStringRef* ptr_kLSDisplayNameKey = reinterpret_cast<CFStringRef*>(
+	    CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey")));
+	if (!ptr_kLSDisplayNameKey)
+		return false;
+	kLSDisplayNameKey = *ptr_kLSDisplayNameKey;
+
+	CFStringRef* ptr_kLSPIDKey =
+	    reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey")));
+	if (!ptr_kLSPIDKey)
+		return false;
+	kLSPIDKey = *ptr_kLSPIDKey;
+
+	return true;
+}
+
+static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) {
+	if (!LSCopyApplicationInformation || !LSASNCreateWithPid || !kLSDisplayNameKey || !kLSPIDKey)
+		if (!GetLaunchServicesPrivateSymbols())
+			return false;
+
+	/* what the hell is an `asn`? */
+	CFPtr<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
+	if (!asn)
+		return false;
+
+	CFPtr<CFArrayRef> request_array = CFArrayCreate(NULL, (const void**)kLSDisplayNameKey, 1, NULL);
+	if (!request_array)
+		return false;
+
+	CFPtr<CFDictionaryRef> dictionary =
+	    LSCopyApplicationInformation(kLSDefaultSessionID, asn.get(), request_array.get());
+	if (!dictionary)
+		return false;
+
+	{
+		/* this doesn't need to be free'd */
+		CFStringRef rstr;
+
+		if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr)
+			return false;
+
+		if (!StringFromCFString(rstr, result))
+			return false;
+	}
+
+	result.resize(result.find('\0'));
+
+	return true;
+}
+
+bool StringFromCFString(CFStringRef string, std::string& result) {
+	if (!string)
+		return false;
+
+	result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1);
+	if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8))
+		return false;
+
+	return true;
+}
+
+static bool GetProcessArgs(pid_t pid, std::string& args) {
+	/* sysctl shouldn't touch these, so we define them as const */
+	int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)};
+	const size_t mib_size = sizeof(mib) / sizeof(*mib);
+
+	/* Get the initial size of the array
+	 *
+	 * NOTE: it IS possible for this value to change inbetween calls to sysctl().
+	 * Unfortunately, I couldn't care less about handling this. :)
+	 *
+	 * is that really true, though? these should be constant values. but are
+	 * argc and argv *really* constant?
+	 */
+	size_t size;
+	{
+		int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
+		if (ret)
+			return false;
+	}
+
+	/* Reserve the space for it in args */
+	args.resize(size);
+
+	/* Get the contents of argc and argv */
+	{
+		int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0);
+		if (ret)
+			return false;
+	}
+
+	/* Is the size big enough to hold at least argc? */
+	if (size < sizeof(int))
+		return false;
+
+	args.resize(size);
+	return true;
+}
+
+static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
+	if (!GetProcessArgs(pid, result))
+		return false;
+
+	/* Get argc using memcpy */
+	int argc = 0;
+	memcpy(&argc, &result.front(), sizeof(argc));
+
+	/* Do we even have argv[0]? */
+	if (argc < 1)
+		return false;
+
+	/* Find the first null character */
+	size_t null_pos = result.find('\0', sizeof(argc));
+	if (null_pos == std::string::npos)
+		return false;
+
+	/* Find the last slash */
+	size_t last_slash = result.rfind('/', null_pos);
+	if (last_slash == std::string::npos)
+		return false;
+
+	/* Return our result */
+	result = result.substr(last_slash + 1, null_pos - last_slash - 1);
+	return true;
+}
+
+static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
+	result.resize(2 * MAXCOMLEN);
+
+	int size = proc_name(pid, &result.front(), result.length());
+	if (!size)
+		return false;
+
+	result.resize(size);
+	return true;
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+	if (LaunchServicesGetProcessName(pid, result))
+		return true;
+
+	/* Try parsing the arguments, this prevents the process name being
+	 * cut off to 2*MAXCOMLEN (32 chars) */
+	if (GetProcessNameFromArgs(pid, result))
+		return true;
+
+	/* Then attempt getting it from the kernel, which results in the
+	 * process name being cut to 32 chars (worse, 16 chars if p_name is
+	 * unavailable) */
+	if (GetProcessNameFromKernel(pid, result))
+		return true;
+
+	return false;
+}
+
+} // namespace animone::internal::osx::util
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/util/win32.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,102 @@
+#include "animone/util/win32.h"
+
+#include <shlobj.h>  /* SHGetKnownFolderPath */
+#include <subauth.h> /* UNICODE_STRING */
+#include <windows.h>
+
+namespace animone::internal::win32 {
+
+std::string ToUtf8String(const std::wstring& string) {
+	if (string.empty())
+		return std::string();
+
+	long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), nullptr, 0, nullptr, nullptr);
+	std::string ret(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(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(size, L'\0');
+	mbtowc(&ret.front(), ret.length());
+	return ret;
+}
+
+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;
+}
+
+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) {
+	return IsSystemDirectory(ToWstring(path));
+}
+
+bool IsSystemDirectory(std::wstring path) {
+	::CharUpperBuffW(&path.front(), path.length());
+
+	std::wstring windir = GetSystemDirectory();
+	::CharUpperBuffW(&windir.front(), windir.length());
+
+	return path.find(windir) == 4;
+}
+
+} // namespace animone::internal::win32
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/win.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,35 @@
+#include "animone/win.h"
+
+#ifdef WIN32
+#	include "animone/win/win32.h"
+#endif
+
+#ifdef MACOSX
+#	include "animone/win/quartz.h"
+#endif
+
+#ifdef X11
+#	include "animone/win/x11.h"
+#endif
+
+namespace animone::internal {
+
+bool EnumerateWindows(window_proc_t window_proc) {
+	bool success = false;
+
+#ifdef WIN32
+	success |= win32::EnumerateWindows(window_proc);
+#endif
+
+#ifdef MACOSX
+	success |= quartz::EnumerateWindows(window_proc);
+#endif
+
+#ifdef X11
+	success |= x11::EnumerateWindows(window_proc);
+#endif
+
+	return success;
+}
+
+} // namespace animone::internal
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/win/quartz.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,251 @@
+/*
+ * win/quartz.cc: support for macOS (the Quartz Compositor)
+ *
+ * This file does not require an Objective-C++ compiler,
+ * but it *does* require an Objective-C runtime.
+ */
+#include "animone/win/quartz.h"
+#include "animone.h"
+#include "animone/util/osx.h"
+
+#include <objc/message.h>
+#include <objc/runtime.h>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+namespace animone::internal::quartz {
+
+#if __LP64__
+typedef long NSInteger;
+#else
+typedef int NSInteger;
+#endif
+typedef int CGSConnection;
+
+typedef CGSConnection (*CGSDefaultConnectionForThreadSpec)(void);
+typedef CGError (*CGSCopyWindowPropertySpec)(const CGSConnection, NSInteger, CFStringRef, CFStringRef*);
+
+static CGSDefaultConnectionForThreadSpec CGSDefaultConnectionForThread = nullptr;
+static CGSCopyWindowPropertySpec CGSCopyWindowProperty = nullptr;
+
+static const CFStringRef kCoreGraphicsBundleID = CFSTR("com.apple.CoreGraphics");
+
+/* Objective-C */
+typedef id (*object_message_send)(id, SEL, ...);
+typedef id (*class_message_send)(Class, SEL, ...);
+
+static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend);
+static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend);
+
+static bool GetCoreGraphicsPrivateSymbols() {
+	CFBundleRef core_graphics_bundle = CFBundleGetBundleWithIdentifier(kCoreGraphicsBundleID);
+	if (!core_graphics_bundle)
+		return false;
+
+	CGSDefaultConnectionForThread = (CGSDefaultConnectionForThreadSpec)CFBundleGetFunctionPointerForName(
+	    core_graphics_bundle, CFSTR("CGSDefaultConnectionForThread"));
+	if (!CGSDefaultConnectionForThread)
+		return false;
+
+	CGSCopyWindowProperty = (CGSCopyWindowPropertySpec)CFBundleGetFunctionPointerForName(
+	    core_graphics_bundle, CFSTR("CGSCopyWindowProperty"));
+	if (!CGSCopyWindowProperty)
+		return false;
+
+	return true;
+}
+
+template<typename T>
+static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) {
+	CFTypeRef data = nullptr;
+	if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data)
+		return false;
+
+	if constexpr (std::is_arithmetic<T>::value)
+		osx::util::GetCFNumber(reinterpret_cast<CFNumberRef>(data), out);
+	else if constexpr (std::is_same<T, std::string>::value)
+		osx::util::StringFromCFString(reinterpret_cast<CFStringRef>(data), out);
+	else
+		return false;
+
+	return true;
+}
+
+static bool GetWindowTitleAccessibility(unsigned int wid, pid_t pid, std::string& result) {
+	CGRect bounds = {0};
+	{
+		const CGWindowID wids[1] = {wid};
+		CFPtr<CFArrayRef> arr(CFArrayCreate(kCFAllocatorDefault, (CFTypeRef*)wids, 1, NULL));
+		CFPtr<CFArrayRef> dicts(CGWindowListCreateDescriptionFromArray(arr));
+
+		if (!dicts.get() || CFArrayGetCount(dicts.get()) < 1)
+			return false;
+
+		CFDictionaryRef dict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(dicts, 0));
+		if (!dict)
+			return false;
+
+		CFDictionaryRef bounds_dict = nullptr;
+		if (!CFDictionaryGetValueIfPresent(dict, kCGWindowBounds, reinterpret_cast<CFTypeRef*>(&bounds_dict)) ||
+		    !bounds_dict)
+			return false;
+
+		if (!CGRectMakeWithDictionaryRepresentation(bounds_dict, &bounds))
+			return false;
+	}
+
+	/* now we can actually do stuff */
+	AXUIElementRef axapp = AXUIElementCreateApplication(pid);
+	CFPtr<CFArrayRef> windows;
+	{
+		CFArrayRef ref;
+		if ((AXUIElementCopyAttributeValue(axapp, kAXWindowsAttribute, reinterpret_cast<CFTypeRef*>(&ref)) !=
+		     kAXErrorSuccess) ||
+		    !windows)
+			return false;
+
+		windows.reset(ref);
+	}
+
+	const CFIndex count = CFArrayGetCount(windows.get());
+	for (CFIndex i = 0; i < count; i++) {
+		const AXUIElementRef window = reinterpret_cast<AXUIElementRef>(CFArrayGetValueAtIndex(windows.get(), i));
+
+		/* does this leak memory? probably. */
+		AXValueRef val;
+		if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, reinterpret_cast<CFTypeRef*>(&val)) ==
+		    kAXErrorSuccess) {
+			CGPoint point;
+			if (!AXValueGetValue(val, kAXValueTypeCGPoint, reinterpret_cast<CFTypeRef>(&point)) ||
+			    (point.x != bounds.origin.x || point.y != bounds.origin.y))
+				continue;
+		} else
+			continue;
+
+		if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, reinterpret_cast<CFTypeRef*>(&val)) ==
+		    kAXErrorSuccess) {
+			CGSize size;
+			if (!AXValueGetValue(val, kAXValueTypeCGSize, reinterpret_cast<CFTypeRef>(&size)) ||
+			    (size.width != bounds.size.width || size.height != bounds.size.height))
+				continue;
+		} else
+			continue;
+
+		CFStringRef title;
+		if (AXUIElementCopyAttributeValue(window, kAXTitleAttribute, reinterpret_cast<CFTypeRef*>(&title)) ==
+		    kAXErrorSuccess)
+			return osx::util::StringFromCFString(title, result);
+	}
+
+	return false;
+}
+
+static bool GetWindowTitle(unsigned int wid, pid_t pid, std::string& result) {
+	/* try using CoreGraphics (only usable on old versions of OS X) */
+	if ((CGSDefaultConnectionForThread && CGSCopyWindowProperty) || GetCoreGraphicsPrivateSymbols()) {
+		CFPtr<CFStringRef> title;
+		{
+			CFStringRef t = nullptr;
+			CGSCopyWindowProperty(CGSDefaultConnectionForThread(), wid, CFSTR("kCGSWindowTitle"), &t);
+			title.reset(t);
+		}
+
+		if (title && CFStringGetLength(title.get()) && osx::util::StringFromCFString(title.get(), result))
+			return true;
+	}
+
+	/* then try linking to a window using the accessibility API */
+	return AXIsProcessTrusted() ? GetWindowTitleAccessibility(wid, pid, result) : false;
+}
+
+static bool GetProcessBundleIdentifierNew(pid_t pid, std::string& result) {
+	/* 10.6 and higher */
+	const id app =
+	    cls_send(objc_getClass("NSRunningApplication"), sel_getUid("runningApplicationWithProcessIdentifier:"), pid);
+	if (!app)
+		return false;
+
+	CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier")));
+	if (!bundle_id)
+		return false;
+
+	result = osx::util::StringFromCFString(bundle_id, result);
+	return true;
+}
+
+static bool GetProcessBundleIdentifierOld(pid_t pid, std::string& result) {
+	/* OS X 10.2; deprecated in 10.9 */
+	ProcessSerialNumber psn;
+	if (GetProcessForPID(pid, &psn))
+		return false;
+
+	CFPtr<CFDictionaryRef> info = ProcessInformationCopyDictionary(psn, kProcessDictionaryIncludeAllInformationMask);
+	if (!info)
+		return false;
+
+	CFStringRef value = reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("CFBundleIdentifier")));
+	if (!value)
+		return false;
+
+	result = osx::util::StringFromCFString(value, result);
+	return true;
+}
+
+static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) {
+	/* The Bundle ID is essentially OS X's solution to Windows'
+	 * "class name"; theoretically, it should be different for
+	 * each program, although it requires an app bundle.
+	 */
+	if (GetProcessBundleIdentifierNew(pid, result))
+		return true;
+
+	return GetProcessBundleIdentifierOld();
+}
+
+bool EnumerateWindows(window_proc_t window_proc) {
+	if (!window_proc)
+		return false;
+
+	const CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
+	if (!windows)
+		return false;
+
+	const CFIndex count = CFArrayGetCount(windows);
+	for (CFIndex i = 0; i < count; i++) {
+		CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows, i));
+		if (!window)
+			continue;
+
+		Process proc;
+		{
+			CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid);
+			if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name))
+				osx::util::GetProcessName(proc.pid, proc.name);
+		}
+
+		Window win;
+		{
+			CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id);
+
+			if (!GetProcessBundleIdentifier(proc.pid, win.class_name))
+				// Fallback to the Quartz window name, which is unlikely to be filled, but it
+				// *could* be.
+				CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name);
+
+			GetWindowTitle(win.id, proc.pid, win.text);
+		}
+
+		if (!window_proc(proc, win)) {
+			CFRelease(windows);
+			return false;
+		}
+	}
+
+	CFRelease(windows);
+
+	return true;
+}
+
+} // namespace animone::internal::quartz
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/win/win32.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,143 @@
+/*
+ * 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 <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
+	    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);
+	process.name = fd::GetProcessName(process.pid)
+
+	    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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/win/x11.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,255 @@
+#include "animone/win/x11.h"
+#include "animone.h"
+#include "animone/fd.h" /* GetProcessName() */
+#include "animone/win.h"
+
+#include <xcb/res.h>
+#include <xcb/xcb.h>
+
+#include <climits>
+#include <cstdint>
+#include <cstring>
+#include <set>
+#include <string>
+
+#include <chrono>
+
+#include <iostream>
+
+/* This uses XCB (and it uses it *right*), so it should be plenty fast */
+
+static size_t str_nlen(const char* s, size_t len) {
+	size_t i = 0;
+	for (; i < len && s[i]; i++)
+		;
+	return i;
+}
+
+namespace animone::internal::x11 {
+
+static bool GetAllTopLevelWindowsEWMH(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots,
+                                      std::set<xcb_window_t>& result) {
+	const xcb_atom_t Atom__NET_CLIENT_LIST = [connection] {
+		static constexpr std::string_view name = "_NET_CLIENT_LIST";
+		xcb_intern_atom_cookie_t cookie = ::xcb_intern_atom(connection, true, name.size(), name.data());
+		std::unique_ptr<xcb_intern_atom_reply_t> reply(::xcb_intern_atom_reply(connection, cookie, NULL));
+
+		xcb_atom_t atom = reply->atom;
+
+		return atom;
+	}();
+	if (Atom__NET_CLIENT_LIST == XCB_ATOM_NONE)
+		return false; // BTFO
+
+	bool success = false;
+
+	std::vector<xcb_get_property_cookie_t> cookies;
+	cookies.reserve(roots.size());
+
+	for (const auto& root : roots)
+		cookies.push_back(::xcb_get_property(connection, 0, root, Atom__NET_CLIENT_LIST, XCB_ATOM_ANY, 0L, UINT_MAX));
+
+	for (const auto& cookie : cookies) {
+		std::unique_ptr<xcb_get_property_reply_t> reply(::xcb_get_property_reply(connection, cookie, NULL));
+
+		if (reply) {
+			xcb_window_t* value = reinterpret_cast<xcb_window_t*>(::xcb_get_property_value(reply));
+			int len = ::xcb_get_property_value_length(reply);
+
+			for (size_t i = 0; i < len; i++)
+				result.insert(value[i]);
+
+			success |= true;
+		}
+	}
+
+	return success;
+}
+
+/* This is called on every window. What this does is:
+ * 1. Gets the tree of children
+ * 2. Searches all children recursively for a WM_STATE property
+ * 3. If that failed... return the original window
+ */
+static bool WalkWindows(xcb_connection_t* connection, int depth, xcb_atom_t Atom_WM_STATE, const xcb_window_t* windows,
+                        int windows_len, std::set<xcb_window_t>& result) {
+	/* The depth we should start returning at. */
+	static constexpr int CUTOFF = 2;
+
+	bool success = false;
+
+	std::vector<xcb_query_tree_cookie_t> cookies;
+	cookies.reserve(windows_len);
+
+	for (int i = 0; i < windows_len; i++)
+		cookies.push_back(::xcb_query_tree(connection, windows[i]));
+
+	for (const auto& cookie : cookies) {
+		std::unique_ptr<xcb_query_tree_reply_t> query_tree_reply(::xcb_query_tree_reply(connection, cookie, NULL));
+
+		xcb_window_t* tree_children = ::xcb_query_tree_children(query_tree_reply);
+		int tree_children_len = ::xcb_query_tree_children_length(query_tree_reply);
+
+		std::vector<xcb_get_property_cookie_t> state_property_cookies;
+		state_property_cookies.reserve(tree_children_len);
+
+		for (int i = 0; i < tree_children_len; i++)
+			state_property_cookies.push_back(
+			    ::xcb_get_property(connection, 0, tree_children[i], Atom_WM_STATE, Atom_WM_STATE, 0, 0));
+
+		for (int i = 0; i < tree_children_len; i++) {
+			std::unique_ptr<xcb_get_property_reply_t> get_property_reply(
+			    ::xcb_get_property_reply(connection, state_property_cookies[i], NULL));
+
+			/* X11 is unfriendly here. what this means is "did the property exist?" */
+			if (get_property_reply->format || get_property_reply->type || get_property_reply->length) {
+				result.insert(tree_children[i]);
+				if (depth >= CUTOFF)
+					return true;
+
+				success |= true;
+				continue;
+			}
+		}
+
+		if (WalkWindows(connection, depth + 1, Atom_WM_STATE, tree_children, tree_children_len, result)) {
+			success |= true;
+			if (depth >= CUTOFF)
+				return true;
+			continue;
+		}
+	}
+
+	return success;
+}
+
+static bool GetAllTopLevelWindowsICCCM(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots,
+                                       std::set<xcb_window_t>& result) {
+	bool success = false;
+
+	xcb_atom_t Atom_WM_STATE = [connection] {
+		static constexpr std::string_view name = "WM_STATE";
+		xcb_intern_atom_cookie_t cookie = ::xcb_intern_atom(connection, true, name.size(), name.data());
+		xcb_intern_atom_reply_t* reply = ::xcb_intern_atom_reply(connection, cookie, NULL);
+
+		xcb_atom_t atom = reply->atom;
+		free(reply);
+		return atom;
+	}();
+	if (Atom_WM_STATE == XCB_ATOM_NONE)
+		return success;
+
+	std::vector<xcb_query_tree_cookie_t> cookies;
+	cookies.reserve(roots.size());
+
+	for (const auto& root : roots)
+		cookies.push_back(::xcb_query_tree(connection, root));
+
+	for (const auto& cookie : cookies)
+		success |= WalkWindows(connection, 0, Atom_WM_STATE, roots.data(), roots.size(), result);
+
+	return success;
+}
+
+bool EnumerateWindows(window_proc_t window_proc) {
+	if (!window_proc)
+		return false;
+
+	xcb_connection_t* connection = ::xcb_connect(NULL, NULL);
+	if (!connection)
+		return false;
+
+	std::set<xcb_window_t> windows;
+	{
+		std::vector<xcb_window_t> roots;
+		{
+			xcb_screen_iterator_t iter = ::xcb_setup_roots_iterator(xcb_get_setup(connection));
+			for (; iter.rem; ::xcb_screen_next(&iter))
+				roots.push_back(iter.data->root);
+		}
+
+		if (!GetAllTopLevelWindowsEWMH(connection, roots, windows))
+			GetAllTopLevelWindowsICCCM(connection, roots, windows);
+	}
+
+	struct WindowCookies {
+		xcb_window_t window;
+		xcb_get_property_cookie_t class_property_cookie;
+		xcb_get_property_cookie_t name_property_cookie;
+		xcb_res_query_client_ids_cookie_t pid_property_cookie;
+	};
+
+	std::vector<WindowCookies> window_cookies;
+	window_cookies.reserve(windows.size());
+
+	for (const auto& window : windows) {
+		xcb_res_client_id_spec_t spec = {.client = window, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID};
+
+		WindowCookies window_cookie = {
+		    window, ::xcb_get_property(connection, 0, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0L, 2048L),
+		    ::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0L, UINT_MAX),
+		    ::xcb_res_query_client_ids(connection, 1, &spec)};
+
+		window_cookies.push_back(window_cookie);
+	}
+
+	for (const auto& window_cookie : window_cookies) {
+		Window win = {0};
+		win.id = window_cookie.window;
+		{
+			/* Class name */
+			std::unique_ptr<xcb_get_property_reply_t> reply(
+			    ::xcb_get_property_reply(connection, window_cookie.class_property_cookie, NULL));
+
+			if (reply && reply->format == 8) {
+				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply.get()));
+				const int data_len = ::xcb_get_property_value_length(reply.get());
+
+				int instance_len = str_nlen(data, data_len);
+				const char* class_name = data + instance_len + 1;
+
+				win.class_name = std::string(class_name, str_nlen(class_name, data_len - (instance_len + 1)));
+			}
+		}
+		{
+			/* Title text */
+			std::unique_ptr<xcb_get_property_reply_t> reply(
+			    ::xcb_get_property_reply(connection, window_cookie.name_property_cookie, NULL));
+
+			if (reply) {
+				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply.get()));
+				int len = ::xcb_get_property_value_length(reply.get());
+
+				win.text = std::string(data, len);
+			}
+		}
+		Process proc = {0};
+		{
+			/* PID */
+			std::unique_ptr<xcb_res_query_client_ids_reply_t> reply(
+			    ::xcb_res_query_client_ids_reply(connection, window_cookie.pid_property_cookie, NULL));
+
+			if (reply) {
+				xcb_res_client_id_value_iterator_t it = ::xcb_res_query_client_ids_ids_iterator(reply);
+				for (; it.rem; ::xcb_res_client_id_value_next(&it)) {
+					if (it.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
+						proc.pid = *reinterpret_cast<uint32_t*>(::xcb_res_client_id_value_value(it.data));
+						fd::GetProcessName(proc.pid, proc.name); /* fill this in if we can */
+						break;
+					}
+				}
+			}
+		}
+
+		if (!window_proc(proc, win)) {
+			::xcb_disconnect(connection);
+			return false;
+		}
+	}
+
+	::xcb_disconnect(connection);
+
+	return true;
+}
+
+} // namespace animone::internal::x11
--- a/include/core/anime.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/anime.h	Mon Apr 01 02:43:44 2024 -0400
@@ -63,124 +63,124 @@
 };
 
 enum class ScoreFormat {
-	POINT_100, // 0-100
+	POINT_100,        // 0-100
 	POINT_10_DECIMAL, // 0.0-10.0
-	POINT_10, // 0-10
-	POINT_5, // 0-5, should be represented in stars
-	POINT_3 // 1-3, should be represented in smileys
+	POINT_10,         // 0-10
+	POINT_5,          // 0-5, should be represented in stars
+	POINT_3           // 1-3, should be represented in smileys
 };
 
-constexpr std::array<ScoreFormat, 5> ScoreFormats{ScoreFormat::POINT_100, ScoreFormat::POINT_10_DECIMAL, ScoreFormat::POINT_10,
-                                                  ScoreFormat::POINT_5,   ScoreFormat::POINT_3};
+constexpr std::array<ScoreFormat, 5> ScoreFormats{ScoreFormat::POINT_100, ScoreFormat::POINT_10_DECIMAL,
+                                                  ScoreFormat::POINT_10, ScoreFormat::POINT_5, ScoreFormat::POINT_3};
 
 struct ListInformation {
-		int id = 0;
-		int progress = 0;
-		int score = 0; // note that this will ALWAYS be in POINT_100 format and must be converted
-		ListStatus status = ListStatus::NOT_IN_LIST;
-		Date started;
-		Date completed;
-		bool is_private = false;
-		unsigned int rewatched_times = 0;
-		bool rewatching = false;
-		uint64_t updated = 0;
-		std::string notes;
+	int id = 0;
+	int progress = 0;
+	int score = 0; // note that this will ALWAYS be in POINT_100 format and must be converted
+	ListStatus status = ListStatus::NOT_IN_LIST;
+	Date started;
+	Date completed;
+	bool is_private = false;
+	unsigned int rewatched_times = 0;
+	bool rewatching = false;
+	uint64_t updated = 0;
+	std::string notes;
 };
 
 struct SeriesInformation {
-		int id;
-		struct {
-				std::string romaji;
-				std::string english;
-				std::string native;
-		} title;
-		std::vector<std::string> synonyms;
-		int episodes = 0;
-		SeriesStatus status = SeriesStatus::UNKNOWN;
-		Date air_date;
-		std::vector<std::string> genres;
-		std::vector<std::string> producers;
-		SeriesFormat format = SeriesFormat::UNKNOWN;
-		SeriesSeason season = SeriesSeason::UNKNOWN;
-		int audience_score = 0;
-		std::string synopsis;
-		int duration = 0;
-		std::string poster_url;
+	int id;
+	struct {
+		std::string romaji;
+		std::string english;
+		std::string native;
+	} title;
+	std::vector<std::string> synonyms;
+	int episodes = 0;
+	SeriesStatus status = SeriesStatus::UNKNOWN;
+	Date air_date;
+	std::vector<std::string> genres;
+	std::vector<std::string> producers;
+	SeriesFormat format = SeriesFormat::UNKNOWN;
+	SeriesSeason season = SeriesSeason::UNKNOWN;
+	int audience_score = 0;
+	std::string synopsis;
+	int duration = 0;
+	std::string poster_url;
 };
 
 class Anime {
-	public:
-		/* User list data */
-		ListStatus GetUserStatus() const;
-		int GetUserProgress() const;
-		int GetUserScore() const;
-		std::string GetUserPresentableScore() const;
-		Date GetUserDateStarted() const;
-		Date GetUserDateCompleted() const;
-		bool GetUserIsPrivate() const;
-		unsigned int GetUserRewatchedTimes() const;
-		bool GetUserIsRewatching() const;
-		uint64_t GetUserTimeUpdated() const;
-		std::string GetUserNotes() const;
+public:
+	/* User list data */
+	ListStatus GetUserStatus() const;
+	int GetUserProgress() const;
+	int GetUserScore() const;
+	std::string GetUserPresentableScore() const;
+	Date GetUserDateStarted() const;
+	Date GetUserDateCompleted() const;
+	bool GetUserIsPrivate() const;
+	unsigned int GetUserRewatchedTimes() const;
+	bool GetUserIsRewatching() const;
+	uint64_t GetUserTimeUpdated() const;
+	std::string GetUserNotes() const;
 
-		void SetUserStatus(ListStatus status);
-		void SetUserScore(int score);
-		void SetUserProgress(int progress);
-		void SetUserDateStarted(Date const& started);
-		void SetUserDateCompleted(Date const& completed);
-		void SetUserIsPrivate(bool is_private);
-		void SetUserRewatchedTimes(int rewatched);
-		void SetUserIsRewatching(bool rewatching);
-		void SetUserTimeUpdated(uint64_t updated);
-		void SetUserNotes(std::string const& notes);
+	void SetUserStatus(ListStatus status);
+	void SetUserScore(int score);
+	void SetUserProgress(int progress);
+	void SetUserDateStarted(Date const& started);
+	void SetUserDateCompleted(Date const& completed);
+	void SetUserIsPrivate(bool is_private);
+	void SetUserRewatchedTimes(int rewatched);
+	void SetUserIsRewatching(bool rewatching);
+	void SetUserTimeUpdated(uint64_t updated);
+	void SetUserNotes(std::string const& notes);
 
-		/* Series data */
-		int GetId() const;
-		std::string GetRomajiTitle() const;
-		std::string GetEnglishTitle() const;
-		std::string GetNativeTitle() const;
-		std::vector<std::string> GetTitleSynonyms() const;
-		int GetEpisodes() const;
-		SeriesStatus GetAiringStatus() const;
-		Date GetAirDate() const;
-		std::vector<std::string> GetGenres() const;
-		std::vector<std::string> GetProducers() const;
-		SeriesFormat GetFormat() const;
-		SeriesSeason GetSeason() const;
-		int GetAudienceScore() const;
-		std::string GetSynopsis() const;
-		int GetDuration() const;
-		std::string GetPosterUrl() const;
-		std::string GetServiceUrl() const;
+	/* Series data */
+	int GetId() const;
+	std::string GetRomajiTitle() const;
+	std::string GetEnglishTitle() const;
+	std::string GetNativeTitle() const;
+	std::vector<std::string> GetTitleSynonyms() const;
+	int GetEpisodes() const;
+	SeriesStatus GetAiringStatus() const;
+	Date GetAirDate() const;
+	std::vector<std::string> GetGenres() const;
+	std::vector<std::string> GetProducers() const;
+	SeriesFormat GetFormat() const;
+	SeriesSeason GetSeason() const;
+	int GetAudienceScore() const;
+	std::string GetSynopsis() const;
+	int GetDuration() const;
+	std::string GetPosterUrl() const;
+	std::string GetServiceUrl() const;
 
-		void SetId(int id);
-		void SetRomajiTitle(std::string const& title);
-		void SetEnglishTitle(std::string const& title);
-		void SetNativeTitle(std::string const& title);
-		void SetTitleSynonyms(std::vector<std::string> const& synonyms);
-		void AddTitleSynonym(std::string const& synonym);
-		void SetEpisodes(int episodes);
-		void SetAiringStatus(SeriesStatus status);
-		void SetAirDate(Date const& date);
-		void SetGenres(std::vector<std::string> const& genres);
-		void SetProducers(std::vector<std::string> const& producers);
-		void SetFormat(SeriesFormat format);
-		void SetSeason(SeriesSeason season);
-		void SetAudienceScore(int audience_score);
-		void SetSynopsis(std::string synopsis);
-		void SetDuration(int duration);
-		void SetPosterUrl(std::string poster);
+	void SetId(int id);
+	void SetRomajiTitle(std::string const& title);
+	void SetEnglishTitle(std::string const& title);
+	void SetNativeTitle(std::string const& title);
+	void SetTitleSynonyms(std::vector<std::string> const& synonyms);
+	void AddTitleSynonym(std::string const& synonym);
+	void SetEpisodes(int episodes);
+	void SetAiringStatus(SeriesStatus status);
+	void SetAirDate(Date const& date);
+	void SetGenres(std::vector<std::string> const& genres);
+	void SetProducers(std::vector<std::string> const& producers);
+	void SetFormat(SeriesFormat format);
+	void SetSeason(SeriesSeason season);
+	void SetAudienceScore(int audience_score);
+	void SetSynopsis(std::string synopsis);
+	void SetDuration(int duration);
+	void SetPosterUrl(std::string poster);
 
-		std::string GetUserPreferredTitle() const;
+	std::string GetUserPreferredTitle() const;
 
-		/* User stuff */
-		void AddToUserList();
-		bool IsInUserList() const;
-		void RemoveFromUserList();
+	/* User stuff */
+	void AddToUserList();
+	bool IsInUserList() const;
+	void RemoveFromUserList();
 
-	private:
-		SeriesInformation info_;
-		std::shared_ptr<struct ListInformation> list_info_;
+private:
+	SeriesInformation info_;
+	std::shared_ptr<struct ListInformation> list_info_;
 };
 
 } // namespace Anime
--- a/include/core/anime_db.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/anime_db.h	Mon Apr 01 02:43:44 2024 -0400
@@ -9,27 +9,26 @@
 namespace Anime {
 
 class Database {
-	public:
-		std::unordered_map<int, Anime> items;
-		size_t GetTotalAnimeAmount();
-		size_t GetTotalEpisodeAmount();
-		size_t GetTotalWatchedAmount();
-		size_t GetTotalPlannedAmount();
-		double GetAverageScore();
-		double GetScoreDeviation();
-		size_t GetListsAnimeAmount(ListStatus status);
-		int GetAnimeFromTitle(const std::string& title);
+public:
+	std::unordered_map<int, Anime> items;
+	size_t GetTotalAnimeAmount();
+	size_t GetTotalEpisodeAmount();
+	size_t GetTotalWatchedAmount();
+	size_t GetTotalPlannedAmount();
+	double GetAverageScore();
+	double GetScoreDeviation();
+	size_t GetListsAnimeAmount(ListStatus status);
+	int GetAnimeFromTitle(const std::string& title);
 
-		bool GetDatabaseAsJSON(nlohmann::json& json);
-		bool SaveDatabaseToDisk();
+	bool GetDatabaseAsJSON(nlohmann::json& json);
+	bool SaveDatabaseToDisk();
 
-		bool ParseDatabaseJSON(const nlohmann::json& json);
-		bool LoadDatabaseFromDisk();
+	bool ParseDatabaseJSON(const nlohmann::json& json);
+	bool LoadDatabaseFromDisk();
 };
 
 extern Database db;
 
 } // namespace Anime
 
-
 #endif // __core__anime_db_h
--- a/include/core/config.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/config.h	Mon Apr 01 02:43:44 2024 -0400
@@ -2,59 +2,59 @@
 #define __core__config_h
 
 #include "core/anime.h"
+#include "gui/locale.h"
 #include "gui/theme.h"
-#include "gui/locale.h"
 
-#include "animia/player.h"
+#include "animone/player.h"
 
+#include <set>
 #include <string>
+#include <utility>
 #include <vector>
-#include <set>
-#include <utility>
 
 struct MediaPlayer {
 	bool enabled = true;
-	animia::Player player;
+	animone::Player player;
 };
 
 class Config {
-	public:
-		int Load();
-		int Save();
+public:
+	int Load();
+	int Save();
 
-		Anime::Services service;
-		Theme::Theme theme;
-		Locale::Locale locale;
+	Anime::Services service;
+	Theme::Theme theme;
+	Locale::Locale locale;
 
-		struct {
-			Anime::TitleLanguage language;
-			Anime::ScoreFormat score_format;
-			bool display_aired_episodes;
-			bool display_available_episodes;
-			bool highlight_anime_if_available;
-			bool highlighted_anime_above_others;
-		} anime_list;
+	struct {
+		Anime::TitleLanguage language;
+		Anime::ScoreFormat score_format;
+		bool display_aired_episodes;
+		bool display_available_episodes;
+		bool highlight_anime_if_available;
+		bool highlighted_anime_above_others;
+	} anime_list;
 
+	struct {
 		struct {
-			struct {
-				std::string auth_token;
-				int user_id;
-			} anilist;
-		} auth;
+			std::string auth_token;
+			int user_id;
+		} anilist;
+	} auth;
 
-		struct {
-			bool detect_media_players;
-			std::vector<MediaPlayer> players;
-		} recognition;
+	struct {
+		bool detect_media_players;
+		std::vector<MediaPlayer> players;
+	} recognition;
 
-		struct {
-			std::string feed_link;
-		} torrents;
+	struct {
+		std::string feed_link;
+	} torrents;
 
-		struct {
-			bool real_time_monitor;
-			std::set<std::string> paths;
-		} library;
+	struct {
+		bool real_time_monitor;
+		std::set<std::string> paths;
+	} library;
 };
 
 #if (defined(WIN32) || defined(MACOSX))
--- a/include/core/date.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/date.h	Mon Apr 01 02:43:44 2024 -0400
@@ -8,29 +8,46 @@
 class QDate;
 
 class Date {
-	public:
-		Date();
-		Date(unsigned int y);
-		Date(unsigned int y, unsigned int m, unsigned int d);
-		Date(const QDate& date);
-		Date(const nlohmann::json& json);
-		bool IsValid() const;
-		void SetYear(unsigned int y);
-		void SetMonth(unsigned int m);
-		void SetDay(unsigned int d);
-		void VoidYear();
-		void VoidMonth();
-		void VoidDay();
-		std::optional<unsigned int> GetYear() const;
-		std::optional<unsigned int> GetMonth() const;
-		std::optional<unsigned int> GetDay() const;
-		QDate GetAsQDate() const;
-		nlohmann::json GetAsAniListJson() const;
+public:
+	using Year = unsigned int;
+	using Day = unsigned char;
+	enum class Month {
+		Jan = 0,
+		Feb,
+		Mar,
+		Apr,
+		May,
+		Jun,
+		Jul,
+		Aug,
+		Sep,
+		Oct,
+		Nov,
+		Dec
+	};
 
-	private:
-		std::optional<unsigned int> year;
-		std::optional<unsigned int> month;
-		std::optional<unsigned int> day;
+	Date();
+	Date(Year y);
+	Date(Year y, Month m, Day d);
+	Date(const QDate& date);
+	Date(const nlohmann::json& json);
+	bool IsValid() const;
+	void SetYear(Year y);
+	void SetMonth(Month m);
+	void SetDay(Day d);
+	void VoidYear();
+	void VoidMonth();
+	void VoidDay();
+	std::optional<Year> GetYear() const;
+	std::optional<Month> GetMonth() const;
+	std::optional<Day> GetDay() const;
+	QDate GetAsQDate() const;
+	nlohmann::json GetAsAniListJson() const;
+
+private:
+	std::optional<Year> year;
+	std::optional<Month> month;
+	std::optional<Day> day;
 };
 
 #endif // __core__date_h
--- a/include/core/filesystem.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/filesystem.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,15 +1,15 @@
 #ifndef __core__filesystem_h
 #define __core__filesystem_h
+#include <filesystem>
 #include <string>
-#include <filesystem>
 
 namespace Filesystem {
 
 void CreateDirectories(const std::filesystem::path& path);
-std::filesystem::path GetDotPath();        // %APPDATA%/minori/, ~/Library/Application Support/minori/, ~/.config/minori/...
-std::filesystem::path GetConfigPath();     // (dotpath)/config.json
-std::filesystem::path GetAnimeDBPath();    // (dotpath)/anime/db.json
-std::filesystem::path GetTorrentsPath();   // (dotpath)/torrents/...
+std::filesystem::path GetDotPath();    // %APPDATA%/minori/, ~/Library/Application Support/minori/, ~/.config/minori/...
+std::filesystem::path GetConfigPath(); // (dotpath)/config.json
+std::filesystem::path GetAnimeDBPath();  // (dotpath)/anime/db.json
+std::filesystem::path GetTorrentsPath(); // (dotpath)/torrents/...
 
 } // namespace Filesystem
 
--- a/include/core/http.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/http.h	Mon Apr 01 02:43:44 2024 -0400
@@ -15,7 +15,8 @@
 	Q_OBJECT
 
 public:
-	GetThread(const std::string& u, const std::vector<std::string>& h = {}, QObject* parent = nullptr) : QThread(parent) {
+	GetThread(const std::string& u, const std::vector<std::string>& h = {}, QObject* parent = nullptr)
+	    : QThread(parent) {
 		url = u;
 		headers = h;
 	}
@@ -24,9 +25,7 @@
 	void ReceivedData(const QByteArray& ba);
 
 protected:
-	void run() override {
-		emit ReceivedData(Get(url, headers));
-	}
+	void run() override { emit ReceivedData(Get(url, headers)); }
 
 	std::string url;
 	std::vector<std::string> headers;
@@ -36,7 +35,9 @@
 	Q_OBJECT
 
 public:
-	PostThread(const std::string& u, const std::string& d, const std::vector<std::string>& h = {}, QObject* parent = nullptr) : QThread(parent) {
+	PostThread(const std::string& u, const std::string& d, const std::vector<std::string>& h = {},
+	           QObject* parent = nullptr)
+	    : QThread(parent) {
 		url = u;
 		data = d;
 		headers = h;
@@ -46,9 +47,7 @@
 	void ReceivedData(const QByteArray& ba);
 
 protected:
-	void run() override {
-		emit ReceivedData(Post(url, data, headers));
-	}
+	void run() override { emit ReceivedData(Post(url, data, headers)); }
 
 	std::string url;
 	std::string data;
--- a/include/core/ini.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/ini.h	Mon Apr 01 02:43:44 2024 -0400
@@ -2,34 +2,32 @@
 #define __core__ini_h
 
 #define MINI_CASE_SENSITIVE
-#include "mini/ini.h"
 #include "core/strings.h"
 #include "gui/translate/anime.h"
 #include "gui/translate/config.h"
+#include "mini/ini.h"
+#include <string>
 #include <type_traits>
-#include <string>
 
 namespace INI {
 
 /* very simple tutorial on how to give anyone who reads
    your code an aneurysm */
-template <typename T, typename = void>
+template<typename T, typename = void>
 struct is_toutf8string_available : std::false_type {};
 
 template<typename T>
-struct is_toutf8string_available<T,
-		std::void_t<decltype(Strings::ToUtf8String(std::declval<T>()))>> : std::true_type {};
+struct is_toutf8string_available<T, std::void_t<decltype(Strings::ToUtf8String(std::declval<T>()))>> : std::true_type {
+};
 
-template <typename T, typename = void>
+template<typename T, typename = void>
 struct is_translation_available : std::false_type {};
 
 template<typename T>
-struct is_translation_available<T,
-		std::void_t<decltype(Translate::ToString(std::declval<T>()))>> : std::true_type {};
+struct is_translation_available<T, std::void_t<decltype(Translate::ToString(std::declval<T>()))>> : std::true_type {};
 
 template<typename T>
-T GetIniValue(const mINI::INIStructure& ini, const std::string& section,
-	          const std::string& value, const T& def) {
+T GetIniValue(const mINI::INIStructure& ini, const std::string& section, const std::string& value, const T& def) {
 	if (!ini.has(section) || !ini.get(section).has(value))
 		return def;
 
@@ -51,8 +49,7 @@
 
 /* this should be able to handle most of our custom types */
 template<typename T>
-void SetIniValue(mINI::INIStructure& ini, const std::string& section,
-	             const std::string& key, const T& value) {
+void SetIniValue(mINI::INIStructure& ini, const std::string& section, const std::string& key, const T& value) {
 	auto& ini_key = ini[section][key];
 
 	if constexpr (is_translation_available<T>::value) {
@@ -66,6 +63,6 @@
 	}
 }
 
-}
+} // namespace INI
 
 #endif
--- a/include/core/json.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/json.h	Mon Apr 01 02:43:44 2024 -0400
@@ -8,8 +8,7 @@
 namespace nlohmann {
 
 template<typename T>
-void to_json(nlohmann::json& j, const std::optional<T>& v)
-{
+void to_json(nlohmann::json& j, const std::optional<T>& v) {
 	if (v.has_value())
 		j = v.value();
 	else
@@ -17,8 +16,7 @@
 }
 
 template<typename T>
-void from_json(const nlohmann::json& j, std::optional<T>& v)
-{
+void from_json(const nlohmann::json& j, std::optional<T>& v) {
 	v = j.is_null() ? std::nullopt : j.get<T>();
 }
 
--- a/include/core/session.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/session.h	Mon Apr 01 02:43:44 2024 -0400
@@ -10,19 +10,19 @@
 class MainWindow;
 
 struct Session {
-	public:
-		Session() { timer.start(); }
-		/* we literally *cannot* be lying to the user by doing this */
-		void IncrementRequests() { requests++; };
-		int GetRequests() { return requests; };
-		int uptime() { return timer.elapsed(); }
+public:
+	Session() { timer.start(); }
+	/* we literally *cannot* be lying to the user by doing this */
+	void IncrementRequests() { requests++; };
+	int GetRequests() { return requests; };
+	int uptime() { return timer.elapsed(); }
 
-		Config config;
-		static constexpr semver::version version{PACKAGE_VERSION};
+	Config config;
+	static constexpr semver::version version{PACKAGE_VERSION};
 
-	private:
-		unsigned int requests = 0;
-		QElapsedTimer timer;
+private:
+	unsigned int requests = 0;
+	QElapsedTimer timer;
 };
 
 extern Session session;
--- a/include/core/strings.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/strings.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,10 +1,10 @@
 #ifndef __core__strings_h
 #define __core__strings_h
 
+#include <set>
+#include <sstream>
 #include <string>
 #include <vector>
-#include <set>
-#include <sstream>
 
 #include <cstdint>
 
@@ -15,10 +15,10 @@
 
 /* Implode function: takes a vector of strings and turns it
  * into a string, separated by delimiters.
-*/
+ */
 std::string Implode(const std::vector<std::string>& vector, const std::string& delimiter);
 std::string Implode(const std::set<std::string>& set, const std::string& delimiter);
-std::vector<std::string> Split(const std::string &text, const std::string& delimiter);
+std::vector<std::string> Split(const std::string& text, const std::string& delimiter);
 
 /* Substring removal functions */
 std::string ReplaceAll(std::string string, const std::string& find, const std::string& replace);
@@ -36,7 +36,7 @@
  * different string formats universal (and these functions
  * typically do things the right way so we avoid retarded
  * code)
-*/
+ */
 std::wstring ToWstring(const std::string& string);
 std::wstring ToWstring(const QString& string);
 std::string ToUtf8String(const std::wstring& wstring);
@@ -46,16 +46,14 @@
 QString ToQString(const std::wstring& wstring);
 
 /* not really an "int"... but who cares? */
-template<typename T = int,
-         std::enable_if_t<std::is_integral<T>::value, bool> = true>
+template<typename T = int, std::enable_if_t<std::is_integral<T>::value, bool> = true>
 T ToInt(const std::string& str, T def = 0) {
 	std::istringstream s(str);
 	s >> std::noboolalpha >> def;
 	return def;
 }
 
-template<typename T,
-         std::enable_if_t<std::is_integral<T>::value && !std::is_same<T, bool>::value, bool> = true>
+template<typename T, std::enable_if_t<std::is_integral<T>::value && !std::is_same<T, bool>::value, bool> = true>
 std::string ToUtf8String(T i) {
 	std::ostringstream s;
 	s << i;
--- a/include/core/time.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/time.h	Mon Apr 01 02:43:44 2024 -0400
@@ -6,16 +6,16 @@
 namespace Time {
 
 class Duration {
-	public:
-		Duration(int64_t l);
-		int64_t InSeconds();
-		int64_t InMinutes();
-		int64_t InHours();
-		int64_t InDays();
-		std::string AsRelativeString();
+public:
+	Duration(int64_t l);
+	int64_t InSeconds();
+	int64_t InMinutes();
+	int64_t InHours();
+	int64_t InDays();
+	std::string AsRelativeString();
 
-	private:
-		int64_t length;
+private:
+	int64_t length;
 };
 
 int64_t GetSystemTime();
--- a/include/core/torrent.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/core/torrent.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,62 +1,62 @@
 #ifndef __core__torrent_h
 #define __core__torrent_h
 
+#include <QDateTime>
 #include <string>
-#include <QDateTime>
 
 /* this is really just a fancy struct...
  *
  * this will be moved into its own namespace if
  * it's deemed necessary
-*/
+ */
 class Torrent {
-	public:
-		std::string GetTitle() const { return _title; };
-		std::string GetCategory() const { return _category; };
-		std::string GetEpisode() const { return _episode; };
-		std::string GetGroup() const { return _group; };
-		size_t GetSize() const { return _size; };
-		std::string GetResolution() const { return _resolution; };
-		int GetSeeders() const { return _seeders; };
-		int GetLeechers() const { return _leechers; };
-		int GetDownloads() const { return _downloads; };
-		std::string GetDescription() const { return _description; };
-		std::string GetFilename() const { return _filename; };
-		std::string GetLink() const { return _link; };
-		std::string GetGuid() const { return _guid; };
-		QDateTime GetDate() const { return _date; };
+public:
+	std::string GetTitle() const { return _title; };
+	std::string GetCategory() const { return _category; };
+	std::string GetEpisode() const { return _episode; };
+	std::string GetGroup() const { return _group; };
+	size_t GetSize() const { return _size; };
+	std::string GetResolution() const { return _resolution; };
+	int GetSeeders() const { return _seeders; };
+	int GetLeechers() const { return _leechers; };
+	int GetDownloads() const { return _downloads; };
+	std::string GetDescription() const { return _description; };
+	std::string GetFilename() const { return _filename; };
+	std::string GetLink() const { return _link; };
+	std::string GetGuid() const { return _guid; };
+	QDateTime GetDate() const { return _date; };
 
-		void SetTitle(const std::string& title) { _title = title; };
-		void SetCategory(const std::string& category) { _category = category; };
-		void SetEpisode(const std::string& episode) { _episode = episode; };
-		void SetGroup(const std::string& group) { _group = group; };
-		void SetSize(const size_t size) { _size = size; };
-		void SetResolution(const std::string& resolution) { _resolution = resolution; };
-		void SetSeeders(const int seeders) { _seeders = seeders; };
-		void SetLeechers(const int leechers) { _leechers = leechers; };
-		void SetDownloads(const int downloads) { _downloads = downloads; };
-		void SetDescription(const std::string& description) { _description = description; };
-		void SetFilename(const std::string& filename) { _filename = filename; };
-		void SetLink(const std::string& link) { _link = link; };
-		void SetGuid(const std::string& guid) { _guid = guid; };
-		void SetDate(const QDateTime& date) { _date = date; };
+	void SetTitle(const std::string& title) { _title = title; };
+	void SetCategory(const std::string& category) { _category = category; };
+	void SetEpisode(const std::string& episode) { _episode = episode; };
+	void SetGroup(const std::string& group) { _group = group; };
+	void SetSize(const size_t size) { _size = size; };
+	void SetResolution(const std::string& resolution) { _resolution = resolution; };
+	void SetSeeders(const int seeders) { _seeders = seeders; };
+	void SetLeechers(const int leechers) { _leechers = leechers; };
+	void SetDownloads(const int downloads) { _downloads = downloads; };
+	void SetDescription(const std::string& description) { _description = description; };
+	void SetFilename(const std::string& filename) { _filename = filename; };
+	void SetLink(const std::string& link) { _link = link; };
+	void SetGuid(const std::string& guid) { _guid = guid; };
+	void SetDate(const QDateTime& date) { _date = date; };
 
-	private:
-		std::string _title;
-		std::string _category;
-		std::string _episode;
-		std::string _group;
-		size_t _size = 0;
-		std::string _resolution; /* technically should be an int,
-		                            but std::string is more useful */
-		int _seeders = 0;
-		int _leechers = 0;
-		int _downloads = 0;
-		std::string _description;
-		std::string _filename;
-		std::string _link;
-		std::string _guid;
-		QDateTime _date;
+private:
+	std::string _title;
+	std::string _category;
+	std::string _episode;
+	std::string _group;
+	size_t _size = 0;
+	std::string _resolution; /* technically should be an int,
+	                            but std::string is more useful */
+	int _seeders = 0;
+	int _leechers = 0;
+	int _downloads = 0;
+	std::string _description;
+	std::string _filename;
+	std::string _link;
+	std::string _guid;
+	QDateTime _date;
 };
 
 #endif // __core__torrent_h
\ No newline at end of file
--- a/include/gui/dialog/about.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/dialog/about.h	Mon Apr 01 02:43:44 2024 -0400
@@ -6,11 +6,11 @@
 class AboutWindow final : public QDialog {
 	Q_OBJECT
 
-	public:
-		AboutWindow(QWidget* parent = nullptr);
+public:
+	AboutWindow(QWidget* parent = nullptr);
 
-	protected:
-		void showEvent(QShowEvent* event) override;
+protected:
+	void showEvent(QShowEvent* event) override;
 };
 
 #endif // __gui__dialog__about_h
--- a/include/gui/dialog/information.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/dialog/information.h	Mon Apr 01 02:43:44 2024 -0400
@@ -7,29 +7,29 @@
 #include <functional>
 
 class InformationDialog final : public QDialog {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		enum Pages {
-			PAGE_MAIN_INFO,
-			PAGE_MY_LIST
-		};
+public:
+	enum Pages {
+		PAGE_MAIN_INFO,
+		PAGE_MY_LIST
+	};
 
-		InformationDialog(Anime::Anime& anime, std::function<void()> accept = {},
-			              enum Pages page = Pages::PAGE_MAIN_INFO, QWidget* parent = nullptr);
+	InformationDialog(Anime::Anime& anime, std::function<void()> accept = {}, enum Pages page = Pages::PAGE_MAIN_INFO,
+	                  QWidget* parent = nullptr);
 
-	protected:
-		void showEvent(QShowEvent* event) override;
+protected:
+	void showEvent(QShowEvent* event) override;
 
-	private:
-		void SaveData(Anime::Anime& anime);
-		unsigned int _progress;
-		unsigned int _score;
-		bool _rewatching;
-		Anime::ListStatus _status;
-		std::string _notes;
-		Date _started;
-		Date _completed;
+private:
+	void SaveData(Anime::Anime& anime);
+	unsigned int _progress;
+	unsigned int _score;
+	bool _rewatching;
+	Anime::ListStatus _status;
+	std::string _notes;
+	Date _started;
+	Date _completed;
 };
 
 #endif // __gui__dialog__information_h
--- a/include/gui/dialog/settings.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/dialog/settings.h	Mon Apr 01 02:43:44 2024 -0400
@@ -5,9 +5,9 @@
 #include "core/config.h"
 #include "core/session.h"
 #include <QDialog>
-#include <QWidget>
+#include <QListWidget>
 #include <QLocale>
-#include <QListWidget>
+#include <QWidget>
 
 class QLabel;
 class QTabWidget;
@@ -31,110 +31,110 @@
 };
 
 class SettingsPage : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPage(QWidget* parent = nullptr, QString title = "");
-		void SetTitle(QString title);
-		virtual void SaveInfo() = 0;
-		void AddTab(QWidget* tab, QString title = "");
+public:
+	SettingsPage(QWidget* parent = nullptr, QString title = "");
+	void SetTitle(QString title);
+	virtual void SaveInfo() = 0;
+	void AddTab(QWidget* tab, QString title = "");
 
-	private:
-		QLabel* page_title;
-		QTabWidget* tab_widget;
+private:
+	QLabel* page_title;
+	QTabWidget* tab_widget;
 };
 
 class SettingsPageServices final : public SettingsPage {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPageServices(QWidget* parent = nullptr);
-		void SaveInfo() override;
+public:
+	SettingsPageServices(QWidget* parent = nullptr);
+	void SaveInfo() override;
 
-	private:
-		QWidget* CreateMainPage();
-		QWidget* CreateAniListPage();
+private:
+	QWidget* CreateMainPage();
+	QWidget* CreateAniListPage();
 
-		decltype(session.config.service) service;
+	decltype(session.config.service) service;
 };
 
 class SettingsPageApplication final : public SettingsPage {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPageApplication(QWidget* parent = nullptr);
-		void SaveInfo() override;
+public:
+	SettingsPageApplication(QWidget* parent = nullptr);
+	void SaveInfo() override;
 
-	private:
-		QWidget* CreateAnimeListWidget();
-		QWidget* CreateGeneralWidget();
+private:
+	QWidget* CreateAnimeListWidget();
+	QWidget* CreateGeneralWidget();
 
-		Themes theme;
-		QLocale locale;
+	Themes theme;
+	QLocale locale;
 
-		decltype(session.config.anime_list.score_format) format;
-		decltype(session.config.anime_list.language) language;
-		decltype(session.config.anime_list.display_aired_episodes) display_aired_episodes;
-		decltype(session.config.anime_list.display_available_episodes) display_available_episodes;
-		decltype(session.config.anime_list.highlight_anime_if_available) highlight_anime_if_available;
-		decltype(session.config.anime_list.highlighted_anime_above_others) highlighted_anime_above_others;
+	decltype(session.config.anime_list.score_format) format;
+	decltype(session.config.anime_list.language) language;
+	decltype(session.config.anime_list.display_aired_episodes) display_aired_episodes;
+	decltype(session.config.anime_list.display_available_episodes) display_available_episodes;
+	decltype(session.config.anime_list.highlight_anime_if_available) highlight_anime_if_available;
+	decltype(session.config.anime_list.highlighted_anime_above_others) highlighted_anime_above_others;
 };
 
 class SettingsPageTorrents final : public SettingsPage {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPageTorrents(QWidget* parent = nullptr);
-		void SaveInfo() override;
+public:
+	SettingsPageTorrents(QWidget* parent = nullptr);
+	void SaveInfo() override;
 
-	private:
-		QWidget* CreateGeneralWidget();
+private:
+	QWidget* CreateGeneralWidget();
 
-		decltype(session.config.torrents.feed_link) feed_link;
+	decltype(session.config.torrents.feed_link) feed_link;
 };
 
 class SettingsPageRecognition final : public SettingsPage {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPageRecognition(QWidget* parent = nullptr);
-		void SaveInfo() override;
+public:
+	SettingsPageRecognition(QWidget* parent = nullptr);
+	void SaveInfo() override;
 
-	private:
-		QWidget* CreatePlayersWidget();
+private:
+	QWidget* CreatePlayersWidget();
 
-		decltype(session.config.recognition.detect_media_players) detect_media_players;
-		decltype(session.config.recognition.players) players;
+	decltype(session.config.recognition.detect_media_players) detect_media_players;
+	decltype(session.config.recognition.players) players;
 };
 
 class SettingsPageLibrary final : public SettingsPage {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsPageLibrary(QWidget* parent = nullptr);
-		void SaveInfo() override;
+public:
+	SettingsPageLibrary(QWidget* parent = nullptr);
+	void SaveInfo() override;
 
-	private:
-		QWidget* CreateFoldersWidget();
+private:
+	QWidget* CreateFoldersWidget();
 
-		decltype(session.config.library.paths) paths;
-		decltype(session.config.library.real_time_monitor) real_time_monitor;
+	decltype(session.config.library.paths) paths;
+	decltype(session.config.library.real_time_monitor) real_time_monitor;
 };
 
 class SettingsDialog final : public QDialog {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SettingsDialog(QWidget* parent = nullptr);
-		QWidget* CreateServicesMainPage(QWidget* parent);
-		void OnOK();
+public:
+	SettingsDialog(QWidget* parent = nullptr);
+	QWidget* CreateServicesMainPage(QWidget* parent);
+	void OnOK();
 
-	protected:
-		void showEvent(QShowEvent* event) override;
+protected:
+	void showEvent(QShowEvent* event) override;
 
-	private:
-		SideBar* sidebar;
-		QStackedWidget* stacked;
+private:
+	SideBar* sidebar;
+	QStackedWidget* stacked;
 };
 
 #endif // __gui__dialog__settings_h
--- a/include/gui/layouts/flow_layout.h	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#ifndef __gui__layouts__flow_layout_h
-#define __gui__layouts__flow_layout_h
-
-#include <QLayout>
-#include <QRect>
-#include <QStyle>
-
-class QWidget;
-class QLayoutItem;
-
-class FlowLayout : public QLayout {
-public:
-    explicit FlowLayout(QWidget* parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
-    explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
-    ~FlowLayout();
-
-    void addItem(QLayoutItem* item) override;
-    int horizontalSpacing() const;
-    int verticalSpacing() const;
-    Qt::Orientations expandingDirections() const override;
-    bool hasHeightForWidth() const override;
-    int heightForWidth(int) const override;
-    int count() const override;
-    QLayoutItem* itemAt(int index) const override;
-    QSize minimumSize() const override;
-    void setGeometry(const QRect& rect) override;
-    QSize sizeHint() const override;
-    QLayoutItem* takeAt(int index) override;
-
-private:
-    int doLayout(const QRect& rect, bool testOnly) const;
-    int smartSpacing(QStyle::PixelMetric pm) const;
-
-    QList<QLayoutItem*> item_list;
-    int _horiz_space;
-    int _vert_space;
-};
-
-#endif // __gui__layouts__flow_layout_h
\ No newline at end of file
--- a/include/gui/locale.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/locale.h	Mon Apr 01 02:43:44 2024 -0400
@@ -3,9 +3,9 @@
 
 #include <QLocale>
 #include <QTranslator>
-#include <vector>
+#include <memory>
 #include <string>
-#include <memory>
+#include <vector>
 
 namespace Locale {
 
@@ -30,6 +30,6 @@
 	std::vector<QLocale> _available_translations = {};
 };
 
-}
+} // namespace Locale
 
 #endif // __gui__locale_h
--- a/include/gui/pages/anime_list.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/anime_list.h	Mon Apr 01 02:43:44 2024 -0400
@@ -12,82 +12,82 @@
 class QTabBar;
 
 class AnimeListPageSortFilter final : public QSortFilterProxyModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		AnimeListPageSortFilter(QObject* parent = nullptr);
+public:
+	AnimeListPageSortFilter(QObject* parent = nullptr);
 
-	protected:
-		bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
+protected:
+	bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
 };
 
 class AnimeListPageModel final : public QAbstractListModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		enum columns {
-			AL_TITLE,
-			AL_PROGRESS,
-			AL_EPISODES,
-			AL_SCORE,
-			AL_AVG_SCORE,
-			AL_TYPE,
-			AL_SEASON,
-			AL_STARTED,
-			AL_COMPLETED,
-			AL_UPDATED,
-			AL_NOTES,
+public:
+	enum columns {
+		AL_TITLE,
+		AL_PROGRESS,
+		AL_EPISODES,
+		AL_SCORE,
+		AL_AVG_SCORE,
+		AL_TYPE,
+		AL_SEASON,
+		AL_STARTED,
+		AL_COMPLETED,
+		AL_UPDATED,
+		AL_NOTES,
 
-			NB_COLUMNS
-		};
+		NB_COLUMNS
+	};
 
-		AnimeListPageModel(QObject* parent, Anime::ListStatus _status);
-		~AnimeListPageModel() override = default;
-		int rowCount(const QModelIndex& parent = QModelIndex()) const override;
-		int columnCount(const QModelIndex& parent = QModelIndex()) const override;
-		QVariant data(const QModelIndex& index, int role) const override;
-		QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
-		void RefreshList();
-		Anime::Anime* GetAnimeFromIndex(QModelIndex index);
+	AnimeListPageModel(QObject* parent, Anime::ListStatus _status);
+	~AnimeListPageModel() override = default;
+	int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+	int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+	QVariant data(const QModelIndex& index, int role) const override;
+	QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
+	void RefreshList();
+	Anime::Anime* GetAnimeFromIndex(QModelIndex index);
 
-	private:
-		Anime::ListStatus status;
-		std::vector<Anime::Anime> list;
+private:
+	Anime::ListStatus status;
+	std::vector<Anime::Anime> list;
 };
 
 /* todo: rename these to "page" or something more
    sensible than "widget" */
 class AnimeListPage final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		AnimeListPage(QWidget* parent);
-		void Refresh();
+public:
+	AnimeListPage(QWidget* parent);
+	void Refresh();
 
-	protected:
-		void paintEvent(QPaintEvent*) override;
-		void InitStyle(QStyleOptionTabWidgetFrame* option) const;
-		void InitBasicStyle(QStyleOptionTabWidgetFrame* option) const;
-		void SetupLayout();
-		void showEvent(QShowEvent*) override;
-		void resizeEvent(QResizeEvent* e) override;
-		void RefreshList();
-		void RefreshTabs();
-		void UpdateAnime(int id);
-		void RemoveAnime(int id);
+protected:
+	void paintEvent(QPaintEvent*) override;
+	void InitStyle(QStyleOptionTabWidgetFrame* option) const;
+	void InitBasicStyle(QStyleOptionTabWidgetFrame* option) const;
+	void SetupLayout();
+	void showEvent(QShowEvent*) override;
+	void resizeEvent(QResizeEvent* e) override;
+	void RefreshList();
+	void RefreshTabs();
+	void UpdateAnime(int id);
+	void RemoveAnime(int id);
 
-	private slots:
-		void DisplayColumnHeaderMenu();
-		void DisplayListMenu();
-		void ItemDoubleClicked();
-		void SetColumnDefaults();
-		int VisibleColumnsCount() const;
+private slots:
+	void DisplayColumnHeaderMenu();
+	void DisplayListMenu();
+	void ItemDoubleClicked();
+	void SetColumnDefaults();
+	int VisibleColumnsCount() const;
 
-	private:
-		QTabBar* tab_bar;
-		QTreeView* tree_view;
-		QRect panelRect;
-		std::array<AnimeListPageSortFilter*, 5> sort_models;
+private:
+	QTabBar* tab_bar;
+	QTreeView* tree_view;
+	QRect panelRect;
+	std::array<AnimeListPageSortFilter*, 5> sort_models;
 };
 
 #endif // __gui__pages__anime_list_h
\ No newline at end of file
--- a/include/gui/pages/history.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/history.h	Mon Apr 01 02:43:44 2024 -0400
@@ -4,10 +4,10 @@
 #include <QWidget>
 
 class HistoryPage final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		HistoryPage(QWidget* parent = nullptr);
+public:
+	HistoryPage(QWidget* parent = nullptr);
 };
 
 #endif // __gui__pages__history_h
--- a/include/gui/pages/now_playing.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/now_playing.h	Mon Apr 01 02:43:44 2024 -0400
@@ -6,8 +6,8 @@
 #include "gui/widgets/text.h"
 
 #include <QFrame>
+#include <memory>
 #include <unordered_map>
-#include <memory>
 
 class QStackedWidget;
 
@@ -22,43 +22,43 @@
 namespace NowPlayingPages {
 
 class Default : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		Default(QWidget* parent = nullptr);
+public:
+	Default(QWidget* parent = nullptr);
 };
 
 class Playing : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		Playing(QWidget* parent = nullptr);
-		void SetPlayingAnime(const Anime::Anime& anime, const anitomy::Elements& info);
-		int GetPlayingAnime();
+public:
+	Playing(QWidget* parent = nullptr);
+	void SetPlayingAnime(const Anime::Anime& anime, const anitomy::Elements& info);
+	int GetPlayingAnime();
 
-	private:
-		int _id = 0;
-		int _episode = 0;
-		std::unique_ptr<QWidget> _main = nullptr;
-		std::unique_ptr<TextWidgets::Title> _title = nullptr;
-		std::unique_ptr<AnimeInfoWidget> _info = nullptr;
-		std::unique_ptr<QWidget> _sidebar = nullptr;
-		std::unique_ptr<Poster> _poster = nullptr;
+private:
+	int _id = 0;
+	int _episode = 0;
+	std::unique_ptr<QWidget> _main = nullptr;
+	std::unique_ptr<TextWidgets::Title> _title = nullptr;
+	std::unique_ptr<AnimeInfoWidget> _info = nullptr;
+	std::unique_ptr<QWidget> _sidebar = nullptr;
+	std::unique_ptr<Poster> _poster = nullptr;
 };
 
-}
+} // namespace NowPlayingPages
 
 class NowPlayingPage final : public QFrame {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		NowPlayingPage(QWidget* parent = nullptr);
-		void SetDefault();
-		void SetPlaying(const Anime::Anime& anime, const anitomy::Elements& episodes);
-		int GetPlayingId();
+public:
+	NowPlayingPage(QWidget* parent = nullptr);
+	void SetDefault();
+	void SetPlaying(const Anime::Anime& anime, const anitomy::Elements& episodes);
+	int GetPlayingId();
 
-	private:
-		QStackedWidget* stack;
+private:
+	QStackedWidget* stack;
 };
 
 #endif // __gui__pages__now_playing_h
--- a/include/gui/pages/search.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/search.h	Mon Apr 01 02:43:44 2024 -0400
@@ -3,64 +3,64 @@
 
 #include "core/anime.h"
 
+#include <QAbstractListModel>
 #include <QFrame>
-#include <QAbstractListModel>
+#include <QItemSelection>
 #include <QSortFilterProxyModel>
-#include <QItemSelection>
 
 class QTreeView;
 
 class SearchPageListSortFilter final : public QSortFilterProxyModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SearchPageListSortFilter(QObject* parent = nullptr);
+public:
+	SearchPageListSortFilter(QObject* parent = nullptr);
 
-	protected:
-		bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
+protected:
+	bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
 };
 
 class SearchPageListModel final : public QAbstractListModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		enum columns {
-			SR_TITLE,
-			SR_TYPE,
-			SR_EPISODES,
-			SR_SCORE,
-			SR_SEASON,
+public:
+	enum columns {
+		SR_TITLE,
+		SR_TYPE,
+		SR_EPISODES,
+		SR_SCORE,
+		SR_SEASON,
 
-			NB_COLUMNS
-		};
+		NB_COLUMNS
+	};
 
-		SearchPageListModel(QObject* parent);
-		~SearchPageListModel() override = default;
-		int rowCount(const QModelIndex& parent = QModelIndex()) const override;
-		int columnCount(const QModelIndex& parent = QModelIndex()) const override;
-		QVariant data(const QModelIndex& index, int role) const override;
-		QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
-		Qt::ItemFlags flags(const QModelIndex& index) const override;
+	SearchPageListModel(QObject* parent);
+	~SearchPageListModel() override = default;
+	int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+	int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+	QVariant data(const QModelIndex& index, int role) const override;
+	QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
+	Qt::ItemFlags flags(const QModelIndex& index) const override;
 
-		void ParseSearch(const std::vector<int>& ids);
-		Anime::Anime* GetAnimeFromIndex(const QModelIndex& index) const;
+	void ParseSearch(const std::vector<int>& ids);
+	Anime::Anime* GetAnimeFromIndex(const QModelIndex& index) const;
 
-	private:
-		std::vector<int> ids;
+private:
+	std::vector<int> ids;
 };
 
 class SearchPage final : public QFrame {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SearchPage(QWidget* parent = nullptr);
-		void Search(const std::string& search);
-		void DisplayListMenu();
-		void ItemDoubleClicked();
+public:
+	SearchPage(QWidget* parent = nullptr);
+	void Search(const std::string& search);
+	void DisplayListMenu();
+	void ItemDoubleClicked();
 
-	private:
-		SearchPageListModel* model = nullptr;
-		SearchPageListSortFilter* sort_model = nullptr;
-		QTreeView* treeview = nullptr;
+private:
+	SearchPageListModel* model = nullptr;
+	SearchPageListSortFilter* sort_model = nullptr;
+	QTreeView* treeview = nullptr;
 };
 #endif // __gui__pages__search_h
--- a/include/gui/pages/statistics.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/statistics.h	Mon Apr 01 02:43:44 2024 -0400
@@ -12,19 +12,19 @@
 }
 
 class StatisticsPage final : public QFrame {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		StatisticsPage(QWidget* parent = nullptr);
-		void UpdateStatistics();
+public:
+	StatisticsPage(QWidget* parent = nullptr);
+	void UpdateStatistics();
 
-	protected:
-		void showEvent(QShowEvent*) override;
+protected:
+	void showEvent(QShowEvent*) override;
 
-	private:
-		std::shared_ptr<TextWidgets::LabelledSection> _anime_list;
-		std::shared_ptr<Graph<int>> _score_distribution_graph;
-		std::shared_ptr<TextWidgets::LabelledSection> _application;
+private:
+	std::shared_ptr<TextWidgets::LabelledSection> _anime_list;
+	std::shared_ptr<Graph<int>> _score_distribution_graph;
+	std::shared_ptr<TextWidgets::LabelledSection> _application;
 };
 
 #endif // __gui__pages__statistics_h
\ No newline at end of file
--- a/include/gui/pages/torrents.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/pages/torrents.h	Mon Apr 01 02:43:44 2024 -0400
@@ -2,83 +2,83 @@
 #define __gui__pages__torrents_h
 
 #include "core/torrent.h"
+#include <QAbstractListModel>
 #include <QFrame>
-#include <QAbstractListModel>
+#include <QItemSelection>
 #include <QSortFilterProxyModel>
-#include <QItemSelection>
 
 class QTreeView;
 
 class TorrentsPageListSortFilter final : public QSortFilterProxyModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		TorrentsPageListSortFilter(QObject* parent = nullptr);
+public:
+	TorrentsPageListSortFilter(QObject* parent = nullptr);
 
-	protected:
-		bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
+protected:
+	bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
 };
 
 class TorrentsPageListModel final : public QAbstractListModel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		enum columns {
-			TL_TITLE,
-			TL_EPISODE,
-			TL_GROUP,
-			TL_SIZE,
-			TL_RESOLUTION,
-			TL_SEEDERS, 
-			TL_LEECHERS,
-			TL_DOWNLOADS,
-			TL_DESCRIPTION,
-			TL_FILENAME,
-			TL_RELEASEDATE,
+public:
+	enum columns {
+		TL_TITLE,
+		TL_EPISODE,
+		TL_GROUP,
+		TL_SIZE,
+		TL_RESOLUTION,
+		TL_SEEDERS,
+		TL_LEECHERS,
+		TL_DOWNLOADS,
+		TL_DESCRIPTION,
+		TL_FILENAME,
+		TL_RELEASEDATE,
+
+		NB_COLUMNS
+	};
 
-			NB_COLUMNS
-		};
+	TorrentsPageListModel(QObject* parent);
+	~TorrentsPageListModel() override = default;
+	int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+	int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+	bool setData(const QModelIndex& index, const QVariant& value, int role) override;
+	QVariant data(const QModelIndex& index, int role) const override;
+	QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
+	Qt::ItemFlags flags(const QModelIndex& index) const override;
 
-		TorrentsPageListModel(QObject* parent);
-		~TorrentsPageListModel() override = default;
-		int rowCount(const QModelIndex& parent = QModelIndex()) const override;
-		int columnCount(const QModelIndex& parent = QModelIndex()) const override;
-		bool setData(const QModelIndex& index, const QVariant& value, int role) override;
-		QVariant data(const QModelIndex& index, int role) const override;
-		QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
-		Qt::ItemFlags flags(const QModelIndex& index) const override;
+	QByteArray DownloadTorrentList();
+	void DownloadTorrents(QItemSelection selection);
+	void ParseFeedDescription(const std::string& description, Torrent& torrent);
+	void ParseTorrentList(const QByteArray& ba);
+	void RefreshTorrentList();
 
-		QByteArray DownloadTorrentList();
-		void DownloadTorrents(QItemSelection selection);
-		void ParseFeedDescription(const std::string& description, Torrent& torrent);
-		void ParseTorrentList(const QByteArray& ba);
-		void RefreshTorrentList();
+private:
+	class TorrentModelItem : public Torrent {
+	public:
+		bool GetChecked() const { return _checked; };
+		void SetChecked(bool checked) { _checked = checked; };
 
 	private:
-		class TorrentModelItem : public Torrent {
-			public:
-				bool GetChecked() const { return _checked; };
-				void SetChecked(bool checked) { _checked = checked; };
+		bool _checked = false;
+	};
 
-			private:
-				bool _checked = false;
-		};
-
-		std::vector<TorrentModelItem> list;
+	std::vector<TorrentModelItem> list;
 };
 
 class TorrentsPage final : public QFrame {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		TorrentsPage(QWidget* parent = nullptr);
-		void DownloadSelection();
-		void Refresh();
+public:
+	TorrentsPage(QWidget* parent = nullptr);
+	void DownloadSelection();
+	void Refresh();
 
-	private:
-		TorrentsPageListModel* model = nullptr;
-		TorrentsPageListSortFilter* sort_model = nullptr;
-		QTreeView* treeview = nullptr;
+private:
+	TorrentsPageListModel* model = nullptr;
+	TorrentsPageListSortFilter* sort_model = nullptr;
+	QTreeView* treeview = nullptr;
 };
 
 #endif // __gui__pages__torrents_h
--- a/include/gui/theme.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/theme.h	Mon Apr 01 02:43:44 2024 -0400
@@ -12,19 +12,19 @@
 namespace Theme {
 
 class Theme final {
-	public:
-		Theme(Themes theme = Themes::OS);
-		void SetTheme(Themes theme);
-		Themes GetTheme() const;
-		bool IsInDarkTheme() const;
-		void RepaintCurrentTheme();
+public:
+	Theme(Themes theme = Themes::OS);
+	void SetTheme(Themes theme);
+	Themes GetTheme() const;
+	bool IsInDarkTheme() const;
+	void RepaintCurrentTheme();
 
-	private:
-		void SetToDarkTheme();
-		void SetToLightTheme();
-		void SetStyleSheet(Themes theme);
-		Themes GetCurrentOSTheme() const;
-		Themes theme;
+private:
+	void SetToDarkTheme();
+	void SetToLightTheme();
+	void SetStyleSheet(Themes theme);
+	Themes GetCurrentOSTheme() const;
+	Themes theme;
 };
 
 } // namespace Theme
--- a/include/gui/translate/config.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/translate/config.h	Mon Apr 01 02:43:44 2024 -0400
@@ -7,6 +7,6 @@
 Themes ToTheme(const std::string& theme);
 std::string ToString(const Themes& theme);
 
-}
+} // namespace Translate
 
 #endif // __gui__translate__config_h
\ No newline at end of file
--- a/include/gui/widgets/anime_info.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/anime_info.h	Mon Apr 01 02:43:44 2024 -0400
@@ -7,24 +7,24 @@
 class OneLineSection;
 class LabelledSection;
 class SelectableSection;
-}
+} // namespace TextWidgets
 
 namespace Anime {
 class Anime;
 }
 
 class AnimeInfoWidget final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		AnimeInfoWidget(QWidget* parent = nullptr);
-		AnimeInfoWidget(const Anime::Anime& anime, QWidget* parent = nullptr);
-		void SetAnime(const Anime::Anime& anime);
+public:
+	AnimeInfoWidget(QWidget* parent = nullptr);
+	AnimeInfoWidget(const Anime::Anime& anime, QWidget* parent = nullptr);
+	void SetAnime(const Anime::Anime& anime);
 
-	private:
-		std::shared_ptr<TextWidgets::OneLineSection> _title = nullptr;
-		std::shared_ptr<TextWidgets::LabelledSection> _details = nullptr;
-		std::shared_ptr<TextWidgets::SelectableSection> _synopsis = nullptr;
+private:
+	std::shared_ptr<TextWidgets::OneLineSection> _title = nullptr;
+	std::shared_ptr<TextWidgets::LabelledSection> _details = nullptr;
+	std::shared_ptr<TextWidgets::SelectableSection> _synopsis = nullptr;
 };
 
 #endif // __gui__widgets__anime_info_h
--- a/include/gui/widgets/clickable_label.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/clickable_label.h	Mon Apr 01 02:43:44 2024 -0400
@@ -4,17 +4,17 @@
 #include <QLabel>
 
 class ClickableLabel final : public QLabel {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		explicit ClickableLabel(QWidget* parent = nullptr);
-		~ClickableLabel();
+public:
+	explicit ClickableLabel(QWidget* parent = nullptr);
+	~ClickableLabel();
 
-	signals:
-		void clicked();
+signals:
+	void clicked();
 
-	protected:
-		void mousePressEvent(QMouseEvent*) override;
+protected:
+	void mousePressEvent(QMouseEvent*) override;
 };
 
 #endif // __gui__widgets__clickable_label_h
--- a/include/gui/widgets/graph.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/graph.h	Mon Apr 01 02:43:44 2024 -0400
@@ -3,89 +3,98 @@
 
 /* This class is defined as a template, so that means everything gets defined here as well :) */
 
-#include <QWidget>
 #include <QDebug>
-#include <QSize>
 #include <QPaintEvent>
-#include <QSize>
-#include <QRect>
 #include <QPainter>
 #include <QPainterPath>
 #include <QPen>
+#include <QRect>
+#include <QSize>
+#include <QWidget>
 #include <algorithm>
 #include <unordered_map>
 
 template<typename T>
 class Graph final : public QWidget {
-	public:
-		Graph(QWidget* parent = nullptr) : QWidget(parent) {};
-		void AddItem(T key, unsigned long val) { map[key] = val; update(); updateGeometry(); };
-		void Clear() { map.clear(); update(); updateGeometry(); };
+public:
+	Graph(QWidget* parent = nullptr) : QWidget(parent){};
+	void AddItem(T key, unsigned long val) {
+		map[key] = val;
+		update();
+		updateGeometry();
+	};
+	void Clear() {
+		map.clear();
+		update();
+		updateGeometry();
+	};
+
+protected:
+	std::unordered_map<T, unsigned long> map = {};
 
-	protected:
-		std::unordered_map<T, unsigned long> map = {};
+	QSize minimumSizeHint() const override {
+		QFontMetrics metric(font());
+		/* wtf?... */
+		return QSize(100, (metric.height() * map.size()) + (2 * map.size()));
+	}
+
+	/* helper functions */
+	inline unsigned long GetTotal() {
+		unsigned long count = 0;
 
-		QSize minimumSizeHint() const override {
-			QFontMetrics metric(font());
-			/* wtf?... */
-			return QSize(100, metric.height() * map.size() + (2 * (map.size() - 2)));
+		for (const auto& item : map)
+			count += item.second;
+
+		return count;
+	}
+
+	inline unsigned long GetTextWidth() {
+		unsigned long ret = 0;
+		QFontMetrics metric(font());
+
+		for (const auto& item : map) {
+			unsigned long width = metric.horizontalAdvance(QString::number(item.first), -1);
+			if (width > ret)
+				ret = width;
 		}
 
-		/* helper functions */
-		inline unsigned long GetTotal() {
-			unsigned long count = 0;
+		return ret;
+	}
 
-			for (const auto& item : map)
-				count += item.second;
+	void paintEvent(QPaintEvent* event) override {
+		static constexpr int HORIZ_SPACING = 5;
+		static constexpr int VERT_SPACING = 2;
+
+		/* these are retrieved from the QPaintEvent */
+		const QRect rect = event->rect();
+		const int width = event->rect().width();
+		const int x = rect.x();
+		int y = rect.y();
 
-			return count;
-		}
+		/* these are calculated from font metrics and such */
+		const int total = GetTotal();
+		const int text_width = GetTextWidth();
+		const int each_height = QFontMetrics(font()).height();
+
+		/* now we do the actual painting */
+		QPainter painter(this);
 
-		inline unsigned long GetTextWidth() {
-			unsigned long ret = 0;
-			QFontMetrics metric(font());
+		for (const auto& [key, value] : map) {
+			painter.drawText(QRect(x, y, text_width, each_height), Qt::AlignVCenter | Qt::AlignRight,
+			                 QString::number(key));
 
-			for (const auto& item : map) {
-				unsigned long width = metric.horizontalAdvance(QString::number(item.first), -1);
-				if (width > ret)
-					ret = width;
+			/* only draw this if we actually have any data */
+			if (total) {
+				QPainterPath path;
+				path.addRect(x + text_width + HORIZ_SPACING, y,
+				             (static_cast<double>(value) / total) * (width - text_width - HORIZ_SPACING), each_height);
+				painter.fillPath(path, Qt::darkBlue);
+				painter.drawPath(path);
 			}
 
-			return ret;
+			y += each_height + VERT_SPACING;
 		}
-
-		void paintEvent(QPaintEvent* event) override {
-			static constexpr int HORIZ_SPACING = 5;
-			static constexpr int VERT_SPACING = 2;
-
-			/* these are retrieved from the QPaintEvent */
-			const QRect rect = event->rect();
-			const int width = event->rect().width();
-			const int x = rect.x();
-			int y = rect.y();
-
-			/* these are calculated from font metrics and such */
-			const int total = GetTotal();
-			const int text_width = GetTextWidth() + 10;
-			const int each_height = QFontMetrics(font()).height();
-
-			/* now we do the actual painting */
-			QPainter painter(this);
-
-			for (const auto& [key, value] : map) {
-				painter.drawText(QRect(x, y, text_width, each_height), Qt::AlignVCenter, QString::number(key));
-
-				/* only draw this if we actually have any data */
-				if (total) {
-					QPainterPath path;
-					path.addRect(x + text_width + HORIZ_SPACING, y, (static_cast<double>(value)/total) * (width - text_width - HORIZ_SPACING), each_height);
-					painter.fillPath(path, Qt::darkBlue);
-					painter.drawPath(path);
-				}
-
-				y += each_height + VERT_SPACING;
-			}
-		}
+	}
 };
 
 #endif // __gui__widgets__graph_h
\ No newline at end of file
--- a/include/gui/widgets/optional_date.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/optional_date.h	Mon Apr 01 02:43:44 2024 -0400
@@ -9,25 +9,25 @@
 class QDate;
 
 class OptionalDate final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		OptionalDate(QWidget* parent = nullptr);
-		OptionalDate(bool enabled, QWidget* parent = nullptr);
-		QDateEdit* GetDateEdit();
-		QCheckBox* GetCheckBox();
-		void SetDate(QDate date);
-		void SetDate(Date date);
-		Date GetDate();
-		void SetEnabled(bool enabled);
-		bool IsEnabled();
+public:
+	OptionalDate(QWidget* parent = nullptr);
+	OptionalDate(bool enabled, QWidget* parent = nullptr);
+	QDateEdit* GetDateEdit();
+	QCheckBox* GetCheckBox();
+	void SetDate(QDate date);
+	void SetDate(Date date);
+	Date GetDate();
+	void SetEnabled(bool enabled);
+	bool IsEnabled();
 
-	signals:
-		void DataChanged(bool checked, Date date);
+signals:
+	void DataChanged(bool checked, Date date);
 
-	private:
-		QDateEdit* _dateedit;
-		QCheckBox* _checkbox;
+private:
+	QDateEdit* _dateedit;
+	QCheckBox* _checkbox;
 };
 
 #endif // __gui__widgets__optional_date_h
--- a/include/gui/widgets/sidebar.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/sidebar.h	Mon Apr 01 02:43:44 2024 -0400
@@ -7,29 +7,28 @@
 class QListWidgetItem;
 
 class SideBar final : public QListWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SideBar(QWidget* parent = nullptr);
-		QListWidgetItem* AddItem(QString name, QIcon icon = QIcon());
-		QListWidgetItem* AddSeparator();
-		int GetCurrentItem();
-		bool IndexIsSeparator(QModelIndex index) const;
-		static QIcon CreateIcon(const char* file);
-		void SetBackgroundColor(QColor color);
+public:
+	SideBar(QWidget* parent = nullptr);
+	QListWidgetItem* AddItem(QString name, QIcon icon = QIcon());
+	QListWidgetItem* AddSeparator();
+	int GetCurrentItem();
+	bool IndexIsSeparator(QModelIndex index) const;
+	static QIcon CreateIcon(const char* file);
+	void SetBackgroundColor(QColor color);
 
-	signals:
-		void CurrentItemChanged(int index);
+signals:
+	void CurrentItemChanged(int index);
 
-	public slots:
-		void SetCurrentItem(int index);
+public slots:
+	void SetCurrentItem(int index);
 
-	protected:
-		virtual void mouseMoveEvent(QMouseEvent* event) override;
-		QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& index,
-		                                                     const QEvent* event) const override;
-		int RemoveSeparatorsFromIndex(int index);
-		int AddSeparatorsToIndex(int index);
+protected:
+	virtual void mouseMoveEvent(QMouseEvent* event) override;
+	QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& index, const QEvent* event) const override;
+	int RemoveSeparatorsFromIndex(int index);
+	int AddSeparatorsToIndex(int index);
 };
 
 #endif // __gui__sidebar_h
--- a/include/gui/widgets/text.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/widgets/text.h	Mon Apr 01 02:43:44 2024 -0400
@@ -14,15 +14,15 @@
 namespace TextWidgets {
 
 class Header : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		Header(const QString& title, QWidget* parent = nullptr);
-		void SetText(const QString& title);
+public:
+	Header(const QString& title, QWidget* parent = nullptr);
+	void SetText(const QString& title);
 
-	private:
-		QLabel* static_text_title;
-		QFrame* static_text_line;
+private:
+	QLabel* static_text_title;
+	QFrame* static_text_line;
 };
 
 class Paragraph : public QLabel {
@@ -56,63 +56,63 @@
 };
 
 class Title final : public Line {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		Title(const QString& title, QWidget* parent = nullptr);
+public:
+	Title(const QString& title, QWidget* parent = nullptr);
 };
 
 class Section final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		Section(const QString& title, const QString& data, QWidget* parent = nullptr);
-		Header* GetHeader();
-		Paragraph* GetParagraph();
+public:
+	Section(const QString& title, const QString& data, QWidget* parent = nullptr);
+	Header* GetHeader();
+	Paragraph* GetParagraph();
 
-	private:
-		Header* header;
-		Paragraph* paragraph;
+private:
+	Header* header;
+	Paragraph* paragraph;
 };
 
 class LabelledSection final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent = nullptr);
-		Header* GetHeader();
-		Paragraph* GetLabels();
-		Paragraph* GetParagraph();
+public:
+	LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent = nullptr);
+	Header* GetHeader();
+	Paragraph* GetLabels();
+	Paragraph* GetParagraph();
 
-	private:
-		Header* header;
-		LabelledParagraph* content;
+private:
+	Header* header;
+	LabelledParagraph* content;
 };
 
 class SelectableSection final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		SelectableSection(const QString& title, const QString& data, QWidget* parent = nullptr);
-		Header* GetHeader();
-		Paragraph* GetParagraph();
+public:
+	SelectableSection(const QString& title, const QString& data, QWidget* parent = nullptr);
+	Header* GetHeader();
+	Paragraph* GetParagraph();
 
-	private:
-		Header* header;
-		Paragraph* paragraph;
+private:
+	Header* header;
+	Paragraph* paragraph;
 };
 
 class OneLineSection final : public QWidget {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		OneLineSection(const QString& title, const QString& data, QWidget* parent = nullptr);
-		Header* GetHeader();
-		Line* GetLine();
+public:
+	OneLineSection(const QString& title, const QString& data, QWidget* parent = nullptr);
+	Header* GetHeader();
+	Line* GetLine();
 
-	private:
-		Header* header;
-		Line* line;
+private:
+	Header* header;
+	Line* line;
 };
 
 } // namespace TextWidgets
--- a/include/gui/window.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/gui/window.h	Mon Apr 01 02:43:44 2024 -0400
@@ -7,64 +7,64 @@
 /* *could* be forward-declared, but this causes
    any file that #includes this to have to #include
    these as well due to unique_ptr */
-#include <QWidget>
-#include <QStackedWidget>
+#include "gui/widgets/sidebar.h"
 #include <QCloseEvent>
+#include <QStackedWidget>
 #include <QThread>
-#include "gui/widgets/sidebar.h"
+#include <QWidget>
 
 class QMenu;
 
 Q_DECLARE_METATYPE(std::vector<std::string>);
 
 class PlayingThread : public QThread {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		PlayingThread(QObject* object = nullptr) : QThread(object) {}
+public:
+	PlayingThread(QObject* object = nullptr) : QThread(object) {}
 
-	private:
-		void run() override;
+private:
+	void run() override;
 
-	signals:
-		void Done(const std::vector<std::string>& files);
+signals:
+	void Done(const std::vector<std::string>& files);
 };
 
 class MainWindow final : public QMainWindow {
-		Q_OBJECT
+	Q_OBJECT
 
-	public:
-		enum class Pages {
-			NOW_PLAYING,
+public:
+	enum class Pages {
+		NOW_PLAYING,
 
-			ANIME_LIST,
-			HISTORY,
-			STATISTICS,
+		ANIME_LIST,
+		HISTORY,
+		STATISTICS,
 
-			SEARCH,
-			SEASONS,
-			TORRENTS
-		};
+		SEARCH,
+		SEASONS,
+		TORRENTS
+	};
 
-		MainWindow(QWidget* parent = nullptr);
-		void SetActivePage(QWidget* page);
-		void CreateBars();
-		void AddMainWidgets();
-		void RetranslateUI();
-		void UpdateFolderMenu();
-		void AsyncSynchronize(QAction* action, QStackedWidget* stack);
-		void changeEvent(QEvent* event) override;
-		void showEvent(QShowEvent* event) override;
-		void closeEvent(QCloseEvent* event) override;
+	MainWindow(QWidget* parent = nullptr);
+	void SetActivePage(QWidget* page);
+	void CreateBars();
+	void AddMainWidgets();
+	void RetranslateUI();
+	void UpdateFolderMenu();
+	void AsyncSynchronize(QAction* action, QStackedWidget* stack);
+	void changeEvent(QEvent* event) override;
+	void showEvent(QShowEvent* event) override;
+	void closeEvent(QCloseEvent* event) override;
 
-	private:
-		std::unique_ptr<QWidget> main_widget = nullptr;
-		std::unique_ptr<QStackedWidget> stack = nullptr;
-		std::unique_ptr<SideBar> sidebar = nullptr;
+private:
+	std::unique_ptr<QWidget> main_widget = nullptr;
+	std::unique_ptr<QStackedWidget> stack = nullptr;
+	std::unique_ptr<SideBar> sidebar = nullptr;
 
-		std::unique_ptr<PlayingThread> thread = nullptr;
+	std::unique_ptr<PlayingThread> thread = nullptr;
 
-		QMenu* folder_menu = nullptr;
+	QMenu* folder_menu = nullptr;
 };
 
 #endif // __window_h
--- a/include/library/library.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/library/library.h	Mon Apr 01 02:43:44 2024 -0400
@@ -3,8 +3,8 @@
 
 #include "library/library.h"
 
+#include <string>
 #include <unordered_map>
-#include <string>
 
 namespace Library {
 
@@ -13,6 +13,6 @@
 
 void SearchLibraryFolders();
 
-}
+} // namespace Library
 
 #endif // __library__library_h
--- a/include/services/anilist.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/services/anilist.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,8 +1,8 @@
 #ifndef __services__anilist_h
 #define __services__anilist_h
 
+#include <string>
 #include <vector>
-#include <string>
 
 namespace Services {
 namespace AniList {
--- a/include/services/services.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/services/services.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,8 +1,8 @@
 #ifndef __services__services_h
 #define __services__services_h
 
+#include <string>
 #include <vector>
-#include <string>
 
 namespace Services {
 
--- a/include/track/media.h	Sun Feb 18 16:02:14 2024 -0500
+++ b/include/track/media.h	Mon Apr 01 02:43:44 2024 -0400
@@ -1,7 +1,7 @@
 #ifndef __track__media_h
 #define __track__media_h
+#include <string>
 #include <unordered_map>
-#include <string>
 #include <vector>
 
 namespace Track {
--- a/m4/autotroll.m4	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,720 +0,0 @@
-# Build Qt apps with the autotools (Autoconf/Automake).
-# M4 macros.
-#
-# This file is part of AutoTroll.
-#
-# Copyright (C) 2006-2018  Benoit Sigoure <benoit.sigoure@lrde.epita.fr>
-# Copyright (C) 2012-2023  Werner Lemberg <wl@gnu.org>
-#
-# AutoTroll is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-# In addition, as a special exception, the copyright holders of
-# AutoTroll give you unlimited permission to copy, distribute and
-# modify the configure scripts that are the output of Autoconf when
-# processing the macros of AutoTroll.  You need not follow the terms
-# of the GNU General Public License when using or distributing such
-# scripts, even though portions of the text of AutoTroll appear in
-# them.  The GNU General Public License (GPL) does govern all other
-# use of the material that constitutes AutoTroll.
-#
-# This special exception to the GPL applies to versions of AutoTroll
-# released by the copyright holders of AutoTroll.  Note that people
-# who make modified versions of AutoTroll are not obligated to grant
-# this special exception for their modified versions; it is their
-# choice whether to do so.  The GNU General Public License gives
-# permission to release a modified version without this exception;
-# this exception also makes it possible to release a modified version
-# which carries forward this exception.
-
- # ------------- #
- # DOCUMENTATION #
- # ------------- #
-
-# Disclaimer: Tested with Qt 4.2, 4.8, Qt 5.x, and Qt 6 only.
-# Feedback welcome.  Simply invoke AT_WITH_QT in your configure.ac.
-# AT_WITH_QT can take arguments which are documented in depth below.
-# The default arguments are equivalent to the default .pro file
-# generated by qmake.
-#
-# Invoking AT_WITH_QT will do the following:
-#
-#  - Add option `--with-qt[=ARG]' to your configure script.  Possible
-#    values for ARG are `yes' (which is the default) and `no' to
-#    enable and disable Qt support, respectively, or a path to the
-#    directory which contains the Qt binaries in case you have a
-#    non-stardard location.
-#
-#  - Add option `--without-qt', which is equivalent to `--with-qt=no'.
-#
-#  - On MacOS, add `-spec macx-g++' or `-spec macx-lang' (if `$CXX
-#    --version' output contains `clang').  This can be overridden with
-#    the QMAKESPEC environment variable, for example
-#
-#      QMAKESPEC='macx-clang' ./configure ...
-#
-#    (The QMAKESPEC variable is honoured for non-MacOS builds also.)
-#
-#  - If Qt support is enabled, define C preprocessor macro HAVE_QT.
-#
-#  - Find the programs `qmake', `moc', `uic', and `rcc' and save them
-#    in the make variables $(QMAKE), $(MOC), $(UIC), and $(RCC).
-#
-#  - Save the path to Qt binaries in $(QT_PATH).
-#
-#  - Find the flags necessary to compile and link Qt, that is:
-#
-#     * $(QT_DEFINES): -D's defined by qmake.
-#     * $(QT_CFLAGS): CFLAGS as defined by qmake (C?!)
-#     * $(QT_CXXFLAGS): CXXFLAGS as defined by qmake.
-#     * $(QT_INCPATH): -I's defined by qmake.
-#     * $(QT_CPPFLAGS): Same as $(QT_DEFINES) + $(QT_INCPATH).
-#     * $(QT_LFLAGS): LFLAGS defined by qmake.
-#     * $(QT_LDFLAGS): Same thing as $(QT_LFLAGS).
-#     * $(QT_LIBS): LIBS defined by qmake.
-#
-#  - Provide @QT_STATIC_PLUGINS@, which holds some additional C++
-#    declarations necessary for linking with static Qt plugins (for
-#    dynamic Qt builds it contains a dummy typedef declaration
-#    instead).  Use this substitution in a `foo.cpp.in' C++ template
-#    file or something similar, which must be registered in
-#    configure.ac's call to AC_CONFIG_FILES so that a proper `foo.cpp'
-#    file gets created.  Then compile and link `foo.cpp' with your
-#    program in the usual automake way.
-#
-#    NOTE: It is not possible to automatically detect whether a Qt
-#    release earlier than version 5 is built as a static library!  For
-#    this reason, @QT_STATIC_PLUGINS@ always contains the dummy
-#    typedef declaration if not using Qt5.
-#
-# You *MUST* invoke $(MOC) and/or $(UIC) by yourself where necessary.
-# AutoTroll provides you with Makerules to ease this; here is a sample
-# Makefile.am to use with AutoTroll which builds the code given in
-# chapter 7 of the Qt Tutorial
-# (http://doc.trolltech.com/4.2/tutorial-t7.html).
-#
-# -------------------------------------------------------------------------
-# include $(top_srcdir)/build-aux/autotroll.mk
-#
-# ACLOCAL_AMFLAGS = -I build-aux
-#
-# bin_PROGRAMS = lcdrange
-# lcdrange_SOURCES  = $(BUILT_SOURCES) lcdrange.cpp lcdrange.h main.cpp
-# lcdrange_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
-# lcdrange_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
-# lcdrange_LDFLAGS  = $(QT_LDFLAGS) $(LDFLAGS)
-# lcdrange_LDADD    = $(QT_LIBS) $(LDADD)
-#
-# BUILT_SOURCES = lcdrange.moc.cpp
-# -------------------------------------------------------------------------
-#
-# Note that your MOC, UIC, and RCC files *MUST* be listed explicitly
-# in BUILT_SOURCES.  If you name them properly (e.g. `.moc.cc',
-# `.qrc.cc', `.ui.cc' -- of course you can use `.cpp' or `.cxx' or
-# `.C' rather than `.cc') AutoTroll will build them automagically for
-# you, using implicit rules defined in `autotroll.mk'.
-
-m4_define([_AUTOTROLL_SERIAL],
-  [m4_translit([
-# serial 17
-], [#
-], [])])
-
-
-m4_ifdef([AX_INSTEAD_IF],
-  [],
-  [AC_DEFUN([AX_INSTEAD_IF],
-    [m4_ifval([$1],
-      [AC_MSG_WARN([$2])
-       [$1]],
-      [AC_MSG_ERROR([$2])])])])
-
-
-# AX_PATH_TOOLS(VARIABLE, PROGS-TO-CHECK-FOR, [VALUE-IF-NOT-FOUND], [PATH])
-# -------------------------------------------------------------------------
-AC_DEFUN([AX_PATH_TOOLS],
-  [for ax_tool in $2; do
-     AC_PATH_TOOL([$1], [$ax_tool], [], [$4])
-     test -n "$$1" && break
-   done
-   m4_ifval([$3], [test -n "$$1" || $1="$3"])
-  ])
-
-
-m4_pattern_forbid([^AT_])
-m4_pattern_forbid([^_AT_])
-
-
-# AT_WITH_QT([QT_modules], [QT_config], [QT_misc], [RUN-IF-FAILED], [RUN-IF-OK])
-# ------------------------------------------------------------------------------
-# Enable Qt support and add an option --with-qt to the configure
-# script.
-#
-# The QT_modules argument is optional and defines extra modules to
-# enable or disable (it's equivalent to the QT variable in .pro
-# files).  Modules can be specified as follows:
-#
-# AT_WITH_QT   =>  No argument -> No QT value.
-#                                 Qmake sets it to "core gui" by
-#                                 default.
-# AT_WITH_QT([xml])   =>  QT += xml
-# AT_WITH_QT([+xml])  =>  QT += xml
-# AT_WITH_QT([-gui])  =>  QT -= gui
-# AT_WITH_QT([xml -gui +sql svg])  =>  QT += xml sql svg
-#                                      QT -= gui
-#
-# The QT_config argument is also optional and follows the same
-# convention as QT_modules.  Instead of changing the QT variable, it
-# changes the CONFIG variable, which is used to tweak configuration
-# and compiler options.
-#
-# The last argument, QT_misc (also optional) will be copied as-is the
-# .pro file used to guess how to compile Qt apps.  You may use it to
-# further tweak the build process of Qt apps if tweaking the QT or
-# CONFIG variables isn't enough for you (for example, to control which
-# static plugins get used).
-#
-# RUN-IF-FAILED is arbitrary code to execute if Qt cannot be found or
-# if any problem happens.  If this argument is omitted, then
-# AC_MSG_ERROR will be called.  RUN-IF-OK is arbitrary code to execute
-# if Qt was successfully found.
-
-AC_DEFUN([AT_WITH_QT],
-  [AC_REQUIRE([AC_CANONICAL_HOST])
-   AC_REQUIRE([AC_CANONICAL_BUILD])
-   AC_REQUIRE([AC_PROG_CXX])
-
-   echo "$as_me: this is autotroll.m4[]_AUTOTROLL_SERIAL" \
-     >& AS_MESSAGE_LOG_FD
-
-   # This is a hack to get decent flow control with `break'.
-   for _qt_ignored in once; do
-
-     AC_ARG_WITH([qt],
-       AS_HELP_STRING([--with-qt@<:@=ARG@:>@],
-         [Qt support.  ARG can be `yes' (the default), `no',
-          or a path to Qt binaries; if `yes' or empty,
-          use PATH and some default directories to find Qt binaries]))
-
-     if test x"$with_qt" = x"no"; then
-       break
-     else
-       AC_DEFINE([HAVE_QT],[1],
-         [Define if the Qt framework is available.])
-     fi
-
-     if test x"$with_qt" = x"yes"; then
-       QT_PATH=
-     else
-       QT_PATH=$with_qt
-     fi
-
-     # Find Qt.
-     AC_ARG_VAR([QT_PATH],
-       [path to Qt binaries])
-     QT_TOOL_PATH=$QT_PATH:$PATH
-
-     # Find qmake.
-     AC_ARG_VAR([QMAKE],
-       [Qt Makefile generator command])
-     AX_PATH_TOOLS([QMAKE],
-       [qmake qmake-qt5 qmake-qt4 qmake-qt3],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test x"$QMAKE" = xmissing; then
-       if test x"$with_qt" = "x"; then
-          with_qt="no"
-       else
-         AX_INSTEAD_IF([$4],
-           [Cannot find qmake.  Try --with-qt=PATH.])
-       fi
-       break
-     fi
-
-     # Find moc (Meta Object Compiler).
-     AC_ARG_VAR([MOC],
-       [Qt Meta Object Compiler command])
-     AX_PATH_TOOLS([MOC],
-       [moc moc-qt5 moc-qt4 moc-qt3],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test x"$MOC" = xmissing; then
-       AX_INSTEAD_IF([$4],
-         [Cannot find moc (Meta Object Compiler).  Try --with-qt=PATH.])
-       break
-     fi
-
-     # Find uic (User Interface Compiler).
-     AC_ARG_VAR([UIC],
-       [Qt User Interface Compiler command])
-     AX_PATH_TOOLS([UIC],
-       [uic uic-qt5 uic-qt4 uic-qt3 uic3],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test x"$UIC" = xmissing; then
-       AX_INSTEAD_IF([$4],
-         [Cannot find uic (User Interface Compiler).  Try --with-qt=PATH.])
-       break
-     fi
-
-     # Find rcc (Qt Resource Compiler).
-     AC_ARG_VAR([RCC],
-       [Qt Resource Compiler command])
-     AX_PATH_TOOLS([RCC],
-       [rcc rcc-qt5],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test x"$RCC" = xmissing; then
-       AC_MSG_WARN(
-         [Cannot find rcc (Qt Resource Compiler).  Try --with-qt=PATH.])
-     fi
-
-     AC_ARG_VAR([LUPDATE],
-       [Qt Linguist updater command])
-     AX_PATH_TOOLS([LUPDATE],
-       [lupdate lupdate-qt5 lrelease-qt6],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test "x$LUPDATE" = "xmissing"; then
-       AC_MSG_WARN(
-         [Cannot find lupdate (Qt Linguist updater).  Try --with-qt=PATH.])
-     fi
-
-     AC_ARG_VAR([LRELEASE],
-       [Qt Linguist compiler command])
-     AX_PATH_TOOLS([LRELEASE],
-       [lrelease lrelease-qt5 lrelease-qt6],
-       [missing],
-       [$QT_TOOL_PATH])
-     if test "x$LRELEASE" = "xmissing"; then
-       AC_MSG_WARN(
-         [Cannot find lrelease (Qt Linguist compiler).  Try --with-qt=PATH.])
-     fi
-
-     AC_MSG_CHECKING([whether host operating system is Darwin])
-     at_darwin=no
-     at_qmake_args=
-     case $host_os in
-       dnl (
-       darwin*)
-         at_darwin=yes
-         ;;
-     esac
-     AC_MSG_RESULT([$at_darwin])
-
-     AC_MSG_CHECKING([whether QMAKESPEC environment variable is set])
-     if test x"$QMAKESPEC" = x; then
-       if test x"$at_darwin" = xyes; then
-         if $CXX --version | grep -q -i clang; then
-           at_qmake_args='-spec macx-clang'
-         else
-           at_qmake_args='-spec macx-g++'
-         fi
-         AC_MSG_RESULT([no, using $at_qmake_args])
-       else
-         AC_MSG_RESULT([no])
-       fi
-     else
-       AC_MSG_RESULT([yes, using $QMAKESPEC])
-     fi
-
-     # If we don't know the path to Qt, guess it from the path to
-     # qmake.
-     if test x"$QT_PATH" = x; then
-       QT_PATH=`dirname "$QMAKE"`
-     fi
-     if test x"$QT_PATH" = x; then
-       AX_INSTEAD_IF([$4],
-         [Cannot find your Qt installation.  Try --with-qt=PATH.])
-       break
-     fi
-     AC_SUBST([QT_PATH])
-
-     # Get ready to build a test-app with Qt.
-     if mkdir conftest.dir \
-        && cd conftest.dir; then
-       :
-     else
-       AX_INSTEAD_IF([$4],
-         [Cannot mkdir conftest.dir or cd to that directory.])
-       break
-     fi
-
-     cat >conftest.h <<_ASEOF
-
-#include <QObject>
-
-class Foo: public QObject
-{
-  Q_OBJECT;
-public:
-  Foo();
-  ~Foo() {}
-public Q_SLOTS:
-  void setValue(int value);
-Q_SIGNALS:
-  void valueChanged(int newValue);
-private:
-  int value_;
-};
-
-_ASEOF
-
-     cat >conftest.cpp <<_ASEOF
-
-#include "conftest.h"
-
-Foo::Foo()
-  : value_ (42)
-{
-  connect(this, SIGNAL(valueChanged(int)),
-          this, SLOT(setValue(int)));
-}
-
-void Foo::setValue(int value)
-{
-  value_ = value;
-}
-
-int main()
-{
-  Foo f;
-}
-
-_ASEOF
-
-     if $QMAKE -project; then
-       :
-     else
-       AX_INSTEAD_IF([$4],
-         [Calling $QMAKE -project failed.])
-       break
-     fi
-
-     # Find the .pro file generated by qmake.
-     pro_file=conftest.dir.pro
-     test -f $pro_file || pro_file=`echo *.pro`
-     if test -f "$pro_file"; then
-       :
-     else
-       AX_INSTEAD_IF([$4],
-         [Can't find the .pro file generated by Qmake.])
-       break
-     fi
-
-     dnl This is for Qt5; for Qt4 it does nothing special.
-     _AT_TWEAK_PRO_FILE([QT], [+widgets])
-
-     dnl Undocumented qmake: always use absolute paths.
-     dnl Defaults to 4.
-     _AT_TWEAK_PRO_FILE([QMAKE_PROJECT_DEPTH], [0])
-
-     dnl Tweak the value of QT in the .pro file if we have a first
-     dnl argument.
-     m4_ifval([$1],
-       [_AT_TWEAK_PRO_FILE([QT], [$1])])
-
-     dnl Tweak the value of CONFIG in the .pro file if we have a
-     dnl second argument.
-     m4_ifval([$2],
-       [_AT_TWEAK_PRO_FILE([CONFIG], [$2])])
-
-     m4_ifval([$3],
-       [ # Add the extra-settings the user wants to set in the .pro
-         # file.
-         echo "$3" >>"$pro_file"
-       ])
-
-     echo "$as_me:$LINENO: Invoking $QMAKE on $pro_file" \
-       >& AS_MESSAGE_LOG_FD
-     sed 's/^/| /' "$pro_file" >& AS_MESSAGE_LOG_FD
-
-     if $QMAKE $at_qmake_args; then
-       :
-     else
-       AX_INSTEAD_IF([$4],
-         [Calling $QMAKE $at_qmake_args failed.])
-       break
-     fi
-
-     # Try to compile a simple Qt app.
-     AC_CACHE_CHECK([whether we can build a simple Qt application],
-       [at_cv_qt_build],
-       [at_cv_qt_build=ko
-        : ${MAKE=make}
-
-        if $MAKE >& AS_MESSAGE_LOG_FD 2>&1; then
-          at_cv_qt_build='ok, looks like Qt 4, Qt 5, or Qt 6'
-        else
-          echo "$as_me:$LINENO: Build failed, trying to #include <qobject.h> instead" \
-            >& AS_MESSAGE_LOG_FD
-          sed 's/<QObject>/<qobject.h>/' conftest.h > tmp.h \
-            && mv tmp.h conftest.h
-          if $MAKE >& AS_MESSAGE_LOG_FD 2>&1; then
-            at_cv_qt_build='ok, looks like Qt 3'
-          else
-            # Sometimes (such as on Debian) build will fail because Qt
-            # hasn't been installed in debug mode and qmake tries (by
-            # default) to build apps in debug mode => Try again in
-            # release mode.
-            echo "$as_me:$LINENO: Build failed, trying to enforce release mode" \
-              >& AS_MESSAGE_LOG_FD
-
-            _AT_TWEAK_PRO_FILE([CONFIG], [+release])
-
-            sed 's/<qobject.h>/<QObject>/' conftest.h > tmp.h \
-              && mv tmp.h conftest.h
-            if $MAKE >& AS_MESSAGE_LOG_FD 2>&1; then
-              at_cv_qt_build='ok, looks like Qt 4 or Qt 5, release mode forced'
-            else
-              echo "$as_me:$LINENO: Build failed, trying to #include <qobject.h> instead" \
-                >& AS_MESSAGE_LOG_FD
-              sed 's/<QObject>/<qobject.h>/' conftest.h > tmp.h \
-                && mv tmp.h conftest.h
-              if $MAKE >& AS_MESSAGE_LOG_FD 2>&1; then
-                at_cv_qt_build='ok, looks like Qt 3, release mode forced'
-              else
-                at_cv_qt_build=ko
-                echo "$as_me:$LINENO: failed program was:" \
-                  >& AS_MESSAGE_LOG_FD
-                sed 's/^/| /' conftest.h >& AS_MESSAGE_LOG_FD
-                echo "$as_me:$LINENO: failed program was:" \
-                  >& AS_MESSAGE_LOG_FD
-                sed 's/^/| /' conftest.cpp >& AS_MESSAGE_LOG_FD
-              fi # if make with Qt3-style #include and release mode forced.
-            fi # if make with Qt4/5-style #include and release mode forced.
-          fi # if make with Qt3-style #include.
-        fi # if make with Qt4/5-style #include.
-       ])dnl end: AC_CACHE_CHECK(at_cv_qt_build)
-
-     if test x"$at_cv_qt_build" = xko; then
-       AX_INSTEAD_IF([$4],
-         [Cannot build a test Qt program])
-       cd ..
-       break
-     fi
-
-     QT_VERSION_MAJOR=`echo "$at_cv_qt_build" | sed 's/[[^0-9]]*//g'`
-     AC_SUBST([QT_VERSION_MAJOR])
-
-     # This sed filter is applied after an expression of the form
-     # /^FOO.*=/!d; it starts by removing the beginning of the line
-     # (using the empty regular expression //, which repeats the last
-     # regular expression match), removing references to SUBLIBS,
-     # removing unnecessary whitespace at the beginning, then prefixing
-     # our exported variables with QT_.  Note that `LDFLAGS' is
-     # intentionally omitted.
-     qt_sed_filter='s///;
-                    s/$(SUBLIBS)//g;
-                    s/^ *//;
-                    s/\$(DEFINES)/$(QT_DEFINES)/g;
-                    s/\$(CFLAGS)/$(QT_CFLAGS)/g;
-                    s/\$(CXXFLAGS)/$(QT_CXXFLAGS)/g;
-                    s/\$(INCPATH)/$(QT_INCPATH)/g;
-                    s/\$(CPPFLAGS)/$(QT_CPPFLAGS)/g;
-                    s/\$(LFLAGS)/$(QT_LFLAGS)/g;
-                    s/\$(LIBS)/$(QT_LIBS)/g'
-
-     # Find the Makefile (qmake happens to generate a fake Makefile
-     # which invokes a Makefile.Debug or Makefile.Release).  If we
-     # have both, we'll pick the Makefile.Release.  The reason is that
-     # this release uses -Os and debug -g.  We can override -Os by
-     # passing another -O but we usually don't override -g.
-     if test -f Makefile.Release; then
-       at_mfile='Makefile.Release'
-     else
-       at_mfile='Makefile'
-     fi
-     if test -f $at_mfile; then
-       :
-     else
-       AX_INSTEAD_IF([$4],
-         [Cannot find the Makefile generated by qmake.])
-       cd ..
-       break
-     fi
-
-     # Find the DEFINES of Qt (should have been named CPPFLAGS).
-     AC_CACHE_CHECK([for the DEFINES to use with Qt],
-       [at_cv_env_QT_DEFINES],
-       [at_cv_env_QT_DEFINES=`sed "/^DEFINES@<:@^A-Z=@:>@*=/!d;
-                                   $qt_sed_filter" $at_mfile`])
-     AC_SUBST([QT_DEFINES],
-       [$at_cv_env_QT_DEFINES])
-
-     # Find the CFLAGS of Qt.  (We can use Qt in C?!)
-     AC_CACHE_CHECK([for the CFLAGS to use with Qt],
-       [at_cv_env_QT_CFLAGS],
-       [at_cv_env_QT_CFLAGS=`sed "/^CFLAGS@<:@^A-Z=@:>@*=/!d;
-                                  $qt_sed_filter" $at_mfile`])
-     AC_SUBST([QT_CFLAGS],
-       [$at_cv_env_QT_CFLAGS])
-
-     # Find the CXXFLAGS of Qt.
-     AC_CACHE_CHECK([for the CXXFLAGS to use with Qt],
-       [at_cv_env_QT_CXXFLAGS],
-       [at_cv_env_QT_CXXFLAGS=`sed "/^CXXFLAGS@<:@^A-Z=@:>@*=/!d;
-                                    $qt_sed_filter" $at_mfile`])
-     AC_SUBST([QT_CXXFLAGS],
-       [$at_cv_env_QT_CXXFLAGS])
-
-     # Find the INCPATH of Qt.
-     AC_CACHE_CHECK([for the INCPATH to use with Qt],
-       [at_cv_env_QT_INCPATH],
-       [at_cv_env_QT_INCPATH=`sed "/^INCPATH@<:@^A-Z=@:>@*=/!d;
-                                   $qt_sed_filter" $at_mfile`])
-     AC_SUBST([QT_INCPATH],
-       [$at_cv_env_QT_INCPATH])
-
-     AC_SUBST([QT_CPPFLAGS],
-       ["$at_cv_env_QT_DEFINES $at_cv_env_QT_INCPATH"])
-
-     # Find the LFLAGS of Qt (should have been named LDFLAGS).
-     AC_CACHE_CHECK([for the LDFLAGS to use with Qt],
-       [at_cv_env_QT_LDFLAGS],
-       [at_cv_env_QT_LDFLAGS=`sed "/^LFLAGS@<:@^A-Z=@:>@*=/!d;
-                                   $qt_sed_filter" $at_mfile`])
-     AC_SUBST([QT_LFLAGS],
-       [$at_cv_env_QT_LDFLAGS])
-     AC_SUBST([QT_LDFLAGS],
-       [$at_cv_env_QT_LDFLAGS])
-
-     # Find the LIBS of Qt.
-     AC_CACHE_CHECK([for the LIBS to use with Qt],
-      [at_cv_env_QT_LIBS],
-      [at_cv_env_QT_LIBS=`sed "/^LIBS@<:@^A-Z@:>@*=/!d;
-                               $qt_sed_filter" $at_mfile`
-       if test x$at_darwin = xyes; then
-         # Fix QT_LIBS: as of today Libtool (GNU Libtool 1.5.23a)
-         # doesn't handle -F properly.  The "bug" has been fixed on 22
-         # October 2006 by Peter O'Gorman but we provide backward
-         # compatibility here.
-         at_cv_env_QT_LIBS=`echo "$at_cv_env_QT_LIBS" \
-                            | sed 's/^-F/-Wl,-F/;
-                                   s/ -F/ -Wl,-F/g'`
-       fi])
-     AC_SUBST([QT_LIBS],
-       [$at_cv_env_QT_LIBS])
-
-     # We can't use AC_CACHE_CHECK for data that contains newlines.
-     AC_MSG_CHECKING([for necessary static plugin code])
-     # find static plugin data generated by qmake
-     if test -f conftest.dir_plugin_import.cpp; then
-       QT_STATIC_PLUGINS=`cat conftest.dir_plugin_import.cpp`
-     else
-       QT_STATIC_PLUGINS="\
-// We have Qt earlier than version 5 or a dynamic build.
-// Provide dummy typedef to avoid empty source code.
-typedef int _qt_not_a_static_build;"
-     fi
-     AC_SUBST([QT_STATIC_PLUGINS])
-     AM_SUBST_NOTMAKE([QT_STATIC_PLUGINS])
-     AC_MSG_RESULT([$QT_STATIC_PLUGINS])
-
-     cd .. && rm -rf conftest.dir
-
-     # Run the user code
-     $5
-
-   done  # end hack (useless FOR to be able to use break)
-  ])
-
-
-# AT_REQUIRE_QT_VERSION(QT_version, [RUN-IF-FAILED], [RUN-IF-OK])
-# ---------------------------------------------------------------
-# Check (using qmake) that Qt's version "matches" QT_version.  Must be
-# run *AFTER* AT_WITH_QT.  Requires autoconf 2.60.
-#
-# This macro is ignored if Qt support has been disabled (using
-# `--with-qt=no' or `--without-qt').
-#
-# RUN-IF-FAILED is arbitrary code to execute if Qt cannot be found or
-# if any problem happens.  If this argument is omitted, then
-# AC_MSG_ERROR will be called.  RUN-IF-OK is arbitrary code to execute
-# if Qt was successfully found.
-#
-# This macro provides the Qt version in $(QT_VERSION).
-
-AC_DEFUN([AT_REQUIRE_QT_VERSION],
-  [AC_PREREQ([2.60])
-
-   # This is a hack to get decent flow control with `break'.
-   for _qt_ignored in once; do
-
-     if test x"$with_qt" = x"no"; then
-       break
-     fi
-
-     if test x"$QMAKE" = x; then
-       AX_INSTEAD_IF([$2],
-        [\$QMAKE is empty.  Did you invoke AT@&t@_WITH_QT before AT@&t@_REQUIRE_QT_VERSION?])
-       break
-     fi
-
-     AC_CACHE_CHECK([for Qt's version],
-       [at_cv_QT_VERSION],
-       [echo "$as_me:$LINENO: Running $QMAKE --version:" \
-          >& AS_MESSAGE_LOG_FD
-        $QMAKE --version >& AS_MESSAGE_LOG_FD 2>&1
-        qmake_version_sed=['/^.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/!d;s//\1/']
-        at_cv_QT_VERSION=`$QMAKE --version 2>&1 \
-                          | sed "$qmake_version_sed"`])
-     if test x"$at_cv_QT_VERSION" = x; then
-       AX_INSTEAD_IF([$2],
-         [Cannot detect Qt's version.])
-       break
-     fi
-     AC_SUBST([QT_VERSION],
-       [$at_cv_QT_VERSION])
-     AS_VERSION_COMPARE([$QT_VERSION], [$1],
-       [AX_INSTEAD_IF([$2],
-          [This package requires Qt $1 or above.])
-        break])
-
-     # Run the user code
-     $3
-
-   done  # end hack (useless FOR to be able to use break)
-  ])
-
-
-# _AT_TWEAK_PRO_FILE(QT_VAR, VALUE)
-# ---------------------------------
-# @internal.  Tweak the variable QT_VAR in the .pro file.  VALUE is an
-# IFS-separated list of values, and each value is rewritten as
-# follows:
-#
-#   +value  => QT_VAR += value
-#   -value  => QT_VAR -= value
-#    value  => QT_VAR += value
-
-AC_DEFUN([_AT_TWEAK_PRO_FILE],
-  [ # Tweak the value of $1 in the .pro file for $2.
-   qt_conf=''
-   for at_mod in $2; do
-     at_mod=`echo "$at_mod" | sed 's/^-//; tough
-                                   s/^+//; beef
-                                   :ough
-                                   s/^/$1 -= /;n
-                                   :eef
-                                   s/^/$1 += /'`
-     qt_conf="\
-$qt_conf
-$at_mod"
-   done
-    echo "$qt_conf" | sed 1d >>"$pro_file"
-  ])
-
-# eof
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m4/m4_ax_have_qt.m4	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,220 @@
+# ===========================================================================
+#        https://www.gnu.org/software/autoconf-archive/ax_have_qt.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_HAVE_QT
+#
+# DESCRIPTION
+#
+#   Searches $PATH and queries qmake for Qt include files, libraries and Qt
+#   binary utilities. The macro only supports Qt5 or later.
+#
+#   The following shell variable is set to either "yes" or "no":
+#
+#     have_qt
+#
+#   Additionally, the following variables are exported:
+#
+#     QT_CXXFLAGS
+#     QT_LIBS
+#     QT_MOC
+#     QT_UIC
+#     QT_RCC
+#     QT_LRELEASE
+#     QT_LUPDATE
+#     QT_DIR
+#     QMAKE
+#
+#   which respectively contain an "-I" flag pointing to the Qt include
+#   directory, link flags necessary to link with Qt and X, the full path to
+#   the meta object compiler and the user interface compiler both, and
+#   finally the variable QTDIR as Qt likes to see it defined.
+#
+#   Example lines for Makefile.in:
+#
+#     CXXFLAGS = @QT_CXXFLAGS@
+#     MOC      = @QT_MOC@
+#
+#   After the variables have been set, a trial compile and link is performed
+#   to check the correct functioning of the meta object compiler. This test
+#   may fail when the different detected elements stem from different
+#   releases of the Qt framework. In that case, an error message is emitted
+#   and configure stops.
+#
+#   No common variables such as $LIBS or $CFLAGS are polluted.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Bastiaan Veelo <Bastiaan@Veelo.net>
+#   Copyright (c) 2014 Alex Henrie <alexhenrie24@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 25
+
+AU_ALIAS([BNV_HAVE_QT], [AX_HAVE_QT])
+AC_DEFUN([AX_HAVE_QT],
+[
+  AC_REQUIRE([AC_PROG_CXX])
+  AC_REQUIRE([AC_PATH_X])
+  AC_REQUIRE([AC_PATH_XTRA])
+  # openSUSE leap 15.3 installs qmake-qt5, not qmake, for example.
+  # Store the full name (like qmake-qt5) into QMAKE
+  # and the specifier (like -qt5 or empty) into am_have_qt_qmexe_suff.
+  AC_ARG_VAR([QMAKE],"Qt make tool")
+  AC_CHECK_TOOLS([QMAKE],[qmake qmake-qt6 qmake-qt5],[false])
+
+  AC_MSG_CHECKING(for Qt)
+  am_have_qt_qmexe_suff=`echo $QMAKE | sed 's,^.*qmake,,'`
+  # If we have Qt5 or later in the path, we're golden
+  ver=`$QMAKE --version | grep -o "Qt version ."`
+
+  if test "$ver" ">" "Qt version 4"; then
+    have_qt=yes
+    # This pro file dumps qmake's variables, but it only works on Qt 5 or later
+    am_have_qt_dir=`mktemp -d`
+    am_have_qt_pro="$am_have_qt_dir/test.pro"
+    am_have_qt_stash="$am_have_qt_dir/.qmake.stash"
+    am_have_qt_makefile="$am_have_qt_dir/Makefile"
+    # http://qt-project.org/doc/qt-5/qmake-variable-reference.html#qt
+    cat > $am_have_qt_pro << EOF
+win32 {
+    CONFIG -= debug_and_release
+    CONFIG += release
+}
+qtHaveModule(core):    QT += core
+qtHaveModule(gui):     QT += gui
+qtHaveModule(widgets): QT += widgets
+percent.target = %
+percent.commands = @echo -n "\$(\$(@))\ "
+QMAKE_EXTRA_TARGETS += percent
+EOF
+    $QMAKE $am_have_qt_pro -o $am_have_qt_makefile
+    QT_CXXFLAGS=`cd $am_have_qt_dir; make -s -f $am_have_qt_makefile CXXFLAGS INCPATH`
+    QT_LIBS=`cd $am_have_qt_dir; make -s -f $am_have_qt_makefile LIBS`
+    rm $am_have_qt_pro $am_have_qt_stash $am_have_qt_makefile
+    rmdir $am_have_qt_dir
+
+    # Look for specific tools in $PATH
+    QT_MOC=`which moc$am_have_qt_qmexe_suff`
+    QT_UIC=`which uic$am_have_qt_qmexe_suff`
+    QT_RCC=`which rcc$am_have_qt_qmexe_suff`
+    QT_LRELEASE=`which lrelease$am_have_qt_qmexe_suff`
+    QT_LUPDATE=`which lupdate$am_have_qt_qmexe_suff`
+
+    # Get Qt version from qmake
+    QT_DIR=`$QMAKE --version | grep -o -E /.+`
+
+    # All variables are defined, report the result
+    AC_MSG_RESULT([$have_qt:
+    QT_CXXFLAGS=$QT_CXXFLAGS
+    QT_DIR=$QT_DIR
+    QT_LIBS=$QT_LIBS
+    QT_UIC=$QT_UIC
+    QT_MOC=$QT_MOC
+    QT_RCC=$QT_RCC
+    QT_LRELEASE=$QT_LRELEASE
+    QT_LUPDATE=$QT_LUPDATE])
+  else
+    # Qt was not found
+    have_qt=no
+    QT_CXXFLAGS=
+    QT_DIR=
+    QT_LIBS=
+    QT_UIC=
+    QT_MOC=
+    QT_RCC=
+    QT_LRELEASE=
+    QT_LUPDATE=
+    AC_MSG_RESULT($have_qt)
+  fi
+  AC_SUBST(QT_CXXFLAGS)
+  AC_SUBST(QT_DIR)
+  AC_SUBST(QT_LIBS)
+  AC_SUBST(QT_UIC)
+  AC_SUBST(QT_MOC)
+  AC_SUBST(QT_RCC)
+  AC_SUBST(QT_LRELEASE)
+  AC_SUBST(QT_LUPDATE)
+  AC_SUBST(QMAKE)
+
+  #### Being paranoid:
+  if test x"$have_qt" = xyes; then
+    AC_MSG_CHECKING(correct functioning of Qt installation)
+    AC_CACHE_VAL(ax_cv_qt_test_result,
+    [
+      cat > ax_qt_test.h << EOF
+#include <qobject.h>
+class Test : public QObject
+{
+Q_OBJECT
+public:
+  Test() {}
+  ~Test() {}
+public slots:
+  void receive() {}
+signals:
+  void send();
+};
+EOF
+
+      cat > ax_qt_main.$ac_ext << EOF
+#include "ax_qt_test.h"
+#include <qapplication.h>
+int main( int argc, char **argv )
+{
+  QApplication app( argc, argv );
+  Test t;
+  QObject::connect( &t, SIGNAL(send()), &t, SLOT(receive()) );
+}
+EOF
+
+      ax_cv_qt_test_result="failure"
+      ax_try_1="$QT_MOC ax_qt_test.h -o moc_ax_qt_test.$ac_ext >/dev/null 2>/dev/null"
+      AC_TRY_EVAL(ax_try_1)
+      if test x"$ac_status" != x0; then
+        echo "$ax_err_1" >&AS_MESSAGE_LOG_FD
+        echo "configure: could not run $QT_MOC on:" >&AS_MESSAGE_LOG_FD
+        cat ax_qt_test.h >&AS_MESSAGE_LOG_FD
+      else
+        ax_try_2="$CXX $QT_CXXFLAGS -c $CXXFLAGS -o moc_ax_qt_test.o moc_ax_qt_test.$ac_ext >/dev/null 2>/dev/null"
+        AC_TRY_EVAL(ax_try_2)
+        if test x"$ac_status" != x0; then
+          echo "$ax_err_2" >&AS_MESSAGE_LOG_FD
+          echo "configure: could not compile:" >&AS_MESSAGE_LOG_FD
+          cat moc_ax_qt_test.$ac_ext >&AS_MESSAGE_LOG_FD
+        else
+          ax_try_3="$CXX $QT_CXXFLAGS -c $CXXFLAGS -o ax_qt_main.o ax_qt_main.$ac_ext >/dev/null 2>/dev/null"
+          AC_TRY_EVAL(ax_try_3)
+          if test x"$ac_status" != x0; then
+            echo "$ax_err_3" >&AS_MESSAGE_LOG_FD
+            echo "configure: could not compile:" >&AS_MESSAGE_LOG_FD
+            cat ax_qt_main.$ac_ext >&AS_MESSAGE_LOG_FD
+          else
+            ax_try_4="$CXX -o ax_qt_main ax_qt_main.o moc_ax_qt_test.o $QT_LIBS $LIBS >/dev/null 2>/dev/null"
+            AC_TRY_EVAL(ax_try_4)
+            if test x"$ac_status" != x0; then
+              echo "$ax_err_4" >&AS_MESSAGE_LOG_FD
+            else
+              ax_cv_qt_test_result="success"
+            fi
+          fi
+        fi
+      fi
+    ])dnl AC_CACHE_VAL ax_cv_qt_test_result
+    AC_MSG_RESULT([$ax_cv_qt_test_result])
+    if test x"$ax_cv_qt_test_result" = "xfailure"; then
+      AC_MSG_ERROR([Failed to find matching components of a complete
+                  Qt installation. Try using more options,
+                  see ./configure --help.])
+    fi
+
+    rm -f ax_qt_test.h moc_ax_qt_test.$ac_ext moc_ax_qt_test.o \
+          ax_qt_main.$ac_ext ax_qt_main.o ax_qt_main
+  fi
+])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/animone.qrc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,5 @@
+<!DOCTYPE rcc><RCC version="1.0">
+	<qresource>
+		<file alias="players.anisthesia">../dep/animone/data/players.anisthesia</file>
+	</qresource>
+</RCC>
--- a/rc/linux/Minori.desktop	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-[Desktop Entry]
-
-# The type as listed above
-Type=Application
-
-# The version of the desktop entry specification to which this file complies
-Version=1.4
-
-# The name of the application
-Name=Minori
-
-# A comment which can/will be used as a tooltip
-Comment=A lightweight anime tracker built with Qt.
-
-# Executable name
-Exec=minori
-
-# Favicon
-Icon=Minori
-
-# Describes whether this application needs to be run in a terminal or not
-Terminal=false
-
-Categories=Utility;Qt;
\ No newline at end of file
Binary file rc/linux/Minori.png has changed
--- a/rc/osx/Minori.app/Contents/Info.plist	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>English</string>
-	<key>CFBundleExecutable</key>
-	<string>minori</string>
-	<key>CFBundleGetInfoString</key>
-	<string>Copyright</string>
-	<key>NSHumanReadableCopyright</key>
-	<string>Copyright</string>
-	<key>CFBundleIconFile</key>
-	<string>Minori.icns</string>
-	<key>CFBundleIdentifier</key>
-	<string>org.eu.us.paper.Minori</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>Minori</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>0.0.0</string>
-	<key>CFBundleSignature</key>
-	<string>Schm</string>
-	<key>CFBundleVersion</key>
-	<string>0.0.0</string>
-	<key>NSMainNibFile</key>
-	<string>MainMenu</string>
-	<key>NSPrincipalClass</key>
-	<string>NSApplication</string>
-	<key>CGDisableCoalescedUpdates</key>
-	<true/>
-	<key>NSHighResolutionCapable</key>
-	<true/>
-</dict>
-</plist>
\ No newline at end of file
--- a/rc/osx/Minori.app/Contents/PkgInfo	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-APPLSchm
\ No newline at end of file
Binary file rc/osx/Minori.app/Contents/Resources/Minori.icns has changed
--- a/rc/player_data.qrc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-<!DOCTYPE rcc><RCC version="1.0">
-	<qresource>
-		<file alias="players.anisthesia">../dep/animia/data/players.anisthesia</file>
-	</qresource>
-</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/linux/Minori.desktop	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,24 @@
+[Desktop Entry]
+
+# The type as listed above
+Type=Application
+
+# The version of the desktop entry specification to which this file complies
+Version=1.4
+
+# The name of the application
+Name=Minori
+
+# A comment which can/will be used as a tooltip
+Comment=A lightweight anime tracker built with Qt.
+
+# Executable name
+Exec=minori
+
+# Favicon
+Icon=Minori
+
+# Describes whether this application needs to be run in a terminal or not
+Terminal=false
+
+Categories=Utility;Qt;
\ No newline at end of file
Binary file rc/sys/linux/Minori.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/osx/Minori.app/Contents/Info.plist	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>minori</string>
+	<key>CFBundleGetInfoString</key>
+	<string>Copyright</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright</string>
+	<key>CFBundleIconFile</key>
+	<string>Minori.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.eu.us.paper.Minori</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>Minori</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.0.0</string>
+	<key>CFBundleSignature</key>
+	<string>Schm</string>
+	<key>CFBundleVersion</key>
+	<string>0.0.0</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+	<key>CGDisableCoalescedUpdates</key>
+	<true/>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/osx/Minori.app/Contents/PkgInfo	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,1 @@
+APPLSchm
\ No newline at end of file
Binary file rc/sys/osx/Minori.app/Contents/Resources/Minori.icns has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/win32/dark/dark.qrc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,5 @@
+<!DOCTYPE rcc><RCC version="1.0">
+	<qresource>
+		<file>dark.qss</file>
+	</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/win32/dark/dark.qss	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,294 @@
+/*
+Much of this is taken from the Breeze Style Sheets,
+which is under the MIT license:
+
+Copyright © `<2013-2014>` `<Colin Duquesnoy>`
+Copyright © `<2015-2016>` `<Alex Huszagh>`
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the “Software”), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* Colors should be the same as the QPalette in gui/theme.cc, i.e.:
+	window == darkgray == #353535;
+	??? == gray == #808080;
+	base == black == #191919;
+	highlighted == blue = #2A82DA;
+   We could include a preprocessor like sass to make these constants,
+   but that's really lame :(
+*/
+
+AnimeListPage::pane {
+	margin: 0.04em;
+	border: 0.04em solid #808080;
+	top: -0.04em;
+	left: -0.04em;
+}
+
+QTabWidget::pane:top {
+	border: 0.04em solid #808080;
+	top: -0.04em;
+}
+
+QTabWidget::pane:bottom {
+	border: 0.04em solid #808080;
+	bottom: -0.04em;
+}
+
+QTabWidget::pane:left {
+	border: 0.04em solid #808080;
+	left: -0.04em;
+}
+
+QTabWidget::pane:right {
+	border: 0.04em solid #808080;
+	right: -0.04em;
+}
+
+QTabBar {
+	qproperty-drawBase: 0;
+	left: 0.23em;
+	border-radius: 0.13em;
+	selection-color: transparent;
+}
+
+QTabBar:focus {
+	border: 0em transparent black;
+}
+
+QTabBar::tab:top,
+QTabBar::tab:top:last,
+QTabBar::tab:top:only-one {
+	color: white;
+	border: 0.04em transparent black;
+	border-left: 0.04em solid #808080;
+	border-right: 0.04em solid #808080;
+	border-top: 0.04em solid #808080;
+	background-color: #353535;
+	min-width: 50px;
+	padding-top: 0.23em;
+	padding-bottom: 0.23em;
+	padding-left: 0.78em;
+	padding-right: 0.78em;
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected {
+	color: white;
+	background-color: #353535;
+	border: 0.04em solid #808080;
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+	margin-top: 0.13em;
+}
+
+QTabBar::tab:top:next-selected {
+	border-right: 0.04em transparent #353535;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:previous-selected {
+	border-left: 0.04em transparent #353535;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected:hover {
+	background-color: rgba(42, 130, 218, 0.1);
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected:first:hover {
+	background-color: rgba(42, 130, 218, 0.1);
+}
+
+QGroupBox::title {
+	color: white;
+}
+
+QComboBox,
+QPushButton,
+QDateEdit,
+QSpinBox {
+	background-color: #353535;
+	color: white;
+}
+
+QComboBox,
+QDateEdit,
+QSpinBox {
+	border: 0.04em solid #808080;
+}
+
+QComboBox:disabled,
+QPushButton:disabled,
+QDateEdit:disabled,
+QSpinBox:disabled {
+	color: #808080;
+}
+
+QPushButton:hover {
+	background-color: #39424B;
+}
+
+/*
+ * QLineEdit
+ */
+
+QLineEdit {
+	background: transparent;
+	color: white;
+}
+
+QLineEdit:!read-only {
+	background-color: #191919;
+	padding: 0.23em;
+	border-style: solid;
+	border: 0.04em solid #808080;
+	border-radius: 0.09em;
+}
+
+/* QMenuBar */
+
+QMenuBar {
+	background-color: #353535;
+	color: white;
+}
+
+QMenuBar::item:selected {
+	background-color: #414141;
+}
+
+QMenuBar::item:disabled {
+	color: #808080;
+}
+
+QMenuBar::item:pressed {
+	background-color: #414141;
+	margin-bottom: -0.09em;
+	padding-bottom: 0.09em;
+}
+
+/* QMenu */
+
+QMenu {
+	color: white;
+	background-color: #353535;
+	padding: 0.18em 0.18em;
+	border: 0.09em solid #A0A0A0;
+}
+
+QMenu::icon {
+	margin: 0.23em;
+}
+
+QMenu::item {
+	/* Add extra padding on the right for the QMenu arrow */
+	padding: 0.23em 1.5em 0.23em 1.3em;
+	border: 0.09em solid transparent;
+	background: transparent;
+}
+
+QMenu::item:selected {
+	color: white;
+	background-color: #414141;
+}
+
+QMenu::item:selected:disabled {
+	background-color: #353535;
+}
+
+QMenu::item:disabled {
+	color: #808080;
+}
+
+QMenu::indicator {
+	width: 0.8em;
+	height: 0.8em;
+	/* To align with QMenu::icon, which has a 0.23em margin. */
+	margin-left: 0.3em;
+	subcontrol-position: center left;
+}
+
+/*
+ * QHeaderView:
+ * Need this for the anime list, on Windows it gets screwed up.
+*/
+
+QHeaderView {
+	background-color: #353535;
+	border: 0.04em transparent;
+	border-radius: 0em;
+	margin: 0em;
+	padding: 0em;
+}
+
+QHeaderView::section {
+	background-color: #353535;
+	border: 0.04em solid #808080;
+	color: #eff0f1;
+	border-radius: 0em;
+	padding: 0em 0.23em 0em 0.23em;
+	text-align: center;
+}
+
+QHeaderView::section::vertical::first,
+QHeaderView::section::vertical::only-one {
+	border-top: 0.04em solid #808080;
+}
+
+QHeaderView::section::vertical {
+	border-top: transparent;
+}
+
+QHeaderView::section::horizontal::first,
+QHeaderView::section::horizontal::only-one {
+	border-left: 0.04em solid #808080;
+}
+
+QHeaderView::section::horizontal {
+	border-left: transparent;
+}
+
+QHeaderView[showSortIndicator="true"]::section::horizontal {
+	/* Same as the width of the arrow subcontrols below. */
+	padding-right: 0.8em;
+}
+
+QHeaderView::section:checked {
+	color: white;
+	background-color: #808080;
+}
+
+/* Note that this doesn't work for QTreeView unless the header is clickable */
+QHeaderView::section:hover,
+QHeaderView::section::horizontal::first:hover,
+QHeaderView::section::horizontal::only-one:hover,
+QHeaderView::section::vertical::first:hover,
+QHeaderView::section::vertical::only-one:hover {
+	background-color: #353535;
+}
Binary file rc/sys/win32/favicon.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/win32/resource.rc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,3 @@
+#include "winver.h"
+
+IDI_ICON1	ICON	"favicon.ico"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/sys/win32/version.rc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,37 @@
+#include "winver.h"
+
+#ifndef WRC_VERSION
+#   define WRC_VERSION 0,0,0,0
+#endif
+
+#ifndef PACKAGE_VERSION
+#	define PACKAGE_VERSION "0.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+	FILEVERSION WRC_VERSION
+	PRODUCTVERSION WRC_VERSION
+	FILEFLAGS 0x0L
+	FILEFLAGSMASK 0x3fL
+	FILEOS 0x00040004L
+	FILETYPE 0x1L
+	FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "000004b0"
+        BEGIN
+            VALUE "CompanyName", "Paper"
+            VALUE "FileDescription", "A lightweight anime tracker built with Qt."
+            VALUE "FileVersion", PACKAGE_VERSION
+            VALUE "InternalName", "minori"
+            VALUE "OriginalFilename", "minori.exe"
+            VALUE "ProductName", "Minori"
+            VALUE "ProductVersion", PACKAGE_VERSION
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+    END
+END
--- a/rc/win32/dark/dark.qrc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-<!DOCTYPE rcc><RCC version="1.0">
-	<qresource>
-		<file>dark.qss</file>
-	</qresource>
-</RCC>
--- a/rc/win32/dark/dark.qss	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,294 +0,0 @@
-/*
-Much of this is taken from the Breeze Style Sheets,
-which is under the MIT license:
-
-Copyright © `<2013-2014>` `<Colin Duquesnoy>`
-Copyright © `<2015-2016>` `<Alex Huszagh>`
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the “Software”), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-/* Colors should be the same as the QPalette in gui/theme.cc, i.e.:
-	window == darkgray == #353535;
-	??? == gray == #808080;
-	base == black == #191919;
-	highlighted == blue = #2A82DA;
-   We could include a preprocessor like sass to make these constants,
-   but that's really lame :(
-*/
-
-AnimeListPage::pane {
-	margin: 0.04em;
-	border: 0.04em solid #808080;
-	top: -0.04em;
-	left: -0.04em;
-}
-
-QTabWidget::pane:top {
-	border: 0.04em solid #808080;
-	top: -0.04em;
-}
-
-QTabWidget::pane:bottom {
-	border: 0.04em solid #808080;
-	bottom: -0.04em;
-}
-
-QTabWidget::pane:left {
-	border: 0.04em solid #808080;
-	left: -0.04em;
-}
-
-QTabWidget::pane:right {
-	border: 0.04em solid #808080;
-	right: -0.04em;
-}
-
-QTabBar {
-	qproperty-drawBase: 0;
-	left: 0.23em;
-	border-radius: 0.13em;
-	selection-color: transparent;
-}
-
-QTabBar:focus {
-	border: 0em transparent black;
-}
-
-QTabBar::tab:top,
-QTabBar::tab:top:last,
-QTabBar::tab:top:only-one {
-	color: white;
-	border: 0.04em transparent black;
-	border-left: 0.04em solid #808080;
-	border-right: 0.04em solid #808080;
-	border-top: 0.04em solid #808080;
-	background-color: #353535;
-	min-width: 50px;
-	padding-top: 0.23em;
-	padding-bottom: 0.23em;
-	padding-left: 0.78em;
-	padding-right: 0.78em;
-	border-radius: 0.09em;
-	border-bottom-left-radius: 0em;
-	border-bottom-right-radius: 0em;
-}
-
-QTabBar::tab:top:!selected {
-	color: white;
-	background-color: #353535;
-	border: 0.04em solid #808080;
-	border-radius: 0.09em;
-	border-bottom-left-radius: 0em;
-	border-bottom-right-radius: 0em;
-	margin-top: 0.13em;
-}
-
-QTabBar::tab:top:next-selected {
-	border-right: 0.04em transparent #353535;
-	border-bottom-left-radius: 0em;
-	border-bottom-right-radius: 0em;
-}
-
-QTabBar::tab:top:previous-selected {
-	border-left: 0.04em transparent #353535;
-	border-bottom-left-radius: 0em;
-	border-bottom-right-radius: 0em;
-}
-
-QTabBar::tab:top:!selected:hover {
-	background-color: rgba(42, 130, 218, 0.1);
-	border-radius: 0.09em;
-	border-bottom-left-radius: 0em;
-	border-bottom-right-radius: 0em;
-}
-
-QTabBar::tab:top:!selected:first:hover {
-	background-color: rgba(42, 130, 218, 0.1);
-}
-
-QGroupBox::title {
-	color: white;
-}
-
-QComboBox,
-QPushButton,
-QDateEdit,
-QSpinBox {
-	background-color: #353535;
-	color: white;
-}
-
-QComboBox,
-QDateEdit,
-QSpinBox {
-	border: 0.04em solid #808080;
-}
-
-QComboBox:disabled,
-QPushButton:disabled,
-QDateEdit:disabled,
-QSpinBox:disabled {
-	color: #808080;
-}
-
-QPushButton:hover {
-	background-color: #39424B;
-}
-
-/*
- * QLineEdit
- */
-
-QLineEdit {
-	background: transparent;
-	color: white;
-}
-
-QLineEdit:!read-only {
-	background-color: #191919;
-	padding: 0.23em;
-	border-style: solid;
-	border: 0.04em solid #808080;
-	border-radius: 0.09em;
-}
-
-/* QMenuBar */
-
-QMenuBar {
-	background-color: #353535;
-	color: white;
-}
-
-QMenuBar::item:selected {
-	background-color: #414141;
-}
-
-QMenuBar::item:disabled {
-	color: #808080;
-}
-
-QMenuBar::item:pressed {
-	background-color: #414141;
-	margin-bottom: -0.09em;
-	padding-bottom: 0.09em;
-}
-
-/* QMenu */
-
-QMenu {
-	color: white;
-	background-color: #353535;
-	padding: 0.18em 0.18em;
-	border: 0.09em solid #A0A0A0;
-}
-
-QMenu::icon {
-	margin: 0.23em;
-}
-
-QMenu::item {
-	/* Add extra padding on the right for the QMenu arrow */
-	padding: 0.23em 1.5em 0.23em 1.3em;
-	border: 0.09em solid transparent;
-	background: transparent;
-}
-
-QMenu::item:selected {
-	color: white;
-	background-color: #414141;
-}
-
-QMenu::item:selected:disabled {
-	background-color: #353535;
-}
-
-QMenu::item:disabled {
-	color: #808080;
-}
-
-QMenu::indicator {
-	width: 0.8em;
-	height: 0.8em;
-	/* To align with QMenu::icon, which has a 0.23em margin. */
-	margin-left: 0.3em;
-	subcontrol-position: center left;
-}
-
-/*
- * QHeaderView:
- * Need this for the anime list, on Windows it gets screwed up.
-*/
-
-QHeaderView {
-	background-color: #353535;
-	border: 0.04em transparent;
-	border-radius: 0em;
-	margin: 0em;
-	padding: 0em;
-}
-
-QHeaderView::section {
-	background-color: #353535;
-	border: 0.04em solid #808080;
-	color: #eff0f1;
-	border-radius: 0em;
-	padding: 0em 0.23em 0em 0.23em;
-	text-align: center;
-}
-
-QHeaderView::section::vertical::first,
-QHeaderView::section::vertical::only-one {
-	border-top: 0.04em solid #808080;
-}
-
-QHeaderView::section::vertical {
-	border-top: transparent;
-}
-
-QHeaderView::section::horizontal::first,
-QHeaderView::section::horizontal::only-one {
-	border-left: 0.04em solid #808080;
-}
-
-QHeaderView::section::horizontal {
-	border-left: transparent;
-}
-
-QHeaderView[showSortIndicator="true"]::section::horizontal {
-	/* Same as the width of the arrow subcontrols below. */
-	padding-right: 0.8em;
-}
-
-QHeaderView::section:checked {
-	color: white;
-	background-color: #808080;
-}
-
-/* Note that this doesn't work for QTreeView unless the header is clickable */
-QHeaderView::section:hover,
-QHeaderView::section::horizontal::first:hover,
-QHeaderView::section::horizontal::only-one:hover,
-QHeaderView::section::vertical::first:hover,
-QHeaderView::section::vertical::only-one:hover {
-	background-color: #353535;
-}
Binary file rc/win32/favicon.ico has changed
--- a/rc/win32/resource.rc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-#include "winver.h"
-
-IDI_ICON1	ICON	"favicon.ico"
--- a/rc/win32/version.rc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-#include "winver.h"
-
-#ifndef WRC_VERSION
-#   define WRC_VERSION 0,0,0,0
-#endif
-
-#ifndef PACKAGE_VERSION
-#	define PACKAGE_VERSION "0.0.0"
-#endif
-
-VS_VERSION_INFO VERSIONINFO
-	FILEVERSION WRC_VERSION
-	PRODUCTVERSION WRC_VERSION
-	FILEFLAGS 0x0L
-	FILEFLAGSMASK 0x3fL
-	FILEOS 0x00040004L
-	FILETYPE 0x1L
-	FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "000004b0"
-        BEGIN
-            VALUE "CompanyName", "Paper"
-            VALUE "FileDescription", "A lightweight anime tracker built with Qt."
-            VALUE "FileVersion", PACKAGE_VERSION
-            VALUE "InternalName", "minori"
-            VALUE "OriginalFilename", "minori.exe"
-            VALUE "ProductName", "Minori"
-            VALUE "ProductVersion", PACKAGE_VERSION
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", 0x409, 1252
-    END
-END
--- a/scripts/osx/deploy_build.sh	Sun Feb 18 16:02:14 2024 -0500
+++ b/scripts/osx/deploy_build.sh	Mon Apr 01 02:43:44 2024 -0400
@@ -6,7 +6,7 @@
 SCRIPT_DIR=$(dirname -- "$0")
 BUNDLE_NAME="Minori"
 
-cp -r "$SCRIPT_DIR/../../rc/osx/$BUNDLE_NAME.app" .
+cp -r "$SCRIPT_DIR/../../rc/sys/osx/$BUNDLE_NAME.app" .
 
 mkdir -p "$BUNDLE_NAME.app/Contents/MacOS"
 cp ".libs/minori" "$BUNDLE_NAME.app/Contents/MacOS/minori"
--- a/src/core/anime.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/anime.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -52,8 +52,7 @@
 	switch (session.config.anime_list.score_format) {
 		case ScoreFormat::POINT_10_DECIMAL:
 			return Strings::ToUtf8String(score / 10) + "." + Strings::ToUtf8String(score % 10);
-		case ScoreFormat::POINT_10:
-			return Strings::ToUtf8String(score / 10);
+		case ScoreFormat::POINT_10: return Strings::ToUtf8String(score / 10);
 		case ScoreFormat::POINT_5: {
 			std::string stars = "";
 
@@ -73,8 +72,7 @@
 				return "";
 		}
 		default:
-		case ScoreFormat::POINT_100:
-			return Strings::ToUtf8String(score);
+		case ScoreFormat::POINT_100: return Strings::ToUtf8String(score);
 	}
 }
 
--- a/src/core/anime_db.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/anime_db.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,18 +1,18 @@
 #include "core/anime_db.h"
 #include "core/anime.h"
-#include "core/strings.h"
+#include "core/filesystem.h"
 #include "core/json.h"
-#include "core/filesystem.h"
+#include "core/strings.h"
 
+#include "gui/translate/anilist.h"
 #include "gui/translate/anime.h"
-#include "gui/translate/anilist.h"
 
 #include <QDate>
 
 #include <fstream>
 
+#include <exception>
 #include <iostream>
-#include <exception>
 
 namespace Anime {
 
@@ -55,8 +55,8 @@
 
 	for (const auto& [id, anime] : items)
 		if (anime.IsInUserList())
-			total += anime.GetDuration() * anime.GetUserProgress()
-			         + anime.GetEpisodes() * anime.GetDuration() * anime.GetUserRewatchedTimes();
+			total += anime.GetDuration() * anime.GetUserProgress() +
+			         anime.GetEpisodes() * anime.GetDuration() * anime.GetUserRewatchedTimes();
 
 	return total;
 }
@@ -122,10 +122,10 @@
 	return id;
 }
 
-/* 
+/*
  * TODO: separate this from the anime DB,
  * provide *some* sort of normalization
-*/
+ */
 int Database::GetAnimeFromTitle(const std::string& title) {
 	if (title.empty())
 		return 0;
@@ -252,7 +252,8 @@
 	anime.SetEnglishTitle(JSON::GetString<std::string>(json, "/title/english"_json_pointer, ""));
 	anime.SetTitleSynonyms(JSON::GetArray<std::vector<std::string>>(json, "/synonyms"_json_pointer, {}));
 	anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0));
-	anime.SetAiringStatus(Translate::ToSeriesStatus(JSON::GetString<std::string>(json, "/airing_status"_json_pointer, "")));
+	anime.SetAiringStatus(
+	    Translate::ToSeriesStatus(JSON::GetString<std::string>(json, "/airing_status"_json_pointer, "")));
 	anime.SetAirDate(Date(JSON::GetValue(json, "/air_date"_json_pointer)));
 	anime.SetGenres(JSON::GetArray<std::vector<std::string>>(json, "/genres"_json_pointer, {}));
 	anime.SetProducers(JSON::GetArray<std::vector<std::string>>(json, "/producers"_json_pointer, {}));
--- a/src/core/config.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/config.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -3,15 +3,15 @@
  * parses the config... lol
  **/
 #include "core/config.h"
-#include "core/strings.h"
 #include "core/anime.h"
+#include "core/filesystem.h"
 #include "core/ini.h"
-#include "core/filesystem.h"
 #include "core/json.h"
+#include "core/strings.h"
 #include "gui/translate/anime.h"
 #include "gui/translate/config.h"
 
-#include "animia/player.h"
+#include "animone/player.h"
 
 #include <algorithm>
 #include <cstdlib>
@@ -30,7 +30,7 @@
  *
  * It technically isn't to spec, because I'm making these case-sensitive.
  * Boohoo.
-*/
+ */
 
 int Config::Load() {
 	std::filesystem::path cfg_path = Filesystem::GetConfigPath();
@@ -41,21 +41,27 @@
 
 	service = Translate::ToService(INI::GetIniValue<std::string>(ini, "General", "Service", "None"));
 
-	anime_list.score_format = Translate::ToScoreFormat(INI::GetIniValue<std::string>(ini, "Anime List", "Score format", "POINT_100"));
-	anime_list.language = Translate::ToLanguage(INI::GetIniValue<std::string>(ini, "Anime List", "Title language", "Romaji"));
+	anime_list.score_format =
+	    Translate::ToScoreFormat(INI::GetIniValue<std::string>(ini, "Anime List", "Score format", "POINT_100"));
+	anime_list.language =
+	    Translate::ToLanguage(INI::GetIniValue<std::string>(ini, "Anime List", "Title language", "Romaji"));
 	anime_list.display_aired_episodes = INI::GetIniValue<bool>(ini, "Anime List", "Display only aired episodes", true);
-	anime_list.display_available_episodes = INI::GetIniValue<bool>(ini, "Anime List", "Display only available episodes in library", true);
-	anime_list.highlight_anime_if_available = INI::GetIniValue<bool>(ini, "Anime List", "Highlight anime if available", true);
+	anime_list.display_available_episodes =
+	    INI::GetIniValue<bool>(ini, "Anime List", "Display only available episodes in library", true);
+	anime_list.highlight_anime_if_available =
+	    INI::GetIniValue<bool>(ini, "Anime List", "Highlight anime if available", true);
 
 	if (anime_list.highlight_anime_if_available) // sanity check
-		anime_list.highlighted_anime_above_others = INI::GetIniValue<bool>(ini, "Anime List", "Display highlighted anime above others", false);
+		anime_list.highlighted_anime_above_others =
+		    INI::GetIniValue<bool>(ini, "Anime List", "Display highlighted anime above others", false);
 	else
 		anime_list.highlighted_anime_above_others = false;
 
 	auth.anilist.auth_token = INI::GetIniValue<std::string>(ini, "Authentication/AniList", "Auth Token", "");
 	auth.anilist.user_id = INI::GetIniValue<int>(ini, "Authentication/AniList", "User ID", 0);
 
-	torrents.feed_link = INI::GetIniValue<std::string>(ini, "Torrents", "RSS feed", "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");
+	torrents.feed_link = INI::GetIniValue<std::string>(ini, "Torrents", "RSS feed",
+	                                                   "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");
 
 	recognition.detect_media_players = INI::GetIniValue<bool>(ini, "Recognition", "Detect media players", true);
 
@@ -69,9 +75,9 @@
 		f.open(QFile::ReadOnly | QFile::Text);
 		QTextStream ts(&f);
 
-		std::vector<animia::Player> players;
+		std::vector<animone::Player> players;
 
-		if (!animia::ParsePlayersData(Strings::ToUtf8String(ts.readAll()), players))
+		if (!animone::ParsePlayersData(Strings::ToUtf8String(ts.readAll()), players))
 			return false;
 
 		recognition.players.reserve(players.size());
@@ -82,17 +88,18 @@
 	for (auto& [enabled, player] : recognition.players) {
 		switch (player.type) {
 			default:
-			case animia::PlayerType::Default:
+			case animone::PlayerType::Default:
 				enabled = INI::GetIniValue<bool>(ini, "Recognition/Players", player.name, true);
 				break;
-			case animia::PlayerType::WebBrowser:
+			case animone::PlayerType::WebBrowser:
 				enabled = INI::GetIniValue<bool>(ini, "Recognition/Browsers", player.name, true);
 				break;
 		}
 	}
 
 	locale.RefreshAvailableLocales();
-	locale.SetActiveLocale(QLocale(Strings::ToQString(INI::GetIniValue<std::string>(ini, "General", "Locale", "en_US"))));
+	locale.SetActiveLocale(
+	    QLocale(Strings::ToQString(INI::GetIniValue<std::string>(ini, "General", "Locale", "en_US"))));
 
 	theme.SetTheme(Translate::ToTheme(INI::GetIniValue<std::string>(ini, "Appearance", "Theme", "Default")));
 
@@ -121,9 +128,11 @@
 	INI::SetIniValue(ini, "Anime List", "Score format", Translate::ToString(anime_list.score_format));
 	INI::SetIniValue(ini, "Anime List", "Title language", anime_list.language);
 	INI::SetIniValue(ini, "Anime List", "Display only aired episodes", anime_list.display_aired_episodes);
-	INI::SetIniValue(ini, "Anime List", "Display only available episodes in library", anime_list.display_available_episodes);
+	INI::SetIniValue(ini, "Anime List", "Display only available episodes in library",
+	                 anime_list.display_available_episodes);
 	INI::SetIniValue(ini, "Anime List", "Highlight anime if available", anime_list.highlight_anime_if_available);
-	INI::SetIniValue(ini, "Anime List", "Display highlighted anime above others", anime_list.highlighted_anime_above_others);
+	INI::SetIniValue(ini, "Anime List", "Display highlighted anime above others",
+	                 anime_list.highlighted_anime_above_others);
 
 	INI::SetIniValue(ini, "Authentication/AniList", "Auth Token", auth.anilist.auth_token);
 	INI::SetIniValue(ini, "Authentication/AniList", "User ID", auth.anilist.user_id);
@@ -137,10 +146,8 @@
 	for (const auto& [enabled, player] : recognition.players) {
 		switch (player.type) {
 			default:
-			case animia::PlayerType::Default:
-				INI::SetIniValue(ini, "Recognition/Players", player.name, enabled);
-				break;
-			case animia::PlayerType::WebBrowser:
+			case animone::PlayerType::Default: INI::SetIniValue(ini, "Recognition/Players", player.name, enabled); break;
+			case animone::PlayerType::WebBrowser:
 				INI::SetIniValue(ini, "Recognition/Browsers", player.name, enabled);
 				break;
 		}
--- a/src/core/date.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/date.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -11,11 +11,11 @@
 Date::Date() {
 }
 
-Date::Date(unsigned int y) {
+Date::Date(Date::Year y) {
 	SetYear(y);
 }
 
-Date::Date(unsigned int y, unsigned int m, unsigned int d) {
+Date::Date(Date::Year y, Date::Month m, Date::Day d) {
 	SetYear(y);
 	SetMonth(m);
 	SetDay(d);
@@ -23,21 +23,28 @@
 
 Date::Date(const QDate& date) {
 	SetYear(date.year());
-	SetMonth(date.month());
+	auto m = date.month();
+	m = std::clamp(m, static_cast<decltype(m)>(Date::Month::Jan), static_cast<decltype(m)>(Date::Month::Dec));
+	SetMonth(static_cast<Date::Month>(m));
 	SetDay(date.day());
 }
 
 Date::Date(const nlohmann::json& json) {
 	/* NOTE: this constructor is made for use with
-	   AniList FuzzyDate-style JSON. In the future, some other
-	   methods may be parsed and whatnot if necessary. */
+	 * AniList FuzzyDate-style JSON. In the future, some other
+	 * methods may be parsed and whatnot if necessary. */
 
 	if (json.contains("/year"_json_pointer) && json.at("/year"_json_pointer).is_number())
 		SetYear(json.at("/year"_json_pointer).get<unsigned int>());
-	if (json.contains("/month"_json_pointer) && json.at("/month"_json_pointer).is_number())
-		SetMonth(json.at("/month"_json_pointer).get<unsigned int>());
+
+	if (json.contains("/month"_json_pointer) && json.at("/month"_json_pointer).is_number()) {
+		auto m = json.at("/month"_json_pointer).get<unsigned int>();
+		m = std::clamp(m, static_cast<decltype(m)>(Date::Month::Jan), static_cast<decltype(m)>(Date::Month::Dec));
+		SetMonth(static_cast<Date::Month>(m));
+	}
+
 	if (json.contains("/day"_json_pointer) && json.at("/day"_json_pointer).is_number())
-		SetDay(json.at("/day"_json_pointer).get<unsigned int>());
+		SetDay(json.at("/day"_json_pointer).get<unsigned char>());
 }
 
 void Date::VoidYear() {
@@ -52,27 +59,27 @@
 	day.reset();
 }
 
-void Date::SetYear(unsigned int y) {
+void Date::SetYear(Date::Year y) {
 	year.emplace(y);
 }
 
-void Date::SetMonth(unsigned int m) {
-	month.emplace(std::clamp(m, 1U, 12U));
+void Date::SetMonth(Date::Month m) {
+	month.emplace(m);
 }
 
-void Date::SetDay(unsigned int d) {
-	day.emplace(std::clamp(d, 1U, 31U));
+void Date::SetDay(Date::Day d) {
+	day.emplace(std::clamp(d, static_cast<Date::Day>(1U), static_cast<Date::Day>(31U)));
 }
 
-std::optional<unsigned int> Date::GetYear() const {
+std::optional<Date::Year> Date::GetYear() const {
 	return year;
 }
 
-std::optional<unsigned int> Date::GetMonth() const {
+std::optional<Date::Month> Date::GetMonth() const {
 	return month;
 }
 
-std::optional<unsigned int> Date::GetDay() const {
+std::optional<Date::Day> Date::GetDay() const {
 	return day;
 }
 
@@ -83,15 +90,26 @@
 QDate Date::GetAsQDate() const {
 	/* QDate doesn't support "missing" values (for good reason),
 	 * so we do our best and return what we can.
-	*/
+	 */
 
-	return QDate(year.value_or(2000), month.value_or(1), day.value_or(1));
+	return QDate(year.value_or(2000), static_cast<unsigned int>(month.value_or(Date::Month::Jan)), day.value_or(1));
 }
 
 nlohmann::json Date::GetAsAniListJson() const {
-	return {
-		{"year", year},
-		{"month", month},
-		{"day", day}
-	};
+	nlohmann::json json = {
+	    {"year",  nullptr},
+        {"month", nullptr},
+        {"day",   nullptr}
+    };
+
+	if (year)
+		json["year"] = static_cast<unsigned int>(year.value());
+
+	if (month)
+		json["month"] = static_cast<unsigned char>(month.value());
+
+	if (day)
+		json["day"] = static_cast<unsigned char>(day.value());
+
+	return json;
 }
--- a/src/core/filesystem.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/filesystem.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -26,7 +26,7 @@
 	 * ...: ~/.config/minori
 	 *
 	 * FIXME: are windows and mac properly cased?
-	*/
+	 */
 #ifdef WIN32
 	return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
 #else
--- a/src/core/http.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/http.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -2,9 +2,9 @@
 #include "core/session.h"
 #include <QByteArray>
 #include <curl/curl.h>
+#include <iostream>
 #include <string>
 #include <vector>
-#include <iostream>
 
 namespace HTTP {
 
--- a/src/core/strings.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/core/strings.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -6,17 +6,17 @@
 
 #include <QByteArray>
 #include <QDebug>
+#include <QLocale>
 #include <QString>
-#include <QLocale>
 
 #include <algorithm>
 #include <cctype>
 #include <codecvt>
+#include <iostream>
 #include <locale>
 #include <string>
+#include <unordered_map>
 #include <vector>
-#include <unordered_map>
-#include <iostream>
 
 namespace Strings {
 
@@ -51,7 +51,7 @@
 	return out;
 }
 
-std::vector<std::string> Split(const std::string &text, const std::string& delimiter) {
+std::vector<std::string> Split(const std::string& text, const std::string& delimiter) {
 	if (text.length() < 1)
 		return {};
 
@@ -69,7 +69,7 @@
 
 /* This function is really only used for cleaning up the synopsis of
  * horrible HTML debris from AniList :)
-*/
+ */
 std::string ReplaceAll(std::string string, const std::string& find, const std::string& replace) {
 	size_t pos = 0;
 	while ((pos = string.find(find, pos)) != std::string::npos) {
@@ -81,21 +81,14 @@
 
 std::string SanitizeLineEndings(const std::string& string) {
 	/* LOL */
-	return
-		ReplaceAll(
-			ReplaceAll(
-				ReplaceAll(
-					ReplaceAll(
-						ReplaceAll(string, "\r\n", "\n"),
-					"</p>", "\n"),
-				"<br>", "\n"),
-			"<br />", "\n"),
-		"\n\n\n", "\n\n");
+	return ReplaceAll(ReplaceAll(ReplaceAll(ReplaceAll(ReplaceAll(string, "\r\n", "\n"), "</p>", "\n"), "<br>", "\n"),
+	                             "<br />", "\n"),
+	                  "\n\n\n", "\n\n");
 }
 
 /* removes dumb HTML tags because anilist is aids and
  * gives us HTML for synopses :/
-*/
+ */
 std::string RemoveHtmlTags(std::string string) {
 	while (string.find("<") != std::string::npos) {
 		auto startpos = string.find("<");
@@ -110,24 +103,24 @@
 /* e.g. "&lt;" for "<" */
 std::string ParseHtmlEntities(std::string string) {
 	const std::unordered_map<std::string, std::string> map = {
-		/* The only one of these I can understand using are the first
-		 * three. why do the rest of these exist?
-		 *
-		 * probably mojibake.
-		*/
-		{"&lt;", "<"},
-		{"&rt;", ">"},
-		{"&nbsp;", "\xA0"},
-		{"&amp;", "&"},
-		{"&quot;", "\""},
-		{"&apos;", "'"},
-		{"&cent;", "¢"},
-		{"&pound;", "£"},
-		{"&euro;", "€"},
-		{"&yen;", "¥"},
-		{"&copy;", "©"},
-		{"&reg;", "®"},
-		{"&rsquo;", "’"} // Haibane Renmei, AniList
+  /* The only one of these I can understand using are the first
+  * three. why do the rest of these exist?
+  *
+  * probably mojibake.
+  */
+	    {"&lt;",    "<"   },
+        {"&rt;",    ">"   },
+        {"&nbsp;",  "\xA0"},
+        {"&amp;",   "&"   },
+        {"&quot;",  "\""  },
+	    {"&apos;",  "'"   },
+        {"&cent;",  "¢"  },
+        {"&pound;", "£"  },
+        {"&euro;",  "€" },
+        {"&yen;",   "¥"  },
+	    {"&copy;",  "©"  },
+        {"&reg;",   "®"  },
+        {"&rsquo;", "’" }  // Haibane Renmei, AniList
 	};
 
 	for (const auto& item : map)
@@ -142,7 +135,7 @@
 
 /* let Qt handle the heavy lifting of locale shit
  * I don't want to deal with
-*/
+ */
 std::string ToUpper(const std::string& string) {
 	return ToUtf8String(session.config.locale.GetLocale().toUpper(ToQString(string)));
 }
@@ -204,11 +197,11 @@
 /* util funcs */
 uint64_t HumanReadableSizeToBytes(const std::string& str) {
 	static const std::unordered_map<std::string, uint64_t> bytes_map = {
-		{"KB", 1ull << 10},
-		{"MB", 1ull << 20},
-		{"GB", 1ull << 30},
-		{"TB", 1ull << 40},
-		{"PB", 1ull << 50} /* surely we won't need more than this */
+	    {"KB", 1ull << 10},
+	    {"MB", 1ull << 20},
+	    {"GB", 1ull << 30},
+	    {"TB", 1ull << 40},
+	    {"PB", 1ull << 50}  /* surely we won't need more than this */
 	};
 
 	for (const auto& suffix : bytes_map) {
--- a/src/gui/dialog/about.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/about.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -4,24 +4,28 @@
 #include "core/strings.h"
 #include "gui/widgets/text.h"
 #include "pugixml.hpp"
+
+#include <QCoreApplication>
 #include <QFont>
 #include <QHBoxLayout>
 #include <QTextBrowser>
 #include <QTextCharFormat>
 #include <QTextCursor>
-#include <QCoreApplication>
+
 #include <curl/curl.h>
 #ifdef WIN32
-#include "sys/win32/dark_theme.h"
+#	include "sys/win32/dark_theme.h"
 #endif
 
-template <typename T, size_t N>
+template<typename T, size_t N>
 constexpr size_t array_size(T (&)[N]) {
 	return N;
 }
 
-static constexpr semver::version pugixml_version{PUGIXML_VERSION / 1000 % 10, PUGIXML_VERSION / 10 % 100, PUGIXML_VERSION % 10};
-static constexpr semver::version json_version{NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH};
+static constexpr semver::version pugixml_version{PUGIXML_VERSION / 1000 % 10, PUGIXML_VERSION / 10 % 100,
+                                                 PUGIXML_VERSION % 10};
+static constexpr semver::version json_version{NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR,
+                                              NLOHMANN_JSON_VERSION_PATCH};
 static constexpr semver::version semver_version{SEMVER_VERSION_MAJOR, SEMVER_VERSION_MINOR, SEMVER_VERSION_PATCH};
 
 const char* get_curl_version() {
@@ -38,43 +42,69 @@
 	QHBoxLayout* layout = new QHBoxLayout(this);
 
 	/* we have to generate this on-the-fly for localization purposes */
-	const QString html = QString(
-		"<body>"
-		"  <h2 style=\"font-weight: normal;\"><strong>Minori</strong> v" + Strings::ToQString(session.version.to_string()) + "</h2>"
-		"  <p>"
-		"    <strong>" + tr("Author:") + "</strong><br>"
-		"    Paper (@mrpapersonic)"
-		"  </p>"
-		"  <p>"
-		"    <strong>" + tr("Third party components:") + "</strong><br>"
-		    "<a href=\"https://curl.se/\">libcurl v") + get_curl_version() + "</a>"
-		    ", "
-		    "<a href=\"https://p.yusukekamiyamane.com/\">Fugue Icons v3.5.6</a>"
-		    ", "
-		    "<a href=\"https://github.com/erengy/anitomy\">Anitomy</a>"
-		    ", "
-		    "<a href=\"https://github.com/nlohmann/json\">JSON for Modern C++ v" + Strings::ToQString(json_version.to_string()) + "</a>"
-		    ", "
-		    "<a href=\"https://pugixml.org/\">pugixml v" + Strings::ToQString(pugixml_version.to_string()) + "</a>"
-		    ", "
-		    "<a href=\"https://github.com/pulzed/mINI\">mINI v0.9.14</a>"
-		    ", "
-		    "<a href=\"https://github.com/Neargye/semver\">semver v" + Strings::ToQString(semver_version.to_string()) + "</a>"
-		"  </p>"
-		"<span>"
-		"<strong>" + tr("Special thanks:") + "</strong>"
-		"</span>"
-		"  <ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 15px; margin-right: 0px; -qt-list-indent:0;\">"
-		"    <li><strong>Eren Okka</strong> " + tr("for creating Taiga") + "</li>"
-		"    <li><strong>Alex Huszagh</strong> " + tr("and") + " <strong>Colin Duquesnoy</strong> " +
-		        tr("for creating BreezeStyleSheets, on which the dark theme in this program is "
-		        "based off of") + "</li>"
-		"    <li><strong>Andy Brice</strong> " + tr("for providing some sample code for "
-		        "detecting dark mode on Windows and macOS") + "</li>"
-		"    <li><strong>Manuel Wudka-Robles</strong> " + tr("for providing information on "
-		        "getting open file descriptors on macOS") + "</li>"
-		"  </ul>"
-		"</body>";
+	const QString html =
+	    QString("<body>"
+	            "  <h2 style=\"font-weight: normal;\"><strong>Minori</strong> v" +
+	            Strings::ToQString(session.version.to_string()) +
+	            "</h2>"
+	            "  <p>"
+	            "    <strong>" +
+	            tr("Author:") +
+	            "</strong><br>"
+	            "    Paper (@mrpapersonic)"
+	            "  </p>"
+	            "  <p>"
+	            "    <strong>" +
+	            tr("Third party components:") +
+	            "</strong><br>"
+	            "<a href=\"https://curl.se/\">libcurl v") +
+	    get_curl_version() +
+	    "</a>"
+	    ", "
+	    "<a href=\"https://p.yusukekamiyamane.com/\">Fugue Icons v3.5.6</a>"
+	    ", "
+	    "<a href=\"https://github.com/erengy/anitomy\">Anitomy</a>"
+	    ", "
+	    "<a href=\"https://github.com/nlohmann/json\">JSON for Modern C++ v" +
+	    Strings::ToQString(json_version.to_string()) +
+	    "</a>"
+	    ", "
+	    "<a href=\"https://pugixml.org/\">pugixml v" +
+	    Strings::ToQString(pugixml_version.to_string()) +
+	    "</a>"
+	    ", "
+	    "<a href=\"https://github.com/pulzed/mINI\">mINI v0.9.14</a>"
+	    ", "
+	    "<a href=\"https://github.com/Neargye/semver\">semver v" +
+	    Strings::ToQString(semver_version.to_string()) +
+	    "</a>"
+	    ", parts of "
+	    "<a href=\"https://github.com/erengy/anisthesia\">Anisthesia</a>"
+	    "  </p>"
+	    "<span>"
+	    "<strong>" +
+	    tr("Special thanks:") +
+	    "</strong>"
+	    "</span>"
+	    "  <ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 15px; margin-right: 0px; -qt-list-indent:0;\">"
+	    "    <li><strong>Eren Okka</strong> " +
+	    tr("for creating Taiga") +
+	    "</li>"
+	    "    <li><strong>Alex Huszagh</strong> " +
+	    tr("and") + " <strong>Colin Duquesnoy</strong> " +
+	    tr("for creating BreezeStyleSheets, on which the dark theme in this program is "
+	       "based off of") +
+	    "</li>"
+	    "    <li><strong>Andy Brice</strong> " +
+	    tr("for providing some sample code for "
+	       "detecting dark mode on Windows and macOS") +
+	    "</li>"
+	    "    <li><strong>Manuel Wudka-Robles</strong> " +
+	    tr("for providing information on "
+	       "getting open file descriptors on macOS") +
+	    "</li>"
+	    "  </ul>"
+	    "</body>";
 
 	{
 		QPalette pal = QPalette();
--- a/src/gui/dialog/information.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/information.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,8 +1,8 @@
 #include "gui/dialog/information.h"
 #include "core/anime.h"
 #include "core/anime_db.h"
+#include "core/session.h"
 #include "core/strings.h"
-#include "core/session.h"
 #include "gui/pages/anime_list.h"
 #include "gui/translate/anime.h"
 #include "gui/widgets/anime_info.h"
@@ -10,6 +10,7 @@
 #include "gui/widgets/poster.h"
 #include "gui/widgets/text.h"
 #include "gui/window.h"
+
 #include <QCheckBox>
 #include <QComboBox>
 #include <QDateEdit>
@@ -22,9 +23,10 @@
 #include <QStringList>
 #include <QTextStream>
 #include <QVBoxLayout>
+
 #include <functional>
 #ifdef WIN32
-#include "sys/win32/dark_theme.h"
+#	include "sys/win32/dark_theme.h"
 #endif
 
 /* TODO: Taiga disables rendering of the tab widget entirely when the anime is not part of a list,
@@ -42,9 +44,10 @@
 	anime.SetUserDateCompleted(_completed);
 }
 
-InformationDialog::InformationDialog(Anime::Anime& anime, std::function<void()> accept, enum Pages page, QWidget* parent)
+InformationDialog::InformationDialog(Anime::Anime& anime, std::function<void()> accept, enum Pages page,
+                                     QWidget* parent)
     : QDialog(parent) {
-    /* ack. lots of brackets here, but MUCH, MUCH MUCH better than what it used to be */
+	/* ack. lots of brackets here, but MUCH, MUCH MUCH better than what it used to be */
 	setFixedSize(842, 613);
 	setWindowTitle(tr("Anime Information"));
 	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
@@ -114,8 +117,8 @@
 						QWidget* sg_anime_list_content = new QWidget(settings_widget);
 
 						constexpr int LAYOUT_HORIZ_SPACING = 25;
-						constexpr int LAYOUT_VERT_SPACING  = 5;
-						constexpr int LAYOUT_ITEM_WIDTH    = 175;
+						constexpr int LAYOUT_VERT_SPACING = 5;
+						constexpr int LAYOUT_ITEM_WIDTH = 175;
 
 						QVBoxLayout* al_layout = new QVBoxLayout(sg_anime_list_content);
 						al_layout->setSpacing(LAYOUT_VERT_SPACING);
@@ -132,13 +135,14 @@
 							parent->layout()->addWidget(section);
 						};
 
-						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
+						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout) {
 							{
 								/* Episodes watched... */
 								layout->addWidget(new QLabel(tr("Episodes watched:"), section), 0, 0);
 
 								QSpinBox* spin_box = new QSpinBox(section);
-								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int i) { _progress = i; });
+								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this,
+								        [this](int i) { _progress = i; });
 								spin_box->setRange(0, anime.GetEpisodes());
 								spin_box->setSingleStep(1);
 								spin_box->setValue(_progress = anime.GetUserProgress());
@@ -151,14 +155,15 @@
 								QCheckBox* checkbox = new QCheckBox(tr("Rewatching"));
 								connect(checkbox, QOverload<int>::of(&QCheckBox::stateChanged), this,
 								        [this](int state) { _rewatching = (state == Qt::Checked); });
-								checkbox->setCheckState((_rewatching = anime.GetUserIsRewatching()) ? Qt::Checked : Qt::Unchecked);
+								checkbox->setCheckState((_rewatching = anime.GetUserIsRewatching()) ? Qt::Checked
+								                                                                    : Qt::Unchecked);
 								checkbox->setFixedWidth(LAYOUT_ITEM_WIDTH);
 								layout->addWidget(checkbox, 1, 1);
 							}
 							layout->setColumnStretch(layout->columnCount(), 1);
 						});
 
-						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
+						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout) {
 							{
 								/* Status */
 								layout->addWidget(new QLabel(tr("Status:"), section), 0, 0);
@@ -166,11 +171,13 @@
 								QComboBox* combo_box = new QComboBox(section);
 
 								for (unsigned int i = 0; i < Anime::ListStatuses.size(); i++)
-									combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])), static_cast<int>(Anime::ListStatuses[i]));
+									combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])),
+									                   static_cast<int>(Anime::ListStatuses[i]));
 
-								connect(combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this, combo_box](int) {
-									_status = static_cast<Anime::ListStatus>(combo_box->currentData().toInt());
-								});
+								connect(combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+								        [this, combo_box](int) {
+									        _status = static_cast<Anime::ListStatus>(combo_box->currentData().toInt());
+								        });
 
 								/* this should NEVER, EVER, be NOT_IN_LIST */
 								combo_box->setCurrentIndex(static_cast<int>(_status = anime.GetUserStatus()) - 1);
@@ -183,9 +190,8 @@
 								layout->addWidget(new QLabel(tr("Score:"), section), 0, 1);
 
 								QSpinBox* spin_box = new QSpinBox(section);
-								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int i) {
-									_score = i;
-								});
+								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this,
+								        [this](int i) { _score = i; });
 								spin_box->setRange(0, 100);
 								spin_box->setSingleStep(5);
 								spin_box->setValue(_score = anime.GetUserScore());
@@ -195,7 +201,7 @@
 							layout->setColumnStretch(layout->columnCount(), 1);
 						});
 
-						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
+						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout) {
 							layout->addWidget(new QLabel(tr("Notes:"), section), 0, 0);
 
 							QLineEdit* line_edit = new QLineEdit(section);
@@ -208,7 +214,7 @@
 							layout->addWidget(line_edit, 1, 0);
 						});
 
-						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
+						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout) {
 							/* Started */
 							{
 								layout->addWidget(new QLabel(tr("Date started:"), section), 0, 0);
@@ -250,28 +256,28 @@
 
 					/*
 					{
-						// commenting this out until it actually gets implemented :)
+					    // commenting this out until it actually gets implemented :)
 
-						settings_layout->addWidget(new TextWidgets::Header(tr("Local settings"), settings_widget));
+					    settings_layout->addWidget(new TextWidgets::Header(tr("Local settings"), settings_widget));
 
-						QWidget* sg_local_content = new QWidget(settings_widget);
-						QVBoxLayout* sg_local_layout = new QVBoxLayout(sg_local_content);
-						sg_local_layout->setSpacing(5);
-						sg_local_layout->setContentsMargins(12, 0, 0, 0);
+					    QWidget* sg_local_content = new QWidget(settings_widget);
+					    QVBoxLayout* sg_local_layout = new QVBoxLayout(sg_local_content);
+					    sg_local_layout->setSpacing(5);
+					    sg_local_layout->setContentsMargins(12, 0, 0, 0);
 
-						CREATE_SECTION(sg_local_content, [this, &anime](QWidget* section, QGridLayout* layout){
-							layout->addWidget(new QLabel(tr("Alternative titles:"), section), 0, 0);
+					    CREATE_SECTION(sg_local_content, [this, &anime](QWidget* section, QGridLayout* layout){
+					        layout->addWidget(new QLabel(tr("Alternative titles:"), section), 0, 0);
 
-							QLineEdit* line_edit = new QLineEdit("", section);
-							line_edit->setPlaceholderText(
-							    tr("Enter alternative titles here, separated by a semicolon (i.e. Title 1; Title 2)"));
-							layout->addWidget(line_edit, 1, 0);
+					        QLineEdit* line_edit = new QLineEdit("", section);
+					        line_edit->setPlaceholderText(
+					            tr("Enter alternative titles here, separated by a semicolon (i.e. Title 1; Title 2)"));
+					        layout->addWidget(line_edit, 1, 0);
 
-							QCheckBox* checkbox = new QCheckBox(tr("Use the first alternative title to search for torrents"));
-							layout->addWidget(checkbox, 2, 0);
-						});
+					        QCheckBox* checkbox = new QCheckBox(tr("Use the first alternative title to search for
+					torrents")); layout->addWidget(checkbox, 2, 0);
+					    });
 
-						settings_layout->addWidget(sg_local_content);
+					    settings_layout->addWidget(sg_local_content);
 					}
 					*/
 
--- a/src/gui/dialog/settings.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -9,7 +9,7 @@
 #include <QVBoxLayout>
 #include <QWidget>
 #ifdef WIN32
-#include "sys/win32/dark_theme.h"
+#	include "sys/win32/dark_theme.h"
 #endif
 
 SettingsPage::SettingsPage(QWidget* parent, QString title) : QWidget(parent) {
--- a/src/gui/dialog/settings/application.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings/application.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,8 +1,8 @@
 #include "core/session.h"
 #include "core/strings.h"
 #include "gui/dialog/settings.h"
+#include "gui/locale.h"
 #include "gui/theme.h"
-#include "gui/locale.h"
 #include "gui/translate/anime.h"
 
 #include <QCheckBox>
@@ -55,7 +55,7 @@
 			middle_click_layout->addWidget(mc_combo_box_label);
 			middle_click_layout->addWidget(mc_combo_box);
 			middle_click_layout->setContentsMargins(0, 0, 0, 0);
-			
+
 			actions_layout->addWidget(middle_click_widget);
 		}
 
@@ -98,10 +98,13 @@
 				QComboBox* rating_combo_box = new QComboBox(appearance_group_box);
 
 				for (const auto& score_format : Anime::ScoreFormats)
-					rating_combo_box->addItem(Strings::ToQString(Translate::ToLocalString(score_format)), static_cast<int>(score_format));
+					rating_combo_box->addItem(Strings::ToQString(Translate::ToLocalString(score_format)),
+					                          static_cast<int>(score_format));
 
 				connect(rating_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-					    [this, rating_combo_box](int index) { format = static_cast<Anime::ScoreFormat>(rating_combo_box->itemData(index).toInt()); });
+				        [this, rating_combo_box](int index) {
+					        format = static_cast<Anime::ScoreFormat>(rating_combo_box->itemData(index).toInt());
+				        });
 
 				rating_combo_box->setCurrentIndex(static_cast<int>(format));
 				appearance_layout->addWidget(rating_combo_box);
@@ -110,7 +113,8 @@
 
 		{
 			/* Hopefully I made this easy to parse... */
-			QCheckBox* hl_above_anime_box = new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box);
+			QCheckBox* hl_above_anime_box =
+			    new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box);
 			hl_above_anime_box->setCheckState(highlighted_anime_above_others ? Qt::Checked : Qt::Unchecked);
 			hl_above_anime_box->setEnabled(highlight_anime_if_available);
 			hl_above_anime_box->setContentsMargins(10, 0, 0, 0);
@@ -120,7 +124,8 @@
 
 			{
 				/* This is here because the above checkbox actually depends on it to be checked. */
-				QCheckBox* hl_anime_box = new QCheckBox(tr("Highlight anime if next episode is available in library folders"), appearance_group_box);
+				QCheckBox* hl_anime_box = new QCheckBox(
+				    tr("Highlight anime if next episode is available in library folders"), appearance_group_box);
 				hl_anime_box->setCheckState(highlight_anime_if_available ? Qt::Checked : Qt::Unchecked);
 
 				connect(hl_anime_box, &QCheckBox::stateChanged, this, [this, hl_above_anime_box](int state) {
@@ -157,7 +162,8 @@
 			    new QCheckBox(tr("Display available episodes in library folders"), progress_group_box);
 			connect(progress_display_available_episodes, &QCheckBox::stateChanged, this,
 			        [this](int state) { display_available_episodes = !(state == Qt::Unchecked); });
-			progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked : Qt::Unchecked);
+			progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked
+			                                                                              : Qt::Unchecked);
 			progress_layout->addWidget(progress_display_available_episodes);
 		}
 
@@ -196,7 +202,7 @@
 				theme_combo_box->addItem(tr("Light"));
 				theme_combo_box->addItem(tr("Dark"));
 				connect(theme_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-					    [this](int index) { theme = static_cast<Themes>(index); });
+				        [this](int index) { theme = static_cast<Themes>(index); });
 				theme_combo_box->setCurrentIndex(static_cast<int>(theme));
 				appearance_layout->addWidget(theme_combo_box);
 			}
@@ -216,7 +222,7 @@
 					locale_combo_box->addItem(Strings::ToQString(Locale::GetLocaleFullName(l)), l);
 
 				connect(locale_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-					    [this, locale_combo_box](int) { locale = locale_combo_box->currentData().toLocale(); });
+				        [this, locale_combo_box](int) { locale = locale_combo_box->currentData().toLocale(); });
 
 				for (size_t i = 0; i < available_locales.size(); i++)
 					if (available_locales[i] == locale)
@@ -235,7 +241,6 @@
 	return result;
 }
 
-
 void SettingsPageApplication::SaveInfo() {
 	session.config.anime_list.language = language;
 	session.config.anime_list.highlighted_anime_above_others = highlighted_anime_above_others;
--- a/src/gui/dialog/settings/library.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings/library.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -2,19 +2,19 @@
 #include "core/strings.h"
 #include "gui/dialog/settings.h"
 
+#include <QCheckBox>
+#include <QDir>
+#include <QDropEvent>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QGroupBox>
+#include <QLabel>
 #include <QListWidget>
 #include <QListWidgetItem>
-#include <QGroupBox>
-#include <QCheckBox>
-#include <QLabel>
+#include <QMimeData>
+#include <QPushButton>
 #include <QSizePolicy>
 #include <QVBoxLayout>
-#include <QDir>
-#include <QFileDialog>
-#include <QFileInfo>
-#include <QPushButton>
-#include <QDropEvent>
-#include <QMimeData>
 
 #include <algorithm>
 #include <iostream>
@@ -24,13 +24,13 @@
 }
 
 void DroppableListWidget::dragMoveEvent(QDragMoveEvent* event) {
-    if (event->mimeData()->hasUrls())
-        event->acceptProposedAction();
+	if (event->mimeData()->hasUrls())
+		event->acceptProposedAction();
 }
 
 void DroppableListWidget::dragEnterEvent(QDragEnterEvent* event) {
-    if (event->mimeData()->hasUrls())
-        event->acceptProposedAction();
+	if (event->mimeData()->hasUrls())
+		event->acceptProposedAction();
 }
 
 void DroppableListWidget::dropEvent(QDropEvent* event) {
@@ -85,7 +85,7 @@
 				/* add icons as well soon */
 			}
 
-			connect(listwidget, &DroppableListWidget::FilesDropped, this, [this, listwidget](QStringList list){
+			connect(listwidget, &DroppableListWidget::FilesDropped, this, [this, listwidget](QStringList list) {
 				for (const auto& dir : list) {
 					paths.insert(Strings::ToUtf8String(dir));
 					QListWidgetItem* item = new QListWidgetItem(listwidget);
@@ -109,11 +109,10 @@
 				{
 					QPushButton* button = new QPushButton(tr("Add new..."), widget);
 
-					connect(button, &QPushButton::clicked, this, [this, listwidget]{
-						const QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
-																			QDir::homePath(),
-																			QFileDialog::ShowDirsOnly
-																			| QFileDialog::DontResolveSymlinks);
+					connect(button, &QPushButton::clicked, this, [this, listwidget] {
+						const QString dir = QFileDialog::getExistingDirectory(
+						    this, tr("Open Directory"), QDir::homePath(),
+						    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
 						const std::string s_dir = Strings::ToUtf8String(dir);
 						if (dir.isEmpty() || paths.count(s_dir))
 							return;
@@ -129,12 +128,12 @@
 				{
 					QPushButton* button = new QPushButton(tr("Remove"), widget);
 
-					connect(listwidget, &QListWidget::itemSelectionChanged, this, [button, listwidget]{
+					connect(listwidget, &QListWidget::itemSelectionChanged, this, [button, listwidget] {
 						QList<QListWidgetItem*> selection = listwidget->selectedItems();
 						button->setEnabled(selection.size() > 0);
 					});
 
-					connect(button, &QPushButton::clicked, this, [this, listwidget]{
+					connect(button, &QPushButton::clicked, this, [this, listwidget] {
 						QList<QListWidgetItem*> selection = listwidget->selectedItems();
 						for (const auto& item : selection) {
 							paths.erase(Strings::ToUtf8String(item->text()));
@@ -162,9 +161,8 @@
 			QCheckBox* checkbox = new QCheckBox(tr("Detect new files and folders under library folders"), group_box);
 			checkbox->setCheckState(real_time_monitor ? Qt::Checked : Qt::Unchecked);
 
-			connect(checkbox, &QCheckBox::stateChanged, this, [this](int state) {
-				real_time_monitor = (state != Qt::Unchecked);
-			});
+			connect(checkbox, &QCheckBox::stateChanged, this,
+			        [this](int state) { real_time_monitor = (state != Qt::Unchecked); });
 
 			group_box_layout->addWidget(checkbox);
 		}
@@ -184,8 +182,7 @@
 }
 
 SettingsPageLibrary::SettingsPageLibrary(QWidget* parent)
-	: SettingsPage(parent, tr("Library")),
-	  paths(session.config.library.paths) {
+    : SettingsPage(parent, tr("Library")), paths(session.config.library.paths) {
 	real_time_monitor = session.config.library.real_time_monitor;
 	AddTab(CreateFoldersWidget(), tr("Folder"));
 }
--- a/src/gui/dialog/settings/recognition.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings/recognition.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -2,11 +2,11 @@
 #include "core/strings.h"
 #include "gui/dialog/settings.h"
 
+#include <QCheckBox>
+#include <QGroupBox>
+#include <QLabel>
 #include <QListWidget>
 #include <QListWidgetItem>
-#include <QGroupBox>
-#include <QCheckBox>
-#include <QLabel>
 #include <QSizePolicy>
 #include <QVBoxLayout>
 
@@ -37,14 +37,14 @@
 			QListWidget* listwidget = new QListWidget(widget);
 			for (size_t i = 0; i < players.size(); i++) {
 				const auto& [enabled, player] = players[i];
-				if (player.type == animia::PlayerType::Default) {
+				if (player.type == animone::PlayerType::Default) {
 					QListWidgetItem* item = new QListWidgetItem(listwidget);
 					item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
 					item->setText(Strings::ToQString(player.name));
 					item->setData(Qt::UserRole, QVariant::fromValue(i));
 				}
 			}
-			connect(listwidget, &QListWidget::itemChanged, this, [this](QListWidgetItem* item){
+			connect(listwidget, &QListWidget::itemChanged, this, [this](QListWidgetItem* item) {
 				if (!item)
 					return;
 				size_t i = item->data(Qt::UserRole).toUInt();
@@ -74,8 +74,7 @@
 }
 
 SettingsPageRecognition::SettingsPageRecognition(QWidget* parent)
-	: SettingsPage(parent, tr("Recognition")),
-	  players(session.config.recognition.players) {
+    : SettingsPage(parent, tr("Recognition")), players(session.config.recognition.players) {
 	detect_media_players = session.config.recognition.detect_media_players;
 	AddTab(CreatePlayersWidget(), tr("Media players"));
 }
--- a/src/gui/dialog/settings/services.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings/services.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -38,14 +38,14 @@
 		}
 
 		{
-			QLabel* sync_note_label =
-			    new QLabel(tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box);
+			QLabel* sync_note_label = new QLabel(
+			    tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box);
 			sync_layout->addWidget(sync_note_label);
 		}
 
 		full_layout->addWidget(sync_group_box);
 	}
-	
+
 	full_layout->setSpacing(10);
 	full_layout->addStretch();
 
@@ -73,7 +73,8 @@
 			{
 				QPushButton* auth_button = new QPushButton(auth_widget);
 				connect(auth_button, &QPushButton::clicked, this, [] { Services::AniList::AuthorizeUser(); });
-				auth_button->setText(session.config.auth.anilist.auth_token.empty() ? tr("Authorize...") : tr("Re-authorize..."));
+				auth_button->setText(session.config.auth.anilist.auth_token.empty() ? tr("Authorize...")
+				                                                                    : tr("Re-authorize..."));
 				auth_layout->addWidget(auth_button);
 			}
 
@@ -82,13 +83,14 @@
 
 		{
 			/* Note on creating new accounts... */
-			QLabel* note_label = new QLabel(tr("<a href=\"http://anilist.co/\">Create a new AniList account</a>"), group_box);
+			QLabel* note_label =
+			    new QLabel(tr("<a href=\"http://anilist.co/\">Create a new AniList account</a>"), group_box);
 			note_label->setTextFormat(Qt::RichText);
 			note_label->setTextInteractionFlags(Qt::TextBrowserInteraction);
 			note_label->setOpenExternalLinks(true);
 			layout->addWidget(note_label);
 		}
-		
+
 		full_layout->addWidget(group_box);
 	}
 
--- a/src/gui/dialog/settings/torrents.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/dialog/settings/torrents.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,9 +1,9 @@
 #include "core/session.h"
 #include "core/strings.h"
 #include "gui/dialog/settings.h"
-#include <QLineEdit>
 #include <QGroupBox>
 #include <QLabel>
+#include <QLineEdit>
 #include <QSizePolicy>
 #include <QVBoxLayout>
 #include <algorithm>
--- a/src/gui/layouts/flow_layout.cc	Sun Feb 18 16:02:14 2024 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/*
-* Copyright (C) 2016 The Qt Company Ltd.
-* Contact: https://www.qt.io/licensing/
-*
-* This file is part of the QtCore module of the Qt Toolkit.
-*
-* "Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*   * Redistributions of source code must retain the above copyright
-*     notice, this list of conditions and the following disclaimer.
-*   * Redistributions in binary form must reproduce the above copyright
-*     notice, this list of conditions and the following disclaimer in
-*     the documentation and/or other materials provided with the
-*     distribution.
-*   * Neither the name of The Qt Company Ltd nor the names of its
-*     contributors may be used to endorse or promote products derived
-*     from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-*/
-#include "gui/layouts/flow_layout.h"
-
-#include <QWidget>
-
-#include <algorithm>
-
-FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
-	: QLayout(parent), _horiz_space(hSpacing), _vert_space(vSpacing) {
-	setContentsMargins(margin, margin, margin, margin);
-}
-
-FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
-	: _horiz_space(hSpacing), _vert_space(vSpacing) {
-	setContentsMargins(margin, margin, margin, margin);
-}
-
-FlowLayout::~FlowLayout() {
-	while (count())
-		delete takeAt(0);
-}
-
-void FlowLayout::addItem(QLayoutItem *item) {
-	item_list.append(item);
-}
-
-int FlowLayout::horizontalSpacing() const {
-	return (_horiz_space >= 0) ? _horiz_space : smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
-}
-
-int FlowLayout::verticalSpacing() const {
-	return (_vert_space >= 0) ? _vert_space : smartSpacing(QStyle::PM_LayoutVerticalSpacing);
-}
-
-int FlowLayout::count() const {
-	return item_list.size();
-}
-
-QLayoutItem* FlowLayout::itemAt(int index) const {
-	return item_list.value(index);
-}
-
-QLayoutItem* FlowLayout::takeAt(int index) {
-	return (index >= 0 && index < item_list.size()) ? item_list.takeAt(index) : nullptr;
-}
-
-Qt::Orientations FlowLayout::expandingDirections() const
-{
-	return {};
-}
-
-bool FlowLayout::hasHeightForWidth() const {
-	return true;
-}
-
-int FlowLayout::heightForWidth(int width) const {
-	return doLayout(QRect(0, 0, width, 0), true);
-}
-
-void FlowLayout::setGeometry(const QRect &rect) {
-	QLayout::setGeometry(rect);
-	doLayout(rect, false);
-}
-
-QSize FlowLayout::sizeHint() const {
-	return minimumSize();
-}
-
-QSize FlowLayout::minimumSize() const {
-	QSize size;
-	for (QLayoutItem* item : item_list)
-		size = size.expandedTo(item->minimumSize());
-
-	const QMargins margins = contentsMargins();
-	size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
-	return size;
-}
-
-int FlowLayout::doLayout(const QRect &rect, bool test) const {
-	int left, top, right, bottom;
-	getContentsMargins(&left, &top, &right, &bottom);
-	QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
-
-	int x = effectiveRect.x();
-	int y = effectiveRect.y();
-	int line_height = 0;
-
-	for (QLayoutItem* item : item_list) {
-		const QWidget* wid = item->widget();
-		int horiz_space = horizontalSpacing();
-		if (horiz_space == -1)
-			horiz_space = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
-
-		int vert_space = verticalSpacing();
-		if (vert_space == -1)
-			vert_space = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
-
-		int next_x = x + item->sizeHint().width() + horiz_space;
-		if ((next_x - horiz_space > effectiveRect.right()) && (line_height > 0)) {
-			x = effectiveRect.x();
-			y = y + line_height + vert_space;
-			next_x = x + item->sizeHint().width() + horiz_space;
-			line_height = 0;
-		}
-
-		if (!test)
-			item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
-
-		x = next_x;
-		line_height = std::max(line_height, item->sizeHint().height());
-	}
-
-	return y + line_height - rect.y() + bottom;
-}
-
-int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const {
-	QObject *parent = this->parent();
-	if (!parent) {
-		return -1;
-	} else if (parent->isWidgetType()) {
-		QWidget *pw = static_cast<QWidget *>(parent);
-		return pw->style()->pixelMetric(pm, nullptr, pw);
-	} else {
-		return static_cast<QLayout *>(parent)->spacing();
-	}
-}
--- a/src/gui/locale.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/locale.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,11 +1,11 @@
 #include "gui/locale.h"
 #include "core/strings.h"
 
-#include <QTranslator>
-#include <QLocale>
+#include <QApplication>
 #include <QDir>
+#include <QLocale>
 #include <QString>
-#include <QApplication>
+#include <QTranslator>
 
 #include <QDebug>
 
@@ -105,4 +105,4 @@
 	return true;
 }
 
-}
+} // namespace Locale
--- a/src/gui/pages/anime_list.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/anime_list.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -25,10 +25,10 @@
 #include <QMenu>
 #include <QProgressBar>
 #include <QShortcut>
-#include <QTreeView>
 #include <QStylePainter>
 #include <QStyledItemDelegate>
 #include <QThread>
+#include <QTreeView>
 
 #include <set>
 
@@ -111,7 +111,7 @@
 				case AL_TITLE: return Strings::ToQString(list[index.row()].GetUserPreferredTitle());
 				case AL_PROGRESS:
 					return QString::number(list[index.row()].GetUserProgress()) + "/" +
-						   QString::number(list[index.row()].GetEpisodes());
+					       QString::number(list[index.row()].GetEpisodes());
 				case AL_EPISODES: return list[index.row()].GetEpisodes();
 				case AL_SCORE: return Strings::ToQString(list[index.row()].GetUserPresentableScore());
 				case AL_TYPE: return Strings::ToQString(Translate::ToString(list[index.row()].GetFormat()));
@@ -119,7 +119,8 @@
 					std::optional<unsigned int> year = list[index.row()].GetAirDate().GetYear();
 					if (!year)
 						return "Unknown Unknown";
-					return Strings::ToQString(Translate::ToLocalString(list[index.row()].GetSeason()) + " " + Strings::ToUtf8String(year.value()));
+					return Strings::ToQString(Translate::ToLocalString(list[index.row()].GetSeason()) + " " +
+					                          Strings::ToUtf8String(year.value()));
 				}
 				case AL_AVG_SCORE: return QString::number(list[index.row()].GetAudienceScore()) + "%";
 				case AL_STARTED: return list[index.row()].GetUserDateStarted().GetAsQDate();
@@ -218,9 +219,7 @@
 }
 
 void AnimeListPage::UpdateAnime(int id) {
-	QThread* thread = QThread::create([this, id] {
-		Services::UpdateAnimeEntry(id);
-	});
+	QThread* thread = QThread::create([this, id] { Services::UpdateAnimeEntry(id); });
 
 	connect(thread, &QThread::finished, this, &AnimeListPage::Refresh);
 	connect(thread, &QThread::finished, thread, &QThread::deleteLater);
@@ -244,7 +243,7 @@
 		if (i == AnimeListPageModel::AL_TITLE)
 			continue;
 		const auto column_name =
-			sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+		    sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
 
 		QAction* action = menu->addAction(column_name, this, [this, i](const bool checked) {
 			if (!checked && (VisibleColumnsCount() <= 1))
@@ -278,9 +277,9 @@
 	menu->setToolTipsVisible(true);
 
 	AnimeListPageModel* source_model =
-		reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
+	    reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
 	const QItemSelection selection =
-		sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
+	    sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
 
 	std::set<Anime::Anime*> animes;
 	for (const auto& index : selection.indexes()) {
@@ -293,9 +292,8 @@
 
 	menu->addAction(tr("Information"), [this, animes] {
 		for (auto& anime : animes) {
-			InformationDialog* dialog = new InformationDialog(*anime, [this, anime] {
-				UpdateAnime(anime->GetId());
-			}, InformationDialog::PAGE_MAIN_INFO, this);
+			InformationDialog* dialog = new InformationDialog(
+			    *anime, [this, anime] { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
 
 			dialog->show();
 			dialog->raise();
@@ -305,9 +303,8 @@
 	menu->addSeparator();
 	menu->addAction(tr("Edit"), [this, animes] {
 		for (auto& anime : animes) {
-			InformationDialog* dialog = new InformationDialog(*anime, [this, anime] {
-				UpdateAnime(anime->GetId());
-			}, InformationDialog::PAGE_MY_LIST, this);
+			InformationDialog* dialog = new InformationDialog(
+			    *anime, [this, anime] { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST, this);
 
 			dialog->show();
 			dialog->raise();
@@ -325,20 +322,19 @@
 void AnimeListPage::ItemDoubleClicked() {
 	/* throw out any other garbage */
 	const QItemSelection selection =
-		sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
+	    sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
 	if (!selection.indexes().first().isValid()) {
 		return;
 	}
 
 	AnimeListPageModel* source_model =
-		reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
+	    reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
 
 	const QModelIndex index = source_model->index(selection.indexes().first().row());
 	Anime::Anime* anime = source_model->GetAnimeFromIndex(index);
 
-	InformationDialog* dialog = new InformationDialog(*anime, [this, anime] {
-		UpdateAnime(anime->GetId());
-	}, InformationDialog::PAGE_MAIN_INFO, this);
+	InformationDialog* dialog = new InformationDialog(
+	    *anime, [this, anime] { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
 
 	dialog->show();
 	dialog->raise();
@@ -353,7 +349,7 @@
 void AnimeListPage::RefreshTabs() {
 	for (unsigned int i = 0; i < sort_models.size(); i++)
 		tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
-								   QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
+		                           QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
 }
 
 void AnimeListPage::Refresh() {
@@ -449,7 +445,7 @@
 
 	for (unsigned int i = 0; i < sort_models.size(); i++) {
 		tab_bar->addTab(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
-						QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
+		                QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
 		sort_models[i] = new AnimeListPageSortFilter(tree_view);
 		sort_models[i]->setSourceModel(new AnimeListPageModel(this, Anime::ListStatuses[i]));
 		sort_models[i]->setSortRole(Qt::UserRole);
@@ -480,10 +476,10 @@
 
 	/* Enter & return keys */
 	connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
-			&AnimeListPage::ItemDoubleClicked);
+	        &AnimeListPage::ItemDoubleClicked);
 
 	connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
-			&AnimeListPage::ItemDoubleClicked);
+	        &AnimeListPage::ItemDoubleClicked);
 
 	tree_view->header()->setStretchLastSection(false);
 	tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
--- a/src/gui/pages/now_playing.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/now_playing.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -2,12 +2,12 @@
 #include "core/anime_db.h"
 #include "core/strings.h"
 #include "gui/widgets/anime_info.h"
+#include "gui/widgets/poster.h"
 #include "gui/widgets/text.h"
-#include "gui/widgets/poster.h"
 
+#include <QHBoxLayout>
 #include <QLabel>
 #include <QStackedWidget>
-#include <QHBoxLayout>
 #include <QVBoxLayout>
 #include <QWidget>
 
@@ -63,7 +63,8 @@
 }
 
 void Playing::SetPlayingAnime(const Anime::Anime& anime, const anitomy::Elements& info) {
-	if (_id == anime.GetId() && _episode == Strings::ToInt(Strings::ToUtf8String(info.get(anitomy::kElementEpisodeNumber))))
+	if (_id == anime.GetId()
+		&& _episode == Strings::ToInt(Strings::ToUtf8String(info.get(anitomy::kElementEpisodeNumber))))
 		return;
 	_id = anime.GetId();
 	_episode = Strings::ToInt(Strings::ToUtf8String(info.get(anitomy::kElementEpisodeNumber)));
--- a/src/gui/pages/search.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/search.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,30 +1,30 @@
 #include "gui/pages/search.h"
 #include "core/anime.h"
 #include "core/anime_db.h"
-#include "core/strings.h"
+#include "core/filesystem.h"
 #include "core/http.h"
 #include "core/session.h"
-#include "core/filesystem.h"
-#include "gui/widgets/text.h"
+#include "core/strings.h"
 #include "gui/dialog/information.h"
-#include "track/media.h"
 #include "gui/translate/anime.h"
+#include "gui/widgets/text.h"
 #include "services/services.h"
+#include "track/media.h"
 
+#include <QDate>
 #include <QHeaderView>
-#include <QVBoxLayout>
+#include <QMenu>
 #include <QToolBar>
 #include <QTreeView>
-#include <QDate>
-#include <QMenu>
+#include <QVBoxLayout>
 
+#include <algorithm>
+#include <fstream>
 #include <iostream>
 #include <sstream>
-#include <fstream>
-#include <algorithm>
 
+#include "anitomy/anitomy.h"
 #include "pugixml.hpp"
-#include "anitomy/anitomy.h"
 
 SearchPageListSortFilter::SearchPageListSortFilter(QObject* parent) : QSortFilterProxyModel(parent) {
 }
@@ -37,10 +37,8 @@
 		case QMetaType::Int:
 		case QMetaType::UInt:
 		case QMetaType::LongLong:
-		case QMetaType::ULongLong:
-			return left.toInt() < right.toInt();
-		case QMetaType::QDate:
-			return left.toDate() < right.toDate();
+		case QMetaType::ULongLong: return left.toInt() < right.toInt();
+		case QMetaType::QDate: return left.toDate() < right.toDate();
 		case QMetaType::QString:
 		default: // meh
 			return QString::compare(left.toString(), right.toString(), Qt::CaseInsensitive) < 0;
@@ -117,7 +115,9 @@
 				case SR_TYPE: return Strings::ToQString(Translate::ToLocalString(anime.GetFormat()));
 				case SR_EPISODES: return anime.GetEpisodes();
 				case SR_SCORE: return QString::number(anime.GetAudienceScore()) + "%";
-				case SR_SEASON: return Strings::ToQString(Translate::ToLocalString(anime.GetSeason())) + " " + QString::number(anime.GetAirDate().GetYear().value_or(2000));
+				case SR_SEASON:
+					return Strings::ToQString(Translate::ToLocalString(anime.GetSeason())) + " " +
+					       QString::number(anime.GetAirDate().GetYear().value_or(2000));
 				default: return {};
 			}
 			break;
@@ -128,7 +128,7 @@
 				case SR_SEASON: return anime.GetAirDate().GetAsQDate();
 				/* We have to use this to work around some stupid
 				 * "conversion ambiguous" error on Linux
-				*/
+				 */
 				default: return data(index, Qt::DisplayRole);
 			}
 			break;
@@ -144,8 +144,7 @@
 			}
 			break;
 		}
-		case Qt::TextAlignmentRole:
-			return headerData(index.column(), Qt::Horizontal, Qt::TextAlignmentRole);
+		case Qt::TextAlignmentRole: return headerData(index.column(), Qt::Horizontal, Qt::TextAlignmentRole);
 	}
 	return QVariant();
 }
@@ -185,9 +184,12 @@
 
 	menu->addAction(tr("Information"), [this, animes] {
 		for (auto& anime : animes) {
-			InformationDialog* dialog = new InformationDialog(*anime, [this, anime] {
-				//UpdateAnime(anime->GetId());
-			}, InformationDialog::PAGE_MAIN_INFO, this);
+			InformationDialog* dialog = new InformationDialog(
+			    *anime,
+			    [this, anime] {
+				    // UpdateAnime(anime->GetId());
+			    },
+			    InformationDialog::PAGE_MAIN_INFO, this);
 
 			dialog->show();
 			dialog->raise();
@@ -197,7 +199,7 @@
 	menu->addSeparator();
 	{
 		QMenu* submenu = menu->addMenu(tr("Add to list..."));
-		submenu->addAction(tr("Currently watching"), [animes]{
+		submenu->addAction(tr("Currently watching"), [animes] {
 			for (auto& anime : animes) {
 				if (!anime->IsInUserList())
 					anime->AddToUserList();
@@ -205,7 +207,7 @@
 				Services::UpdateAnimeEntry(anime->GetId());
 			}
 		});
-		submenu->addAction(tr("Completed"), [animes]{
+		submenu->addAction(tr("Completed"), [animes] {
 			for (auto& anime : animes) {
 				if (!anime->IsInUserList())
 					anime->AddToUserList();
@@ -213,7 +215,7 @@
 				Services::UpdateAnimeEntry(anime->GetId());
 			}
 		});
-		submenu->addAction(tr("On hold"), [animes]{
+		submenu->addAction(tr("On hold"), [animes] {
 			for (auto& anime : animes) {
 				if (!anime->IsInUserList())
 					anime->AddToUserList();
@@ -221,7 +223,7 @@
 				Services::UpdateAnimeEntry(anime->GetId());
 			}
 		});
-		submenu->addAction(tr("Dropped"), [animes]{
+		submenu->addAction(tr("Dropped"), [animes] {
 			for (auto& anime : animes) {
 				if (!anime->IsInUserList())
 					anime->AddToUserList();
@@ -229,7 +231,7 @@
 				Services::UpdateAnimeEntry(anime->GetId());
 			}
 		});
-		submenu->addAction(tr("Plan to watch"), [animes]{
+		submenu->addAction(tr("Plan to watch"), [animes] {
 			for (auto& anime : animes) {
 				if (!anime->IsInUserList())
 					anime->AddToUserList();
@@ -251,9 +253,12 @@
 	const QModelIndex index = model->index(selection.indexes().first().row());
 	Anime::Anime* anime = model->GetAnimeFromIndex(index);
 
-	InformationDialog* dialog = new InformationDialog(*anime, [this, anime] {
-		//UpdateAnime(anime->GetId());
-	}, InformationDialog::PAGE_MAIN_INFO, this);
+	InformationDialog* dialog = new InformationDialog(
+	    *anime,
+	    [this, anime] {
+		    // UpdateAnime(anime->GetId());
+	    },
+	    InformationDialog::PAGE_MAIN_INFO, this);
 
 	dialog->show();
 	dialog->raise();
@@ -275,18 +280,18 @@
 
 		{
 			QLineEdit* line_edit = new QLineEdit("", toolbar);
-			connect(line_edit, &QLineEdit::returnPressed, this, [this, line_edit]{
+			connect(line_edit, &QLineEdit::returnPressed, this, [this, line_edit] {
 				/* static thread here. */
 				static QThread* thread = nullptr;
 
 				if (thread)
 					return;
 
-				thread = QThread::create([this, line_edit]{
+				thread = QThread::create([this, line_edit] {
 					model->ParseSearch(Services::Search(Strings::ToUtf8String(line_edit->text())));
 				});
 
-				connect(thread, &QThread::finished, this, []{
+				connect(thread, &QThread::finished, this, [] {
 					thread->deleteLater();
 					thread = nullptr;
 				});
@@ -329,11 +334,11 @@
 		}
 
 		// set column sizes
-		treeview->setColumnWidth(SearchPageListModel::SR_TITLE,    400);
-		treeview->setColumnWidth(SearchPageListModel::SR_TYPE,     60);
+		treeview->setColumnWidth(SearchPageListModel::SR_TITLE, 400);
+		treeview->setColumnWidth(SearchPageListModel::SR_TYPE, 60);
 		treeview->setColumnWidth(SearchPageListModel::SR_EPISODES, 60);
-		treeview->setColumnWidth(SearchPageListModel::SR_SCORE,    60);
-		treeview->setColumnWidth(SearchPageListModel::SR_SEASON,   100);
+		treeview->setColumnWidth(SearchPageListModel::SR_SCORE, 60);
+		treeview->setColumnWidth(SearchPageListModel::SR_SEASON, 100);
 
 		treeview->header()->setStretchLastSection(false);
 
--- a/src/gui/pages/seasons.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/seasons.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -2,15 +2,14 @@
 
 #include "core/anime_db.h"
 #include "gui/widgets/anime_button.h"
-#include "gui/layouts/flow_layout.h"
 
-#include <QVBoxLayout>
-#include <QToolBar>
 #include <QFrame>
 #include <QListWidget>
 #include <QListWidgetItem>
 #include <QMenu>
+#include <QToolBar>
 #include <QToolButton>
+#include <QVBoxLayout>
 
 SeasonsPage::SeasonsPage(QWidget* parent) : QWidget(parent) {
 	QVBoxLayout* full_layout = new QVBoxLayout(this);
@@ -37,14 +36,13 @@
 				toolbar->addAction(action);
 			}
 
-			toolbar->addAction(QIcon(":/icons/16x16/calendar.png"), "Fall 2024"); // this must be named the name of the season
+			toolbar->addAction(QIcon(":/icons/16x16/calendar.png"),
+			                   "Fall 2024"); // this must be named the name of the season
 		}
 
 		toolbar->addSeparator();
 
-		{
-			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("Refresh data"));
-		}
+		{ toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("Refresh data")); }
 
 		toolbar->addSeparator();
 
--- a/src/gui/pages/statistics.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/statistics.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,10 +1,10 @@
 #include "gui/pages/statistics.h"
 #include "core/anime_db.h"
+#include "core/session.h"
 #include "core/strings.h"
-#include "core/session.h"
 #include "gui/pages/anime_list.h"
+#include "gui/widgets/graph.h"
 #include "gui/widgets/text.h"
-#include "gui/widgets/graph.h"
 
 #include <QString>
 #include <QTextDocument>
@@ -13,8 +13,8 @@
 #include <QVBoxLayout>
 #include <QWidget>
 
+#include <cmath>
 #include <sstream>
-#include <cmath>
 
 enum class TimeUnits {
 	SECONDS,
@@ -60,7 +60,6 @@
 		layout->addWidget(score_dist_widget);
 	}
 
-
 	_application.reset(new TextWidgets::LabelledSection(tr("Minori"), tr("Uptime:\nRequests made:"), "\n\n", this));
 	layout->addWidget(_application.get());
 
@@ -84,7 +83,7 @@
  *       amount of units to parse
  *  [in, defaults to 1.0] double unit_in_seconds:
  *       equivalent of one of 'amount' in seconds, e.g. minutes would be 60.0
-*/
+ */
 static std::string TimeToDateString(TimeUnits unit, int amount, double unit_in_seconds = 1.0) {
 	/* avoid calculating this twice */
 	const double years_conv = (31556952.0 / unit_in_seconds);
@@ -94,14 +93,15 @@
 	const double minutes_conv = (60.0 / unit_in_seconds);
 	const double seconds_conv = (1.0 / unit_in_seconds);
 
-	const int years   = amount / years_conv;
-	const int months  = std::fmod(amount, years_conv)   / months_conv;
-	const int days    = std::fmod(amount, months_conv)  / days_conv;
-	const int hours   = std::fmod(amount, days_conv)    / hours_conv;
-	const int minutes = std::fmod(amount, hours_conv)   / minutes_conv;
+	const int years = amount / years_conv;
+	const int months = std::fmod(amount, years_conv) / months_conv;
+	const int days = std::fmod(amount, months_conv) / days_conv;
+	const int hours = std::fmod(amount, days_conv) / hours_conv;
+	const int minutes = std::fmod(amount, hours_conv) / minutes_conv;
 	const int seconds = std::fmod(amount, minutes_conv) / seconds_conv;
 
-	const auto add_time_segment = [](std::ostringstream& str, int amount, const std::string_view& singular, const std::string_view& plural, bool always = false) {
+	const auto add_time_segment = [](std::ostringstream& str, int amount, const std::string_view& singular,
+	                                 const std::string_view& plural, bool always = false) {
 		if (amount > 0 || always)
 			str << amount << ((amount == 1) ? singular : plural);
 	};
--- a/src/gui/pages/torrents.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/pages/torrents.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,34 +1,34 @@
 #include "gui/pages/torrents.h"
-#include "core/strings.h"
+#include "core/filesystem.h"
 #include "core/http.h"
 #include "core/session.h"
-#include "core/filesystem.h"
+#include "core/strings.h"
 #include "gui/widgets/text.h"
 #include "track/media.h"
 
-#include <QHeaderView>
-#include <QVBoxLayout>
-#include <QToolBar>
-#include <QTreeView>
 #include <QByteArray>
 #include <QDataStream>
+#include <QDebug>
+#include <QHeaderView>
 #include <QThread>
-#include <QDebug>
+#include <QToolBar>
+#include <QTreeView>
+#include <QVBoxLayout>
 
+#include <algorithm>
+#include <fstream>
 #include <iostream>
 #include <sstream>
-#include <fstream>
-#include <algorithm>
 
+#include "anitomy/anitomy.h"
 #include "pugixml.hpp"
-#include "anitomy/anitomy.h"
 
 /* This file is very, very similar to the anime list page.
  *
  * It differs from Taiga in that it uses tabs instead of
  * those "groups", but those are custom painted and a pain in the ass to
  * maintain over multiple platforms.
-*/
+ */
 
 TorrentsPageListSortFilter::TorrentsPageListSortFilter(QObject* parent) : QSortFilterProxyModel(parent) {
 }
@@ -86,14 +86,19 @@
 
 void TorrentsPageListModel::ParseFeedDescription(const std::string& description, Torrent& torrent) {
 	/* Parse description... */
-	enum class Keys { SIZE, AUTHORIZED, SUBMITTER, COMMENT };
+	enum class Keys {
+		SIZE,
+		AUTHORIZED,
+		SUBMITTER,
+		COMMENT
+	};
 
 	const std::unordered_map<std::string, Keys> KeyMap = {
-		{"Size", Keys::SIZE},
-		{"Authorized", Keys::AUTHORIZED},
-		{"Submitter", Keys::SUBMITTER},
-		{"Comment", Keys::COMMENT}
-	};
+	    {"Size",       Keys::SIZE      },
+	    {"Authorized", Keys::AUTHORIZED},
+	    {"Submitter",  Keys::SUBMITTER },
+	    {"Comment",    Keys::COMMENT   }
+    };
 
 	/* Parse size from description */
 	std::istringstream descstream(description);
@@ -107,18 +112,13 @@
 		const std::string value = line.substr(line.find_first_not_of(": ", pos));
 
 		switch (KeyMap.at(key)) {
-			case Keys::COMMENT:
-				torrent.SetDescription(value);
-				break;
-			case Keys::SIZE:
-				torrent.SetSize(Strings::HumanReadableSizeToBytes(value));
-				break;
+			case Keys::COMMENT: torrent.SetDescription(value); break;
+			case Keys::SIZE: torrent.SetSize(Strings::HumanReadableSizeToBytes(value)); break;
 			case Keys::AUTHORIZED:
 				if (torrent.GetGroup().empty() && value != "N/A")
 					torrent.SetGroup(value);
 				break;
-			default:
-				break;
+			default: break;
 		}
 	}
 }
@@ -151,7 +151,8 @@
 
 			/* todo: patch Anitomy so that it doesn't use wide strings */
 			torrent.SetTitle(Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle)));
-			torrent.SetEpisode(Strings::RemoveLeadingChars(Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)), '0'));
+			torrent.SetEpisode(
+			    Strings::RemoveLeadingChars(Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)), '0'));
 			torrent.SetGroup(Strings::ToUtf8String(elements.get(anitomy::kElementReleaseGroup)));
 			torrent.SetResolution(Strings::ToUtf8String(elements.get(anitomy::kElementVideoResolution)));
 		}
@@ -229,8 +230,7 @@
 
 	if (index.column() == 0) {
 		switch (role) {
-			case Qt::EditRole:
-				return false;
+			case Qt::EditRole: return false;
 			case Qt::CheckStateRole:
 				item.SetChecked(value.toBool());
 				emit dataChanged(index, index);
@@ -269,7 +269,7 @@
 				case TL_EPISODE: return Strings::ToInt(item.GetEpisode(), -1);
 				/* We have to use this to work around some stupid
 				 * "conversion ambiguous" error on Linux
-				*/
+				 */
 				case TL_SIZE: return QVariant::fromValue(item.GetSize());
 				default: return data(index, Qt::DisplayRole);
 			}
@@ -331,28 +331,22 @@
 		{
 			/* this needs to be stored somewhere to replicate Taiga's
 			   "timer" feature */
-			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("&Check new torrents"), [this] {
-				Refresh();
-			});
+			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("&Check new torrents"),
+			                   [this] { Refresh(); });
 		}
 
 		toolbar->addSeparator();
 
 		{
-			toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"), [this] {
-				DownloadSelection();
-			});
+			toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"),
+			                   [this] { DownloadSelection(); });
 		}
 
-		{
-			toolbar->addAction(QIcon(":/icons/16x16/cross-button.png"), tr("&Discard all"));
-		}
+		{ toolbar->addAction(QIcon(":/icons/16x16/cross-button.png"), tr("&Discard all")); }
 
 		toolbar->addSeparator();
 
-		{
-			toolbar->addAction(QIcon(":/icons/16x16/gear.png"), tr("&Settings"));
-		}
+		{ toolbar->addAction(QIcon(":/icons/16x16/gear.png"), tr("&Settings")); }
 
 		layout->addWidget(toolbar);
 	}
@@ -387,16 +381,16 @@
 		}
 
 		// set column sizes
-		treeview->setColumnWidth(TorrentsPageListModel::TL_TITLE,       240);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_EPISODE,     60);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_GROUP,       100);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_SIZE,        70);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_RESOLUTION,  100);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_SEEDERS,     20);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_LEECHERS,    20);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_DOWNLOADS,   20);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_TITLE, 240);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_EPISODE, 60);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_GROUP, 100);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_SIZE, 70);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_RESOLUTION, 100);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_SEEDERS, 20);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_LEECHERS, 20);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_DOWNLOADS, 20);
 		treeview->setColumnWidth(TorrentsPageListModel::TL_DESCRIPTION, 200);
-		treeview->setColumnWidth(TorrentsPageListModel::TL_FILENAME,    200);
+		treeview->setColumnWidth(TorrentsPageListModel::TL_FILENAME, 200);
 		treeview->setColumnWidth(TorrentsPageListModel::TL_RELEASEDATE, 190);
 
 		treeview->header()->setStretchLastSection(false);
@@ -423,7 +417,7 @@
 	connect(thread, &HTTP::GetThread::ReceivedData, this, [&](const QByteArray& ba) {
 		/* This is to make sure we aren't in a different thread
 		 * messing around with GUI stuff
-		*/
+		 */
 		treeview->setUpdatesEnabled(false);
 		model->ParseTorrentList(ba);
 		treeview->setUpdatesEnabled(true);
--- a/src/gui/theme.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/theme.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,10 +1,10 @@
 #include "core/config.h"
 #include "core/session.h"
 #include <QApplication>
+#include <QDebug>
 #include <QFile>
-#include <QDebug>
+#include <QStyleFactory>
 #include <QTextStream>
-#include <QStyleFactory>
 #ifdef MACOSX
 #	include "sys/osx/dark_theme.h"
 #elif WIN32
@@ -20,7 +20,7 @@
  *   2. Some widgets, i.e. QTabWidget, QTabBar, etc., just completely IGNORE the QPalette setting
  *      on different platforms and the only way to fix it is by using Fusion
  *   3. Windows dark mode support in Qt 6.5 (with Fusion) is completely unavoidable
-*/
+ */
 
 namespace Theme {
 
@@ -115,7 +115,7 @@
 			qApp->setPalette(pal);
 
 #ifdef WIN32
-			qApp->setStyleSheet([]{
+			qApp->setStyleSheet([] {
 				QFile f(":/dark.qss");
 				if (!f.exists())
 					return QStringLiteral("");
@@ -142,12 +142,8 @@
 
 void Theme::SetTheme(Themes theme) {
 	switch (theme) {
-		case Themes::LIGHT:
-			SetToLightTheme();
-			break;
-		case Themes::DARK:
-			SetToDarkTheme();
-			break;
+		case Themes::LIGHT: SetToLightTheme(); break;
+		case Themes::DARK: SetToDarkTheme(); break;
 		case Themes::OS:
 			if (GetCurrentOSTheme() == Themes::LIGHT)
 				SetToLightTheme();
@@ -162,4 +158,4 @@
 	Theme::SetTheme(theme);
 }
 
-} // namespace DarkTheme
+} // namespace Theme
--- a/src/gui/translate/anime.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/translate/anime.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -61,16 +61,16 @@
 	switch (service) {
 		case Anime::Services::ANILIST: return "AniList";
 		default:
-		case Anime::Services::NONE:    return "None";
+		case Anime::Services::NONE: return "None";
 	}
 }
 
 std::string ToString(const Anime::TitleLanguage language) {
 	switch (language) {
-		case Anime::TitleLanguage::NATIVE:  return "Native";
+		case Anime::TitleLanguage::NATIVE: return "Native";
 		case Anime::TitleLanguage::ENGLISH: return "English";
 		default:
-		case Anime::TitleLanguage::ROMAJI:  return "Romaji";
+		case Anime::TitleLanguage::ROMAJI: return "Romaji";
 	}
 }
 
@@ -87,12 +87,12 @@
 
 Anime::ListStatus ToListStatus(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::ListStatus> map = {
-	    {"Currently watching", Anime::ListStatus::CURRENT},
-	    {"Plan to watch", Anime::ListStatus::PLANNING},
-	    {"Completed", Anime::ListStatus::COMPLETED},
-	    {"Dropped", Anime::ListStatus::DROPPED},
-	    {"On hold", Anime::ListStatus::PAUSED}
-	};
+	    {"Currently watching", Anime::ListStatus::CURRENT  },
+	    {"Plan to watch",      Anime::ListStatus::PLANNING },
+	    {"Completed",          Anime::ListStatus::COMPLETED},
+	    {"Dropped",            Anime::ListStatus::DROPPED  },
+	    {"On hold",            Anime::ListStatus::PAUSED   }
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::ListStatus::NOT_IN_LIST;
@@ -101,12 +101,12 @@
 
 Anime::SeriesStatus ToSeriesStatus(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::SeriesStatus> map = {
-	    {"Currently airing", Anime::SeriesStatus::RELEASING},
-	    {"Finished airing", Anime::SeriesStatus::FINISHED},
-	    {"Not yet aired", Anime::SeriesStatus::NOT_YET_RELEASED},
-	    {"Cancelled", Anime::SeriesStatus::CANCELLED},
-	    {"On hiatus", Anime::SeriesStatus::HIATUS}
-	};
+	    {"Currently airing", Anime::SeriesStatus::RELEASING       },
+	    {"Finished airing",  Anime::SeriesStatus::FINISHED        },
+	    {"Not yet aired",    Anime::SeriesStatus::NOT_YET_RELEASED},
+	    {"Cancelled",        Anime::SeriesStatus::CANCELLED       },
+	    {"On hiatus",        Anime::SeriesStatus::HIATUS          }
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::SeriesStatus::UNKNOWN;
@@ -117,9 +117,9 @@
 	static const std::unordered_map<std::string, Anime::SeriesSeason> map = {
 	    {"Winter", Anime::SeriesSeason::WINTER},
 	    {"Summer", Anime::SeriesSeason::SUMMER},
-	    {"Fall", Anime::SeriesSeason::FALL},
+	    {"Fall",   Anime::SeriesSeason::FALL  },
 	    {"Spring", Anime::SeriesSeason::SPRING}
-	};
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::SeriesSeason::UNKNOWN;
@@ -128,14 +128,14 @@
 
 Anime::SeriesFormat ToSeriesFormat(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::SeriesFormat> map = {
-	    {"TV", Anime::SeriesFormat::TV},
-	    {"TV short", Anime::SeriesFormat::TV_SHORT},
-	    {"OVA", Anime::SeriesFormat::OVA},
-	    {"Movie", Anime::SeriesFormat::MOVIE},
-	    {"Special", Anime::SeriesFormat::SPECIAL},
-	    {"ONA", Anime::SeriesFormat::ONA},
-	    {"Music", Anime::SeriesFormat::MUSIC}
-	};
+	    {"TV",       Anime::SeriesFormat::TV      },
+        {"TV short", Anime::SeriesFormat::TV_SHORT},
+	    {"OVA",      Anime::SeriesFormat::OVA     },
+        {"Movie",    Anime::SeriesFormat::MOVIE   },
+	    {"Special",  Anime::SeriesFormat::SPECIAL },
+        {"ONA",      Anime::SeriesFormat::ONA     },
+	    {"Music",    Anime::SeriesFormat::MUSIC   }
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::SeriesFormat::UNKNOWN;
@@ -145,7 +145,7 @@
 Anime::Services ToService(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::Services> map = {
 	    {"AniList", Anime::Services::ANILIST}
-	};
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::Services::NONE;
@@ -154,10 +154,10 @@
 
 Anime::TitleLanguage ToLanguage(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::TitleLanguage> map = {
-	    {"Romaji", Anime::TitleLanguage::ROMAJI},
-	    {"Native", Anime::TitleLanguage::NATIVE},
+	    {"Romaji",  Anime::TitleLanguage::ROMAJI },
+	    {"Native",  Anime::TitleLanguage::NATIVE },
 	    {"English", Anime::TitleLanguage::ENGLISH}
-	};
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::TitleLanguage::ROMAJI;
@@ -166,12 +166,12 @@
 
 Anime::ScoreFormat ToScoreFormat(const std::string& str) {
 	static const std::unordered_map<std::string, Anime::ScoreFormat> map = {
-	    {"POINT_3", Anime::ScoreFormat::POINT_3},
-	    {"POINT_5", Anime::ScoreFormat::POINT_5},
-	    {"POINT_10", Anime::ScoreFormat::POINT_10},
+	    {"POINT_3",          Anime::ScoreFormat::POINT_3         },
+	    {"POINT_5",          Anime::ScoreFormat::POINT_5         },
+	    {"POINT_10",         Anime::ScoreFormat::POINT_10        },
 	    {"POINT_10_DECIMAL", Anime::ScoreFormat::POINT_10_DECIMAL},
-	    {"POINT_100", Anime::ScoreFormat::POINT_100}
-	};
+	    {"POINT_100",        Anime::ScoreFormat::POINT_100       }
+    };
 
 	if (map.find(str) == map.end())
 		return Anime::ScoreFormat::POINT_100;
@@ -233,16 +233,16 @@
 	switch (service) {
 		case Anime::Services::ANILIST: return Strings::ToUtf8String(QCoreApplication::tr("AniList"));
 		default:
-		case Anime::Services::NONE:    return Strings::ToUtf8String(QCoreApplication::tr("None"));
+		case Anime::Services::NONE: return Strings::ToUtf8String(QCoreApplication::tr("None"));
 	}
 }
 
 std::string ToLocalString(const Anime::TitleLanguage language) {
 	switch (language) {
-		case Anime::TitleLanguage::NATIVE:  return Strings::ToUtf8String(QCoreApplication::tr("Native"));
+		case Anime::TitleLanguage::NATIVE: return Strings::ToUtf8String(QCoreApplication::tr("Native"));
 		case Anime::TitleLanguage::ENGLISH: return Strings::ToUtf8String(QCoreApplication::tr("English"));
 		default:
-		case Anime::TitleLanguage::ROMAJI:  return Strings::ToUtf8String(QCoreApplication::tr("Romaji"));
+		case Anime::TitleLanguage::ROMAJI: return Strings::ToUtf8String(QCoreApplication::tr("Romaji"));
 	}
 }
 
@@ -251,7 +251,8 @@
 		case Anime::ScoreFormat::POINT_3: return Strings::ToUtf8String(QCoreApplication::tr("3-point"));
 		case Anime::ScoreFormat::POINT_5: return Strings::ToUtf8String(QCoreApplication::tr("5-point"));
 		case Anime::ScoreFormat::POINT_10: return Strings::ToUtf8String(QCoreApplication::tr("10-point"));
-		case Anime::ScoreFormat::POINT_10_DECIMAL: return Strings::ToUtf8String(QCoreApplication::tr("10-point (Decimal)"));
+		case Anime::ScoreFormat::POINT_10_DECIMAL:
+			return Strings::ToUtf8String(QCoreApplication::tr("10-point (Decimal)"));
 		default:
 		case Anime::ScoreFormat::POINT_100: return Strings::ToUtf8String(QCoreApplication::tr("100-point"));
 	}
--- a/src/gui/translate/config.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/translate/config.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -7,10 +7,10 @@
 
 Themes ToTheme(const std::string& theme) {
 	const std::unordered_map<std::string, Themes> map = {
-		{"Default", Themes::OS   },
-		{"Light",   Themes::LIGHT},
-		{"Dark",    Themes::DARK }
-	};
+	    {"Default", Themes::OS   },
+        {"Light",   Themes::LIGHT},
+        {"Dark",    Themes::DARK }
+    };
 
 	if (map.find(theme) == map.end())
 		return Themes::OS;
@@ -20,10 +20,10 @@
 std::string ToString(const Themes& theme) {
 	switch (theme) {
 		default:
-		case Themes::OS:    return "Default";
+		case Themes::OS: return "Default";
 		case Themes::LIGHT: return "Light";
-		case Themes::DARK:  return "Dark";
+		case Themes::DARK: return "Dark";
 	}
 }
 
-}
+} // namespace Translate
--- a/src/gui/widgets/anime_button.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/anime_button.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,16 +1,16 @@
 #include "gui/widgets/anime_button.h"
 
 #include "core/anime_db.h"
+#include "core/session.h"
 #include "core/strings.h"
-#include "core/session.h"
-#include "gui/widgets/text.h"
 #include "gui/widgets/elided_label.h"
 #include "gui/widgets/poster.h"
+#include "gui/widgets/text.h"
 
-#include <QWidget>
+#include <QDate>
+#include <QHBoxLayout>
 #include <QVBoxLayout>
-#include <QHBoxLayout>
-#include <QDate>
+#include <QWidget>
 
 /* This widget is only used on the Seasons page. */
 
@@ -54,7 +54,8 @@
 		}
 		misc_layout->addWidget(_title);
 
-		_info = new TextWidgets::LabelledParagraph(tr("Aired:\nEpisodes:\nGenres:\nProducers:\nScore:\nPopularity:"), "\n\n\n\n\n", misc_section);
+		_info = new TextWidgets::LabelledParagraph(tr("Aired:\nEpisodes:\nGenres:\nProducers:\nScore:\nPopularity:"),
+		                                           "\n\n\n\n\n", misc_section);
 		{
 			QFont fnt(_info->GetLabels()->font());
 			fnt.setWeight(QFont::Bold);
@@ -90,14 +91,10 @@
 
 	{
 		const QLocale& locale = session.config.locale.GetLocale();
-		_info->GetParagraph()->SetText(
-			locale.toString(anime.GetAirDate().GetAsQDate(), "dd MMM yyyy") + "\n" +
-			QString::number(anime.GetEpisodes()) + "\n" +
-			Strings::ToQString(Strings::Implode(anime.GetGenres(), ", ")) + "\n" +
-			"...\n" +
-			QString::number(anime.GetAudienceScore()) + "%\n" +
-			"..."
-		);
+		_info->GetParagraph()->SetText(locale.toString(anime.GetAirDate().GetAsQDate(), "dd MMM yyyy") + "\n" +
+		                               QString::number(anime.GetEpisodes()) + "\n" +
+		                               Strings::ToQString(Strings::Implode(anime.GetGenres(), ", ")) + "\n" + "...\n" +
+		                               QString::number(anime.GetAudienceScore()) + "%\n" + "...");
 	}
 
 	{
--- a/src/gui/widgets/anime_info.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/anime_info.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -12,7 +12,8 @@
 	_title.reset(new TextWidgets::OneLineSection(tr("Alternative titles"), "", this));
 	layout->addWidget(_title.get());
 
-	_details.reset(new TextWidgets::LabelledSection(tr("Details"), tr("Type:\nEpisodes:\nStatus:\nSeason:\nGenres:\nScore:"), "", this));
+	_details.reset(new TextWidgets::LabelledSection(
+	    tr("Details"), tr("Type:\nEpisodes:\nStatus:\nSeason:\nGenres:\nScore:"), "", this));
 	layout->addWidget(_details.get());
 
 	_synopsis.reset(new TextWidgets::SelectableSection(tr("Synopsis"), "", this));
@@ -36,13 +37,13 @@
 	/* we have to convert ALL of these strings to
 	 * QString because QTextStream sucks and assumes
 	 * Latin1 (on Windows?)
-	*/
+	 */
 	const auto genres = anime.GetGenres();
 	details_data_s << Strings::ToQString(Translate::ToLocalString(anime.GetFormat())) << "\n"
 	               << anime.GetEpisodes() << "\n"
 	               << Strings::ToQString(Translate::ToLocalString(anime.GetAiringStatus())) << "\n"
 	               << Strings::ToQString(Translate::ToLocalString(anime.GetSeason())) << " "
-	                   << anime.GetAirDate().GetYear().value_or(2000) << "\n"
+	               << anime.GetAirDate().GetYear().value_or(2000) << "\n"
 	               << Strings::ToQString((genres.size() > 1) ? Strings::Implode(genres, ", ") : "-") << "\n"
 	               << anime.GetAudienceScore() << "%";
 	_details->GetParagraph()->SetText(details_data);
--- a/src/gui/widgets/elided_label.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/elided_label.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,34 +1,34 @@
 /*
-* Copyright (C) 2016 The Qt Company Ltd.
-* Contact: https://www.qt.io/licensing/
-*
-* This file is part of the QtCore module of the Qt Toolkit.
-*
-* "Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*   * Redistributions of source code must retain the above copyright
-*     notice, this list of conditions and the following disclaimer.
-*   * Redistributions in binary form must reproduce the above copyright
-*     notice, this list of conditions and the following disclaimer in
-*     the documentation and/or other materials provided with the
-*     distribution.
-*   * Neither the name of The Qt Company Ltd nor the names of its
-*     contributors may be used to endorse or promote products derived
-*     from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-*/
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Contact: https://www.qt.io/licensing/
+ *
+ * This file is part of the QtCore module of the Qt Toolkit.
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of The Qt Company Ltd nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
 
 #include "gui/widgets/elided_label.h"
 
@@ -36,8 +36,7 @@
 #include <QSizePolicy>
 #include <QTextLayout>
 
-ElidedLabel::ElidedLabel(const QString& text, QWidget* parent)
-	: QFrame(parent), content(text) {
+ElidedLabel::ElidedLabel(const QString& text, QWidget* parent) : QFrame(parent), content(text) {
 	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 }
 
@@ -46,7 +45,7 @@
 	update();
 }
 
-void ElidedLabel::paintEvent(QPaintEvent *event) {
+void ElidedLabel::paintEvent(QPaintEvent* event) {
 	QFrame::paintEvent(event);
 
 	QPainter painter(this);
--- a/src/gui/widgets/poster.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/poster.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -49,8 +49,7 @@
 
 	if (clickable) {
 		label->disconnect();
-		connect(label, &ClickableLabel::clicked, this,
-				[this] { QDesktopServices::openUrl(service_url); });
+		connect(label, &ClickableLabel::clicked, this, [this] { QDesktopServices::openUrl(service_url); });
 	}
 }
 
@@ -60,8 +59,7 @@
 	if (clickable && !service_url.isEmpty()) {
 		setCursor(Qt::PointingHandCursor);
 		label->disconnect();
-		connect(label, &ClickableLabel::clicked, this,
-				[this] { QDesktopServices::openUrl(service_url); });
+		connect(label, &ClickableLabel::clicked, this, [this] { QDesktopServices::openUrl(service_url); });
 	} else {
 		setCursor(Qt::ArrowCursor);
 		label->disconnect();
--- a/src/gui/widgets/sidebar.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/sidebar.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -69,7 +69,7 @@
 int SideBar::AddSeparatorsToIndex(int index) {
 	int separators = 0, items = 0;
 
-	for (; items <= index; ) {
+	for (; items <= index;) {
 		if (IndexIsSeparator(indexFromItem(item(items + separators)))) {
 			separators++;
 		} else {
--- a/src/gui/widgets/text.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/widgets/text.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -42,7 +42,7 @@
  *
  * eventually I'll have to implement this as a QScrollArea, just in case
  * some random text decides to overflow or something.
-*/
+ */
 Paragraph::Paragraph(const QString& text, QWidget* parent) : QLabel(text, parent) {
 	setTextInteractionFlags(Qt::TextBrowserInteraction);
 	setFrameShape(QFrame::NoFrame);
@@ -142,7 +142,8 @@
 	return paragraph;
 }
 
-LabelledSection::LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent) : QWidget(parent) {
+LabelledSection::LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent)
+    : QWidget(parent) {
 	QVBoxLayout* layout = new QVBoxLayout(this);
 
 	header = new Header(title, this);
--- a/src/gui/window.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/gui/window.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -3,7 +3,6 @@
 #include "core/config.h"
 #include "core/session.h"
 #include "core/strings.h"
-#include "gui/theme.h"
 #include "gui/dialog/about.h"
 #include "gui/dialog/settings.h"
 #include "gui/pages/anime_list.h"
@@ -13,10 +12,11 @@
 #include "gui/pages/seasons.h"
 #include "gui/pages/statistics.h"
 #include "gui/pages/torrents.h"
+#include "gui/theme.h"
 #include "gui/widgets/sidebar.h"
+#include "library/library.h"
 #include "services/services.h"
 #include "track/media.h"
-#include "library/library.h"
 
 #include "anitomy/anitomy.h"
 
@@ -72,7 +72,7 @@
 	/* This thread will be destroyed on
 	 * close of the program OR on the destruction
 	 * of MainWindow
-	*/
+	 */
 	thread.reset(new PlayingThread(this));
 
 	connect(thread.get(), &PlayingThread::Done, this, [page](const std::vector<std::string>& files) {
@@ -82,7 +82,6 @@
 
 			const auto& elements = anitomy.elements();
 			const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));
-			std::cout << title << std::endl;
 
 			int id = Anime::db.GetAnimeFromTitle(title);
 			if (id <= 0)
@@ -171,22 +170,20 @@
 		}
 
 		{
-			menu->addAction(tr("&Scan available episodes"), []{
-				Library::SearchLibraryFolders();
-			});
+			menu->addAction(tr("&Scan available episodes"), [] { Library::SearchLibraryFolders(); });
 		}
 
 		menu->addSeparator();
 
-//		{
-//			QAction* action = menu->addAction(tr("Play &next episode"));
-//			action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
-//		}
-//
-//		{
-//			QAction* action = menu->addAction(tr("Play &random episode"));
-//			action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
-//		}
+		//		{
+		//			QAction* action = menu->addAction(tr("Play &next episode"));
+		//			action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
+		//		}
+		//
+		//		{
+		//			QAction* action = menu->addAction(tr("Play &random episode"));
+		//			action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
+		//		}
 
 		menu->addSeparator();
 
@@ -203,74 +200,73 @@
 			{
 				sync_action = menu->addAction(tr("Synchronize &list"));
 
-				connect(sync_action, &QAction::triggered, this, [this, sync_action]{
-					AsyncSynchronize(sync_action, stack.get());
-				});
+				connect(sync_action, &QAction::triggered, this,
+				        [this, sync_action] { AsyncSynchronize(sync_action, stack.get()); });
 
 				sync_action->setIcon(QIcon(":/icons/24x24/arrow-circle-double-135.png"));
 				sync_action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
 			}
 
-//			menu->addSeparator();
-//
-//			{
-//				/* AniList */
-//				QMenu* submenu = menu->addMenu(tr("&AniList"));
-//				QAction* action = submenu->addAction(tr("Go to my &profile"));
-//				action = submenu->addAction(tr("Go to my &stats"));
-//			}
-//
-//			{
-//				/* Kitsu */
-//				QMenu* submenu = menu->addMenu(tr("&Kitsu"));
-//				QAction* action = submenu->addAction(tr("Go to my &feed"));
-//				action = submenu->addAction(tr("Go to my &library"));
-//				action = submenu->addAction(tr("Go to my &profile"));
-//			}
-//			{
-//				QMenu* submenu = menu->addMenu(tr("&MyAnimeList"));
-//				QAction* action = submenu->addAction(tr("Go to my p&anel"));
-//				action = submenu->addAction(tr("Go to my &profile"));
-//				action = submenu->addAction(tr("Go to my &history"));
-//			}
+			//			menu->addSeparator();
+			//
+			//			{
+			//				/* AniList */
+			//				QMenu* submenu = menu->addMenu(tr("&AniList"));
+			//				QAction* action = submenu->addAction(tr("Go to my &profile"));
+			//				action = submenu->addAction(tr("Go to my &stats"));
+			//			}
+			//
+			//			{
+			//				/* Kitsu */
+			//				QMenu* submenu = menu->addMenu(tr("&Kitsu"));
+			//				QAction* action = submenu->addAction(tr("Go to my &feed"));
+			//				action = submenu->addAction(tr("Go to my &library"));
+			//				action = submenu->addAction(tr("Go to my &profile"));
+			//			}
+			//			{
+			//				QMenu* submenu = menu->addMenu(tr("&MyAnimeList"));
+			//				QAction* action = submenu->addAction(tr("Go to my p&anel"));
+			//				action = submenu->addAction(tr("Go to my &profile"));
+			//				action = submenu->addAction(tr("Go to my &history"));
+			//			}
 		}
 	}
 
 	{
 		/* Tools */
 		QMenu* menu = menubar->addMenu(tr("&Tools"));
-//		{
-//			/* Export anime list */
-//			QMenu* submenu = menu->addMenu(tr("&Export anime list"));
-//
-//			{
-//				/* Markdown export */
-//				QAction* action = submenu->addAction(tr("Export as &Markdown..."));
-//			}
-//
-//			{
-//				/* XML export */
-//				QAction* action = submenu->addAction(tr("Export as MyAnimeList &XML..."));
-//			}
-//		}
-//		menu->addSeparator();
-//
-//		{
-//			QAction* action = menu->addAction(tr("Enable anime &recognition"));
-//			action->setCheckable(true);
-//		}
-//
-//		{
-//			QAction* action = menu->addAction(tr("Enable auto &sharing"));
-//			action->setCheckable(true);
-//		}
-//
-//		{
-//			QAction* action = menu->addAction(tr("Enable &auto synchronization"));
-//			action->setCheckable(true);
-//		}
-//
-//		menu->addSeparator();
+		//		{
+		//			/* Export anime list */
+		//			QMenu* submenu = menu->addMenu(tr("&Export anime list"));
+		//
+		//			{
+		//				/* Markdown export */
+		//				QAction* action = submenu->addAction(tr("Export as &Markdown..."));
+		//			}
+		//
+		//			{
+		//				/* XML export */
+		//				QAction* action = submenu->addAction(tr("Export as MyAnimeList &XML..."));
+		//			}
+		//		}
+		//		menu->addSeparator();
+		//
+		//		{
+		//			QAction* action = menu->addAction(tr("Enable anime &recognition"));
+		//			action->setCheckable(true);
+		//		}
+		//
+		//		{
+		//			QAction* action = menu->addAction(tr("Enable auto &sharing"));
+		//			action->setCheckable(true);
+		//		}
+		//
+		//		{
+		//			QAction* action = menu->addAction(tr("Enable &auto synchronization"));
+		//			action->setCheckable(true);
+		//		}
+		//
+		//		menu->addSeparator();
 
 		{
 			QAction* action = menu->addAction(tr("&Settings"), [this] {
@@ -294,58 +290,44 @@
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("&Now Playing")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(0);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(0); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("&Anime List")));
 				action->setCheckable(true);
 				action->setChecked(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(1);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(1); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("&History")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(2);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(2); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("&Statistics")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(3);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(3); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("S&earch")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(4);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(4); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("Se&asons")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(5);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(5); });
 			}
 
 			{
 				QAction* action = pages_group->addAction(menu->addAction(tr("&Torrents")));
 				action->setCheckable(true);
-				connect(action, &QAction::toggled, this, [this] {
-					sidebar->SetCurrentItem(6);
-				});
+				connect(action, &QAction::toggled, this, [this] { sidebar->SetCurrentItem(6); });
 			}
 
 			/* pain in my ass */
@@ -364,9 +346,9 @@
 
 		menu->addSeparator();
 
-//		{
-//			QAction* action = menu->addAction(tr("Show sidebar"));
-//		}
+		//		{
+		//			QAction* action = menu->addAction(tr("Show sidebar"));
+		//		}
 	}
 
 	{
@@ -389,7 +371,7 @@
 	}
 	/* QMainWindow will delete the old one for us,
 	 * according to the docs
-	*/
+	 */
 	setMenuBar(menubar);
 
 	/* Toolbar */
@@ -410,9 +392,7 @@
 
 		{
 			QToolButton* button = new QToolButton(toolbar);
-			{
-				button->setMenu(folder_menu);
-			}
+			{ button->setMenu(folder_menu); }
 			button->setIcon(QIcon(":/icons/24x24/folder-open.png"));
 			button->setPopupMode(QToolButton::InstantPopup);
 			toolbar->addWidget(button);
@@ -424,29 +404,18 @@
 			{
 				/* links */
 				QMenu* menu = new QMenu(button);
-				menu->addAction("Hibari", []{
-					QDesktopServices::openUrl(QUrl("https://hb.wopian.me/"));
-				});
-				menu->addAction("MALgraph", []{
-					QDesktopServices::openUrl(QUrl("https://graph.anime.plus/"));
-				});
+				menu->addAction("Hibari", [] { QDesktopServices::openUrl(QUrl("https://hb.wopian.me/")); });
+				menu->addAction("MALgraph", [] { QDesktopServices::openUrl(QUrl("https://graph.anime.plus/")); });
 				menu->addSeparator();
-				menu->addAction("AniChart", []{
-					QDesktopServices::openUrl(QUrl("https://anichart.net/airing"));
-				});
-				menu->addAction("Monthly.moe", []{
-					QDesktopServices::openUrl(QUrl("https://www.monthly.moe/weekly"));
-				});
-				menu->addAction("Senpai Anime Charts", []{
-					QDesktopServices::openUrl(QUrl("https://www.senpai.moe/?mode=calendar"));
-				});
+				menu->addAction("AniChart", [] { QDesktopServices::openUrl(QUrl("https://anichart.net/airing")); });
+				menu->addAction("Monthly.moe",
+				                [] { QDesktopServices::openUrl(QUrl("https://www.monthly.moe/weekly")); });
+				menu->addAction("Senpai Anime Charts",
+				                [] { QDesktopServices::openUrl(QUrl("https://www.senpai.moe/?mode=calendar")); });
 				menu->addSeparator();
-				menu->addAction("Anime Streaming Search Engine", []{
-					QDesktopServices::openUrl(QUrl("https://because.moe/"));
-				});
-				menu->addAction("The Fansub Database", []{
-					QDesktopServices::openUrl(QUrl("https://fansubdb.com"));
-				});
+				menu->addAction("Anime Streaming Search Engine",
+				                [] { QDesktopServices::openUrl(QUrl("https://because.moe/")); });
+				menu->addAction("The Fansub Database", [] { QDesktopServices::openUrl(QUrl("https://fansubdb.com")); });
 
 				button->setMenu(menu);
 			}
@@ -477,14 +446,13 @@
 	std::size_t i = 0;
 	for (const auto& path : session.config.library.paths) {
 		const QString folder = Strings::ToQString(path);
-		QAction* action = folder_menu->addAction(folder, [folder]{
-			QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
-		});
+		QAction* action =
+		    folder_menu->addAction(folder, [folder] { QDesktopServices::openUrl(QUrl::fromLocalFile(folder)); });
 
 		if (i < 9) {
 			/* Qt::Key_1 is equivalent to 1 in ASCII, so we can use the same
 			 * stupid `'0' + i` trick here
-			*/
+			 */
 			action->setShortcut(QKeySequence(Qt::ALT | static_cast<Qt::Modifier>(Qt::Key_1 + i)));
 		} else if (i == 9) {
 			action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_0));
@@ -496,11 +464,10 @@
 	folder_menu->addSeparator();
 
 	{
-		folder_menu->addAction(tr("&Add new folder..."), [this]{
-			const QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
-																  QDir::homePath(),
-																  QFileDialog::ShowDirsOnly
-																  | QFileDialog::DontResolveSymlinks);
+		folder_menu->addAction(tr("&Add new folder..."), [this] {
+			const QString dir =
+			    QFileDialog::getExistingDirectory(this, tr("Open Directory"), QDir::homePath(),
+			                                      QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
 			if (dir.isEmpty())
 				return;
 			session.config.library.paths.insert(Strings::ToUtf8String(dir));
@@ -549,12 +516,9 @@
 	if (event) { /* is this really necessary */
 		switch (event->type()) {
 			// this event is send if a translator is loaded
-			case QEvent::LanguageChange:
-				RetranslateUI();
-				break;
+			case QEvent::LanguageChange: RetranslateUI(); break;
 
-			default:
-				break;
+			default: break;
 		}
 	}
 	QMainWindow::changeEvent(event);
--- a/src/library/library.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/library/library.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,13 +1,13 @@
 #include "library/library.h"
 #include "core/anime_db.h"
+#include "core/session.h"
 #include "core/strings.h"
-#include "core/session.h"
 
 #include "anitomy/anitomy.h"
 
 #include <filesystem>
+#include <string>
 #include <unordered_map>
-#include <string>
 
 #include <iostream>
 
@@ -46,4 +46,4 @@
 	}
 }
 
-}
+} // namespace Library
--- a/src/main.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/main.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,13 +1,13 @@
+#include "core/anime_db.h"
 #include "core/session.h"
-#include "core/anime_db.h"
 #include "core/strings.h"
+#include "gui/window.h"
 #include "services/anilist.h"
-#include "gui/window.h"
 
 #include <QApplication>
+#include <QLocale>
 #include <QStyleFactory>
 #include <QTranslator>
-#include <QLocale>
 
 #include <iostream>
 
--- a/src/services/anilist.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/services/anilist.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -8,8 +8,8 @@
 #include "core/strings.h"
 #include "gui/translate/anilist.h"
 
+#include <QByteArray>
 #include <QDate>
-#include <QByteArray>
 #include <QDesktopServices>
 #include <QInputDialog>
 #include <QLineEdit>
@@ -29,22 +29,22 @@
 constexpr int CLIENT_ID = 13706;
 
 class Account {
-	public:
-		int UserId() const { return session.config.auth.anilist.user_id; }
-		void SetUserId(const int id) { session.config.auth.anilist.user_id = id; }
+public:
+	int UserId() const { return session.config.auth.anilist.user_id; }
+	void SetUserId(const int id) { session.config.auth.anilist.user_id = id; }
 
-		std::string AuthToken() const { return session.config.auth.anilist.auth_token; }
-		void SetAuthToken(std::string const& auth_token) { session.config.auth.anilist.auth_token = auth_token; }
+	std::string AuthToken() const { return session.config.auth.anilist.auth_token; }
+	void SetAuthToken(std::string const& auth_token) { session.config.auth.anilist.auth_token = auth_token; }
 
-		bool Authenticated() const { return !AuthToken().empty(); }
-		bool IsValid() const { return UserId() && Authenticated(); }
+	bool Authenticated() const { return !AuthToken().empty(); }
+	bool IsValid() const { return UserId() && Authenticated(); }
 };
 
 static Account account;
 
 std::string SendRequest(std::string data) {
 	std::vector<std::string> headers = {"Authorization: Bearer " + account.AuthToken(), "Accept: application/json",
-										"Content-Type: application/json"};
+	                                    "Content-Type: application/json"};
 	return Strings::ToUtf8String(HTTP::Post("https://graphql.anilist.co", data, headers));
 }
 
@@ -63,7 +63,8 @@
 
 	if (ret.contains("/errors"_json_pointer) && ret.at("/errors"_json_pointer).is_array()) {
 		for (const auto& error : ret.at("/errors"_json_pointer))
-			std::cerr << "[AniList] Received an error in response: " << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl;
+			std::cerr << "[AniList] Received an error in response: "
+			          << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl;
 
 		return {};
 	}
@@ -73,12 +74,12 @@
 
 void ParseListStatus(std::string status, Anime::Anime& anime) {
 	static const std::unordered_map<std::string, Anime::ListStatus> map = {
-		{"CURRENT",   Anime::ListStatus::CURRENT  },
-		{"PLANNING",  Anime::ListStatus::PLANNING },
-		{"COMPLETED", Anime::ListStatus::COMPLETED},
-		{"DROPPED",   Anime::ListStatus::DROPPED  },
-		{"PAUSED",    Anime::ListStatus::PAUSED   }
-	};
+	    {"CURRENT",   Anime::ListStatus::CURRENT  },
+	    {"PLANNING",  Anime::ListStatus::PLANNING },
+	    {"COMPLETED", Anime::ListStatus::COMPLETED},
+	    {"DROPPED",   Anime::ListStatus::DROPPED  },
+	    {"PAUSED",    Anime::ListStatus::PAUSED   }
+    };
 
 	if (status == "REPEATING") {
 		anime.SetUserIsRewatching(true);
@@ -127,7 +128,8 @@
 	anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0));
 	anime.SetFormat(Translate::AniList::ToSeriesFormat(JSON::GetString<std::string>(json, "/format"_json_pointer, "")));
 
-	anime.SetAiringStatus(Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, "")));
+	anime.SetAiringStatus(
+	    Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, "")));
 
 	anime.SetAirDate(Date(json["/startDate"_json_pointer]));
 
@@ -179,54 +181,54 @@
 
 	/* NOTE: these really ought to be in the qrc file */
 	constexpr std::string_view query = "query ($id: Int) {\n"
-							  "  MediaListCollection (userId: $id, type: ANIME) {\n"
-							  "    lists {\n"
-							  "      name\n"
-							  "      entries {\n"
-							  "        score\n"
-							  "        notes\n"
-							  "        status\n"
-							  "        progress\n"
-							  "        startedAt {\n"
-							  "          year\n"
-							  "          month\n"
-							  "          day\n"
-							  "        }\n"
-							  "        completedAt {\n"
-							  "          year\n"
-							  "          month\n"
-							  "          day\n"
-							  "        }\n"
-							  "        updatedAt\n"
-							  "        media {\n"
-							  "          coverImage {\n"
-							  "            large\n"
-							  "          }\n"
-							  "          id\n"
-							  "          title {\n"
-							  "            romaji\n"
-							  "            english\n"
-							  "            native\n"
-							  "          }\n"
-							  "          format\n"
-							  "          status\n"
-							  "          averageScore\n"
-							  "          season\n"
-							  "          startDate {\n"
-							  "            year\n"
-							  "            month\n"
-							  "            day\n"
-							  "          }\n"
-							  "          genres\n"
-							  "          episodes\n"
-							  "          duration\n"
-							  "          synonyms\n"
-							  "          description(asHtml: false)\n"
-							  "        }\n"
-							  "      }\n"
-							  "    }\n"
-							  "  }\n"
-							  "}\n";
+	                                   "  MediaListCollection (userId: $id, type: ANIME) {\n"
+	                                   "    lists {\n"
+	                                   "      name\n"
+	                                   "      entries {\n"
+	                                   "        score\n"
+	                                   "        notes\n"
+	                                   "        status\n"
+	                                   "        progress\n"
+	                                   "        startedAt {\n"
+	                                   "          year\n"
+	                                   "          month\n"
+	                                   "          day\n"
+	                                   "        }\n"
+	                                   "        completedAt {\n"
+	                                   "          year\n"
+	                                   "          month\n"
+	                                   "          day\n"
+	                                   "        }\n"
+	                                   "        updatedAt\n"
+	                                   "        media {\n"
+	                                   "          coverImage {\n"
+	                                   "            large\n"
+	                                   "          }\n"
+	                                   "          id\n"
+	                                   "          title {\n"
+	                                   "            romaji\n"
+	                                   "            english\n"
+	                                   "            native\n"
+	                                   "          }\n"
+	                                   "          format\n"
+	                                   "          status\n"
+	                                   "          averageScore\n"
+	                                   "          season\n"
+	                                   "          startDate {\n"
+	                                   "            year\n"
+	                                   "            month\n"
+	                                   "            day\n"
+	                                   "          }\n"
+	                                   "          genres\n"
+	                                   "          episodes\n"
+	                                   "          duration\n"
+	                                   "          synonyms\n"
+	                                   "          description(asHtml: false)\n"
+	                                   "        }\n"
+	                                   "      }\n"
+	                                   "    }\n"
+	                                   "  }\n"
+	                                   "}\n";
 	// clang-format off
 	nlohmann::json json = {
 		{"query", query},
@@ -246,36 +248,35 @@
 
 /* return is a vector of anime ids */
 std::vector<int> Search(const std::string& search) {
-	constexpr std::string_view query =
-		"query ($search: String) {\n"
-		"  Page (page: 1, perPage: 50) {\n"
-		"    media (search: $search, type: ANIME) {\n"
-		"      coverImage {\n"
-		"        large\n"
-		"      }\n"
-		"      id\n"
-		"      title {\n"
-		"        romaji\n"
-		"        english\n"
-		"        native\n"
-		"      }\n"
-		"      format\n"
-		"      status\n"
-		"      averageScore\n"
-		"      season\n"
-		"      startDate {\n"
-		"        year\n"
-		"        month\n"
-		"        day\n"
-		"      }\n"
-		"      genres\n"
-		"      episodes\n"
-		"      duration\n"
-		"      synonyms\n"
-		"      description(asHtml: false)\n"
-		"    }\n"
-		"  }\n"
-		"}\n";
+	constexpr std::string_view query = "query ($search: String) {\n"
+	                                   "  Page (page: 1, perPage: 50) {\n"
+	                                   "    media (search: $search, type: ANIME) {\n"
+	                                   "      coverImage {\n"
+	                                   "        large\n"
+	                                   "      }\n"
+	                                   "      id\n"
+	                                   "      title {\n"
+	                                   "        romaji\n"
+	                                   "        english\n"
+	                                   "        native\n"
+	                                   "      }\n"
+	                                   "      format\n"
+	                                   "      status\n"
+	                                   "      averageScore\n"
+	                                   "      season\n"
+	                                   "      startDate {\n"
+	                                   "        year\n"
+	                                   "        month\n"
+	                                   "        day\n"
+	                                   "      }\n"
+	                                   "      genres\n"
+	                                   "      episodes\n"
+	                                   "      duration\n"
+	                                   "      synonyms\n"
+	                                   "      description(asHtml: false)\n"
+	                                   "    }\n"
+	                                   "  }\n"
+	                                   "}\n";
 
 	// clang-format off
 	nlohmann::json json = {
@@ -308,7 +309,7 @@
 	 * int progress,
 	 * int progressVolumes, // manga-specific.
 	 * int repeat, // rewatch
-	 * int priority,  
+	 * int priority,
 	 * bool private,
 	 * string notes,
 	 * bool hiddenFromStatusLists,
@@ -316,17 +317,19 @@
 	 * float[] advancedScores,
 	 * Date startedAt,
 	 * Date completedAt
-	**/
+	 **/
 	Anime::Anime& anime = Anime::db.items[id];
 	if (!anime.IsInUserList())
 		return 0;
 
 	constexpr std::string_view query =
-		"mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String, $start: FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n"
-		"  SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score, notes: $notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n"
-		"    id\n"
-		"  }\n"
-		"}\n";
+	    "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String, $start: "
+	    "FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n"
+	    "  SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score, notes: "
+	    "$notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n"
+	    "    id\n"
+	    "  }\n"
+	    "}\n";
 	// clang-format off
 	nlohmann::json json = {
 		{"query", query},
@@ -355,13 +358,13 @@
 
 bool AuthorizeUser() {
 	/* Prompt for PIN */
-	QDesktopServices::openUrl(
-		QUrl(Strings::ToQString("https://anilist.co/api/v2/oauth/authorize?client_id=" + Strings::ToUtf8String(CLIENT_ID) + "&response_type=token")));
+	QDesktopServices::openUrl(QUrl(Strings::ToQString("https://anilist.co/api/v2/oauth/authorize?client_id=" +
+	                                                  Strings::ToUtf8String(CLIENT_ID) + "&response_type=token")));
 
 	bool ok;
 	QString token = QInputDialog::getText(
-		0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal,
-		"", &ok);
+	    0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal,
+	    "", &ok);
 
 	if (!ok || token.isEmpty())
 		return false;
@@ -369,17 +372,17 @@
 	account.SetAuthToken(Strings::ToUtf8String(token));
 
 	constexpr std::string_view query = "query {\n"
-							  "  Viewer {\n"
-							  "    id\n"
-							  "    name\n"
-							  "    mediaListOptions {\n"
-							  "      scoreFormat\n" // this will be used... eventually
-							  "    }\n"
-							  "  }\n"
-							  "}\n";
+	                                   "  Viewer {\n"
+	                                   "    id\n"
+	                                   "    name\n"
+	                                   "    mediaListOptions {\n"
+	                                   "      scoreFormat\n" // this will be used... eventually
+	                                   "    }\n"
+	                                   "  }\n"
+	                                   "}\n";
 	nlohmann::json json = {
-		{"query", query}
-	};
+	    {"query", query}
+    };
 
 	auto ret = SendJSONRequest(json);
 
--- a/src/sys/glib/dark_theme.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/sys/glib/dark_theme.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,51 +1,72 @@
+#include <cstring>
 #include <gio/gio.h>
-#include <cstring>
 #include <string_view>
 
 namespace glib {
 
 bool IsInDarkTheme() {
-	bool success = false;
-
 	GSettings* settings = ::g_settings_new("org.gnome.desktop.interface");
 	if (!settings)
 		return false;
 
-	GVariant* val = ::g_settings_get_value(settings, "color-scheme");
-	if (!val)
-		return false;
+	{
+		GVariant* val = ::g_settings_get_value(settings, "color-scheme");
+		if (!val) {
+			::g_object_unref(settings);
+			return false;
+		}
 
-	const gchar* str;
-	::g_variant_get(val, "&s", &str); /* should not be freed */
-	if (!str) /* how */
-		return false;
+		const gchar* str = nullptr;
+		::g_variant_get(val, "&s", &str); /* should not be freed */
+		if (!str) {                       /* how */
+			::g_variant_unref(val);
+			::g_object_unref(settings);
+			return false;
+		}
 
-	success |= !std::strcmp(str, "prefer-dark");
-
-	::g_variant_unref(val);
+		bool success = !std::strcmp(str, "prefer-dark");
 
-	if (success) {
-		::g_object_unref(settings);
-		return success;
+		::g_variant_unref(val);
+
+		if (success)
+			return true;
 	}
 
-	GVariant* gtk_theme = ::g_settings_get_value(settings, "gtk-theme");
-	if (!gtk_theme)
-		return false;
+	{
+		GVariant* gtk_theme = ::g_settings_get_value(settings, "gtk-theme");
+		if (!gtk_theme) {
+			::g_object_unref(settings);
+			return false;
+		}
 
-	const gchar* gtk_theme_str;
-	::g_variant_get(gtk_theme, "&s", gtk_theme_str);
-	if (!gtk_theme_str)
-		return false;
+		const gchar* gtk_theme_str = nullptr;
+		::g_variant_get(gtk_theme, "&s", gtk_theme_str);
+		if (!gtk_theme_str) {
+			::g_variant_unref(gtk_theme);
+			::g_object_unref(settings);
+			return false;
+		}
+
+		static constexpr std::string_view suffix = "-dark";
 
-	static constexpr std::string_view suffix = "-dark";
+		size_t gtk_theme_len = strlen(gtk_theme_str);
 
-	size_t gtk_theme_len = strlen(gtk_theme_str);
+		if (gtk_theme_len < suffix.length()) {
+			::g_variant_unref(gtk_theme);
+			::g_object_unref(settings);
+			return false;
+		}
 
-	success |= !std::strncmp(gtk_theme_str + gtk_theme_len - suffix.length(), suffix.data(), suffix.length());
+		bool success = !std::strncmp(gtk_theme_str + gtk_theme_len - suffix.length(), suffix.data(), suffix.length());
+
+		::g_variant_unref(gtk_theme);
+		::g_object_unref(settings);
 
-	::g_object_unref(settings);
-	return success;
+		if (success)
+			return true;
+	}
+
+	return false;
 }
 
-}
+} // namespace glib
--- a/src/sys/osx/dark_theme.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/sys/osx/dark_theme.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,7 +1,7 @@
 #include "sys/osx/dark_theme.h"
 
+#include <objc/message.h>
 #include <objc/runtime.h>
-#include <objc/message.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 
@@ -25,11 +25,13 @@
 	if (!appkit_bundle)
 		return false;
 
-	NSAppearanceNameAqua = *reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(appkit_bundle, CFSTR("NSAppearanceNameAqua")));
+	NSAppearanceNameAqua =
+	    *reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(appkit_bundle, CFSTR("NSAppearanceNameAqua")));
 	if (!NSAppearanceNameAqua)
 		return false;
 
-	NSAppearanceNameDarkAqua = *reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(appkit_bundle, CFSTR("NSAppearanceNameDarkAqua")));
+	NSAppearanceNameDarkAqua = *reinterpret_cast<CFStringRef*>(
+	    CFBundleGetDataPointerForName(appkit_bundle, CFSTR("NSAppearanceNameDarkAqua")));
 	if (!NSAppearanceNameDarkAqua)
 		return false;
 
@@ -88,7 +90,8 @@
 	const id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
 
 	// NSAppearance* appearance = [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua];
-	const id appearance = cls_send(objc_getClass("NSAppearance"), sel_getUid("appearanceNamed:"), NSAppearanceNameDarkAqua);
+	const id appearance =
+	    cls_send(objc_getClass("NSAppearance"), sel_getUid("appearanceNamed:"), NSAppearanceNameDarkAqua);
 	if (!appearance)
 		return false;
 
--- a/src/sys/osx/permissions.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/sys/osx/permissions.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -3,8 +3,8 @@
 #include <ApplicationServices/ApplicationServices.h>
 
 #include <QCoreApplication>
+#include <QDesktopServices>
 #include <QMessageBox>
-#include <QDesktopServices>
 #include <QUrl>
 
 namespace osx {
@@ -16,7 +16,8 @@
 	QMessageBox msg;
 	msg.setIcon(QMessageBox::Information);
 	msg.setText(QCoreApplication::tr("Permissions needed!"));
-	msg.setInformativeText(QCoreApplication::tr("Minori needs access to accessibility features for certain features to work. Open System Preferences?"));
+	msg.setInformativeText(QCoreApplication::tr(
+	    "Minori needs access to accessibility features for certain features to work. Open System Preferences?"));
 	msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 	msg.setDefaultButton(QMessageBox::Yes);
 	int ret = msg.exec();
@@ -27,4 +28,4 @@
 	return true;
 }
 
-}
+} // namespace osx
--- a/src/sys/win32/dark_theme.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/sys/win32/dark_theme.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -11,31 +11,36 @@
 #include <dwmapi.h>
 
 struct LibraryDeconstructor {
-		using pointer = HINSTANCE;
-		void operator()(pointer t) const { ::FreeLibrary(t); };
+	using pointer = HINSTANCE;
+	void operator()(pointer t) const { ::FreeLibrary(t); };
 };
 
 using Library = std::unique_ptr<HINSTANCE, LibraryDeconstructor>;
 
 class Dwmapi {
-	public:
-		Dwmapi() { library.reset( ::LoadLibraryW(L"dwmapi.dll")); }
+public:
+	Dwmapi() {
+		/* load functions */
+		library.reset(::LoadLibraryW(L"dwmapi.dll"));
+		set_wind_attrib = reinterpret_cast<decltype(::DwmSetWindowAttribute)*>(
+		    GetProcAddress(library.get(), "DwmSetWindowAttribute"));
+	}
 
-		HRESULT SetWindowAttribute(HWND hWnd, DWORD key, LPCVOID data, DWORD sz_data) {
-			if (!library.get())
-				return E_POINTER;
+	HRESULT SetWindowAttribute(HWND hWnd, DWORD key, LPCVOID data, DWORD sz_data) {
+		if (!library.get())
+			return E_POINTER;
 
-			// GCC throws a fit here because C/C++ lacks a "generic" function pointer type.
-			// Ignore.
-			auto set_wind_attrib = reinterpret_cast<decltype(::DwmSetWindowAttribute)*>(GetProcAddress(library.get(), "DwmSetWindowAttribute"));
-			if (!set_wind_attrib)
-				return E_POINTER;
+		/* GCC throws a fit here because C++ lacks a "generic" function pointer type.
+		 * Ignore. */
+		if (!set_wind_attrib)
+			return E_POINTER;
 
-			return set_wind_attrib(hWnd, key, data, sz_data);
-		}
+		return set_wind_attrib(hWnd, key, data, sz_data);
+	}
 
-	protected:
-		Library library = nullptr;
+protected:
+	Library library = nullptr;
+	decltype(::DwmSetWindowAttribute)* set_wind_attrib;
 };
 
 Dwmapi dwmapi;
@@ -47,20 +52,22 @@
 	 *
 	 * It's 20 on newer versions of windows (i.e. win11 and late win10),
 	 * but it's 19 on very old versions of win10 nobody ought to be using anymore.
-	*/
+	 */
 	static constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_OLD = 19;
 	static constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
 
 	BOOL b = enabled;
 
 	{
-		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), DWMWA_USE_IMMERSIVE_DARK_MODE, &b, sizeof(b));
+		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), DWMWA_USE_IMMERSIVE_DARK_MODE,
+		                                           &b, sizeof(b));
 		if (result == S_OK)
 			return b;
 	}
 
 	{
-		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &b, sizeof(b));
+		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()),
+		                                           DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &b, sizeof(b));
 		if (result == S_OK)
 			return b;
 	}
--- a/src/track/media.cc	Sun Feb 18 16:02:14 2024 -0500
+++ b/src/track/media.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -1,40 +1,42 @@
 #include "track/media.h"
 #include "core/filesystem.h"
+#include "core/session.h"
 #include "core/strings.h"
-#include "core/session.h"
 
 #include <QFile>
 #include <QTextStream>
 
+#include <filesystem>
 #include <string>
 #include <unordered_map>
 #include <vector>
-#include <filesystem>
 
 #include <iostream>
 
-#include "animia.h"
+#include "animone.h"
 
 namespace Track {
 namespace Media {
 
-static bool GetCurrentlyPlayingResults(std::vector<animia::Result>& results) {
-	std::vector<animia::Player> players;
+static bool GetCurrentlyPlayingResults(std::vector<animone::Result>& results) {
+	std::vector<animone::Player> players;
 
 	players.reserve(session.config.recognition.players.size());
 	for (const auto& [enabled, player] : session.config.recognition.players)
-		if (enabled && player.type == animia::PlayerType::Default)
+		if (enabled && player.type == animone::PlayerType::Default)
 			players.push_back(player);
 
-	if (!animia::GetResults(players, results))
+	if (!animone::GetResults(players, results)) {
+		std::cout << "FAIL!" << std::endl;
 		return false;
+	}
 
 	return true;
 }
 
 /* meh */
 bool GetCurrentlyPlaying(std::vector<std::string>& vec) {
-	std::vector<animia::Result> results;
+	std::vector<animone::Result> results;
 
 	if (!GetCurrentlyPlayingResults(results))
 		return false;
@@ -44,17 +46,18 @@
 	for (const auto& result : results) {
 		for (const auto& media : result.media) {
 			for (const auto& info : media.information) {
+				std::cout << info.value << std::endl;
+
 				switch (info.type) {
-					case animia::MediaInfoType::File:
+					case animone::MediaInfoType::File:
 						vec.push_back(std::filesystem::path(info.value).filename().u8string());
 						success |= true;
 						break;
-					case animia::MediaInfoType::Title:
+					case animone::MediaInfoType::Title:
 						vec.push_back(info.value);
 						success |= true;
 						break;
-					default:
-						break;
+					default: break;
 				}
 			}
 		}