hmp3-ng-2.19.0: app/Main.hs
-- Copyright (c) Don Stewart 2004-2008.
-- Copyright (c) Tuomo Valkonen 2004.
-- Copyright (c) 2019-2026 Galen Huntington
-- SPDX-License-Identifier: GPL-2.0-or-later
module Main where
import Base
import Core (start, shutdown, Options(..))
import Config qualified
import Keymap (keyLoop)
import Playlist (buildPlaylist, isEmpty)
import System.Posix.Signals (installHandler, Handler(Ignore, Default, Catch),
sigTERM, sigPIPE, sigINT, sigHUP , sigALRM, sigABRT)
import Data.ByteString.UTF8 qualified as UTF8
import Options.Applicative
-- ---------------------------------------------------------------------
-- | Set up the signal handlers
initSignals :: IO ()
initSignals = do
-- ignore
for_ [sigPIPE, sigALRM] \sig ->
installHandler sig Ignore Nothing
-- and exit if we get the following
for_ [sigINT, sigHUP, sigABRT, sigTERM] \sig ->
installHandler sig (Catch exitHandler) Nothing
exitHandler :: IO ()
exitHandler = do
releaseSignals -- in case shutdown itself gets stuck
shutdown $ Just "Killed"
releaseSignals :: IO ()
releaseSignals =
for_ [sigINT, sigPIPE, sigHUP, sigABRT, sigTERM] \sig ->
installHandler sig Default Nothing
------------------------------------------------------------------------
-- | Command-line parsing.
-- | The options together with the file/directory arguments.
invocation :: Parser (Options, [ByteString])
invocation = (,) <$> opts <*> files
where
opts = Options
<$> switch
(long "paused" <> short 'P' <> help "Start in a paused state")
<*> optional (strOption -- temporarily internal since feature needs work
(long "config" <> short 'c' <> metavar "FILE" <> internal
<> help "Read this config file instead of the XDG default"))
<*> optional (option (maybeReader prefixMatch) (
long "mode" <> short 'm' <> metavar "MODE"
<> help "Initial play mode (default: last selected, or once)"))
<*> option auto (
long "history" <> short 'h' <> metavar "NUM" <> value 61
<> help "Size of play history, up to 61 selectable" <> showDefault)
files = some $ argument (UTF8.fromString <$> str) (metavar "FILE|DIR...")
parserInfo :: ParserInfo (Options, [ByteString])
parserInfo = info (invocation <**> versionOpt <**> helper) $
fullDesc
<> header Config.versinfo
<> progDesc "Play mp3 files in a curses interface."
where
versionOpt = infoOption Config.versinfo
(hidden <> long "version" <> short 'V' <> help "Show version information")
-- XXX should this have tests?
prefixMatch :: (Enum a, Bounded a, Show a) => String -> Maybe a
prefixMatch s =
case [ x | x <- [minBound .. maxBound], s' `isPrefixOf` map toLower (show x) ] of
[x] -> Just x
_ -> Nothing
where s' = map toLower s
------------------------------------------------------------------------
main :: IO ()
main = do
(opts, args) <- customExecParser (prefs showHelpOnEmpty) parserInfo
list <- buildPlaylist args
when (isEmpty list) $
errorWithoutStackTrace "Error: No music files found."
initSignals
err <- either id absurd <$> try @SomeException do
start opts list
keyLoop
shutdown $ Just $ "Error: " ++ show err