# HG changeset patch # User Paper # Date 1699642367 18000 # Node ID 69db40272acd95cad2ff351a9e9455b794ef3350 # Parent 7d3ad9529c4c8b34fbcb501ce26fd43a01aebeb8 dep/animia: [WIP] huge refactor this WILL NOT compile, because lots of code has been changed and every API in the original codebase has been removed. note that this api setup is not exactly permanent... diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/CMakeLists.txt --- a/dep/animia/CMakeLists.txt Fri Nov 10 10:07:01 2023 -0500 +++ b/dep/animia/CMakeLists.txt Fri Nov 10 13:52:47 2023 -0500 @@ -1,26 +1,35 @@ cmake_minimum_required(VERSION 3.9) project(animia) set(SRC_FILES - src/main.cpp + # any non-platform-specific files go here + src/animia.cc + src/matroska.cc + src/player.cc + src/util.cc ) if(LINUX) - list(APPEND SRC_FILES src/linux.cpp) + list(APPEND SRC_FILES + # linux + src/linux/fd.cc + ) elseif(UNIX) # this won't run on Linux - list(APPEND SRC_FILES src/bsd.cpp) + list(APPEND SRC_FILES + # bsd + src/bsd/fd.cc + ) elseif(WIN32) - list(APPEND SRC_FILES src/win32.cpp) + list(APPEND SRC_FILES + # win32 + src/platform/win32.cc + src/platform/win32/fd.cc + src/platform/win32/ui_auto.cc + src/platform/win32/util.cc + src/platform/win32/win.cc + ) endif() add_library(animia SHARED ${SRC_FILES}) set_target_properties(animia PROPERTIES - PUBLIC_HEADER animia/animia.h CXX_STANDARD 11) + PUBLIC_HEADER include/animia.h + CXX_STANDARD 17 +) target_include_directories(animia PRIVATE include) -option(BUILD_TESTS "Build tests" OFF) - -if(BUILD_TESTS) - project(test LANGUAGES CXX) - add_executable(test test/main.cpp) - - target_include_directories(test PUBLIC include) - target_link_libraries(test PUBLIC animia) - set_target_properties(test PROPERTIES CXX_STANDARD 17) -endif() diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/LICENSE --- a/dep/animia/LICENSE Fri Nov 10 10:07:01 2023 -0500 +++ b/dep/animia/LICENSE Fri Nov 10 13:52:47 2023 -0500 @@ -1,29 +1,22 @@ -BSD 3-Clause License - -Copyright (c) 2023, 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. +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. diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/README.md --- a/dep/animia/README.md Fri Nov 10 10:07:01 2023 -0500 +++ b/dep/animia/README.md Fri Nov 10 13:52:47 2023 -0500 @@ -1,5 +1,5 @@ # Animia -Animia is a cross-platform library for getting a list of open read-only files from a PID (or, an entire list of open files, in an std::unordered_map). +Animia is a work-in-progress cross-platform fork of Anisthesia and part of Minori. -## Usage -Check the `test/` directory for a simple usage example. +## License +Because this project is a hard-fork of Anisthesia, it is under the MIT license. diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/data/players.anisthesia --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/data/players.anisthesia Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,494 @@ +# 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: +# +# +# 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 + 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 diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia.h --- a/dep/animia/include/animia.h Fri Nov 10 10:07:01 2023 -0500 +++ b/dep/animia/include/animia.h Fri Nov 10 13:52:47 2023 -0500 @@ -1,16 +1,12 @@ #ifndef __animia__animia_h #define __animia__animia_h -#include -#include -#include -namespace Animia { +#include "animia/media.h" +#include "animia/player.h" -std::vector get_all_pids(); -std::string get_process_name(int pid); -std::vector get_open_files(int pid); -std::vector filter_system_files(const std::vector& source); -std::unordered_map> get_all_open_files(); +namespace animia { + + } // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/matroska.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/matroska.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,70 @@ +#ifndef __animia__animia__matroska_h +#define __animia__animia__matroska_h + +#include +#include +#include +#include + +namespace animia::matroska { + +namespace detail { + +using timecode_scale_t = std::chrono::duration; +constexpr uint32_t kDefaultTimecodeScale = 1000000; // 1 milliseconds + +enum ElementId { + // EBML Header + EL_EBML = 0x1A45DFA3, + // Segment + EL_SEGMENT = 0x18538067, + // Segment Information + EL_INFO = 0x1549A966, + EL_TIMECODESCALE = 0x2AD7B1, + EL_DURATION = 0x4489, + EL_TITLE = 0x7BA9, + // Track + EL_TRACKS = 0x1654AE6B, + EL_TRACKENTRY = 0xAE, + EL_TRACKTYPE = 0x83, + EL_TRACKNAME = 0x536E +}; + +enum TrackType { + kVideo = 1 +}; + +class Buffer { + public: + Buffer(size_t size); + + uint8_t* data(); + size_t pos() const; + size_t size() const; + void skip(size_t size); + + bool read_encoded_value(uint32_t& value, bool clear_leading_bits); + uint32_t read_uint32(const size_t size); + float read_float(const size_t size); + std::string read_string(const size_t size); + + private: + std::vector data_; + size_t pos_ = 0; +}; + +} // namespace detail + +using duration_t = std::chrono::duration; + +struct Info { + duration_t duration = duration_t::zero(); + std::string title; + std::string video_track_name; +}; + +bool ReadInfoFromFile(const std::string& path, Info& info); + +} + +#endif // __animia__animia__matroska_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/media.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/media.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,42 @@ +#ifndef __animia__animia__media_h +#define __animia__animia__media_h + +#include +#include +#include +#include + +namespace animia { + +using media_time_t = std::chrono::milliseconds; + +enum class MediaInfoType { + Unknown, + File, + Tab, + Title, + Url, +}; + +enum class MediaState { + Unknown, + Playing, + Paused, + Stopped, +}; + +struct MediaInfo { + MediaInfoType type = MediaInfoType::Unknown; + std::string value; +}; + +struct Media { + MediaState state = MediaState::Unknown; // currently unused + media_time_t duration; // currently unused + media_time_t position; // currently unused + std::vector information; +}; + +using media_proc_t = std::function; + +#endif // __animia__animia__media_h \ No newline at end of file diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/platform/win32.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/platform/win32.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,43 @@ +#ifndef __animia__animia__platform__win32_h +#define __animia__animia__platform__win32_h + +#include +#include + +#include + +#include "animia/media.h" +#include "animia/player.h" + +namespace animia::win { + +struct Process { + DWORD id = 0; + std::wstring name; +}; + +struct Window { + HWND handle = nullptr; + std::wstring class_name; + std::wstring text; +}; + +struct Result { + Player player; + Process process; + Window window; + std::vector media; +}; + +bool GetResults(const std::vector& players, media_proc_t media_proc, + std::vector& results); + +namespace detail { + +bool ApplyStrategies(media_proc_t media_proc, std::vector& results); + +} // namespace detail + +} // namespace animia::win + +#endif // __animia__animia__platform__win32_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/platform/win32/fd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/platform/win32/fd.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,24 @@ +#ifndef __animia__animia__platform__win32__fd_h +#define __animia__animia__platform__win32__fd_h + +#include +#include +#include + +#include + +namespace animia::win::detail { + +struct OpenFile { + DWORD proc_id; + std::wstring path; +} + +using open_file_proc_t = std::function; + +bool EnumerateOpenFiles(const std::set& process_ids, + open_file_proc_t open_file_proc); + +} + +#endif // __animia__animia__platform__win32__fd_h \ No newline at end of file diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/platform/win32/ui_auto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/platform/win32/ui_auto.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,30 @@ +#ifndef __animia__animia__win32__ui_auto_h +#define __animia__animia__win32__ui_auto_h + +/* "UI automation" == web browser stuff.. */ + +#include +#include + +#include + +namespace animia::win::detail { + +enum class WebBrowserInformationType { + Address, + Tab, + Title, +}; + +struct WebBrowserInformation { + WebBrowserInformationType type = WebBrowserInformationType::Title; + std::string value; +}; + +using web_browser_proc_t = std::function; + +bool GetWebBrowserInformation(HWND hwnd, web_browser_proc_t web_browser_proc); + +} // namespace animia::win::detail + +#endif // __animia__animia__win32__ui_auto_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/platform/win32/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/platform/win32/util.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,37 @@ +#ifndef __animia__animia__win32__util_h +#define __animia__animia__win32__util_h + +#include +#include +#include + +#include + +namespace animia::win::detail { + +struct HandleDeleter { + void operator()(HANDLE p) const { ::CloseHandle(p); } +}; + +using Handle = std::unique_ptr; + +/* ----------- Alternative to Microsoft::WRL::ComPtr ------------- */ +template +struct ComInterfaceDeleter { + static_assert(std::is_base_of::value, "Invalid COM interface"); + void operator()(T* p) const { if (p) p->Release(); } +}; + +template +using ComInterface = std::unique_ptr>; +/* --------------------------------------------------------------- */ + +std::wstring GetFileNameFromPath(const std::wstring& path); +std::wstring GetFileNameWithoutExtension(const std::wstring& filename); +bool IsSystemDirectory(const std::wstring& path); + +std::string ToUtf8String(const std::wstring& str); + +} + +#endif // __animia__animia__win32__util_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/platform/win32/win.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/platform/win32/win.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,21 @@ +#ifndef __animia__animia__win32__win_h +#define __animia__animia__win32__win_h + +#include + +namespace animia::win { + +struct Process; +struct Window; + +namespace detail { + +using window_proc_t = std::function; + +bool EnumerateWindows(window_proc_t window_proc); + +} // namespace detail + +} // namespace animia::win + +#endif // __animia__animia__win32__win_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/player.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/player.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,34 @@ +#ifndef __animia__animia__player_h +#define __animia__animia__player_h + +#include +#include + +namespace animia { + +enum class Strategy { + WindowTitle, + OpenFiles, + UiAutomation // ??? +} + +enum class PlayerType { + Default, + WebBrowser +} + +struct Player { + PlayerType type = PlayerType::Default; + std::string name; + std::string window_title_format; + std::vector windows; + std::vector executables; + std::vector strategies; +} + +bool ParsePlayersData(const std::string& data, std::vector& players); +bool ParsePlayersFile(const std::string& path, std::vector& players); + +} + +#endif // __animia__animia__player_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/animia/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/util.h Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,16 @@ +#ifndef __animia__animia__util_h +#define __animia__animia__util_h + +#include + +namespace animia::detail::util { + +bool ReadFile(const std::string& path, std::string& data); + +bool EqualStrings(const std::string& str1, const std::string& str2); +bool TrimLeft(std::string& str, const char* chars); +bool TrimRight(std::string& str, const char* chars); + +} // namespace animia::detail::util + +#endif // __animia__animia__util_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/bsd.h --- a/dep/animia/include/bsd.h Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#ifndef __animia__bsd_h -#define __animia__bsd_h -#include -#include -#include - -namespace Animia { namespace Unix { - -std::vector get_all_pids(); -std::string get_process_name(const int pid); -std::vector get_open_files(const int pid); -std::vector filter_system_files(const std::vector& in); -std::unordered_map> get_all_open_files(); - -} // namespace Unix -} // namespace Animia - -#endif // __animia__bsd_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/linux.h --- a/dep/animia/include/linux.h Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#ifndef __animia__linux_h -#define __animia__linux_h -#include -#include -#include - -namespace Animia { namespace Linux { - -std::vector get_all_pids(); -std::string get_process_name(int pid); -std::vector get_open_files(int pid); -std::unordered_map> get_all_open_files(); - -} // namespace Linux -} // namespace Animia - -#endif // __animia__linux_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/os.h --- a/dep/animia/include/os.h Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -/* can this be moved to cmake? */ -#ifndef __animia__os_h -#define __animia__os_h - -#ifdef __linux__ -# define ON_LINUX -#elif (defined(unix) || defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) -# if (defined(__APPLE__) && defined(__MACH__)) -# define ON_OSX -# endif -# define ON_UNIX -#elif defined(_WIN32) -# define ON_WINDOWS -#endif - -#endif // __animia__os_h \ No newline at end of file diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/include/win32.h --- a/dep/animia/include/win32.h Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#ifndef __animia__windows_h -#define __animia__windows_h -#include -#include -#include - -namespace Animia { namespace Windows { - -std::vector get_all_pids(); -std::string get_process_name(int pid); -std::vector get_open_files(int pid); -std::vector filter_system_files(const std::vector& source); -std::unordered_map> get_all_open_files(); - -} // namespace Windows -} // namespace Animia - -#endif // __animia__windows_h diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/animia.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/bsd.cpp --- a/dep/animia/src/bsd.cpp Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -/** - * bsd.cpp - * - provides support for most* versions of BSD - * - this also works for OS X :) - * more technical details: this is essentially a wrapper - * around the very C-like BSD system functions that are... - * kind of unnatural to use in modern C++. - **/ -#include "bsd.h" -#include "os.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __FreeBSD__ -# include -#elif defined(__APPLE__) -# include -#endif - -namespace Animia { namespace Unix { - -/* this is a cleaned up version of a function from... Apple? - ...anyway, what it essentially does is gets the size and stuff from - sysctl() and reserves the space in a vector to store the PIDs */ -std::vector get_all_pids() { - std::vector ret; - struct kinfo_proc* result = NULL; - size_t length = 0; - static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; - - /* get appropriate length from sysctl() - note: the reason this isn't checked is actually because this will - *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */ - sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); - - result = (struct kinfo_proc*)malloc(length); - if (result == NULL) - return std::vector(); - - /* actually get our results */ - if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) { - assert(result != NULL); - free(result); - throw std::bad_alloc(); - } - - /* add pids to our vector */ - ret.reserve(length / sizeof(*result)); - for (int i = 0; i < length / sizeof(*result); i++) - ret.push_back(result[i].kp_proc.p_pid); - - return ret; -} - -std::string get_process_name(const int pid) { - std::string ret; -#ifdef __FreeBSD__ - struct kinfo_proc* proc = kinfo_getproc(pid); - if (!proc) - return ""; - ret = proc->ki_comm; - free(proc); -#elif defined(__APPLE__) - struct proc_bsdinfo proc; - - int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE); - if (st != PROC_PIDTBSDINFO_SIZE) - return ""; - ret = proc.pbi_comm; -#endif - return ret; -} - -std::vector get_open_files(const int pid) { - /* note: this is OS X only right now. eventually, I'll find a way - to do this in FreeBSD, OpenBSD and the like */ - std::vector ret; - - if (pid == 0) - return ret; - - int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); - if (bufsz == -1) - return ret; - - struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz); - if (!info) - return ret; - - proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz); - - // iterate over stuff - ret.reserve(bufsz / sizeof(info[0])); - 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) - continue; - - /* I'm 99.9% sure this is incorrect. We can't pick up QuickTime files with this :( - - Actually, we can't pick up QuickTime files regardless. WTF? */ - if ((vnodeInfo.pfi.fi_status & O_ACCMODE) == O_WRONLY) - continue; - - ret.push_back(vnodeInfo.pvip.vip_path); - } - } - return ret; -} - -std::vector filter_system_files(const std::vector& in) { -#ifdef ON_OSX - std::vector ret; - for (const auto& str : in) - /* these are some places nobody would ever want to store media files. */ - if (str.find("/Library") && str.find("/System") && str.find("/Applications") && - str.find("/dev") && str.find("/private")) - ret.push_back(str); - return ret; -#else - return in; -#endif -} - - -std::unordered_map> get_all_open_files() { - std::unordered_map> map; - std::vector pids = get_all_pids(); - for (int i : pids) { - map[i] = get_open_files(i); - } - return map; -} - -} // namespace Unix -} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/linux.cpp --- a/dep/animia/src/linux.cpp Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PROC_LOCATION "/proc" - -namespace Animia { namespace Linux { - -std::vector get_all_files_in_dir(const std::string& _dir) { - std::vector ret; - - DIR* dir = opendir(_dir.c_str()); - if (!dir) - return ret; - - struct dirent* dp; - while ((dp = readdir(dir)) != NULL) { - if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))) - ret.push_back(_dir + "/" + dp->d_name); - } - - closedir(dir); - return ret; -} - -std::string basename(const std::string& path) { - return path.substr(path.find_last_of("/") + 1, path.length()); -} - -std::string stem(const std::string& path) { - std::string bn = basename(path); - return bn.substr(0, path.find_last_of(".")); -} - -std::vector get_all_pids() { - std::vector ret; - - for (const auto& dir : get_all_files_in_dir(PROC_LOCATION)) { - int pid; - try { - pid = std::stoi(basename(dir)); - } catch (std::invalid_argument) { - continue; - } - ret.push_back(pid); - } - - return ret; -} - -std::string get_process_name(int pid) { - std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm"; - std::ifstream t(path); - std::stringstream buf; - buf << t.rdbuf(); - - std::string str = buf.str(); - str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); - return str; -} - -static bool is_regular_file(std::string link) { - struct stat sb; - if (stat(link.c_str(), &sb) == -1) - return false; - return S_ISREG(sb.st_mode); -} - -static bool are_flags_ok(int pid, int fd) { - std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd); - std::ifstream t(path); - std::stringstream buffer; - buffer << t.rdbuf(); - std::string raw; - int flags = 0; - while (std::getline(buffer, raw)) { - if (raw.rfind("flags:", 0) == 0) { - flags = std::stoi(raw.substr(raw.find_last_not_of("0123456789") + 1)); - } - } - if (flags & O_WRONLY || flags & O_RDWR) - return false; - return true; -} - -static std::string get_name_from_fd(std::string link) { - size_t exe_size = 1024; - ssize_t exe_used; - std::string ret; - while (1) { - ret = std::string(exe_size, '\0'); - exe_used = readlink(link.c_str(), &ret.front(), ret.length()); - if (exe_used == (ssize_t)-1) - return NULL; - - if (exe_used < (ssize_t)1) { - errno = ENOENT; - return NULL; - } - - if (exe_used < (ssize_t)(exe_size - 1)) - break; - - exe_size += 1024; - } - - return ret.c_str(); -} - -std::vector get_open_files(int pid) { - std::vector ret; - std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd"; - - for (const auto& dir : get_all_files_in_dir(path)) { - if (!are_flags_ok(pid, std::stoi(basename(dir)))) - continue; - - std::string buf = get_name_from_fd(dir); - - if (!is_regular_file(buf)) - continue; - - ret.push_back(buf); - } - return ret; -} - -std::unordered_map> get_all_open_files() { - std::unordered_map> map; - std::vector pids = get_all_pids(); - for (int i : pids) - map[i] = get_open_files(i); - return map; -} - -} // namespace Linux -} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/main.cpp --- a/dep/animia/src/main.cpp Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -#include "bsd.h" -#include "os.h" -#include "linux.h" -#include "win32.h" -#include "animia.h" -#include -#include -#include - -namespace Animia { - -std::vector get_all_pids() { -#ifdef ON_UNIX - return Unix::get_all_pids(); -#elif defined(ON_LINUX) - return Linux::get_all_pids(); -#elif defined(ON_WINDOWS) - return Windows::get_all_pids(); -#else - return {}; -#endif -} - -std::string get_process_name(int pid) { -#ifdef ON_UNIX - return Unix::get_process_name(pid); -#elif defined(ON_LINUX) - return Linux::get_process_name(pid); -#elif defined(ON_WINDOWS) - return Windows::get_process_name(pid); -#else - return ""; -#endif -} - -std::vector get_open_files(int pid) { -#ifdef ON_UNIX - return Unix::get_open_files(pid); -#elif defined(ON_LINUX) - return Linux::get_open_files(pid); -#elif defined(ON_WINDOWS) - return Windows::get_open_files(pid); -#else - return {}; -#endif -} - -std::vector filter_system_files(const std::vector& source) { -#ifdef ON_WINDOWS - return Windows::filter_system_files(source); -#elif defined(ON_OSX) - return Unix::filter_system_files(source); -#else - return source; -#endif -} - -std::unordered_map> get_all_open_files() { -#ifdef ON_UNIX - return Unix::get_all_open_files(); -#elif defined(ON_LINUX) - return Linux::get_all_open_files(); -#elif defined(ON_WINDOWS) - return Windows::get_all_open_files(); -#else - return {}; -#endif -} - -} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/matroska.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/bsd/fd.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/platform/bsd/fd.cc Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,124 @@ +/** + * bsd.cpp + * - provides support for most* versions of BSD + * - this also works for OS X :) + * more technical details: this is essentially a wrapper + * around the very C-like BSD system functions that are... + * kind of unnatural to use in modern C++. + **/ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +# include +#elif defined(__APPLE__) +# include +#endif + +namespace Animia { namespace Unix { + +/* this is a cleaned up version of a function from... Apple? + ...anyway, what it essentially does is gets the size and stuff from + sysctl() and reserves the space in a vector to store the PIDs */ +std::vector get_all_pids() { + std::vector ret; + struct kinfo_proc* result = NULL; + size_t length = 0; + static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + + /* get appropriate length from sysctl() + note: the reason this isn't checked is actually because this will + *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */ + sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); + + result = (struct kinfo_proc*)malloc(length); + if (result == NULL) + return std::vector(); + + /* actually get our results */ + if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) { + assert(result != NULL); + free(result); + throw std::bad_alloc(); + } + + /* add pids to our vector */ + ret.reserve(length / sizeof(*result)); + for (int i = 0; i < length / sizeof(*result); i++) + ret.push_back(result[i].kp_proc.p_pid); + + return ret; +} + +std::string get_process_name(int pid) { + std::string ret; +#ifdef __FreeBSD__ + struct kinfo_proc* proc = kinfo_getproc(pid); + if (!proc) + return ""; + ret = proc->ki_comm; + free(proc); +#elif defined(__APPLE__) + struct proc_bsdinfo proc; + + int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE); + if (st != PROC_PIDTBSDINFO_SIZE) + return ""; + ret = proc.pbi_comm; +#endif + return ret; +} + +std::vector get_open_files(int pid) { + /* note: this is OS X only right now. eventually, I'll find a way + to do this in FreeBSD, OpenBSD and the like */ + std::vector ret; + + if (pid == 0) + return ret; + + int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (bufsz == -1) + return ret; + + struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz); + if (!info) + return ret; + + proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz); + + // iterate over stuff + ret.reserve(bufsz / sizeof(info[0])); + 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) + continue; + + if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR) + continue; + + ret.push_back(vnodeInfo.pvip.vip_path); + } + } + return ret; +} + +std::unordered_map> get_all_open_files() { + std::unordered_map> map; + std::vector pids = get_all_pids(); + for (int i : pids) { + map[i] = get_open_files(i); + } + return map; +} + +} // namespace Unix +} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/linux/fd.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/platform/linux/fd.cc Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROC_LOCATION "/proc" + +namespace Animia { namespace Linux { + +std::vector get_all_files_in_dir(const std::string& _dir) { + std::vector ret; + + DIR* dir = opendir(_dir.c_str()); + if (!dir) + return ret; + + struct dirent* dp; + while ((dp = readdir(dir)) != NULL) { + if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))) + ret.push_back(_dir + "/" + dp->d_name); + } + + closedir(dir); + return ret; +} + +std::string basename(const std::string& path) { + return path.substr(path.find_last_of("/") + 1, path.length()); +} + +std::string stem(const std::string& path) { + std::string bn = basename(path); + return bn.substr(0, path.find_last_of(".")); +} + +std::vector get_all_pids() { + std::vector ret; + + for (const auto& dir : get_all_files_in_dir(PROC_LOCATION)) { + int pid; + try { + pid = std::stoi(basename(dir)); + } catch (std::invalid_argument) { + continue; + } + ret.push_back(pid); + } + + return ret; +} + +std::string get_process_name(int pid) { + std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm"; + std::ifstream t(path); + std::stringstream buf; + buf << t.rdbuf(); + + std::string str = buf.str(); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + return str; +} + +static bool is_regular_file(std::string link) { + struct stat sb; + if (stat(link.c_str(), &sb) == -1) + return false; + return S_ISREG(sb.st_mode); +} + +static bool are_flags_ok(int pid, int fd) { + std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd); + std::ifstream t(path); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string raw; + int flags = 0; + while (std::getline(buffer, raw)) { + if (raw.rfind("flags:", 0) == 0) { + flags = std::stoi(raw.substr(raw.find_last_not_of("0123456789") + 1)); + } + } + if (flags & O_WRONLY || flags & O_RDWR) + return false; + return true; +} + +static std::string get_name_from_fd(std::string link) { + size_t exe_size = 1024; + ssize_t exe_used; + std::string ret; + while (1) { + ret = std::string(exe_size, '\0'); + exe_used = readlink(link.c_str(), &ret.front(), ret.length()); + if (exe_used == (ssize_t)-1) + return NULL; + + if (exe_used < (ssize_t)1) { + errno = ENOENT; + return NULL; + } + + if (exe_used < (ssize_t)(exe_size - 1)) + break; + + exe_size += 1024; + } + + return ret.c_str(); +} + +std::vector get_open_files(int pid) { + std::vector ret; + std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd"; + + for (const auto& dir : get_all_files_in_dir(path)) { + if (!are_flags_ok(pid, std::stoi(basename(dir)))) + continue; + + std::string buf = get_name_from_fd(dir); + + if (!is_regular_file(buf)) + continue; + + ret.push_back(buf); + } + return ret; +} + +std::unordered_map> get_all_open_files() { + std::unordered_map> map; + std::vector pids = get_all_pids(); + for (int i : pids) + map[i] = get_open_files(i); + return map; +} + +} // namespace Linux +} // namespace Animia \ No newline at end of file diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/win32.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/win32/fd.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/platform/win32/fd.cc Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,314 @@ +/** + * win32.cpp + * - provides support for Windows clients + * + **/ +#include "win32.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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. */ + +#define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40) +constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; +constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL; + +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 { namespace Windows { + +/* All of this BS is required on Windows. Why? */ + +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; +} + +PVOID GetNTDLLAddress(LPCSTR proc_name) { + return reinterpret_cast(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name)); +} + +NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) { + static const auto func = + reinterpret_cast(GetNTDLLAddress("NtQuerySystemInformation")); + return func(cls, sysinfo, len, retlen); +} + +NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) { + static const auto func = reinterpret_cast(GetNTDLLAddress("NtQueryObject")); + return func(handle, cls, objinf, objinflen, retlen); +} + +std::vector GetSystemHandleInformation() { + std::vector res; + ULONG cb = 1 << 19; + NTSTATUS status = STATUS_SUCCESS; + SYSTEM_HANDLE_INFORMATION_EX* info; + + do { + status = STATUS_NO_MEMORY; + + if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2))) + continue; + + res.reserve(cb); + + if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) { + if (ULONG_PTR handles = info->NumberOfHandles) { + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles; + do { + if (entry) + res.push_back(*entry); + } while (entry++, --handles); + } + } + free(info); + } while (status == STATUS_INFO_LENGTH_MISMATCH); + + return res; +} + +OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) { + OBJECT_TYPE_INFORMATION info; + QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL); + return info; +} + +/* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that + sucks massive dick. this way we get unicode in the way every single other OS does it */ +std::string UnicodeStringToUtf8(std::wstring string) { + unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL); + std::string ret = std::string(size, '\0'); + ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL); + return ret; +} + +std::string UnicodeStringToUtf8(UNICODE_STRING string) { + unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL); + std::string ret = std::string(size, '\0'); + ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL); + return ret; +} + +std::wstring Utf8StringToUnicode(std::string string) { + unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0); + std::wstring ret = std::wstring(size, L'\0'); + ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length()); + return ret; +} + +std::string GetHandleType(HANDLE handle) { + OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle); + return UnicodeStringToUtf8(info.TypeName); +} + +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 UnicodeStringToUtf8(buffer); +} + +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; +} + +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; +} + +bool IsFilePathOk(const std::string& path) { + if (path.empty()) + 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; +} + +std::string GetSystemDirectory() { + PWSTR path_wch; + SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch); + std::wstring path_wstr(path_wch); + CoTaskMemFree(path_wch); + return UnicodeStringToUtf8(path_wstr); +} + +bool IsSystemFile(const std::string& path) { + std::wstring path_w = Utf8StringToUnicode(path); + CharUpperBuffW(&path_w.front(), path_w.length()); + std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory()); + CharUpperBuffW(&windir_w.front(), windir_w.length()); + return path_w.find(windir_w) == 4; +} + +std::vector get_all_pids() { + std::vector ret; + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(PROCESSENTRY32); + + if (hProcessSnap == INVALID_HANDLE_VALUE) + return std::vector(); + + if (!Process32First(hProcessSnap, &pe32)) + return std::vector(); + + ret.push_back(pe32.th32ProcessID); + while (Process32Next(hProcessSnap, &pe32)) { + ret.push_back(pe32.th32ProcessID); + } + // clean the snapshot object + CloseHandle(hProcessSnap); + + return ret; +} + +std::string get_process_name(int pid) { + unsigned long size = 256, ret_size = 0; + HANDLE handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!handle) + return ""; + + std::wstring ret(size, L'\0'); + while (size < 32768) { + ret.resize(size, L'\0'); + + if (!(ret_size = ::GetModuleBaseNameW(handle, 0, &ret.front(), ret.length()))) + ret = L""; + else if (size > ret_size) + ret.resize(ret.find('\0')); + + size *= 2; + } + + CloseHandle(handle); + + return UnicodeStringToUtf8(ret); +} + +std::vector get_open_files(int pid) { + std::vector ret; + std::vector info = GetSystemHandleInformation(); + for (auto& h : info) { + if (h.UniqueProcessId != pid) + continue; + + if (!IsFileHandle(nullptr, h.ObjectTypeIndex)) + continue; + if (!IsFileMaskOk(h.GrantedAccess)) + continue; + + const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); + HANDLE handle = DuplicateHandle(proc, h.HandleValue); + if (!handle) + continue; + + if (GetFileType(handle) != FILE_TYPE_DISK) + continue; + + std::string path = GetFinalPathNameByHandle(handle); + if (!IsFilePathOk(path)) + continue; + + ret.push_back(path); + } + return ret; +} + +std::vector filter_system_files(const std::vector& source) { + std::vector ret; + for (const std::string& s : source) { + if (!IsSystemFile(s)) + ret.push_back(s); + } + return ret; +} + +std::unordered_map> get_all_open_files() { + std::unordered_map> map; + std::vector info = GetSystemHandleInformation(); + for (auto& h : info) { + int pid = h.UniqueProcessId; + + if (!IsFileHandle(nullptr, h.ObjectTypeIndex)) + continue; + if (!IsFileMaskOk(h.GrantedAccess)) + continue; + + const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); + HANDLE handle = DuplicateHandle(proc, h.HandleValue); + if (!handle) + continue; + + if (GetFileType(handle) != FILE_TYPE_DISK) + continue; + + std::string path = GetFinalPathNameByHandle(handle); + if (!IsFilePathOk(path)) + continue; + + if (map.find(pid) == map.end()) + map[pid] = {}; + map[pid].push_back(path); + } + return map; +} + +} // namespace Windows +} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/win32/ui_auto.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/win32/util.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/platform/win32/win.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/player.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/util.cc diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/src/win32.cpp --- a/dep/animia/src/win32.cpp Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -/** - * win32.cpp - * - provides support for Windows clients - * - **/ -#include "win32.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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. */ - -#define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40) -constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; -constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL; - -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 { namespace Windows { - -/* All of this BS is required on Windows. Why? */ - -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; -} - -PVOID GetNTDLLAddress(LPCSTR proc_name) { - return reinterpret_cast(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name)); -} - -NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) { - static const auto func = - reinterpret_cast(GetNTDLLAddress("NtQuerySystemInformation")); - return func(cls, sysinfo, len, retlen); -} - -NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) { - static const auto func = reinterpret_cast(GetNTDLLAddress("NtQueryObject")); - return func(handle, cls, objinf, objinflen, retlen); -} - -std::vector GetSystemHandleInformation() { - std::vector res; - ULONG cb = 1 << 19; - NTSTATUS status = STATUS_SUCCESS; - SYSTEM_HANDLE_INFORMATION_EX* info; - - do { - status = STATUS_NO_MEMORY; - - if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2))) - continue; - - res.reserve(cb); - - if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) { - if (ULONG_PTR handles = info->NumberOfHandles) { - SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles; - do { - if (entry) - res.push_back(*entry); - } while (entry++, --handles); - } - } - free(info); - } while (status == STATUS_INFO_LENGTH_MISMATCH); - - return res; -} - -OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) { - OBJECT_TYPE_INFORMATION info; - QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL); - return info; -} - -/* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that - sucks massive dick. this way we get unicode in the way every single other OS does it */ -std::string UnicodeStringToUtf8(std::wstring string) { - unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL); - std::string ret = std::string(size, '\0'); - ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL); - return ret; -} - -std::string UnicodeStringToUtf8(UNICODE_STRING string) { - unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL); - std::string ret = std::string(size, '\0'); - ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL); - return ret; -} - -std::wstring Utf8StringToUnicode(std::string string) { - unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0); - std::wstring ret = std::wstring(size, L'\0'); - ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length()); - return ret; -} - -std::string GetHandleType(HANDLE handle) { - OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle); - return UnicodeStringToUtf8(info.TypeName); -} - -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 UnicodeStringToUtf8(buffer); -} - -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; -} - -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; -} - -bool IsFilePathOk(const std::string& path) { - if (path.empty()) - 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; -} - -std::string GetSystemDirectory() { - PWSTR path_wch; - SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch); - std::wstring path_wstr(path_wch); - CoTaskMemFree(path_wch); - return UnicodeStringToUtf8(path_wstr); -} - -bool IsSystemFile(const std::string& path) { - std::wstring path_w = Utf8StringToUnicode(path); - CharUpperBuffW(&path_w.front(), path_w.length()); - std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory()); - CharUpperBuffW(&windir_w.front(), windir_w.length()); - return path_w.find(windir_w) == 4; -} - -std::vector get_all_pids() { - std::vector ret; - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(PROCESSENTRY32); - - if (hProcessSnap == INVALID_HANDLE_VALUE) - return std::vector(); - - if (!Process32First(hProcessSnap, &pe32)) - return std::vector(); - - ret.push_back(pe32.th32ProcessID); - while (Process32Next(hProcessSnap, &pe32)) { - ret.push_back(pe32.th32ProcessID); - } - // clean the snapshot object - CloseHandle(hProcessSnap); - - return ret; -} - -std::string get_process_name(int pid) { - unsigned long size = 256, ret_size = 0; - HANDLE handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); - if (!handle) - return ""; - - std::wstring ret(size, L'\0'); - while (size < 32768) { - ret.resize(size, L'\0'); - - if (!(ret_size = ::GetModuleBaseNameW(handle, 0, &ret.front(), ret.length()))) - ret = L""; - else if (size > ret_size) - ret.resize(ret.find('\0')); - - size *= 2; - } - - CloseHandle(handle); - - return UnicodeStringToUtf8(ret); -} - -std::vector get_open_files(int pid) { - std::vector ret; - std::vector info = GetSystemHandleInformation(); - for (auto& h : info) { - if (h.UniqueProcessId != pid) - continue; - - if (!IsFileHandle(nullptr, h.ObjectTypeIndex)) - continue; - if (!IsFileMaskOk(h.GrantedAccess)) - continue; - - const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); - HANDLE handle = DuplicateHandle(proc, h.HandleValue); - if (!handle) - continue; - - if (GetFileType(handle) != FILE_TYPE_DISK) - continue; - - std::string path = GetFinalPathNameByHandle(handle); - if (!IsFilePathOk(path)) - continue; - - ret.push_back(path); - } - return ret; -} - -std::vector filter_system_files(const std::vector& source) { - std::vector ret; - for (const std::string& s : source) { - if (!IsSystemFile(s)) - ret.push_back(s); - } - return ret; -} - -std::unordered_map> get_all_open_files() { - std::unordered_map> map; - std::vector info = GetSystemHandleInformation(); - for (auto& h : info) { - int pid = h.UniqueProcessId; - - if (!IsFileHandle(nullptr, h.ObjectTypeIndex)) - continue; - if (!IsFileMaskOk(h.GrantedAccess)) - continue; - - const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); - HANDLE handle = DuplicateHandle(proc, h.HandleValue); - if (!handle) - continue; - - if (GetFileType(handle) != FILE_TYPE_DISK) - continue; - - std::string path = GetFinalPathNameByHandle(handle); - if (!IsFilePathOk(path)) - continue; - - if (map.find(pid) == map.end()) - map[pid] = {}; - map[pid].push_back(path); - } - return map; -} - -} // namespace Windows -} // namespace Animia diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/test/main.cpp --- a/dep/animia/test/main.cpp Fri Nov 10 10:07:01 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -#include "animia.h" -#include - -int main() { - /* getting all open files */ - std::vector pids = Animia::get_all_pids(); - for (int i: pids) { - if (Animia::get_process_name(i) == "mpc-hc64.exe") { - std::vector files = Animia::filter_system_files(Animia::get_open_files(i)); - for (std::string s: files) { - std::cout << Animia::get_process_name(i) << " (" << i << "): " << s << "\n"; - } - } - } -} diff -r 7d3ad9529c4c -r 69db40272acd dep/animia/util/Anisthesia.sublime-syntax --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/util/Anisthesia.sublime-syntax Fri Nov 10 13:52:47 2023 -0500 @@ -0,0 +1,38 @@ +%YAML 1.2 +--- +name: Anisthesia +file_extensions: [anisthesia] +scope: text.anisthesia + +contexts: + main: + - include: comments + - include: keywords + - include: player + - include: strategies + - include: type + - include: string + + comments: + - match: ^\t*#.*\n + scope: comment.anisthesia + + keywords: + - match: ^\t+(executables|strategies|type|windows):?\n + scope: keyword.anisthesia + + player: + - match: ^[^\s].+\n + scope: markup.heading.player.anisthesia + + strategies: + - match: ^\t+(open_files|ui_automation|window_title):?\n + scope: constant.strategy.anisthesia + + type: + - match: ^\t+(default|web_browser)\n + scope: constant.type.anisthesia + + string: + - match: ^\t+\^?(.+)\n + scope: string.anisthesia diff -r 7d3ad9529c4c -r 69db40272acd src/gui/dialog/settings/recognition.cc --- a/src/gui/dialog/settings/recognition.cc Fri Nov 10 10:07:01 2023 -0500 +++ b/src/gui/dialog/settings/recognition.cc Fri Nov 10 13:52:47 2023 -0500 @@ -21,61 +21,85 @@ QVBoxLayout* full_layout = new QVBoxLayout(result); { - /* URLs */ - QGroupBox* group = new QGroupBox(tr("Media players"), result); - group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + /* Feed link */ + QWidget* widget = new QWidget(result); + QVBoxLayout* widget_layout = new QVBoxLayout(widget); - QVBoxLayout* group_layout = new QVBoxLayout(group); + QCheckBox* checkbox = new QCheckBox(result); + checkbox->setText(tr("Enable media player detection")); + checkbox->setCheckState(detect_media_players ? Qt::Checked : Qt::Unchecked); + widget_layout->addWidget(checkbox); + + { + QLabel* label = new QLabel(tr("Allowed media players:"), widget); + widget_layout->addWidget(label); + } { - /* Feed link */ - QWidget* widget = new QWidget(group); - QVBoxLayout* widget_layout = new QVBoxLayout(widget); - - QCheckBox* checkbox = new QCheckBox(group); - checkbox->setText(tr("Enable media player detection")); - widget_layout->addWidget(checkbox); - - { - QLabel* label = new QLabel(tr("Allowed media players:"), widget); - widget_layout->addWidget(label); + QListWidget* listwidget = new QListWidget(widget); + for (size_t i = 0; i < players.size(); i++) { + const auto& player = players[i]; + { + QListWidgetItem* item = new QListWidgetItem(listwidget); + item->setCheckState(player.GetEnabled() ? Qt::Checked : Qt::Unchecked); + item->setText(Strings::ToQString(player.GetName() + " (" + player.GetExecutable() + ")")); + item->setData(Qt::UserRole, QVariant::fromValue(i)); + } } + connect(listwidget, &QListWidget::itemChanged, this, [this](QListWidgetItem* item){ + if (!item) + return; + size_t i = item->data(Qt::UserRole).toUInt(); + players[i].SetEnabled(item->checkState()); + }); + /* this is down here because the listwidget state depends on it */ + connect(checkbox, &QCheckBox::stateChanged, this, [this, listwidget](int state) { + detect_media_players = (state == Qt::Checked); + listwidget->setEnabled(detect_media_players); + }); + listwidget->setEnabled(checkbox->checkState() == Qt::Checked); - { - QListWidget* listwidget = new QListWidget(widget); - for (size_t i = 0; i < players.size(); i++) { - const auto& player = players[i]; - { - QListWidgetItem* item = new QListWidgetItem(listwidget); - item->setCheckState(player.GetEnabled() ? Qt::Checked : Qt::Unchecked); - item->setText(Strings::ToQString(player.GetName() + " (" + player.GetExecutable() + ")")); - item->setData(Qt::UserRole, QVariant::fromValue(i)); - } - } - connect(listwidget, &QListWidget::itemChanged, this, [this](QListWidgetItem* item){ - if (!item) - return; - size_t i = item->data(Qt::UserRole).toUInt(); - players[i].SetEnabled(item->checkState()); - }); - /* this is down here because the listwidget state depends on it */ - connect(checkbox, &QCheckBox::stateChanged, this, [this, listwidget](int state) { - detect_media_players = (state == Qt::Checked); - listwidget->setEnabled(detect_media_players); - }); - listwidget->setEnabled(detect_media_players); - - widget_layout->addWidget(listwidget); - } - - group_layout->addWidget(widget); + widget_layout->addWidget(listwidget); } - full_layout->addWidget(group); + full_layout->addWidget(widget); + } + + { + /* Feed link */ + QWidget* widget = new QWidget(result); + QVBoxLayout* widget_layout = new QVBoxLayout(widget); + + { + QLabel* label = new QLabel(tr("Allowed file extensions:"), widget); + widget_layout->addWidget(label); + } + + { + QListWidget* listwidget = new QListWidget(widget); + for (size_t i = 0; i < extensions.size(); i++) { + const auto& extension = extensions[i]; + { + QListWidgetItem* item = new QListWidgetItem(listwidget); + item->setCheckState(extension.GetEnabled() ? Qt::Checked : Qt::Unchecked); + item->setText(Strings::ToQString("." + extension.GetExtension())); + item->setData(Qt::UserRole, QVariant::fromValue(i)); + } + } + connect(listwidget, &QListWidget::itemChanged, this, [this](QListWidgetItem* item){ + if (!item) + return; + size_t i = item->data(Qt::UserRole).toUInt(); + extensions[i].SetEnabled(item->checkState()); + }); + + widget_layout->addWidget(listwidget); + } + + full_layout->addWidget(widget); } full_layout->setSpacing(10); - full_layout->addStretch(); return result; } @@ -83,11 +107,13 @@ void SettingsPageRecognition::SaveInfo() { session.config.recognition.detect_media_players = detect_media_players; session.recognition.players = players; + session.recognition.extensions = extensions; } SettingsPageRecognition::SettingsPageRecognition(QWidget* parent) : SettingsPage(parent, tr("Recognition")), - players(session.recognition.players) { + players(session.recognition.players), + extensions(session.recognition.extensions) { detect_media_players = session.config.recognition.detect_media_players; AddTab(CreatePlayersWidget(), tr("Media players")); }