mpvguihs-0.1.1: MpvPlayer/Frontend/Main.hs
{-# LANGUAGE TemplateHaskell #-}
module Main where
import MpvPlayer.Frontend.GUITemplates
import MpvPlayer.Backend
import Data.IORef
import Data.Maybe
import Data.Char
import System.Directory
import System.FilePath
import qualified System.IO as SIO
import Control.Monad
import Control.Monad.Reader
import System.Environment
import System.Directory
import Data.Version
import Paths_mpvguihs
data App = App {
appHandles :: Handles,
appPlayer :: Maybe (IORef Player),
appToggleSigId :: Maybe (ConnectId ToggleToolButton),
appVolSigId :: Maybe (ConnectId VolumeButton),
appStatusContextId :: ContextId,
appCmdLine :: String
}
confFile :: IO FilePath
confFile = do
dir <- getAppUserDataDirectory "mpvguihs"
exists <- doesDirectoryExist dir
when (not exists) $ createDirectoryIfMissing True dir
return $ dir `combine` "mpvguihs.conf"
openFile :: IORef App -> FilePath -> IO ()
openFile appRef filename = do
app <- readIORef appRef
when (isJust $ appPlayer app) $ do
let p = fromJust $ appPlayer app
mpvStop p
mpvTerminate p
let fsWin = fullscreenWindow $ appHandles app
let box = background $ appHandles app
containerForeach box $ containerRemove box
socket <- socketNew
set socket [widgetCanFocus := True,
widgetSensitive := True]
widgetAddEvents socket [AllEventsMask]
containerAdd box socket
widgetShow socket
wid <- socketGetId socket
playerRef <- mpvPlay (fromNativeWindowId wid) filename (appCmdLine app)
writeIORef appRef $ app { appPlayer = Just playerRef }
closeFile :: IORef App -> IO ()
closeFile appRef = do
app <- readIORef appRef
let hs = appHandles app
case appPlayer app of
Nothing -> return ()
Just playerRef -> do
mpvTerminate playerRef
let fsWin = fullscreenWindow hs
normalWin = mainWindow hs
box = background hs
containerForeach box $ containerRemove box
containerAdd box $ backgroundImage hs
widgetShowAll normalWin
fs <- widgetGetMapped fsWin
when fs $ do
windowUnfullscreen fsWin
widgetHide fsWin
writeIORef appRef $ app { appPlayer = Nothing }
showOpenDialog :: IORef App -> IO ()
showOpenDialog appRef = do
app <- readIORef appRef
dialog <- fileChooserDialogNew Nothing
(Just $ mainWindow $ appHandles app) FileChooserActionOpen []
dialogAddButton dialog "gtk-cancel" $ ResponseUser 0
dialogAddButton dialog "gtk-open" $ ResponseUser 1
ResponseUser resp <- dialogRun dialog
when (resp == 1) $ do file <- fileChooserGetFilename dialog
case file of
Just fn -> openFile appRef fn
Nothing -> return ()
widgetDestroy dialog
setCmdLine appRef cmdLine = do
modifyIORef appRef $ \app -> app { appCmdLine = cmdLine }
cf <- confFile
h <- SIO.openFile cf SIO.WriteMode
SIO.hPutStr h cmdLine
SIO.hClose h
showSettingsDialog :: IORef App -> IO ()
showSettingsDialog appRef = do
app <- readIORef appRef
let hs = appHandles app
let s = settingsDialog $ appHandles app
entrySetText (cmdLineEntry hs) (appCmdLine app)
r <- dialogRun s
case r of
ResponseUser 1 -> do
cmdLine <- entryGetText (cmdLineEntry hs)
setCmdLine appRef cmdLine
_ -> return ()
widgetHide s
showAboutDialog :: IORef App -> IO ()
showAboutDialog appRef = do
app <- readIORef appRef
let d = aboutDialog $ appHandles app
void $ dialogRun d
widgetHide d
playToggle :: IORef App -> IO ()
playToggle appRef = do
app <- readIORef appRef
pressed <- toggleToolButtonGetActive (playButton $ appHandles app)
case (appPlayer app) of
Just p -> if pressed
then mpvUnpause p
else mpvPause p
Nothing -> return ()
seek :: IORef App -> Double -> IO ()
seek appRef value = do
app <- readIORef appRef
when (isJust $ appPlayer app) $
mpvSeek (fromJust $ appPlayer app) value
setVolume :: IORef App -> Double -> IO ()
setVolume appRef vol = do
app <- readIORef appRef
when (isJust $ appPlayer app) $
mpvSetVolume (fromJust $ appPlayer app) $ round (vol * 100)
toggleFullscreen :: IORef App -> IO ()
toggleFullscreen appRef = do
app <- readIORef appRef
when (isJust $ appPlayer app) $
mpvToggleFullscreen (fromJust $ appPlayer app)
connectSignals :: IORef App -> IO ()
connectSignals appRef = do
app <- readIORef appRef
let hs = appHandles app
onDestroy (mainWindow hs) mainQuit
onDestroy (fullscreenWindow hs) mainQuit
onToolButtonClicked (openButton hs) $ showOpenDialog appRef
idPlay <- afterToolButtonToggled (playButton hs) $ playToggle appRef
onAdjustBounds (scale hs) $ seek appRef
idVol <- on (volumeButton hs) scaleButtonValueChanged $ setVolume appRef
onToolButtonClicked (fullscreenButton hs) $ toggleFullscreen appRef
onToolButtonClicked (aboutButton hs) $ showAboutDialog appRef
onToolButtonClicked (settingsButton hs) $ showSettingsDialog appRef
on (background hs) enterNotifyEvent $ tryEvent $
liftIO $ focusOnChild hs
on (fullscreenWindow hs) enterNotifyEvent $ tryEvent $
liftIO $ focusOnChild hs
writeIORef appRef app { appToggleSigId = Just idPlay,
appVolSigId = Just idVol }
where focusOnChild hs = do
containerForeach (fullscreenWindow hs) widgetGrabFocus
containerForeach (background hs) widgetGrabFocus
prepareUI :: Handles -> IO Handles
prepareUI hs = do
windowSetTitle (mainWindow hs) "mpv GUI"
let about = aboutDialog hs
set about [aboutDialogVersion := showVersion version]
-- HACK: volume button shows no icon when loaded from Glade.
p <- widgetGetParent (volumeButton hs)
let box = castToBox $ fromJust p
widgetDestroy (volumeButton hs)
volBut <- volumeButtonNew
boxPackEnd box volBut PackNatural 0
let s = settingsDialog hs
dialogAddButton s "gtk-cancel" $ ResponseUser 0
dialogAddButton s "gtk-ok" $ ResponseUser 1
widgetModifyBg (background hs) StateNormal $ Color 0 0 0
return $ hs { volumeButton = volBut }
formatPlayMessage :: Double -> Double -> String
formatPlayMessage pos total = concat
[str $ hours pos ,":", str $ mins pos ,":",str $ secs pos, " / ",
str $ hours total,":", str $ mins total,":",str $ secs total]
where comp t = floor $ 60 * (snd $ properFraction t)
str t = if t < 10 then "0" ++ show t else show t
secs t = comp $ t / 60 :: Int
mins t = comp $ t / 60 / 60 :: Int
hours t = floor $ t / 60 / 60 :: Int
checkFullscreen :: IORef App -> PlayStatus -> IO ()
checkFullscreen appRef status = do
app <- readIORef appRef
let fsWin = fullscreenWindow $ appHandles app
let normalWin = mainWindow $ appHandles app
let box = background $ appHandles app
let fsFlag = playStatusFullscreen status
fs <- widgetGetMapped fsWin
when (fsFlag && (not fs)) $ do
cs <- containerGetChildren box
when (not . null $ cs) $ do
let socket = head cs
widgetShowAll fsWin
widgetReparent socket fsWin
windowFullscreen fsWin
widgetHide normalWin
when ((not fsFlag) && fs) $ do
cs <- containerGetChildren fsWin
when (not . null $ cs) $ do
let socket = head cs
widgetReparent socket box
widgetShowAll normalWin
windowUnfullscreen fsWin
widgetHide fsWin
updateUI :: IORef App -> IO Bool
updateUI appRef = do
app <- readIORef appRef
let hs = appHandles app
signalBlock $ fromJust $ appToggleSigId app
signalBlock $ fromJust $ appVolSigId app
let playing = isJust $ appPlayer app
widgetSetSensitive (scale hs) playing
widgetSetSensitive (playButton hs) playing
widgetSetSensitive (fullscreenButton hs) playing
if playing
then do
let p = fromJust $ appPlayer app
status' <- mpvGetPlayStatus p
case status' of
Right Nothing -> return ()
Right (Just s) -> do
let ratio = (playStatusPos s) / (playStatusLength s)
rangeSetValue (scale hs) ratio
set (playButton hs) [toggleToolButtonActive :=
(not $ playStatusPaused s)]
set (volumeButton hs) [scaleButtonValue :=
(fromIntegral $ playStatusVol s) / 100]
statusbarPop (statusbar hs) (appStatusContextId app)
void $ statusbarPush (statusbar hs) (appStatusContextId app) $
formatPlayMessage (playStatusPos s) (playStatusLength s)
checkFullscreen appRef s
Left _ -> closeFile appRef
else do
rangeSetValue (scale hs) 0.0
set (playButton hs) [toggleToolButtonActive := False]
statusbarRemoveAll (statusbar hs) (fromIntegral $ appStatusContextId app)
signalUnblock $ fromJust $ appToggleSigId app
signalUnblock $ fromJust $ appVolSigId app
return True
loadCmdLine :: IO String
loadCmdLine = do
cf <- confFile
exists <- doesFileExist cf
if exists
then do str <- readFile cf
return $ case filter (any isAlpha) $ lines str of
(l:_) -> l
_ -> ""
else return ""
main :: IO ()
main = do
initGUI
builder <- builderNew
builderAddFromFile builder =<< getDataFileName "main-gtk2.ui"
hs <- prepareUI =<< $(getHandlesExp [| builder |])
statusId <- statusbarGetContextId (statusbar hs) "SimpleStatus"
cmdLine <- loadCmdLine
appRef <- newIORef $ App hs Nothing Nothing Nothing statusId cmdLine
connectSignals appRef
widgetShowAll (mainWindow hs)
args <- getArgs
when ((length args) > 0) $ do
let file = head args
exists <- doesFileExist file
if exists
then openFile appRef file
else do
dir <- getCurrentDirectory
let file' = dir ++ "/" ++ file
exists' <- doesFileExist file'
if exists'
then openFile appRef file'
else putStrLn "Could not find file, ignoring..."
timeoutId <- timeoutAdd (updateUI appRef) 50
mainGUI
timeoutRemove timeoutId
putStrLn "Closing..."
app <- readIORef appRef
when (isJust $ appPlayer app) $ do
let p = fromJust $ appPlayer app
mpvStop p
mpvTerminate p