    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE CPP#-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Util.Brightness
    -- License     :  MIT
    --
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Module to control the brightness of the screen in linux environments
    --
    -- [@Requirements@]
    --     This module assumes that the following files exists:
    --
    --     * __\/sys\/class\/backlight\/intel_backlight\/max_brightness__
    --
    --     * __\/sys\/class\/backlight\/intel_backlight\/brightness__
    --
    --     Also, brightness should be updatable by changing the content of
    --     __\/sys\/class\/backlight\/intel_backlight\/brightness__.
    --
    -- [@Permissions@]
    --     To use this module, the owner of the __xmonad__ process will need to
    --     have permission to write to __\/sys\/class\/backlight\/intel_backlight\/brightness__.
    --     To achieve this, you can:
    --
    --     * Create a group with your user and root and give permissions to this
    --     group to write to the file. I usually follow these steps:
    --
    --         * Create a group named xmonad
    --
    --         > $ sudo groupadd xmonad
    --
    --         * Add user root and your user name to the group xmonad.
    --
    --         > $ sudo usermod -a -G xmonad root
    --         > $ sudo usermod -a -G xmonad sibi
    --
    --         * The files under __\/sys__ are virtual. It's a RAM based filesystem through which you can access kernel data structures. The permission you give there won't persist after reboot. One of the way for persisting is creating a <https://unix.stackexchange.com/a/409780/29539 systemd script>:
    --
    --         > $ cat /etc/systemd/system/brightness.service
    --         > [Unit]
    --         > Description=Set brightness writable to everybody
    --         > Before=nodered.service
    --         >
    --         > [Service]
    --         > Type=oneshot
    --         > User=root
    --         > ExecStart=/bin/bash -c "chgrp -R -H xmonad /sys/class/backlight/intel_backlight && chmod g+w /sys/class/backlight/intel_backlight/brightness"
    --         >
    --         > [Install]
    --         > WantedBy=multi-user.target
    --         >
    --         > $ sudo systemctl enable brightness.service
    --         > $ sudo systemctl start brightness.service
    --         > $ sudo systemctl status brightness.service
    --
    --
    --     * Allow anyone to write the file through 646 permissions: __-rw-r--rw-__;
    --
    -----------------------------------------------------------------------------
    module XMonad.Util.Brightness
        ( increase
        , decrease
        , change
        , setBrightness
        ) where

    import XMonad
    #if (MIN_VERSION_base(4,10,0))
    import Data.Traversable (traverse)
    #endif
    import Prelude
    import System.IO (hPutStrLn, stderr)
    import Control.Monad (join)
    import Data.Bifunctor (first)
    import Control.Exception (try)
    import Control.Applicative (liftA2)
    import Data.ByteString.Char8 (unpack)
    import qualified Data.ByteString as BS

    maxfile :: FilePath
    maxfile :: FilePath
    maxfile = "/sys/class/backlight/intel_backlight/max_brightness"

    currentfile :: FilePath
    currentfile :: FilePath
    currentfile = "/sys/class/backlight/intel_backlight/brightness"

    -- | Update brightness by +100
    increase :: X ()
    increase :: X ()
    increase = IO () -> X ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> X ()) -> IO () -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Int -> Int) -> IO (Either () ())
    change (Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +100) IO (Either () ()) -> IO () -> IO ()
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> (() -> IO ()
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ())

    -- | Update brightness by -100
    decrease :: X ()
    decrease :: X ()
    decrease = IO () -> X ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> X ()) -> IO () -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Int -> Int) -> IO (Either () ())
    change (Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + (-100)) IO (Either () ()) -> IO () -> IO ()
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> (() -> IO ()
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ())

    -- | Change brightness to a particular level
    --
    -- @since 0.13.4
    setBrightness :: Int -> X ()
    setBrightness :: Int -> X ()
    setBrightness level :: Int
    level = IO () -> X ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> X ()) -> IO () -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Int -> Int) -> IO (Either () ())
    change (\_ -> Int
    level) IO (Either () ()) -> IO () -> IO ()
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> () -> IO ()
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ()

    -- | Perform all needed IO to update screen brightness
    change :: (Int -> Int) -> IO (Either () ())
    change :: (Int -> Int) -> IO (Either () ())
    change f :: Int -> Int
    f = do
      Either FilePath Int
    maxBright <- FilePath
    -> (ByteString -> Either FilePath Int) -> IO (Either FilePath Int)
    forall a.
    FilePath
    -> (ByteString -> Either FilePath a) -> IO (Either FilePath a)
    getFromFile FilePath
    maxfile ByteString -> Either FilePath Int
    readInt
      Either FilePath Int
    current <- FilePath
    -> (ByteString -> Either FilePath Int) -> IO (Either FilePath Int)
    forall a.
    FilePath
    -> (ByteString -> Either FilePath a) -> IO (Either FilePath a)
    getFromFile FilePath
    currentfile ByteString -> Either FilePath Int
    readInt
      Either FilePath () -> IO (Either () ())
    forall e. Either FilePath e -> IO (Either () e)
    printError (Either FilePath () -> IO (Either () ()))
    -> IO (Either FilePath ()) -> IO (Either () ())
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< (Int -> IO (Either FilePath ()))
    -> Either FilePath Int -> IO (Either FilePath ())
    apply (FilePath -> Int -> IO (Either FilePath ())
    writeToFile FilePath
    currentfile) ((Int -> Int -> Int)
    -> Either FilePath Int
    -> Either FilePath Int
    -> Either FilePath Int
    forall (f :: * -> *) a b c.
    Applicative f =>
    (a -> b -> c) -> f a -> f b -> f c
    liftA2 ((Int -> Int) -> Int -> Int -> Int
    guard Int -> Int
    f) Either FilePath Int
    maxBright Either FilePath Int
    current)

    apply :: (Int -> IO (Either String ())) -> Either String Int -> IO (Either String ())
    apply :: (Int -> IO (Either FilePath ()))
    -> Either FilePath Int -> IO (Either FilePath ())
    apply f :: Int -> IO (Either FilePath ())
    f = (Either FilePath (Either FilePath ()) -> Either FilePath ())
    -> IO (Either FilePath (Either FilePath ()))
    -> IO (Either FilePath ())
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Either FilePath (Either FilePath ()) -> Either FilePath ()
    forall (m :: * -> *) a. Monad m => m (m a) -> m a
    join (IO (Either FilePath (Either FilePath ()))
     -> IO (Either FilePath ()))
    -> (Either FilePath Int
        -> IO (Either FilePath (Either FilePath ())))
    -> Either FilePath Int
    -> IO (Either FilePath ())
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Int -> IO (Either FilePath ()))
    -> Either FilePath Int -> IO (Either FilePath (Either FilePath ()))
    forall (t :: * -> *) (f :: * -> *) a b.
    (Traversable t, Applicative f) =>
    (a -> f b) -> t a -> f (t b)
    traverse Int -> IO (Either FilePath ())
    f

    guard :: (Int -> Int) -> Int -> Int -> Int
    guard :: (Int -> Int) -> Int -> Int -> Int
    guard f :: Int -> Int
    f limit :: Int
    limit current :: Int
    current
      | Int
    value Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    limit = Int
    limit
      | Int
    value Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < 0   = 0
      | Bool
    otherwise = Int
    value
      where value :: Int
    value = Int -> Int
    f Int
    current

    readInt :: BS.ByteString -> Either String Int
    readInt :: ByteString -> Either FilePath Int
    readInt str :: ByteString
    str = case (ReadS Int
    forall a. Read a => ReadS a
    reads (ByteString -> FilePath
    unpack ByteString
    str)) of
                    [(n :: Int
    n, "\n")] -> Int -> Either FilePath Int
    forall a b. b -> Either a b
    Right Int
    n
                    [(n :: Int
    n, "")]   -> Int -> Either FilePath Int
    forall a b. b -> Either a b
    Right Int
    n
                    _           -> FilePath -> Either FilePath Int
    forall a b. a -> Either a b
    Left "Could not parse string to int"

    printError :: Either String e -> IO (Either () e)
    printError :: Either FilePath e -> IO (Either () e)
    printError es :: Either FilePath e
    es = (FilePath -> IO (Either () e))
    -> (e -> IO (Either () e)) -> Either FilePath e -> IO (Either () e)
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either (\str :: FilePath
    str -> Handle -> FilePath -> IO ()
    hPutStrLn Handle
    stderr FilePath
    str IO () -> IO (Either () e) -> IO (Either () e)
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> (Either () e -> IO (Either () e)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either () e -> IO (Either () e))
    -> (() -> Either () e) -> () -> IO (Either () e)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . () -> Either () e
    forall a b. a -> Either a b
    Left (() -> IO (Either () e)) -> () -> IO (Either () e)
    forall a b. (a -> b) -> a -> b
    $ ())) (\_ -> Either () e -> IO (Either () e)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either () e -> IO (Either () e))
    -> (() -> Either () e) -> () -> IO (Either () e)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . () -> Either () e
    forall a b. a -> Either a b
    Left (() -> IO (Either () e)) -> () -> IO (Either () e)
    forall a b. (a -> b) -> a -> b
    $ ()) Either FilePath e
    es

    getFromFile :: FilePath -> (BS.ByteString -> Either String a) -> IO (Either String a)
    getFromFile :: FilePath
    -> (ByteString -> Either FilePath a) -> IO (Either FilePath a)
    getFromFile filename :: FilePath
    filename fcast :: ByteString -> Either FilePath a
    fcast = (Either FilePath ByteString -> Either FilePath a)
    -> IO (Either FilePath ByteString) -> IO (Either FilePath a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (ByteString -> Either FilePath a
    fcast (ByteString -> Either FilePath a)
    -> Either FilePath ByteString -> Either FilePath a
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<<) (IO ByteString -> IO (Either FilePath ByteString)
    forall a. IO a -> IO (Either FilePath a)
    try' (IO ByteString -> IO (Either FilePath ByteString))
    -> IO ByteString -> IO (Either FilePath ByteString)
    forall a b. (a -> b) -> a -> b
    $ FilePath -> IO ByteString
    BS.readFile FilePath
    filename)

    writeToFile :: FilePath -> Int -> IO (Either String ())
    writeToFile :: FilePath -> Int -> IO (Either FilePath ())
    writeToFile filename :: FilePath
    filename value :: Int
    value = IO () -> IO (Either FilePath ())
    forall a. IO a -> IO (Either FilePath a)
    try' (IO () -> IO (Either FilePath ()))
    -> IO () -> IO (Either FilePath ())
    forall a b. (a -> b) -> a -> b
    $ FilePath -> FilePath -> IO ()
    writeFile FilePath
    filename (Int -> FilePath
    forall a. Show a => a -> FilePath
    show Int
    value)

    try' :: forall a . IO a -> IO (Either String a)
    try' :: IO a -> IO (Either FilePath a)
    try' x :: IO a
    x = (Either IOError a -> Either FilePath a)
    -> IO (Either IOError a) -> IO (Either FilePath a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((IOError -> FilePath) -> Either IOError a -> Either FilePath a
    forall (p :: * -> * -> *) a b c.
    Bifunctor p =>
    (a -> b) -> p a c -> p b c
    first IOError -> FilePath
    forall a. Show a => a -> FilePath
    show) (IO a -> IO (Either IOError a)
    forall e a. Exception e => IO a -> IO (Either e a)
    try IO a
    x :: IO (Either IOError a))