-----------------------------------------------------------------------------
-- |
-- Module : System.Taffybar
-- Copyright : (c) Ivan A. Malison
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Ivan A. Malison
-- Stability : unstable
-- Portability : unportable
-----------------------------------------------------------------------------
module System.Taffybar
(
-- * Detail
--
-- | This is a system status bar meant for use with window managers like
-- XMonad. It is similar to xmobar, but with more visual flare and a different
-- widget set. Contributed widgets are more than welcome. The bar is drawn
-- using gtk and cairo. It is actually the simplest possible thing that could
-- plausibly work: you give Taffybar a list of GTK widgets and it will render
-- them in a horizontal bar for you (taking care of ugly details like
-- reserving strut space so that window managers don't put windows over it).
--
-- This is the real main module. The default bar should be customized to taste
-- in the config file (~/.config/taffybar/taffybar.hs). Typically, this means
-- adding widgets to the default config. A default configuration file is
-- included in the distribution, but the essentials are covered here.
-- * Config File
--
-- | The config file is just a Haskell source file that is compiled at startup
-- (if it has changed) to produce a custom executable with the desired set of
-- widgets. You will want to import this module along with the modules of any
-- widgets you want to add to the bar. Note, you can define any widgets that
-- you want in your config file or other libraries. Taffybar only cares that
-- you give it some GTK widgets to display.
--
-- Below is a fairly typical example:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > import System.Taffybar
-- > import System.Taffybar.Information.CPU
-- > import System.Taffybar.SimpleConfig
-- > import System.Taffybar.Widget
-- > import System.Taffybar.Widget.Generic.Graph
-- > import System.Taffybar.Widget.Generic.PollingGraph
-- >
-- > cpuCallback = do
-- > (_, systemLoad, totalLoad) <- cpuLoad
-- > return [ totalLoad, systemLoad ]
-- >
-- > main = do
-- > let cpuCfg = defaultGraphConfig { graphDataColors = [ (0, 1, 0, 1), (1, 0, 1, 0.5)]
-- > , graphLabel = Just "cpu"
-- > }
-- > clock = textClockNew Nothing "%a %b %_d %H:%M" 1
-- > cpu = pollingGraphNew cpuCfg 0.5 cpuCallback
-- > workspaces = workspacesNew defaultWorkspacesConfig
-- > simpleConfig = defaultSimpleTaffyConfig
-- > { startWidgets = [ workspaces ]
-- > , endWidgets = [ sniTrayNew, clock, cpu ]
-- > }
-- > simpleTaffybar simpleConfig
--
-- This configuration creates a bar with four widgets. On the left is a widget
-- that shows information about the workspace configuration. The rightmost
-- widget is the system tray, with a clock and then a CPU graph. The clock is
-- formatted using standard strftime-style format strings (see the clock
-- module). Note that the clock is colored using Pango markup (again, see the
-- clock module).
--
-- The CPU widget plots two graphs on the same widget: total CPU use in green
-- and then system CPU use in a kind of semi-transparent purple on top of the
-- green.
--
-- It is important to note that the widget lists are *not* [Widget]. They are
-- actually [TaffyIO Widget] since the bar needs to construct them after performing
-- some GTK initialization.
--
-- ** A note about taffybar's dependency on DBus:
-- |
-- * If you start your window manager using a graphical login manager like gdm
-- or kdm, DBus should be started automatically for you.
--
-- * If you start xmonad with a different graphical login manager that does
-- not start DBus for you automatically, put the line @eval \`dbus-launch
-- --auto-syntax\`@ into your ~\/.xsession *before* xmonad and taffybar are
-- started. This command sets some environment variables that the two must
-- agree on.
--
-- * If you start xmonad via @startx@ or a similar command, add the
-- above command to ~\/.xinitrc
-- * Colors
--
-- | While taffybar is based on GTK+, it ignores your GTK+ theme. The default
-- theme that it uses lives at
-- https://github.com/taffybar/taffybar/blob/master/taffybar.css You can alter
-- this theme by editing @~\/.config\/taffybar\/taffybar.css@ to your liking.
-- For an idea of the customizations you can make, see
-- .
taffybarDyreParams
, dyreTaffybar
, startTaffybar
, dyreTaffybarMain
) where
import qualified Config.Dyre as Dyre
import qualified Config.Dyre.Params as Dyre
import Control.Monad
import qualified Data.GI.Gtk.Threading as GIThreading
import qualified Data.Text as T
import qualified GI.Gdk as Gdk
import qualified GI.Gtk as Gtk
import Graphics.X11.Xlib.Misc
import System.Directory
import System.Environment.XDG.BaseDir ( getUserConfigFile )
import System.Exit ( exitFailure )
import System.FilePath ( (>) )
import qualified System.IO as IO
import System.Taffybar.Context
import Paths_taffybar ( getDataDir )
-- | The parameters that are passed to Dyre when taffybar is invoked with
-- 'dyreTaffybar'.
taffybarDyreParams :: Dyre.Params TaffybarConfig
taffybarDyreParams =
Dyre.defaultParams
{ Dyre.projectName = "taffybar"
, Dyre.realMain = dyreTaffybarMain
, Dyre.showError = showError
, Dyre.ghcOpts = ["-threaded", "-rtsopts"]
, Dyre.rtsOptsHandling = Dyre.RTSAppend ["-I0", "-V0"]
}
-- | Use Dyre to configure and start taffybar. This will automatically recompile
-- taffybar whenever there are changes to your taffybar.hs configuration file.
dyreTaffybar :: TaffybarConfig -> IO ()
dyreTaffybar = Dyre.wrapMain taffybarDyreParams
showError :: TaffybarConfig -> String -> TaffybarConfig
showError cfg msg = cfg { errorMsg = Just msg }
dyreTaffybarMain :: TaffybarConfig -> IO ()
dyreTaffybarMain cfg =
case errorMsg cfg of
Nothing -> startTaffybar cfg
Just err -> do
IO.hPutStrLn IO.stderr ("Error: " ++ err)
exitFailure
getDefaultConfigFile :: String -> IO FilePath
getDefaultConfigFile name = do
dataDir <- getDataDir
return (dataDir > name)
startCSS :: IO Gtk.CssProvider
startCSS = do
-- Override the default GTK theme path settings. This causes the
-- bar (by design) to ignore the real GTK theme and just use the
-- provided minimal theme to set the background and text colors.
-- Users can override this default.
taffybarProvider <- Gtk.cssProviderNew
let loadIfExists filePath =
doesFileExist filePath >>=
flip when (Gtk.cssProviderLoadFromPath taffybarProvider (T.pack filePath))
loadIfExists =<< getDefaultConfigFile "taffybar.css"
loadIfExists =<< getUserConfigFile "taffybar" "taffybar.css"
Just scr <- Gdk.screenGetDefault
Gtk.styleContextAddProviderForScreen scr taffybarProvider 800
return taffybarProvider
-- | Start taffybar with the provided 'TaffybarConfig'. Because this function
-- will not handle recompiling taffybar automatically when taffybar.hs is
-- updated, it is generally recommended that end users use 'dyreTaffybar'
-- instead. If automatic recompilation is handled by another mechanism such as
-- stack or a custom user script or not desired for some reason, it is
-- perfectly fine to use this function.
startTaffybar :: TaffybarConfig -> IO ()
startTaffybar config = do
_ <- initThreads
_ <- Gtk.init Nothing
GIThreading.setCurrentThreadAsGUIThread
_ <- startCSS
_ <- buildContext config
Gtk.main
return ()