packages feed

msu-0.2.0.0: src/MSU/Xrandr/Parse.hs

{-# LANGUAGE RecordWildCards #-}

module MSU.Xrandr.Parse
    ( Display(..)
    , parseXrandr
    , parseXrandrUnsafe
    )
where

import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO)
import Text.Parsec
import Text.Parsec.String
import UnliftIO.Exception (throwString)

data Display = Display
    { name :: String
    , connected :: Bool
    , modes :: [(Int, Int)]
    }
    deriving (Eq, Show)

parseXrandr :: String -> Either ParseError [Display]
parseXrandr = parse parseDisplays "xrandr --query"

parseXrandrUnsafe :: MonadIO m => String -> m [Display]
parseXrandrUnsafe = either (throwString . show) pure . parseXrandr

parseDisplays :: Parser [Display]
parseDisplays = string "Screen" *> ignoreLine *> manyTill parseDisplay eof

parseDisplay :: Parser Display
parseDisplay = do
    name <- manyTill anyToken space
    connected <- parseConnected <* ignoreLine
    modes <- if connected then parseModeLines else skipModeLines
    pure Display { .. }

parseConnected :: Parser Bool
parseConnected = True <$ string "connected" <|> False <$ string "disconnected"

parseModeLines :: Parser [(Int, Int)]
parseModeLines = manyTill parseModeLine nextDisplay

skipModeLines :: Parser [(Int, Int)]
skipModeLines = [] <$ ignoreLinesTill nextDisplay

parseModeLine :: Parser (Int, Int)
parseModeLine =
    (,)
        <$> (read <$> (spaces *> many digit))
        <*> (read <$> (char 'x' *> many digit <* ignoreLine))

nextDisplay :: Parser ()
nextDisplay = lookAhead $ void $ try parseDisplay

ignoreLinesTill :: Parser () -> Parser ()
ignoreLinesTill p = void $ manyTill ignoreLine $ p <|> eof

ignoreLine :: Parser ()
ignoreLine = void $ manyTill anyToken eol
  where
    eol :: Parser Char
    eol = char '\n'