{-# LANGUAGE FlexibleContexts, RankNTypes #-} module Hbro.Boot where -- {{{ Imports import Hbro.Core import qualified Hbro.Gui as Gui import qualified Hbro.Prompt as Prompt import qualified Hbro.Socket as Socket import Hbro.Types import Hbro.Util import Hbro.Webkit.WebView as WebView import Control.Concurrent import qualified Config.Dyre as D import Config.Dyre.Compile import Config.Dyre.Paths import Control.Applicative import Control.Monad import Control.Monad.Error import Control.Monad.Reader -- import Data.Functor import Data.IORef -- import Data.Maybe import Graphics.UI.Gtk.General.General hiding(initGUI) import Network.URI as N import System.Console.CmdArgs import System.Directory import System.FilePath import System.Exit import System.IO import System.Posix.Signals import qualified System.ZMQ as ZMQ -- }}} -- {{{ Commandline options baseOptions :: CliOptions baseOptions = CliOptions { __startURI = def &= explicit &= name "u" &= name "uri" &= typ "URI" &= help "URI to open at start-up", __vanilla = def &= explicit &= name "1" &= name "vanilla" &= help "Do not read custom configuration file.", __recompile = def &= explicit &= name "r" &= name "recompile" &= help "Force recompilation and do not launch browser.", __denyReconf = def &= explicit &= name "deny-reconf" &= help "Deny recompilation even if the configuration file has changed.", __forceReconf = def &= explicit &= name "force-reconf" &= help "Force recompilation even if the configuration file hasn't changed.", __dyreDebug = def &= explicit &= name "dyre-debug" &= help "Force the application to use './cache/' as the cache directory, and ./ as the configuration directory. Useful to debug the program without installation.", __masterBinary = def &= explicit &= name "dyre-master-binary"} -- | Available commandline options (cf hbro -h). cliOptions :: Mode (CmdArgs CliOptions) cliOptions = cmdArgsMode $ baseOptions &= verbosityArgs [explicit, name "verbose", name "v"] [] &= versionArg [ignore] &= help "A minimal KISS-compliant browser." &= helpArg [explicit, name "help", name "h"] &= program "hbro" -- }}} -- {{{ Dynamic reconfiguration -- | Print various paths used for dynamic reconfiguration printDyrePaths :: IO () printDyrePaths = do (a, b, c, d, e) <- getPaths dyreParameters putStrLn $ unlines [ "Current binary: " ++ a, "Custom binary: " ++ b, "Config file: " ++ c, "Cache directory: " ++ d, "Lib directory: " ++ e, []] -- | Dynamic reconfiguration settings dyreParameters :: D.Params (Either String (Config, Setup, CliOptions)) dyreParameters = D.defaultParams { D.projectName = "hbro", D.showError = \_ -> Left, D.realMain = realMain, D.ghcOpts = ["-threaded"], D.statusOut = hPutStrLn stderr, D.includeCurrentDirectory = False} -- | Launch a recompilation of the configuration file recompile :: IO (Maybe String) recompile = do customCompile dyreParameters getErrorString dyreParameters -- }}} -- | Main function to call in the configuration file (cf 'Hbro.Main') -- First parse commandline options, then perform dynamic reconfiguration process hbro :: Config -> Setup -> IO () hbro config startUp = do options <- cmdArgsRun cliOptions when (_recompile options) $ recompile >>= maybe exitSuccess (\e -> putStrLn e >> exitFailure) D.wrapMain dyreParameters{ D.configCheck = not $ _vanilla options } $ Right (config, startUp, options) -- | Entry point called after dynamic recompilation. realMain :: Either String (Config, Setup, CliOptions) -> IO () realMain (Left e) = putStrLn e realMain (Right (config, Setup customSetup, options)) = do void $ installHandler sigINT (Catch interruptHandler) Nothing whenLoud printDyrePaths gui <- runReaderT Gui.build' config hooks <- Hooks <$> newIORef Nothing <*> newIORef Nothing <*> newIORef Nothing startURI <- getStartURI options keys <- newIORef "" zmqContext <- ZMQ.init 1 result <- runErrorT . (`runReaderT` Context options config gui zmqContext hooks keys) $ do threadSync <- fork Socket.open Gui.setupWindow Gui.setupScrollWindow Prompt.setup WebView.setup customSetup maybe goHome loadURI startURI -- Load home page io mainGUI -- Main loop Socket.close io $ takeMVar threadSync either print return result ZMQ.term zmqContext logNormal "Exiting..." -- getStartURI :: CliOptions -> IO (Maybe URI) getStartURI options = case (__startURI options) of Just uri -> do fileURI <- doesFileExist uri case fileURI of True -> getCurrentDirectory >>= return . N.parseURIReference . ("file://" ++) . ( uri) _ -> return $ N.parseURIReference uri _ -> return Nothing -- interruptHandler :: IO () interruptHandler = logVerbose "Received SIGINT." >> mainQuit