# HG changeset patch # User Paper # Date 1718815875 14400 # Node ID a7d4e5107531c91d98299a6ab057e952faa12474 # Parent d260549151d63e8ed017b53e53616fbfd49cc2b6 dep/animone: REFACTOR ALL THE THINGS 1: animone now has its own syntax divergent from anisthesia, making different platforms actually have their own sections 2: process names in animone are now called `comm' (this will probably break things). this is what its called in bsd/linux so I'm just going to use it everywhere 3: the X11 code now checks for the existence of a UTF-8 window title and passes it if available 4: ANYTHING THATS NOT LINUX IS 100% UNTESTED AND CAN AND WILL BREAK! I still actually need to test the bsd code. to be honest I'm probably going to move all of the bsds into separate files because they're all essentially different operating systems at this point diff -r d260549151d6 -r a7d4e5107531 Makefile.am --- a/Makefile.am Wed Jun 19 06:32:25 2024 -0400 +++ b/Makefile.am Wed Jun 19 12:51:15 2024 -0400 @@ -106,18 +106,15 @@ $(top_srcdir)/rc/icons/24x24/question.png minori_linux_rc = \ - $(top_srcdir)/rc/sys/linux/Minori.desktop \ $(top_srcdir)/rc/sys/linux/Minori.png minori_osx_rc = \ - $(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/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/version.rc minori_scripts = \ diff -r d260549151d6 -r a7d4e5107531 dep/animone/README --- a/dep/animone/README Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/README Wed Jun 19 12:51:15 2024 -0400 @@ -33,7 +33,13 @@ what Animone will attempt to grab before falling back to the Quartz window name. On X11, Animone requires that the XRes extension is installed to retrieve window -PIDs. +PIDs. Animone will also attempt to retrieve window names in UTF-8 encoding; if +this is not possible it will be passed as whatever the current locale encoding +is. + +File paths will always be in UTF-8 on Windows and macOS. Linux and BSD treat +filenames as just a pile of bytes and it's no telling what encoding they're in +(but in most if not all cases they will be in UTF-8 as well) --- HISTORY --- Animone used to be under the name Animia, as in you'd contract anemia just from diff -r d260549151d6 -r a7d4e5107531 dep/animone/configure.ac --- a/dep/animone/configure.ac Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/configure.ac Wed Jun 19 12:51:15 2024 -0400 @@ -1,4 +1,4 @@ -AC_INIT([animone], [1.0.0]) +AC_INIT([animone], [2.0.0]) AC_CANONICAL_HOST @@ -29,16 +29,16 @@ build_windows=yes AC_CHECK_TOOL([WINDRES], [windres]) AC_SUBST(WINDRES) - AC_DEFINE([WIN32]) + AC_DEFINE([USE_WIN32]) ;; darwin*) # Mac OS X build_osx=yes - AC_DEFINE([MACOSX]) + AC_DEFINE([USE_MACOSX]) ;; linux*) build_linux=yes - AC_DEFINE([LINUX]) + AC_DEFINE([USE_LINUX]) ;; *) dnl BSDs @@ -46,10 +46,10 @@ AC_CHECK_LIB([kvm], [kvm_getfiles], [build_kvm=yes], [build_kvm=no]) if test "x$build_kvm" = "xyes"; then - AC_DEFINE([LIBKVM]) + AC_DEFINE([USE_LIBKVM]) AC_DEFINE([BSD]) elif test "x$build_libutil" = "xyes"; then - AC_DEFINE([LIBUTIL]) + AC_DEFINE([USE_LIBUTIL]) AC_DEFINE([BSD]) fi ;; @@ -58,7 +58,7 @@ 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]) + AC_DEFINE([USE_X11]) AC_SUBST([XCB_LIBS]) AC_SUBST([XCB_CFLAGS]) fi diff -r d260549151d6 -r a7d4e5107531 dep/animone/data/players.animone --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animone/data/players.animone Wed Jun 19 12:51:15 2024 -0400 @@ -0,0 +1,580 @@ +# 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: + win32: + Qt5QWindowIcon + executables: + win32: + 5KPlayer.exe + strategies: + open_files + +Ace Player HD + windows: + win32: + QWidget + executables: + win32: + ace_player.exe + 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: + win32: + TApplication + executables: + win32: + ALLPlayer.exe + strategies: + open_files + +Baka MPlayer + windows: + win32: + Qt5QWindowIcon + executables: + win32: + Baka MPlayer.exe + 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: + win32: + TBESTplayerApp.UnicodeClass + executables: + win32: + BESTplayer.exe + strategies: + open_files + window_title: + ^BESTplayer.*|(.+) - BESTplayer.* + +bomi + windows: + win32: + Qt5QWindowGLOwnDCIcon + executables: + win32: + bomi.exe + strategies: + open_files + window_title: + ^bomi|(.+) - bomi + +BS.Player + windows: + win32: + BSPlayer + executables: + win32: + bsplayer.exe + strategies: + open_files + +DivX Player + windows: + win32: + Qt5QWindowIcon + QWidget + executables: + win32: + DivX Player.exe + DivX Plus Player.exe + strategies: + open_files + +GOM Player + windows: + win32: + GomPlayer1.x + GomPlayerPlus32_2.x + GomPlayerPlus64_2.x + executables: + win32: + GOM.exe + GOM64.exe + strategies: + open_files + window_title: + ^GOM Player(?: Plus)|(.+)(?:\[Subtitle\]) - GOM Player(?: Plus) + +Kantaris + windows: + win32: + ^WindowsForms10\.Window\.20008\.app\..+ + executables: + win32: + Kantaris.exe + KantarisMain.exe + strategies: + open_files + window_title: + ^Kantaris.*|(.+) \d{2}:\d{2}:\d{2} - \d{2}:\d{2}:\d{2} + +KMPlayer + windows: + win32: + KMPlayer 64X + TApplication + executables: + win32: + KMPlayer.exe + KMPlayer64.exe + strategies: + open_files + window_title: + ^(?:The )?KMPlayer|(?:\[\d+/\d+\] )?(.+) - (?:The )?KMPlayer|(.+) + +Kodi + windows: + win32: + Kodi + XBMC + executables: + win32: + kodi.exe + XBMC.exe + strategies: + open_files + +Light Alloy + windows: + win32: + TApplication + executables: + win32: + LA.exe + strategies: + open_files + window_title: + ^Light Alloy.*|(.+) - Light Alloy.* + +Media Player Classic + windows: + win32: + MediaPlayerClassicW + executables: + win32: + mplayerc.exe + mplayerc64.exe + strategies: + open_files + # Depends on: Options -> Player -> Title bar + window_title: + ^Media Player Classic|(.+) - Media Player Classic + +Media Player Classic Qute Theater + windows: + win32: + ^Qt.+QWindowIcon + executables: + win32: + mpc-qt.exe + 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: + win32: + ^Qt.+QWindowIcon + executables: + win32: + memento.exe + strategies: + open_files + window_title: + ^Memento|(.+) - Memento + +Miro + windows: + win32: + gdkWindowToplevel + executables: + win32: + Miro.exe + strategies: + open_files + +MPC-BE + windows: + win32: + MediaPlayerClassicW + MPC-BE + executables: + win32: + mpc-be.exe + mpc-be64.exe + strategies: + open_files + # Depends on: Options -> Player -> Title bar + window_title: + ^MPC-BE.*|(.+) - MPC-BE.* + +MPC-HC + windows: + win32: + MediaPlayerClassicW + executables: + win32: + mpc-hc.exe + mpc-hc64.exe + # 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: + win32: + ^wxWindow@.* + wxWindowClassNR + executables: + win32: + mpcstar.exe + strategies: + open_files + window_title: + ^MPCSTAR.*|(.+) - MPCSTAR.* + +MPDN + windows: + win32: + ^WindowsForms10\.Window\.8\.app\..+ + executables: + win32: + MediaPlayerDotNet.exe + strategies: + open_files + window_title: + ^MPDN - Media Player .NET \((?:32|64)-bit Edition\)|(.*) - MPDN \((?:32|64)-bit Edition\) + +mpv + windows: + win32: + mpv + executables: + win32: + mpv.exe + 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: + win32: + ^WindowsForms10\.Window\.8\.app\..+ + executables: + win32: + mpvnet.exe + strategies: + open_files + window_title: + ^mpv\.net.*|(.+) - mpv\.net.* + +MV2Player + windows: + win32: + TApplication + executables: + win32: + Mv2Player.exe + Mv2PlayerPlus.exe + strategies: + open_files + # Depends on: Options -> Player -> Constant app. title + window_title: + ^MV2 Player|(.+) + +PotPlayer + windows: + win32: + PotPlayer + PotPlayer64 + executables: + win32: + PotPlayer.exe + PotPlayer64.exe + PotPlayerMini.exe + PotPlayerMini64.exe + # LAV Filters Megamix + sumire.exe + zuikaku.exe + strategies: + open_files + window_title: + ^PotPlayer|(.+) - PotPlayer + +SMPlayer + windows: + win32: + # Qt5QWindowIcon, Qt5152QWindowIcon, etc. + ^Qt.+QWindowIcon + # Older versions + QWidget + executables: + win32: + smplayer.exe + smplayer2.exe + 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: + win32: + DX_DISPLAY0 + executables: + win32: + Splash.exe + SplashLite.exe + strategies: + open_files + +SPlayer + windows: + win32: + MediaPlayerClassicW + executables: + win32: + splayer.exe + strategies: + open_files + # Does not work in theater mode. + window_title: + ^SPlayer|(?:\[(?:GPU Accel\+)?EVR\] )?(.+) - SPlayer + +UMPlayer + windows: + win32: + QWidget + executables: + win32: + umplayer.exe + 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: + win32: + # Qt5QWindowIcon, Qt5151QWindowIcon, etc. + ^Qt.+QWindowIcon + # Older versions + QWidget + # Skinnable interface + SkinWindowClass + x11: + vlc + executables: + win32: + vlc.exe + posix: + 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: + win32: + Chrome_WidgetWin_1 + executables: + win32: + WebTorrent.exe + strategies: + window_title: + ^WebTorrent(?: \(BETA\))?|Main Window|Preferences|About WebTorrent.*|(.+) + +Winamp + windows: + win32: + Winamp v1.x + executables: + win32: + winamp.exe + strategies: + open_files + window_title: + ^Winamp [\d.]+ Build \d+|\d+\. (.+) - Winamp(?: \[.+\])? + +Windows Media Player + windows: + win32: + WMPlayerApp + WMP Skin Host + executables: + win32: + wmplayer.exe + strategies: + open_files + +Zoom Player + windows: + win32: + TApplication + executables: + win32: + zplayer.exe + strategies: + open_files + window_title: + ^Zoom Player|(.+) - Zoom Player (?:FREE|MAX) + +################################################################################ +# Web browsers + +Brave Browser + windows: + win32: + Chrome_WidgetWin_1 + executables: + win32: + brave.exe + strategies: + ui_automation + window_title: + ^(.+) \(Private\)(?: - Brave)?|(.+) - Brave|(.+) + type: + web_browser + +Google Chrome + windows: + win32: + Chrome_WidgetWin_1 + executables: + win32: + chrome.exe + strategies: + ui_automation + window_title: + ^(.+) \(Incognito\)(?: - Google Chrome)?|(.+) - Google Chrome|(.+) + type: + web_browser + +Internet Explorer + windows: + win32: + IEFrame + executables: + win32: + iexplore.exe + strategies: + ui_automation + window_title: + ^(.+) - Internet Explorer(?: - \[InPrivate\])? + type: + web_browser + +Microsoft Edge + windows: + win32: + Chrome_WidgetWin_1 + executables: + win32: + msedge.exe + strategies: + ui_automation + window_title: + ^(.+) and \d+ more pages? - .+|(.+) - [^-]+ - Microsoft.*Edge|(.+) + type: + web_browser + +Mozilla Firefox + windows: + win32: + MozillaUIWindowClass + MozillaWindowClass + executables: + win32: + firefox.exe + strategies: + ui_automation + window_title: + ^(?:Mozilla Firefox|Firefox Developer Edition)|(.+) (?:-|—) (?:Mozilla Firefox|Firefox Developer Edition)(?: \(Private Browsing\))? + type: + web_browser + +Opera + windows: + win32: + Chrome_WidgetWin_1 + executables: + win32: + opera.exe + strategies: + ui_automation + window_title: + ^(.+) - Opera(?: \(Private\))? + type: + web_browser + +Waterfox + windows: + win32: + MozillaWindowClass + executables: + win32: + waterfox.exe + strategies: + ui_automation + window_title: + ^(.+) - Waterfox(?: \(Private Browsing\))? + type: + web_browser diff -r d260549151d6 -r a7d4e5107531 dep/animone/data/players.anisthesia --- a/dep/animone/data/players.anisthesia Wed Jun 19 06:32:25 2024 -0400 +++ /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: -# -# -# 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 diff -r d260549151d6 -r a7d4e5107531 dep/animone/include/animone.h --- a/dep/animone/include/animone.h Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/include/animone.h Wed Jun 19 12:51:15 2024 -0400 @@ -1,26 +1,25 @@ #ifndef ANIMONE_ANIMONE_H_ #define ANIMONE_ANIMONE_H_ +#include + #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 everywhere else */ - std::string name; + internal::pid_t pid = 0; /* platform-dependent PID */ + ExecutablePlatform platform = ExecutablePlatform::Unknown; /* platform of the executable */ + std::string comm; /* the full filename of the executable */ }; struct Window { - unsigned int id = 0; - std::string class_name; - std::string text; /* title bar text */ + std::uint32_t id = 0; /* platform-dependent window id */ + WindowPlatform platform = WindowPlatform::Unknown; /* platform of the window */ + std::string class_name; /* class name on win32 and x11, bundle ID on macos */ + std::string text; /* title bar text if available */ }; struct Result { diff -r d260549151d6 -r a7d4e5107531 dep/animone/include/animone/media.h --- a/dep/animone/include/animone/media.h Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/include/animone/media.h Wed Jun 19 12:51:15 2024 -0400 @@ -8,6 +8,7 @@ namespace animone { +// XXX where is this used? using media_time_t = std::chrono::milliseconds; enum class MediaInfoType { diff -r d260549151d6 -r a7d4e5107531 dep/animone/include/animone/player.h --- a/dep/animone/include/animone/player.h Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/include/animone/player.h Wed Jun 19 12:51:15 2024 -0400 @@ -3,9 +3,24 @@ #include #include +#include namespace animone { +enum class ExecutablePlatform { + Posix, // Posix platforms that aren't OS X + Win32, // Windows + Xnu, // OS X + Unknown, // ... +}; + +enum class WindowPlatform { + Quartz, // OS X + Win32, // Windows + X11, // X11 + Unknown, // ... +}; + enum class Strategy { WindowTitle, OpenFiles, @@ -21,8 +36,8 @@ PlayerType type = PlayerType::Default; std::string name; std::string window_title_format; - std::vector windows; - std::vector executables; + std::map> windows; + std::map> executables; std::vector strategies; }; diff -r d260549151d6 -r a7d4e5107531 dep/animone/include/animone/types.h --- a/dep/animone/include/animone/types.h Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/include/animone/types.h Wed Jun 19 12:51:15 2024 -0400 @@ -2,16 +2,18 @@ #define ANIMONE_ANIMONE_TYPES_H_ /* define this as unsigned long (DWORD) on win32 so we - don't force the user to include or */ + * don't force the user to include or */ #ifdef _WIN32 +# include + namespace animone::internal { -typedef unsigned long pid_t; +using pid_t = std::uint32_t; } #else /* shouldn't be that big, right? */ # include namespace animone::internal { -typedef ::pid_t pid_t; +using pid_t = ::pid_t; } #endif diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/animone.cc --- a/dep/animone/src/animone.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/animone.cc Wed Jun 19 12:51:15 2024 -0400 @@ -16,17 +16,19 @@ namespace internal { static bool IsExecutableInList(const Player& player, const Process& proc) { - for (const auto& pattern : player.executables) - if (util::CheckPattern(pattern, proc.name)) - return true; + for (const auto& [platform, comms] : player.executables) + for (const auto& comm : comms) + if (platform == proc.platform && util::CheckPattern(comm, proc.comm)) + 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; + for (const auto& [platform, class_names] : player.windows) + for (const auto& class_name : class_names) + if (platform == window.platform && util::CheckPattern(class_name, window.class_name)) + return true; return false; } diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/fd.cc --- a/dep/animone/src/fd.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/fd.cc Wed Jun 19 12:51:15 2024 -0400 @@ -1,18 +1,18 @@ #include "animone/fd.h" -#ifdef WIN32 +#ifdef USE_WIN32 # include "animone/fd/win32.h" #endif -#ifdef LINUX +#ifdef USE_LINUX # include "animone/fd/proc.h" #endif -#ifdef MACOSX +#ifdef USE_MACOSX # include "animone/fd/xnu.h" #endif -#ifdef BSD +#ifdef USE_BSD # include "animone/fd/bsd.h" #endif @@ -21,19 +21,19 @@ bool EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) { bool success = false; -#ifdef WIN32 +#ifdef USE_WIN32 success ^= win32::EnumerateOpenFiles(pids, open_file_proc); #endif -#ifdef LINUX +#ifdef USE_LINUX success ^= proc::EnumerateOpenFiles(pids, open_file_proc); #endif -#ifdef MACOSX +#ifdef USE_MACOSX success ^= xnu::EnumerateOpenFiles(pids, open_file_proc); #endif -#ifdef BSD +#ifdef USE_BSD success ^= bsd::EnumerateOpenFiles(pids, open_file_proc); #endif @@ -43,19 +43,19 @@ bool EnumerateOpenProcesses(process_proc_t process_proc) { bool success = false; -#ifdef WIN32 +#ifdef USE_WIN32 success ^= win32::EnumerateOpenProcesses(process_proc); #endif -#ifdef LINUX +#ifdef USE_LINUX success ^= proc::EnumerateOpenProcesses(process_proc); #endif -#ifdef MACOSX +#ifdef USE_MACOSX success ^= xnu::EnumerateOpenProcesses(process_proc); #endif -#ifdef BSD +#ifdef USE_BSD success ^= bsd::EnumerateOpenProcesses(process_proc); #endif @@ -65,19 +65,19 @@ bool GetProcessName(pid_t pid, std::string& name) { bool success = false; -#ifdef WIN32 +#ifdef USE_WIN32 success ^= win32::GetProcessName(pid, name); #endif -#ifdef LINUX +#ifdef USE_LINUX success ^= proc::GetProcessName(pid, name); #endif -#ifdef MACOSX +#ifdef USE_MACOSX success ^= xnu::GetProcessName(pid, name); #endif -#ifdef BSD +#ifdef USE_BSD success ^= bsd::GetProcessName(pid, name); #endif diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/fd/bsd.cc --- a/dep/animone/src/fd/bsd.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/fd/bsd.cc Wed Jun 19 12:51:15 2024 -0400 @@ -93,7 +93,7 @@ } for (int i = 0; i < entries; i++) { - if (!process_proc({kinfo[i].ki_paddr->p_pid, Basename(kinfo[i].ki_paddr->p_comm)})) { + if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = kinfo[i].ki_paddr->p_pid, .comm = Basename(kinfo[i].ki_paddr->p_comm)})) { kvm_close(kernel); return false; } @@ -123,7 +123,7 @@ return false; for (int i = 0; i < length / sizeof(result[0]); i++) - if (!process_proc({result[i].ki_pid, result[i].ki_comm})) + if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = result[i].ki_pid, .comm = result[i].ki_comm})) return false; return true; diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/fd/proc.cc --- a/dep/animone/src/fd/proc.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/fd/proc.cc Wed Jun 19 12:51:15 2024 -0400 @@ -95,6 +95,7 @@ for (const auto& dir : std::filesystem::directory_iterator{PROC_LOCATION}) { Process proc; + proc.platform = ExecutablePlatform::Posix; try { proc.pid = util::StringToInt(dir.path().stem()); @@ -103,7 +104,7 @@ continue; } - if (!GetProcessName(proc.pid, proc.name)) + if (!GetProcessName(proc.pid, proc.comm)) continue; if (!process_proc(proc)) diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/fd/win32.cc --- a/dep/animone/src/fd/win32.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/fd/win32.cc Wed Jun 19 12:51:15 2024 -0400 @@ -180,33 +180,36 @@ } bool GetProcessName(pid_t pid, std::string& name) { - std::wstring wname = GetProcessPath(pid); - if (wname.empty()) + std::string path = GetProcessPath(pid); + if (path.empty() || !VerifyProcessPath(path)) return false; - name = ToUtf8String(GetFileNameWithoutExtension(GetFileNameFromPath(wname))); + name = GetFileNameFromPath(path); + if (!VerifyProcessFileName(name)) + return false; + return true; } bool EnumerateOpenProcesses(process_proc_t process_proc) { - HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) + Handle process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (process_snap.get() == INVALID_HANDLE_VALUE) return false; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); - if (!::Process32First(hProcessSnap, &pe32)) - return false; - - if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) + if (!::Process32First(process_snap.get(), &pe32)) return false; - while (::Process32Next(hProcessSnap, &pe32)) - if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) + do { + std::string name; + if (!GetProcessName(pe32.th32ProcessID, name)) + continue; + + if (!process_proc({.platform = ExecutablePlatform::Win32, .pid = pe32.th32ProcessID, .comm = name})) return false; - - ::CloseHandle(hProcessSnap); + } while (::Process32Next(process_snap.get(), &pe32)); return true; } diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/fd/xnu.cc --- a/dep/animone/src/fd/xnu.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/fd/xnu.cc Wed Jun 19 12:51:15 2024 -0400 @@ -48,8 +48,8 @@ for (int i = 0; i < pids_size; i++) { std::string result; - osx::util::GetProcessName(pids[i], result); - if (!process_proc({pids[i], result})) + GetProcessName(pids[i], result); + if (!process_proc({.platform = ExecutablePlatform::Xnu, .pid = pids[i], .comm = result})) return false; } diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/player.cc --- a/dep/animone/src/player.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/player.cc Wed Jun 19 12:51:15 2024 -0400 @@ -5,19 +5,30 @@ #include #include #include +#include + +#include namespace animone { namespace internal::parser { -enum class State { - ExpectPlayerName, - ExpectSection, - ExpectWindow, - ExpectExecutable, - ExpectStrategy, - ExpectType, - ExpectWindowTitle, +struct State { + enum class Name { + ExpectPlayerName, + ExpectSection, + ExpectWindowPlatform, + ExpectExecutablePlatform, + ExpectWindow, + ExpectExecutable, + ExpectStrategy, + ExpectType, + ExpectWindowTitle, + }; + + Name state = Name::ExpectPlayerName; + WindowPlatform window_platform = WindowPlatform::Unknown; + ExecutablePlatform executable_platform = ExecutablePlatform::Unknown; }; size_t GetIndentation(const std::string& line) { @@ -26,42 +37,64 @@ bool HandleIndentation(const size_t current, const std::vector& players, State& state) { // Each state has a definitive expected indentation - const auto expected = [&state]() -> size_t { + const auto expected = [](const State::Name& 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; + case State::Name::ExpectPlayerName: return 0; + case State::Name::ExpectSection: return 1; + case State::Name::ExpectWindowPlatform: + case State::Name::ExpectExecutablePlatform: + case State::Name::ExpectStrategy: + case State::Name::ExpectType: return 2; + case State::Name::ExpectWindow: + case State::Name::ExpectExecutable: + case State::Name::ExpectWindowTitle: return 3; } - }(); + }(state.state); - if (current > expected) + if (current > expected) { + std::cerr << "animone: excessive indentation found" << std::endl; return false; // Disallow excessive indentation + } if (current < expected) { - auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; }; - switch (state) { - case State::ExpectWindow: + const std::optional st = [current, state]() -> std::optional { + switch (current) { + case 0: return State::Name::ExpectPlayerName; + default: + case 1: return State::Name::ExpectSection; + case 2: + switch (state.state) { + case State::Name::ExpectWindow: return State::Name::ExpectWindowPlatform; + case State::Name::ExpectExecutable: return State::Name::ExpectExecutablePlatform; + default: return std::nullopt; + } + } + }(); + if (!st.has_value()) + return false; + + switch (state.state) { + case State::Name::ExpectWindow: if (players.back().windows.empty()) return false; - fix_state(); + state.state = st.value(); break; - case State::ExpectExecutable: + case State::Name::ExpectExecutable: if (players.back().executables.empty()) return false; - fix_state(); + state.state = st.value(); break; - case State::ExpectStrategy: + case State::Name::ExpectStrategy: if (players.back().strategies.empty()) return false; - fix_state(); + state.state = st.value(); break; - case State::ExpectType: fix_state(); break; - case State::ExpectWindowTitle: return false; + case State::Name::ExpectType: + state.state = st.value(); + break; + case State::Name::ExpectWindowTitle: + return false; } } @@ -69,33 +102,63 @@ } bool HandleState(std::string& line, std::vector& players, State& state) { - switch (state) { - case State::ExpectPlayerName: + switch (state.state) { + case State::Name::ExpectPlayerName: players.push_back(Player()); players.back().name = line; - state = State::ExpectSection; + state.state = State::Name::ExpectSection; break; - case State::ExpectSection: { - static const std::map sections = { - {"windows", State::ExpectWindow }, - {"executables", State::ExpectExecutable}, - {"strategies", State::ExpectStrategy }, - {"type", State::ExpectType }, + case State::Name::ExpectSection: { + static const std::map sections = { + {"windows", State::Name::ExpectWindowPlatform}, + {"executables", State::Name::ExpectExecutablePlatform}, + {"strategies", State::Name::ExpectStrategy }, + {"type", State::Name::ExpectType }, }; util::TrimRight(line, ":"); const auto it = sections.find(line); if (it == sections.end()) return false; - state = it->second; + state.state = it->second; + break; + } + + case State::Name::ExpectWindowPlatform: { + static const std::map platforms = { + {"quartz", WindowPlatform::Quartz}, + {"win32", WindowPlatform::Win32}, + {"x11", WindowPlatform::X11}, + }; + util::TrimRight(line, ":"); + const auto it = platforms.find(line); + if (it == platforms.end()) + return false; + state.state = State::Name::ExpectWindow; + state.window_platform = it->second; break; } - case State::ExpectWindow: players.back().windows.push_back(line); break; + case State::Name::ExpectExecutablePlatform: { + static const std::map platforms = { + {"posix", ExecutablePlatform::Posix}, + {"win32", ExecutablePlatform::Win32}, + {"macosx", ExecutablePlatform::Xnu}, + }; + util::TrimRight(line, ":"); + const auto it = platforms.find(line); + if (it == platforms.end()) + return false; + state.state = State::Name::ExpectExecutable; + state.executable_platform = it->second; + break; + } - case State::ExpectExecutable: players.back().executables.push_back(line); break; + case State::Name::ExpectWindow: players.back().windows[state.window_platform].push_back(line); break; - case State::ExpectStrategy: { + case State::Name::ExpectExecutable: players.back().executables[state.executable_platform].push_back(line); break; + + case State::Name::ExpectStrategy: { static const std::map strategies = { {"window_title", Strategy::WindowTitle }, {"open_files", Strategy::OpenFiles }, @@ -108,12 +171,12 @@ const auto strategy = it->second; players.back().strategies.push_back(strategy); switch (strategy) { - case Strategy::WindowTitle: state = State::ExpectWindowTitle; break; + case Strategy::WindowTitle: state.state = State::Name::ExpectWindowTitle; break; } break; } - case State::ExpectType: { + case State::Name::ExpectType: { static const std::map types = { {"default", PlayerType::Default }, {"web_browser", PlayerType::WebBrowser}, @@ -125,9 +188,9 @@ break; } - case State::ExpectWindowTitle: + case State::Name::ExpectWindowTitle: players.back().window_title_format = line; - state = State::ExpectStrategy; + state.state = State::Name::ExpectStrategy; break; } @@ -145,9 +208,10 @@ std::istringstream stream(data); std::string line; size_t indentation = 0; - auto state = internal::parser::State::ExpectPlayerName; + internal::parser::State state; - while (std::getline(stream, line, '\n')) { + int ln = 1; + for (; std::getline(stream, line, '\n'); ln++) { if (line.empty()) continue; // Ignore empty lines @@ -159,11 +223,15 @@ if (line.empty() || line.front() == '#') continue; // Ignore empty lines and comments - if (!internal::parser::HandleIndentation(indentation, players, state)) + if (!internal::parser::HandleIndentation(indentation, players, state)) { + std::cerr << "animone: indentation: failed on line " << ln << std::endl; return false; + } - if (!internal::parser::HandleState(line, players, state)) + if (!internal::parser::HandleState(line, players, state)) { + std::cerr << "animone: state: failed on line " << ln << std::endl; return false; + } } return !players.empty(); diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/util/win32.cc --- a/dep/animone/src/util/win32.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/util/win32.cc Wed Jun 19 12:51:15 2024 -0400 @@ -47,7 +47,7 @@ return ret; } -std::wstring GetProcessPath(DWORD process_id) { +std::string GetProcessPath(DWORD process_id) { // If we try to open a SYSTEM process, this function fails and the last error // code is ERROR_ACCESS_DENIED. // @@ -68,22 +68,17 @@ return std::wstring(); buffer.resize(buf_size); - return buffer; + return ToUtf8String(buffer); } -std::wstring GetFileNameFromPath(const std::wstring& path) { +std::string GetFileNameFromPath(const std::string& 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); + SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, SHGFP_TYPE_CURRENT, &path_wch); std::wstring path_wstr(path_wch); CoTaskMemFree(path_wch); return path_wstr; @@ -99,7 +94,33 @@ std::wstring windir = GetSystemDirectory(); ::CharUpperBuffW(&windir.front(), windir.length()); + // XXX wtf is 4? return path.find(windir) == 4; } +bool VerifyProcessPath(const std::string& path) { + return !path.empty() && !IsSystemDirectory(path); +} + +bool VerifyProcessFileName(const std::string& name) { + static const std::set invalid_names = { + // System files + "explorer.exe", // Windows Explorer + "taskeng.exe", // Task Scheduler Engine + "taskhost.exe", // Host Process for Windows Tasks + "taskhostex.exe", // Host Process for Windows Tasks + "taskmgr.exe", // Task Manager + "services.exe", // Service Control Manager + }; + + if (name.empty()) + return false; + + for (const auto& invalid_name : invalid_names) + if (util::EqualStrings(name, invalid_name)) + return false; + + return true; +} + } // namespace animone::internal::win32 diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/win.cc --- a/dep/animone/src/win.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/win.cc Wed Jun 19 12:51:15 2024 -0400 @@ -1,14 +1,14 @@ #include "animone/win.h" -#ifdef WIN32 +#ifdef USE_WIN32 # include "animone/win/win32.h" #endif -#ifdef MACOSX +#ifdef USE_MACOSX # include "animone/win/quartz.h" #endif -#ifdef X11 +#ifdef USE_X11 # include "animone/win/x11.h" #endif @@ -17,15 +17,15 @@ bool EnumerateWindows(window_proc_t window_proc) { bool success = false; -#ifdef WIN32 +#ifdef USE_WIN32 success |= win32::EnumerateWindows(window_proc); #endif -#ifdef MACOSX +#ifdef USE_MACOSX success |= quartz::EnumerateWindows(window_proc); #endif -#ifdef X11 +#ifdef USE_X11 success |= x11::EnumerateWindows(window_proc); #endif diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/win/quartz.cc --- a/dep/animone/src/win/quartz.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/win/quartz.cc Wed Jun 19 12:51:15 2024 -0400 @@ -265,22 +265,17 @@ continue; Process proc; - { - CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); - if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name)) - fd::GetProcessName(proc.pid, proc.name); - } + proc.platform = ExecutablePlatform::Xnu; + CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); + if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.comm)) + fd::GetProcessName(proc.pid, proc.comm); Window win; - { - CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); + win.platform = WindowPlatform::Quartz; + CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); - if (!GetProcessBundleIdentifier(proc.pid, win.class_name)) - /* XXX is this right? */ - CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name); - - GetWindowTitle(win.id, proc.pid, win.text); - } + GetProcessBundleIdentifier(proc.pid, win.class_name); + GetWindowTitle(win.id, proc.pid, win.text); if (!window_proc(proc, win)) { CFRelease(windows); diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/win/win32.cc --- a/dep/animone/src/win/win32.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/win/win32.cc Wed Jun 19 12:51:15 2024 -0400 @@ -66,34 +66,17 @@ static bool VerifyClassName(const std::wstring& name) { static const std::set 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 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 + "#32770", // Dialog box + "CabinetWClass", // Windows Explorer + "ComboLBox", + "DDEMLEvent", + "DDEMLMom", + "DirectUIHWND", + "GDI+ Hook Window Class", + "IME", + "Internet Explorer_Hidden", + "MSCTFIME UI", + "tooltips_class32", }; return !name.empty() && !invalid_names.count(name); @@ -109,19 +92,17 @@ return TRUE; Window window; + window.platform = WindowPlatform::Win32; window.id = static_cast(reinterpret_cast(hwnd)); window.text = ToUtf8String(GetWindowText(hwnd)); - - { - std::wstring class_name = GetWindowClassName(hwnd); - window.class_name = ToUtf8String(class_name); - if (!VerifyClassName(class_name)) - return TRUE; - } + window.class_name = ToUtf8String(GetWindowClassName(hwnd)); + if (!VerifyClassName(window.class_name)) + return TRUE; Process process; + process.platform = ExecutablePlatform::Win32; process.pid = GetWindowProcessId(hwnd); - GetProcessName(process.pid, process.name); + GetProcessName(process.pid, process.comm); auto& window_proc = *reinterpret_cast(param); if (!window_proc(process, window)) diff -r d260549151d6 -r a7d4e5107531 dep/animone/src/win/x11.cc --- a/dep/animone/src/win/x11.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/dep/animone/src/win/x11.cc Wed Jun 19 12:51:15 2024 -0400 @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include +#include #include @@ -42,18 +44,32 @@ template using XcbPtr = std::unique_ptr>; -static bool GetAllTopLevelWindowsEWMH(xcb_connection_t* connection, const std::vector& roots, +/* -------------------------------------------------------------- + * atom cruft */ + +enum class NeededAtom { + /* EWMH */ + NET_CLIENT_LIST, + NET_WM_NAME, + UTF8_STRING, + + /* ICCCM */ + WM_STATE, +}; + +static const std::unordered_map atom_strings = { + {NeededAtom::NET_CLIENT_LIST, "_NET_CLIENT_LIST"}, + {NeededAtom::NET_WM_NAME, "_NET_WM_NAME"}, + {NeededAtom::UTF8_STRING, "UTF8_STRING"}, + + {NeededAtom::WM_STATE, "WM_STATE"}, +}; + +using XcbAtoms = std::unordered_map; + +static bool GetAllTopLevelWindowsEWMH(xcb_connection_t* connection, const XcbAtoms& atoms, const std::vector& roots, std::set& 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()); - XcbPtr reply(::xcb_intern_atom_reply(connection, cookie, NULL)); - - xcb_atom_t atom = reply->atom; - - return atom; - }(); - if (Atom__NET_CLIENT_LIST == XCB_ATOM_NONE) + if (atoms.at(NeededAtom::NET_CLIENT_LIST) == XCB_ATOM_NONE) return false; // BTFO bool success = false; @@ -62,7 +78,7 @@ 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)); + cookies.push_back(::xcb_get_property(connection, 0, root, atoms.at(NeededAtom::NET_CLIENT_LIST), XCB_ATOM_ANY, 0L, UINT_MAX)); for (const auto& cookie : cookies) { XcbPtr reply(::xcb_get_property_reply(connection, cookie, NULL)); @@ -81,88 +97,95 @@ 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 - */ +/* This should be called with a list of toplevels for each root. */ 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& result) { - /* The depth we should start returning at. */ - static constexpr int CUTOFF = 1; - - bool success = false; - + /* The level of depth we want to cut off past; since we want to go over each top level window, + * we cut off after we've passed the root window and the toplevel. */ + static constexpr int CUTOFF = 2; std::vector 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) { - XcbPtr query_tree_reply(::xcb_query_tree_reply(connection, cookie, NULL)); + for (int i = 0; i < cookies.size(); i++) { + /* XXX is it *really* okay to ask xcb for a cookie and then never ask for a reply? + * valgrind doesn't complain, so I'm not gonna care for now. */ + XcbPtr query_tree_reply(::xcb_query_tree_reply(connection, cookies[i], NULL)); xcb_window_t* tree_children = ::xcb_query_tree_children(query_tree_reply.get()); int tree_children_len = ::xcb_query_tree_children_length(query_tree_reply.get()); - std::vector state_property_cookies; - state_property_cookies.reserve(tree_children_len); + /* search for any window with a WM_STATE property */ + std::vector state_cookies; + state_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)); + state_cookies.push_back( + ::xcb_get_property(connection, 0, tree_children[i], Atom_WM_STATE, Atom_WM_STATE, 0, 4L)); + + bool found = false; for (int i = 0; i < tree_children_len; i++) { - XcbPtr get_property_reply(::xcb_get_property_reply(connection, state_property_cookies[i], NULL)); + XcbPtr get_property_reply(::xcb_get_property_reply(connection, state_cookies[i], NULL)); + if (!get_property_reply) + continue; - /* 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) { + /* did we get valid data? */ + if (get_property_reply->type == Atom_WM_STATE || get_property_reply->format != 0 || get_property_reply->bytes_after != 0) { + int len = ::xcb_get_property_value_length(get_property_reply.get()); + if (len < sizeof(uint32_t)) + continue; + + uint32_t state = *reinterpret_cast(::xcb_get_property_value(get_property_reply.get())); + if (state != 1) // NormalState + continue; + result.insert(tree_children[i]); + found = true; 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; + if (found) continue; - } + + bool res = WalkWindows(connection, depth + 1, Atom_WM_STATE, tree_children, tree_children_len, result); + + if (depth >= CUTOFF) + return res; } - return success; + return true; } -static bool GetAllTopLevelWindowsICCCM(xcb_connection_t* connection, const std::vector& roots, +static bool GetAllTopLevelWindowsICCCM(xcb_connection_t* connection, const XcbAtoms& atoms, const std::vector& roots, std::set& result) { - bool success = false; + if (atoms.at(NeededAtom::WM_STATE) == XCB_ATOM_NONE) + return 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()); - XcbPtr reply(::xcb_intern_atom_reply(connection, cookie, NULL)); + return WalkWindows(connection, 0, atoms.at(NeededAtom::WM_STATE), roots.data(), roots.size(), result); +} - xcb_atom_t atom = reply->atom; +static XcbAtoms InitializeAtoms(xcb_connection_t* connection) { + XcbAtoms atoms; + + std::unordered_map atom_cookies; - return atom; - }(); - if (Atom_WM_STATE == XCB_ATOM_NONE) - return success; - - std::vector cookies; - cookies.reserve(roots.size()); + for (const auto& [atom, str] : atom_strings) + atom_cookies[atom] = ::xcb_intern_atom(connection, 1, str.size(), str.data()); - for (const auto& root : roots) - cookies.push_back(::xcb_query_tree(connection, root)); + for (const auto& [atom, cookie] : atom_cookies) { + XcbPtr reply(::xcb_intern_atom_reply(connection, cookie, NULL)); + if (!reply) + atoms[atom] = XCB_ATOM_NONE; - for (const auto& cookie : cookies) - success |= WalkWindows(connection, 0, Atom_WM_STATE, roots.data(), roots.size(), result); + atoms[atom] = reply->atom; + } - return success; + return atoms; } bool EnumerateWindows(window_proc_t window_proc) { @@ -173,6 +196,8 @@ if (xcb_connection_has_error(connection)) return false; + XcbAtoms atoms = InitializeAtoms(connection); + std::set windows; { std::vector roots; @@ -182,15 +207,16 @@ roots.push_back(iter.data->root); } - if (!GetAllTopLevelWindowsEWMH(connection, roots, windows)) - GetAllTopLevelWindowsICCCM(connection, roots, windows); + if (!GetAllTopLevelWindowsEWMH(connection, atoms, roots, windows)) + GetAllTopLevelWindowsICCCM(connection, atoms, 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; + xcb_get_property_cookie_t class_name; + xcb_get_property_cookie_t name_utf8; + xcb_get_property_cookie_t name; + xcb_res_query_client_ids_cookie_t pid; }; std::vector window_cookies; @@ -200,19 +226,22 @@ 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 = window, + .class_name = ::xcb_get_property(connection, 0, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0L, 2048L), + .name_utf8 = ::xcb_get_property(connection, 0, window, atoms[NeededAtom::NET_WM_NAME], atoms[NeededAtom::UTF8_STRING], 0L, UINT_MAX), + .name = ::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0L, UINT_MAX), + .pid = ::xcb_res_query_client_ids(connection, 1, &spec), + }; window_cookies.push_back(window_cookie); } for (const auto& window_cookie : window_cookies) { - Window win = {0}; + Window win; win.id = window_cookie.window; { /* Class name */ - XcbPtr reply(::xcb_get_property_reply(connection, window_cookie.class_property_cookie, NULL)); + XcbPtr reply(::xcb_get_property_reply(connection, window_cookie.class_name, NULL)); if (reply && reply->format == 8) { const char* data = reinterpret_cast(::xcb_get_property_value(reply.get())); @@ -226,32 +255,47 @@ } { /* Title text */ - XcbPtr reply(::xcb_get_property_reply(connection, window_cookie.name_property_cookie, NULL)); + XcbPtr reply_utf8(::xcb_get_property_reply(connection, window_cookie.name_utf8, NULL)); + XcbPtr reply(::xcb_get_property_reply(connection, window_cookie.name, NULL)); + int utf8_len = ::xcb_get_property_value_length(reply_utf8.get()); + int len = ::xcb_get_property_value_length(reply.get()); - if (reply) { + if (reply_utf8 && utf8_len > 0) { + const char* data = reinterpret_cast(::xcb_get_property_value(reply_utf8.get())); + + win.text = std::string(data, utf8_len); + } else if (reply && len > 0) { const char* data = reinterpret_cast(::xcb_get_property_value(reply.get())); - int len = ::xcb_get_property_value_length(reply.get()); win.text = std::string(data, len); } } - Process proc = {0}; + Process proc; + proc.platform = ExecutablePlatform::Posix; // not entirely correct, but whatever. who cares { /* PID */ - XcbPtr reply(::xcb_res_query_client_ids_reply(connection, window_cookie.pid_property_cookie, NULL)); + XcbPtr reply(::xcb_res_query_client_ids_reply(connection, window_cookie.pid, NULL)); if (reply) { xcb_res_client_id_value_iterator_t it = ::xcb_res_query_client_ids_ids_iterator(reply.get()); 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 = *::xcb_res_client_id_value_value(it.data); - GetProcessName(proc.pid, proc.name); /* fill this in if we can */ + GetProcessName(proc.pid, proc.comm); /* fill this in if we can */ break; } } } } + /* debug printing + std::cout << "window found: " << std::hex << win.id << std::dec << "\n" + << " name: " << win.text << "\n" + << " class: " << win.class_name << "\n" + << " pid: " << proc.pid << "\n" + << " comm: " << proc.name << std::endl; + */ + if (!window_proc(proc, win)) { ::xcb_disconnect(connection); return false; diff -r d260549151d6 -r a7d4e5107531 rc/animone.qrc --- a/rc/animone.qrc Wed Jun 19 06:32:25 2024 -0400 +++ b/rc/animone.qrc Wed Jun 19 12:51:15 2024 -0400 @@ -1,5 +1,5 @@ - ../dep/animone/data/players.anisthesia + ../dep/animone/data/players.animone diff -r d260549151d6 -r a7d4e5107531 src/core/config.cc --- a/src/core/config.cc Wed Jun 19 06:32:25 2024 -0400 +++ b/src/core/config.cc Wed Jun 19 12:51:15 2024 -0400 @@ -69,7 +69,7 @@ recognition.detect_media_players = toml::find_or(data, "Recognition", "Detect media players", true); { - QFile f(":/players.anisthesia"); + QFile f(":/players.animone"); if (!f.exists()) return false;