├── .gitignore ├── LICENSE ├── hbacklight.cabal ├── README.md ├── .travis.yml └── src └── Hbacklight.hs /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.hie 7 | *.chi 8 | *.chs.h 9 | *.dyn_o 10 | *.dyn_hi 11 | .hpc 12 | .hsenv 13 | .cabal-sandbox/ 14 | cabal.sandbox.config 15 | *.prof 16 | *.aux 17 | *.hp 18 | *.eventlog 19 | .stack-work/ 20 | cabal.project.local 21 | cabal.project.local~ 22 | .HTF/ 23 | .ghc.environment.* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 fireflower 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hbacklight.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: >= 1.10 2 | -- Initial package description 'hbacklight.cabal' generated by 'cabal 3 | -- init'. For further documentation, see 4 | -- http://haskell.org/cabal/users-guide/ 5 | 6 | name: hbacklight 7 | version: 0.1.3 8 | synopsis: Backlight controller for Linux systems 9 | author: fireflower 10 | maintainer: fireflower.hs@gmail.com 11 | license: MIT 12 | license-file: LICENSE 13 | build-type: Simple 14 | tested-with: GHC == 8.6.5 15 | GHC == 8.8.1 16 | 17 | executable hbacklight 18 | main-is: Hbacklight.hs 19 | ghc-options: -main-is Hbacklight 20 | build-depends: base >= 4.12 && <4.14, 21 | boxes >= 0.1.5 && < 0.2, 22 | directory >= 1.3.3 && < 1.4, 23 | exceptions >= 0.10.4 && < 0.11, 24 | optparse-applicative >= 0.15.1 && < 0.16, 25 | split >= 0.2.3 && < 0.3, 26 | strict >= 0.3.2 && < 0.4, 27 | text >= 1.2.3 && < 1.3, 28 | unix >= 2.7.2 && < 2.8, 29 | pprecord 30 | hs-source-dirs: src 31 | default-language: Haskell2010 32 | ghc-options: -Wall 33 | -O2 34 | -main-is Hbacklight 35 | 36 | source-repository head 37 | type: git 38 | location: https://github.com/paroxayte/hbacklight 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hbacklight [![Build Status](https://api.travis-ci.org/paroxayte/hbacklight.svg?branch=master)](https://travis-ci.org/paroxayte/hbacklight) 2 | Backlight controller for Linux systems 3 | 4 | ## Installation 5 | ``` 6 | git clone https://github.com/paroxayte/hbacklight.git 7 | cd hbacklight 8 | cabal install 9 | ``` 10 | 11 | Alternatively, an aur package is available and can be installed with an aur-helper. Ex: (with yay) 12 | 13 | **note:** ghc-8.6.5 is an unlisted build dependency of the aur package. See PKGBUILD for more information. 14 | ``` 15 | yay -S hbacklight-git 16 | ``` 17 | 18 | In order to use hbacklight without sudo, you may need to create a udev rule and ensure you are part of the group granted permissions. EG: 19 | ``` 20 | » cat /etc/udev/rules.d/10-backlight.rules 21 | ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness" 22 | ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness" 23 | » cat /etc/udev/rules.d/10-kbd_backlight.rules 24 | ACTION=="add", SUBSYSTEM=="leds", KERNEL=="dell::kbd_backlight", RUN+="/bin/chgrp video /sys/class/leds/%k/brightness" 25 | ACTION=="add", SUBSYSTEM=="leds", KERNEL=="dell::kbd_backlight", RUN+="/bin/chmod g+w /sys/class/leds/%k/brightness" 26 | ``` 27 | ## Usage 28 | ``` 29 | Usage: hbacklight ((-e|--enum) | [-l|--led] (-i|--id TARGET) [-v|--verbose] 30 | [-d|--delta [+,-,%,~]AMOUNT] [-f|--floor FLOOR]) 31 | Adjust device brightness 32 | 33 | Available options: 34 | -h,--help Show this help text 35 | -e,--enum List available devices 36 | -l,--led Target led device 37 | -i,--id TARGET Identifier of backlight backlight 38 | -v,--verbose Informative summary backlight backlight state 39 | -d,--delta [+,-,%,~]AMOUNT 40 | Modify the backlight value, ~ sets the value to 41 | AMOUNT, elseshift is relative. Defaults to ~ 42 | -f,--floor FLOOR Set the minimum value which the brightness may be 43 | assigned. (default: 1) 44 | ``` 45 | 46 | ## Examples 47 | ``` 48 | » hbacklight -vi intel_backlight 49 | device intel_backlight 50 | bl_power 0 51 | brightness 200 52 | actual_brightness 200 53 | max_brightness 7500 54 | type raw 55 | 56 | » hbacklight -vli dell::kbd_backlight 57 | led dell::kbd_backlight 58 | brightness 2 59 | max_brightness 2 60 | 61 | » hbacklight -i intel_backlight -d 1000 62 | 63 | # turns the screen off 64 | » hbacklight -i intel_backlight -d 0 -f 0 65 | ``` 66 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This Travis job script has been generated by a script via 2 | # 3 | # haskell-ci '--no-cabal-check' 'hbacklight.cabal' 4 | # 5 | # For more information, see https://github.com/haskell-CI/haskell-ci 6 | # 7 | # version: 0.3.20190814 8 | # 9 | language: c 10 | dist: xenial 11 | sudo: required 12 | git: 13 | # whether to recursively clone submodules 14 | submodules: false 15 | cache: 16 | directories: 17 | - $HOME/.cabal/packages 18 | - $HOME/.cabal/store 19 | before_cache: 20 | - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log 21 | # remove files that are regenerated by 'cabal update' 22 | - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* 23 | - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json 24 | - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache 25 | - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar 26 | - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx 27 | - rm -rfv $CABALHOME/packages/head.hackage 28 | matrix: 29 | include: 30 | - compiler: ghc-8.8.1 31 | addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}} 32 | - compiler: ghc-8.6.5 33 | addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}} 34 | before_install: 35 | - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') 36 | - WITHCOMPILER="-w $HC" 37 | - HCPKG="$HC-pkg" 38 | - unset CC 39 | - CABAL=/opt/ghc/bin/cabal 40 | - CABALHOME=$HOME/.cabal 41 | - export PATH="$CABALHOME/bin:$PATH" 42 | - TOP=$(pwd) 43 | - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" 44 | - echo $HCNUMVER 45 | - CABAL="$CABAL -vnormal+nowrap+markoutput" 46 | - set -o pipefail 47 | - | 48 | echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk 49 | echo 'BEGIN { state = "output"; }' >> .colorful.awk 50 | echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk 51 | echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk 52 | echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk 53 | echo ' if (state == "cabal") {' >> .colorful.awk 54 | echo ' print blue($0)' >> .colorful.awk 55 | echo ' } else {' >> .colorful.awk 56 | echo ' print $0' >> .colorful.awk 57 | echo ' }' >> .colorful.awk 58 | echo '}' >> .colorful.awk 59 | - cat .colorful.awk 60 | - | 61 | color_cabal_output () { 62 | awk -f $TOP/.colorful.awk 63 | } 64 | - echo text | color_cabal_output 65 | install: 66 | - ${CABAL} --version 67 | - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" 68 | - TEST=--enable-tests 69 | - BENCH=--enable-benchmarks 70 | - HEADHACKAGE=false 71 | - rm -f $CABALHOME/config 72 | - | 73 | echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config 74 | echo "remote-build-reporting: anonymous" >> $CABALHOME/config 75 | echo "write-ghc-environment-files: always" >> $CABALHOME/config 76 | echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config 77 | echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config 78 | echo "world-file: $CABALHOME/world" >> $CABALHOME/config 79 | echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config 80 | echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config 81 | echo "installdir: $CABALHOME/bin" >> $CABALHOME/config 82 | echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config 83 | echo "store-dir: $CABALHOME/store" >> $CABALHOME/config 84 | echo "install-dirs user" >> $CABALHOME/config 85 | echo " prefix: $CABALHOME" >> $CABALHOME/config 86 | echo "repository hackage.haskell.org" >> $CABALHOME/config 87 | echo " url: http://hackage.haskell.org/" >> $CABALHOME/config 88 | - cat $CABALHOME/config 89 | - rm -fv cabal.project cabal.project.local cabal.project.freeze 90 | - travis_retry ${CABAL} v2-update -v 91 | # Generate cabal.project 92 | - rm -rf cabal.project cabal.project.local cabal.project.freeze 93 | - touch cabal.project 94 | - | 95 | echo "packages: ." >> cabal.project 96 | - | 97 | - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(hbacklight)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" 98 | - cat cabal.project || true 99 | - cat cabal.project.local || true 100 | - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi 101 | - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output 102 | - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" 103 | - rm cabal.project.freeze 104 | - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output 105 | - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output 106 | script: 107 | - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) 108 | # Packaging... 109 | - ${CABAL} v2-sdist all | color_cabal_output 110 | # Unpacking... 111 | - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ 112 | - cd ${DISTDIR} || false 113 | - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; 114 | - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; 115 | - PKGDIR_hbacklight="$(find . -maxdepth 1 -type d -regex '.*/hbacklight-[0-9.]*')" 116 | # Generate cabal.project 117 | - rm -rf cabal.project cabal.project.local cabal.project.freeze 118 | - touch cabal.project 119 | - | 120 | echo "packages: ${PKGDIR_hbacklight}" >> cabal.project 121 | - | 122 | - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(hbacklight)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" 123 | - cat cabal.project || true 124 | - cat cabal.project.local || true 125 | # Building... 126 | # this builds all libraries and executables (without tests/benchmarks) 127 | - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output 128 | # Building with tests and benchmarks... 129 | # build & run tests, build benchmarks 130 | - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output 131 | # Building without installed constraints for packages in global-db... 132 | - rm -f cabal.project.local 133 | - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output 134 | 135 | # REGENDATA ["--no-cabal-check","hbacklight.cabal"] 136 | # EOF 137 | -------------------------------------------------------------------------------- /src/Hbacklight.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | {-# LANGUAGE LambdaCase #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE TupleSections #-} 5 | 6 | module Hbacklight (main) where 7 | 8 | import Control.Applicative (optional, (<|>)) 9 | import Control.Exception (Exception) 10 | import Control.Monad (when, join) 11 | import Control.Monad.Catch (throwM) 12 | 13 | import Data.Char (isDigit) 14 | import Data.Either (either) 15 | import Data.List.Split (splitOn) 16 | import Data.Maybe (fromJust) 17 | 18 | import GHC.Generics 19 | 20 | import Options.Applicative (Parser, customExecParser, prefs, showHelpOnEmpty 21 | , flag', info, helper, fullDesc, progDesc, header, metavar, long, short 22 | , strOption, help, switch, auto, option, value, showDefault) 23 | 24 | import System.Directory (listDirectory) 25 | import System.Posix.IO (openFd, closeFd, fdWrite, OpenMode(WriteOnly) 26 | , defaultFileFlags) 27 | 28 | import Text.PrettyPrint.Boxes (Box, text, vcat, left, right, printBox, (<+>) 29 | , emptyBox, hsep, top) 30 | import Text.PrettyPrint.Records 31 | import Text.Read (readMaybe) 32 | 33 | import qualified Data.Text as T (Text, pack, unpack, uncons) 34 | import qualified System.IO.Strict as Strict (readFile) 35 | 36 | -------------------------------------------------------------------------------- 37 | -- types & defaults 38 | 39 | data Device = Backlight 40 | { power :: Int 41 | , brightness :: Int 42 | , actual :: Int 43 | , maxB :: Int 44 | , typeB :: T.Text 45 | , name :: T.Text } 46 | | Led 47 | { brightness :: Int 48 | , maxB :: Int 49 | , name :: T.Text } 50 | deriving (Generic, Show) 51 | 52 | instance FQuery Device 53 | instance VQuery Device 54 | 55 | data Config = Dim 56 | { led :: Bool 57 | , dId :: T.Text 58 | , verbose :: Bool 59 | , delta :: Maybe T.Text 60 | , floor' :: Int } 61 | | Enumerate Bool 62 | 63 | data OpT = Plus | Minus | Percent | Set deriving (Show) 64 | data Operation = Op OpT Int deriving (Show) 65 | 66 | type TFilePath = T.Text 67 | 68 | type DeviceProps = [(T.Text, TFilePath)] 69 | 70 | type DeviceMap = (TFilePath, DeviceProps) 71 | 72 | data Env = Env 73 | { blMap :: DeviceMap 74 | , ledMap :: DeviceMap } 75 | 76 | defaultEnv :: Env 77 | defaultEnv = Env 78 | { blMap = (blPath, blProps) 79 | , ledMap = (ldPath, ldProps) } 80 | where 81 | blPath = "/sys/class/backlight/" 82 | ldPath = "/sys/class/leds/" 83 | blProps = 84 | [ ("power" , "bl_power") 85 | , ("brightness" , "brightness") 86 | , ("actual" , "actual_brightness") 87 | , ("max" , "max_brightness") 88 | , ("type" , "type") ] 89 | ldProps = 90 | [ ("brightness", "brightness") 91 | , ("max", "max_brightness") ] 92 | 93 | data Ex = ParseFailure String 94 | 95 | instance Exception Ex 96 | 97 | instance Show Ex where 98 | show (ParseFailure s) = "*** Exception: parseFailure: " <> s 99 | 100 | -------------------------------------------------------------------------------- 101 | -- main logic 102 | 103 | readEither :: Read a => b -> String -> Either b a 104 | readEither err s = case readMaybe s of 105 | Just x -> Right x 106 | _ -> Left err 107 | 108 | readErr :: Read a => String -> Either Ex a 109 | readErr s = readEither (ParseFailure s) s 110 | 111 | liftEIO :: Exception e => Either e a -> IO a -- may throw IOError 112 | liftEIO = either throwM return 113 | 114 | -- Print device as table 115 | table :: Device -> Box 116 | table d = hsep 2 top [lhs, rhs] where 117 | lhs = vcat left $ text <$> fields d 118 | rhs = vcat right $ text <$> values d 119 | 120 | 121 | readOpT :: Char -> Either Ex OpT 122 | readOpT = \case 123 | '+' -> Right Plus 124 | '-' -> Right Minus 125 | '%' -> Right Percent 126 | '~' -> Right Set 127 | x -> Left . ParseFailure $ "unsupported mode \"" <> (x:"\"") 128 | 129 | readOp :: T.Text -> Either Ex Operation 130 | readOp s = do 131 | (x, xs) <- uncons' s 132 | opType <- readOpT x 133 | op <- readErr $ T.unpack xs 134 | return $ Op opType op 135 | where 136 | uncons' x = case T.uncons x of 137 | Nothing -> Left . ParseFailure $ "cannot parse empty input" 138 | Just u@(h,_) -> Right $ if isDigit h then ('~', x) else u --deflt ~ 139 | 140 | -- read DeviceMap attributes from filesystem 141 | readDProps :: T.Text -> DeviceMap -> IO [String] 142 | readDProps dname (dloc, dmap) = mapM 143 | (Strict.readFile . toPath . ('/' :) . T.unpack . snd) 144 | dmap 145 | where 146 | dpath = T.unpack dloc <> T.unpack dname 147 | toPath = (dpath <>) 148 | 149 | parseBl :: T.Text -> DeviceMap -> IO Device -- may throw Ex or IOError 150 | parseBl dname dmap = do 151 | -- read each property from DeviceMap and apply to Device cons 152 | (a1:a2:a3:a4:a5:_) <- readDProps dname dmap 153 | liftEIO $ Backlight 154 | <$> readErr a1 155 | <*> readErr a2 156 | <*> readErr a3 157 | <*> readErr a4 158 | <*> Right (T.pack . removeLF $ a5) 159 | <*> Right dname 160 | where 161 | removeLF xs = case last xs of 162 | '\n' -> init xs 163 | _ -> xs 164 | 165 | parseLed :: T.Text -> DeviceMap -> IO Device -- may throw Ex or IOError 166 | parseLed dname dmap = do 167 | (a1:a2:_) <- readDProps dname dmap 168 | liftEIO $ Led 169 | <$> readErr a1 170 | <*> readErr a2 171 | <*> Right dname 172 | 173 | -- dims and returns updated device 174 | dim :: DeviceMap -> Device -> Operation -> Int -> IO Device 175 | dim (dpath, dsub) i op minV = do 176 | device <- runOp op 177 | () <- closeFd =<< fd 178 | return device 179 | where 180 | path = T.unpack $ dpath <> name i <> "/" 181 | <> (fromJust $ lookup "brightness" dsub) 182 | lvl = brightness i 183 | fd = openFd 184 | path 185 | WriteOnly 186 | Nothing 187 | defaultFileFlags 188 | toMin n = if n > minV then n else minV 189 | writeV v = fd >>= \handle -> handle `fdWrite` show v 190 | >> return (i { brightness = v } ) 191 | setV = writeV . toMin 192 | runOp = \case 193 | Op Plus x -> setV $ min (lvl + x) (maxB i) 194 | Op Minus x -> setV $ lvl - x 195 | Op Percent x -> setV $ x * maxB i `div` 100 196 | Op Set x -> setV $ min x (maxB i) 197 | 198 | -- list all available led and backlight devices 199 | enumDevices :: TFilePath -> TFilePath -> IO Box 200 | enumDevices blPath ledPath = do 201 | let blHeader = text "backlight" 202 | let ledHeader = text "led" 203 | blDir <- listDirectory $ T.unpack blPath 204 | ledDir <- listDirectory $ T.unpack ledPath 205 | let blBody = mkBody blDir 206 | let ledBody = mkBody ledDir 207 | return $ vcat left 208 | [ blHeader 209 | , blBody 210 | , ledHeader 211 | , ledBody ] 212 | where 213 | fname = text . last . splitOn "/" 214 | mkBody = (<+>) (emptyBox 1 3) . vcat left . fmap fname 215 | 216 | run :: Env -> Config -> IO () 217 | run Env{blMap=bM, ledMap=lM} = \case 218 | c@Dim{} -> do 219 | tmp <- if led c 220 | then (lM, ) <$> parseLed (dId c) lM 221 | else (bM, ) <$> parseBl (dId c) bM 222 | device <- uncurry (dimUpdate c) tmp -- update device if dimmed 223 | when (verbose c) $ printBox $ table device 224 | Enumerate{} -> printBox =<< enumDevices (fst bM) (fst lM) 225 | where 226 | dimUpdate :: Config -> DeviceMap -> Device -> IO Device 227 | dimUpdate c dmap device = case liftEIO . readOp <$> delta c of 228 | Just op -> join $ dim dmap device <$> op <*> pure (floor' c) 229 | Nothing -> return device 230 | 231 | main :: IO () 232 | main = run defaultEnv =<< customExecParser (prefs showHelpOnEmpty) cmd where 233 | cmd = info ( helper <*> enumParser <|> dimParser ) 234 | ( fullDesc 235 | <> progDesc "Adjust device brightness" 236 | <> header "hbacklight - backlight manager" ) 237 | 238 | -------------------------------------------------------------------------------- 239 | -- app parser 240 | 241 | dimParser :: Parser Config 242 | dimParser = Dim 243 | <$> switch 244 | (long "led" 245 | <> short 'l' 246 | <> help "Target led device" 247 | ) 248 | <*> strOption 249 | ( long "id" 250 | <> short 'i' 251 | <> metavar "TARGET" 252 | <> help "Identifier of backlight backlight" ) 253 | <*> switch 254 | (long "verbose" 255 | <> short 'v' 256 | <> help "Informative summary backlight backlight state" 257 | ) 258 | <*> (optional . strOption) 259 | ( long "delta" 260 | <> short 'd' 261 | <> metavar "[+,-,%,~]AMOUNT" 262 | <> help ( "Modify the backlight value, ~ sets the value to AMOUNT, else" 263 | <> "shift is relative. Defaults to ~" ) 264 | ) 265 | <*> (option auto) 266 | ( long "floor" 267 | <> short 'f' 268 | <> metavar "FLOOR" 269 | <> value 1 270 | <> showDefault 271 | <> help "Set the minimum value which the brightness may be assigned." 272 | ) 273 | 274 | enumParser :: Parser Config 275 | enumParser = Enumerate 276 | <$> flag' 277 | True 278 | (long "enum" 279 | <> short 'e' 280 | <> help "List available devices" 281 | ) 282 | --------------------------------------------------------------------------------