Mercurial > minori
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 :)
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, ®istry_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
--- 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
--- 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
--- /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
--- /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; +}
--- /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; -}
--- 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. "<" 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. - */ - {"<", "<"}, - {"&rt;", ">"}, - {" ", "\xA0"}, - {"&", "&"}, - {""", "\""}, - {"'", "'"}, - {"¢", "¢"}, - {"£", "£"}, - {"€", "€"}, - {"¥", "¥"}, - {"©", "©"}, - {"®", "®"}, - {"’", "’"} // 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. + */ + {"<", "<" }, + {"&rt;", ">" }, + {" ", "\xA0"}, + {"&", "&" }, + {""", "\"" }, + {"'", "'" }, + {"¢", "¢" }, + {"£", "£" }, + {"€", "€" }, + {"¥", "¥" }, + {"©", "©" }, + {"®", "®" }, + {"’", "’" } // 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; } } }