1{-# LANGUAGE CPP #-}23module Main where45import Control.Exception (throwIO)6import Data.Time.Calendar qualified as Cal7import Data.Time.LocalTime8 ( LocalTime (LocalTime),9 ZonedTime (ZonedTime),10 getCurrentTimeZone,11 getZonedTime,12 localDay,13 localTimeOfDay,14 zonedTimeToLocalTime,15 )16import DatePicker.CmdLine17 ( getCmdArgs,18 getTime,19 optDuration,20 optFormat,21 optLogical,22 optMonday,23 optNoTime,24 optSelect,25 optTime,26 optsPeriod,27 )28import DatePicker.UI qualified as UI29import DatePicker.UI.Month qualified as M30import DatePicker.UI.Time qualified as T31import DatePicker.Util (format, parseTime)32import Graphics.Vty qualified as V33import Graphics.Vty.Input.Events qualified as E34import Graphics.Vty.Platform.Unix (mkVtyWithSettings)35import Graphics.Vty.Platform.Unix.Settings qualified as VU36import System.Posix.IO (OpenMode (ReadWrite), defaultFileFlags, openFd)3738isTerm :: E.Event -> Bool39isTerm (E.EvKey key _) =40 key == E.KEsc || key == E.KChar 'q'41isTerm _ = False4243-- Make sure we read and write to /dev/tty instead of relying on stdin/stdout.44-- This allows using datepicker within pipes where stdin/stdout is redirected.45unixSettings :: IO VU.UnixSettings46unixSettings = do47#if MIN_VERSION_unix(2,8,0)48 ttyFd <- openFd "/dev/tty" ReadWrite defaultFileFlags49#else50 ttyFd <- openFd "/dev/tty" ReadWrite Nothing defaultFileFlags51#endif5253 -- Can't build upon defaultSettings here as it flushes standard input and54 -- if standard input is a pipe it may not necessarily be flushable.55 mb <- VU.currentTerminalName56 case mb of57 Nothing -> throwIO VU.MissingTermEnvVar58 Just t -> do59 return $60 VU.UnixSettings61 { VU.settingVmin = 1,62 VU.settingVtime = 100,63 VU.settingInputFd = ttyFd,64 VU.settingOutputFd = ttyFd,65 VU.settingTermName = t66 }6768main :: IO ()69main = do70 args <- getCmdArgs71 let dateFmt = optFormat args7273 localTime <- zonedTimeToLocalTime <$> getZonedTime74 baseDay <- case optTime args of75 Nothing -> pure $ localDay localTime76 Just it -> getTime localTime it77 LocalTime selDay selTime <- case optSelect args of78 Nothing -> pure (LocalTime baseDay $ localTimeOfDay localTime)79 Just sd -> parseTime False dateFmt sd8081 let range = optsPeriod (optDuration args) baseDay82 mview =83 M.mkMonthView84 range85 selDay86 (if optMonday args then Cal.Monday else Cal.Sunday)87 (optLogical args)88 view <- case mview of89 Nothing -> fail $ "specified date (" ++ show selDay ++ ") is not in displayed range"90 Just x -> pure x9192 vty <- unixSettings >>= mkVtyWithSettings V.defaultConfig93 lt@(LocalTime date _) <- UI.showView view isTerm vty9495 timeZone <- getCurrentTimeZone96 let mkZonedTime local = ZonedTime local timeZone9798 if optNoTime args99 then V.shutdown vty >> putStrLn (format dateFmt $ mkZonedTime lt)100 else do101 (LocalTime _ time) <- UI.showView (T.mkTimeView selTime lt) isTerm vty102103 let res = LocalTime date time104 V.shutdown vty >> putStrLn (format dateFmt $ mkZonedTime res)