packages feed

xmobar 0.39 → 0.40

raw patch · 36 files changed

+1575/−627 lines, 36 files

Files

changelog.md view
@@ -1,4 +1,13 @@-## Version 0.39 (unreleased)+## Version 0.40 (November, 2021)++_New features_++  - New plugin: `QueueReader` (Guy Gastineau).+  - Greatly improved FreeBSD support: Mem, Network and Swap monitors+    fixes, and CI build for FreeBSD (Michal Zielonka).+  - New template markup: `<hspace>`(tulthix)++## Version 0.39 (August, 2021)  _New features_ 
doc/plugins.org view
@@ -1,35 +1,37 @@ * System Monitor Plugins -This is the description of the system monitor plugins available in-xmobar. Some of them are only installed when an optional build option is-set: we mention that fact, when needed, in their description.+  This is the description of the system monitor plugins available in+  xmobar. Some of them are only installed when an optional build+  option is set: we mention that fact, when needed, in their+  description. -Each monitor has an =alias= to be used in the output template. Monitors-may have default aliases, see the documentation of the monitor in-question.+  Each monitor has an =alias= to be used in the output+  template. Monitors may have default aliases, see the documentation+  of the monitor in question. -There are two types of arguments: ones that all monitors share (the so-called /default monitor arguments/) and arguments that are specific to a-certain monitor.+  There are two types of arguments: ones that all monitors share (the+  so called /default monitor arguments/) and arguments that are specific+  to a certain monitor. -All Monitors accept a common set of arguments, described below in-[[Default Monitor Arguments]]. Some monitors also accept additional options-that are specific to them. When specifying the list of arguments in your-configuration, the common options come first, followed by =--=, followed-by any monitor-specific options. For example, the following [[=Battery Args RefreshRate=][Battery]]-configuration first sets the global =template= and =Low= arguments and-then specifies the battery-specific =off= option.+  All Monitors accept a common set of arguments, described below in+  [[Default Monitor Arguments]]. Some monitors also accept additional+  options that are specific to them. When specifying the list of+  arguments in your configuration, the common options come first,+  followed by =--=, followed by any monitor-specific options. For+  example, the following [[=Battery Args RefreshRate=][Battery]] configuration first sets the global+  =template= and =Low= arguments and then specifies the battery-specific+  =off= option. -#+begin_src haskell-  Run Battery-    [ "--template", "<acstatus>"-    , "--Low"     , "15"-    -- battery specific options start here.-    , "--"-    , "--off"     , "<left> (<timeleft>)"-    ]-    100-#+end_src+  #+begin_src haskell+    Run Battery+      [ "--template", "<acstatus>"+      , "--Low"     , "15"+      -- battery specific options start here.+      , "--"+      , "--off"     , "<left> (<timeleft>)"+      ]+      100+  #+end_src  ** Icon Patterns @@ -1144,7 +1146,7 @@  - Default template: =<station>: <tempC>C, rh <rh>% (<hour>)=  - Retrieves weather information from http://tgftp.nws.noaa.gov. Here is    an [[https://tgftp.nws.noaa.gov/data/observations/metar/decoded/CYLD.TXT][example]], also showcasing the kind of information that may be-   extracted.+   extracted. Here is [[https://weather.rap.ucar.edu/surface/stations.txt][a sample list of station IDs]].  *** =WeatherX StationID SkyConditions Args RefreshRate= @@ -1260,8 +1262,8 @@  * Interfacing with Window Managers -Listed below are ways to interface xmobar with your window manager of-choice.+  Listed below are ways to interface xmobar with your window manager+  of choice.  ** Property-based Logging *** =XMonadLog=@@ -1436,71 +1438,116 @@      hPutStr writeHandle "Hello World"    #+end_src +** Software Transactional Memory++   When invoking xmobar from other Haskell code it can be easier and+   more performant to use shared memory.  The following plugins+   leverage =Control.Concurrent.STM= to realize these gains for xmobar.++*** =QueueReader (TQueue a) (a -> String) String=++ - Display data from a Haskell =TQueue a=.++ - This plugin is only useful if you are running xmobar from another+   haskell program like xmonad.++ - You should make an =IO= safe =TQueue a= with =Control.Concurrent.STM.newTQueueIO=.+   Write to it from the user code with =writeTQueue=, and read with =readTQueue=.+   A common use is to overwite =ppOutput= from =XMonad.Hooks.DynamicLog= as shown+   below.++   #+begin_src haskell+     main :: IO ()+     main = do+       q <- STM.newTQueueIO @String+       bar <- forkIO $ xmobar myConf+         { commands = Run (QueueReader q id "XMonadLog") : commands myConf }+       xmonad $ def { logHook = logWorkspacesToQueue q }++     logWorkspacesToQueue :: STM.TQueue String -> X ()+     logWorkspacesToQueue q =+       dynamicLogWithPP def { ppOutput = STM.atomically . STM.writeTQueue q }+       where+         -- Manage the PrettyPrinting configuration here.+         ppLayout' :: String -> String+         ppLayout' "Spacing Tall"        = xpm "layout-spacing-tall"+         ppLayout' "Spacing Mirror Tall" = xpm "layout-spacing-mirror"+         ppLayout' "Spacing Full"        = xpm "layout-full"+         ppLayout' x = x++         icon :: String -> String+         icon path = "<icon=" ++ path ++ "/>"++         xpm :: String -> String+         xpm = icon . (++ ".xpm")+   #+end_src+ * Executing External Commands -In order to execute an external command you can either write the command-name in the template, in this case it will be executed without-arguments, or you can configure it in the "commands" configuration-option list with the Com template command:+  In order to execute an external command you can either write the+  command name in the template, in this case it will be executed+  without arguments, or you can configure it in the "commands"+  configuration option list with the Com template command: -=Com ProgramName Args Alias RefreshRate=+  =Com ProgramName Args Alias RefreshRate= -- ProgramName: the name of the program-- Args: the arguments to be passed to the program at execution time-- RefreshRate: number of tenths of second between re-runs of the-  command. A zero or negative rate means that the command will be-  executed only once.-- Alias: a name to be used in the template. If the alias is en empty-  string the program name can be used in the template.+  - ProgramName: the name of the program+  - Args: the arguments to be passed to the program at execution time+  - RefreshRate: number of tenths of second between re-runs of the+    command. A zero or negative rate means that the command will be+    executed only once.+  - Alias: a name to be used in the template. If the alias is en empty+    string the program name can be used in the template. -E.g.:+  E.g.: -#+begin_src haskell-  Run Com "uname" ["-s","-r"] "" 0-#+end_src+  #+begin_src haskell+    Run Com "uname" ["-s","-r"] "" 0+  #+end_src -can be used in the output template as =%uname%= (and xmobar will call-/uname/ only once), while+  can be used in the output template as =%uname%= (and xmobar will call+  /uname/ only once), while -#+begin_src haskell-  Run Com "date" ["+\"%a %b %_d %H:%M\""] "mydate" 600-#+end_src+  #+begin_src haskell+    Run Com "date" ["+\"%a %b %_d %H:%M\""] "mydate" 600+  #+end_src -can be used in the output template as =%mydate%=.+  can be used in the output template as =%mydate%=. -Sometimes, you don't mind if the command executed exits with an error,-or you might want to display a custom message in that case. To that end,-you can use the =ComX= variant:+  Sometimes, you don't mind if the command executed exits with an+  error, or you might want to display a custom message in that+  case. To that end, you can use the =ComX= variant: -=ComX ProgramName Args ExitMessage Alias RefreshRate=+    =ComX ProgramName Args ExitMessage Alias RefreshRate= -Works like =Com=, but displaying =ExitMessage= (a string) if the-execution fails. For instance:+  Works like =Com=, but displaying =ExitMessage= (a string) if the+  execution fails. For instance: -#+begin_src haskell-  Run ComX "date" ["+\"%a %b %_d %H:%M\""] "N/A" "mydate" 600-#+end_src+  #+begin_src haskell+    Run ComX "date" ["+\"%a %b %_d %H:%M\""] "N/A" "mydate" 600+  #+end_src -will display "N/A" if for some reason the =date= invocation fails.+  will display "N/A" if for some reason the =date= invocation fails.  * The DBus Interface -When compiled with the optional =with_dbus= flag, xmobar can be-controlled over dbus. All signals defined in [[https://github.com/jaor/xmobar/blob/master/src/Xmobar/System/Signal.hs][src/Signal.hs]] as =data-SignalType= can now be sent over dbus to xmobar. Due to current-limitations of the implementation only one process of xmobar can acquire-the dbus. This is handled on a first-come-first-served basis, meaning-that the first process will get the dbus interface. Other processes will-run without further problems, yet have no dbus interface.+  When compiled with the optional =with_dbus= flag, xmobar can be+  controlled over dbus. All signals defined in [[https://github.com/jaor/xmobar/blob/master/src/Xmobar/System/Signal.hs][src/Signal.hs]] as =data+  SignalType= can now be sent over dbus to xmobar. Due to current+  limitations of the implementation only one process of xmobar can+  acquire the dbus. This is handled on a first-come-first-served+  basis, meaning that the first process will get the dbus+  interface. Other processes will run without further problems, yet+  have no dbus interface. -- Bus Name: =org.Xmobar.Control=-- Object Path: =/org/Xmobar/Control=-- Member Name: Any of SignalType, e.g. =string:Reveal=-- Interface Name: =org.Xmobar.Control=+  - Bus Name: =org.Xmobar.Control=+  - Object Path: =/org/Xmobar/Control=+  - Member Name: Any of SignalType, e.g. =string:Reveal=+  - Interface Name: =org.Xmobar.Control= -An example using the =dbus-send= command line utility:+  An example using the =dbus-send= command line utility: -#+begin_src shell+  #+begin_src shell   dbus-send \       --session \       --dest=org.Xmobar.Control \@@ -1509,82 +1556,82 @@       '/org/Xmobar/Control' \       org.Xmobar.Control.SendSignal \       "string:Toggle 0"-#+end_src+  #+end_src -It is also possible to send multiple signals at once:+  It is also possible to send multiple signals at once: -#+begin_src shell-  # send to another screen, reveal and toggle the persistent flag-  dbus-send [..] \-      "string:ChangeScreen 0" "string:Reveal 0" "string:TogglePersistent"-#+end_src+  #+begin_src shell+    # send to another screen, reveal and toggle the persistent flag+    dbus-send [..] \+        "string:ChangeScreen 0" "string:Reveal 0" "string:TogglePersistent"+  #+end_src -The =Toggle=, =Reveal=, and =Hide= signals take an additional integer-argument that denotes an initial delay, in tenths of a second, before-the command takes effect.+  The =Toggle=, =Reveal=, and =Hide= signals take an additional integer+  argument that denotes an initial delay, in tenths of a second,+  before the command takes effect.  ** Example for using the DBus IPC interface with XMonad -Bind the key which should {,un}map xmobar to a dummy value. This is-necessary for {,un}grabKey in xmonad.+   Bind the key which should {,un}map xmobar to a dummy value. This is+   necessary for {,un}grabKey in xmonad. -#+begin_src haskell-  ((0, xK_Alt_L), pure ())-#+end_src+   #+begin_src haskell+     ((0, xK_Alt_L), pure ())+   #+end_src -Also, install =avoidStruts= layout modifier from-=XMonad.Hooks.ManageDocks=+   Also, install =avoidStruts= layout modifier from+   =XMonad.Hooks.ManageDocks= -Finally, install these two event hooks (=handleEventHook= in =XConfig=)-=myDocksEventHook= is a replacement for =docksEventHook= which reacts on-unmap events as well (which =docksEventHook= doesn't).+   Finally, install these two event hooks (=handleEventHook= in =XConfig=)+   =myDocksEventHook= is a replacement for =docksEventHook= which reacts+   on unmap events as well (which =docksEventHook= doesn't). -#+begin_src haskell-  import qualified XMonad.Util.ExtensibleState as XS+   #+begin_src haskell+     import qualified XMonad.Util.ExtensibleState as XS -  data DockToggleTime = DTT { lastTime :: Time } deriving (Eq, Show, Typeable)+     data DockToggleTime = DTT { lastTime :: Time } deriving (Eq, Show, Typeable) -  instance ExtensionClass DockToggleTime where-      initialValue = DTT 0+     instance ExtensionClass DockToggleTime where+         initialValue = DTT 0 -  toggleDocksHook :: Int -> KeySym -> Event -> X All-  toggleDocksHook to ks ( KeyEvent { ev_event_display = d-                                  , ev_event_type    = et-                                  , ev_keycode       = ekc-                                  , ev_time          = etime-                                  } ) =-          io (keysymToKeycode d ks) >>= toggleDocks >> return (All True)-      where-      toggleDocks kc-          | ekc == kc && et == keyPress = do-              safeSendSignal ["Reveal 0", "TogglePersistent"]-              XS.put ( DTT etime )-          | ekc == kc && et == keyRelease = do-              gap <- XS.gets ( (-) etime . lastTime )-              safeSendSignal [ "TogglePersistent"-                          , "Hide " ++ show (if gap < 400 then to else 0)-                          ]-          | otherwise = return ()+     toggleDocksHook :: Int -> KeySym -> Event -> X All+     toggleDocksHook to ks ( KeyEvent { ev_event_display = d+                                     , ev_event_type    = et+                                     , ev_keycode       = ekc+                                     , ev_time          = etime+                                     } ) =+             io (keysymToKeycode d ks) >>= toggleDocks >> return (All True)+         where+         toggleDocks kc+             | ekc == kc && et == keyPress = do+                 safeSendSignal ["Reveal 0", "TogglePersistent"]+                 XS.put ( DTT etime )+             | ekc == kc && et == keyRelease = do+                 gap <- XS.gets ( (-) etime . lastTime )+                 safeSendSignal [ "TogglePersistent"+                             , "Hide " ++ show (if gap < 400 then to else 0)+                             ]+             | otherwise = return () -      safeSendSignal s = catchX (io $ sendSignal s) (return ())-      sendSignal    = withSession . callSignal-      withSession mc = connectSession >>= \c -> callNoReply c mc >> disconnect c-      callSignal :: [String] -> MethodCall-      callSignal s = ( methodCall-                      ( objectPath_    "/org/Xmobar/Control" )-                      ( interfaceName_ "org.Xmobar.Control"  )-                      ( memberName_    "SendSignal"          )-                  ) { methodCallDestination = Just $ busName_ "org.Xmobar.Control"-                      , methodCallBody        = map toVariant s-                      }+         safeSendSignal s = catchX (io $ sendSignal s) (return ())+         sendSignal    = withSession . callSignal+         withSession mc = connectSession >>= \c -> callNoReply c mc >> disconnect c+         callSignal :: [String] -> MethodCall+         callSignal s = ( methodCall+                         ( objectPath_    "/org/Xmobar/Control" )+                         ( interfaceName_ "org.Xmobar.Control"  )+                         ( memberName_    "SendSignal"          )+                     ) { methodCallDestination = Just $ busName_ "org.Xmobar.Control"+                         , methodCallBody        = map toVariant s+                         } -  toggleDocksHook _ _ _ = return (All True)+     toggleDocksHook _ _ _ = return (All True) -  myDocksEventHook :: Event -> X All-  myDocksEventHook e = do-      when (et == mapNotify || et == unmapNotify) $-          whenX ((not `fmap` (isClient w)) <&&> runQuery checkDock w) refresh-      return (All True)-      where w  = ev_window e-          et = ev_event_type e-#+end_src+     myDocksEventHook :: Event -> X All+     myDocksEventHook e = do+         when (et == mapNotify || et == unmapNotify) $+             whenX ((not `fmap` (isClient w)) <&&> runQuery checkDock w) refresh+         return (All True)+         where w  = ev_window e+             et = ev_event_type e+   #+end_src
doc/quick-start.org view
@@ -215,6 +215,10 @@ - =<fn=1>string</fn>= will print =string= with the first font from   =additionalFonts=. The index =0= corresponds to the standard font. +- =<hspace=X/>= will insert a blank horizontal space of =X= pixels.+  For example, to add a blank horizontal space of 123 pixels,+  =<hspace=123/>= may be used.+ - =<icon=/path/to/icon.xbm/>= will insert the given bitmap. XPM image   format is also supported when compiled with the =with_xpm= flag. 
readme.org view
@@ -129,21 +129,22 @@ In particular, xmobar incorporates patches by Mohammed Alshiekh, Alex Ameen, Axel Angel, Dhananjay Balan, Claudio Bley, Dragos Boca, Ben Boeckel, Ivan Brennan, Duncan Burke, Roman Cheplyaka, Patrick Chilton,-Antoine Eiche, Nathaniel Wesley Filardo, John Goerzen, Reto Hablützel,-Juraj Hercek, Tomáš Janoušek, Ada Joule, Spencer Janssen, Roman Joost,-Jochen Keil, Sam Kirby, Lennart Kolmodin, Krzysztof Kosciuszkiewicz,-Dmitry Kurochkin, Todd Lunter, Vanessa McHale, Robert J. Macomber,-Dmitry Malikov, David McLean, Joan MIlev, Marcin Mikołajczyk, Dino-Morelli, Tony Morris, Eric Mrak, Thiago Negri, Edward O'Callaghan,-Svein Ove, Martin Perner, Jens Petersen, Alexander Polakov, Sibi-Prabakaran, Pavan Rikhi, Petr Rockai, Andrew Emmanuel Rosa,-Sackville-West, Amir Saeid, Markus Scherer, Daniel Schüssler, Olivier-Schneider, Alexander Shabalin, Valentin Shirokov, Peter Simons,-Alexander Solovyov, Will Song, John Soros, Felix Springer, Travis-Staton, Artem Tarasov, Samuli Thomasson, Edward Tjörnhammar, Sergei-Trofimovich, Thomas Tuegel, John Tyree, Jan Vornberger, Anton-Vorontsov, Daniel Wagner, Zev Weiss, Phil Xiaojun Hu, Nikolay Yakimov,-aEdward Z. Yang, Leo Zhang, and Norbert Zeh.+Antoine Eiche, Nathaniel Wesley Filardo, Guy Gastineau, John Goerzen,+Reto Hablützel, Juraj Hercek, Tomáš Janoušek, Ada Joule, Spencer+Janssen, Roman Joost, Jochen Keil, Sam Kirby, Lennart Kolmodin,+Krzysztof Kosciuszkiewicz, Dmitry Kurochkin, Todd Lunter, Vanessa+McHale, Robert J. Macomber, Dmitry Malikov, David McLean, Joan MIlev,+Marcin Mikołajczyk, Dino Morelli, Tony Morris, Eric Mrak, Thiago+Negri, Edward O'Callaghan, Svein Ove, Martin Perner, Jens Petersen,+Alexander Polakov, Sibi Prabakaran, Pavan Rikhi, Petr Rockai, Andrew+Emmanuel Rosa, Sackville-West, Amir Saeid, Markus Scherer, Daniel+Schüssler, Olivier Schneider, Alexander Shabalin, Valentin Shirokov,+Peter Simons, Alexander Solovyov, Will Song, John Soros, Felix+Springer, Travis Staton, Artem Tarasov, Samuli Thomasson, Edward+Tjörnhammar, Sergei Trofimovich, Thomas Tuegel, John Tyree, Jan+Vornberger, Anton Vorontsov, Daniel Wagner, Zev Weiss, Phil Xiaojun+Hu, Nikolay Yakimov, Edward Z. Yang, Leo Zhang, Norbert Zeh, and+Michal Zielonka.  ** Thanks 
src/Xmobar.hs view
@@ -33,6 +33,7 @@ #endif               , module Xmobar.Plugins.EWMH               , module Xmobar.Plugins.HandleReader+              , module Xmobar.Plugins.QueueReader               , module Xmobar.Plugins.Kbd               , module Xmobar.Plugins.Locks #ifdef INOTIFY@@ -60,6 +61,7 @@ #endif import Xmobar.Plugins.EWMH import Xmobar.Plugins.HandleReader+import Xmobar.Plugins.QueueReader import Xmobar.Plugins.Kbd import Xmobar.Plugins.Locks #ifdef INOTIFY
src/Xmobar/App/EventLoop.hs view
@@ -265,6 +265,7 @@       iconW i = maybe 0 Bitmap.width (lookup i $ iconS conf)       getCoords (Text s,_,i,a) = textWidth d (safeIndex fs i) s >>= \tw -> return (a, 0, fi tw)       getCoords (Icon s,_,_,a) = return (a, 0, fi $ iconW s)+      getCoords (Hspace w,_,_,a) = return (a, 0, fi w)       partCoord off xs = map (\(a, x, x') -> (fromJust a, x, x')) $                          filter (\(a, _,_) -> isJust a) $                          scanl (\(_,_,x') (a,_,w') -> (a, x', x' + w'))
src/Xmobar/Plugins/Monitors/Batt.hs view
@@ -17,45 +17,19 @@  module Xmobar.Plugins.Monitors.Batt ( battConfig, runBatt, runBatt' ) where -import System.Process (system)-import Control.Monad (void, unless)+import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..)+                                           , Result(..)+                                           , Status(..)) import Xmobar.Plugins.Monitors.Common-import Control.Exception (SomeException, handle)-import System.FilePath ((</>))-import System.IO (IOMode(ReadMode), hGetLine, withFile)-import System.Posix.Files (fileExist)-#ifdef FREEBSD-import System.BSD.Sysctl (sysctlReadInt)-#endif import System.Console.GetOpt-import Data.List (sort, sortBy, group)-import Data.Maybe (fromMaybe)-import Data.Ord (comparing)-import Text.Read (readMaybe) -data BattOpts = BattOpts-  { onString :: String-  , offString :: String-  , idleString :: String-  , posColor :: Maybe String-  , lowWColor :: Maybe String-  , mediumWColor :: Maybe String-  , highWColor :: Maybe String-  , lowThreshold :: Float-  , highThreshold :: Float-  , onLowAction :: Maybe String-  , actionThreshold :: Float-  , onlineFile :: FilePath-  , scale :: Float-  , onIconPattern :: Maybe IconPattern-  , offIconPattern :: Maybe IconPattern-  , idleIconPattern :: Maybe IconPattern-  , lowString :: String-  , mediumString :: String-  , highString :: String-  , incPerc :: Bool-  }+#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Batt.FreeBSD as MB+#else+import qualified Xmobar.Plugins.Monitors.Batt.Linux as MB+#endif + defaultOpts :: BattOpts defaultOpts = BattOpts   { onString = "On"@@ -108,34 +82,11 @@   , Option "" ["highs"] (ReqArg (\x o -> o { highString = x }) "") ""   ] -data Status = Charging | Discharging | Full | Idle | Unknown deriving (Read, Eq)--- Result perc watts time-seconds Status-data Result = Result Float Float Float Status | NA--sysDir :: FilePath-sysDir = "/sys/class/power_supply"- battConfig :: IO MConfig battConfig = mkMConfig        "Batt: <watts>, <left>% / <timeleft>" -- template        ["leftbar", "leftvbar", "left", "acstatus", "timeleft", "watts", "leftipat"] -- replacements -data Files = Files-  { fFull :: String-  , fNow :: String-  , fVoltage :: String-  , fCurrent :: String-  , fStatus :: String-  , isCurrent :: Bool-  } | NoFiles deriving Eq--data Battery = Battery-  { full :: !Float-  , now :: !Float-  , power :: !Float-  , status :: !String-  }- data BatteryStatus   = BattHigh   | BattMedium@@ -153,130 +104,13 @@  where    c = 100 * min 1 charge -maybeAlert :: BattOpts -> Float -> IO ()-maybeAlert opts left =-  case onLowAction opts of-    Nothing -> return ()-    Just x -> unless (isNaN left || actionThreshold opts < 100 * left)-                $ void $ system x---- | FreeBSD battery query-#ifdef FREEBSD-battStatusFbsd :: Int -> Status-battStatusFbsd x-  | x == 1 = Discharging-  | x == 2 = Charging-  | otherwise = Unknown--readBatteriesFbsd :: BattOpts -> IO Result-readBatteriesFbsd opts = do-  lf <- sysctlReadInt "hw.acpi.battery.life"-  rt <- sysctlReadInt "hw.acpi.battery.rate"-  tm <- sysctlReadInt "hw.acpi.battery.time"-  st <- sysctlReadInt "hw.acpi.battery.state"-  acline <- sysctlReadInt "hw.acpi.acline"-  let p = fromIntegral lf / 100-      w = fromIntegral rt-      t = fromIntegral tm * 60-      ac = acline == 1-      -- battery full when rate is 0 and on ac.-      sts = if (w == 0 && ac) then Full else (battStatusFbsd $ fromIntegral st)-  unless ac (maybeAlert opts p)-  return (Result p w t sts)--#else--- | query linux battery-safeFileExist :: String -> String -> IO Bool-safeFileExist d f = handle noErrors $ fileExist (d </> f)-  where noErrors = const (return False) :: SomeException -> IO Bool--batteryFiles :: String -> IO Files-batteryFiles bat =-  do is_charge <- exists "charge_now"-     is_energy <- if is_charge then return False else exists "energy_now"-     is_power <- exists "power_now"-     plain <- exists (if is_charge then "charge_full" else "energy_full")-     let cf = if is_power then "power_now" else "current_now"-         sf = if plain then "" else "_design"-     return $ case (is_charge, is_energy) of-       (True, _) -> files "charge" cf sf is_power-       (_, True) -> files "energy" cf sf is_power-       _ -> NoFiles-  where prefix = sysDir </> bat-        exists = safeFileExist prefix-        files ch cf sf ip = Files { fFull = prefix </> ch ++ "_full" ++ sf-                                  , fNow = prefix </> ch ++ "_now"-                                  , fCurrent = prefix </> cf-                                  , fVoltage = prefix </> "voltage_now"-                                  , fStatus = prefix </> "status"-                                  , isCurrent = not ip}--haveAc :: FilePath -> IO Bool-haveAc f =-  handle onError $ withFile (sysDir </> f) ReadMode (fmap (== "1") . hGetLine)-  where onError = const (return False) :: SomeException -> IO Bool--readBattery :: Float -> Files -> IO Battery-readBattery _ NoFiles = return $ Battery 0 0 0 "Unknown"-readBattery sc files =-    do a <- grab $ fFull files-       b <- grab $ fNow files-       d <- grab $ fCurrent files-       s <- grabs $ fStatus files-       let sc' = if isCurrent files then sc / 10 else sc-           a' = max a b -- sometimes the reported max charge is lower than-       return $ Battery (3600 * a' / sc') -- wattseconds-                        (3600 * b / sc') -- wattseconds-                        (abs d / sc') -- watts-                        s -- string: Discharging/Charging/Full-    where grab f = handle onError $ withFile f ReadMode (fmap read . hGetLine)-          onError = const (return (-1)) :: SomeException -> IO Float-          grabs f = handle onError' $ withFile f ReadMode hGetLine-          onError' = const (return "Unknown") :: SomeException -> IO String---- sortOn is only available starting at ghc 7.10-sortOn :: Ord b => (a -> b) -> [a] -> [a]-sortOn f =-  map snd . sortBy (comparing fst) . map (\x -> let y = f x in y `seq` (y, x))--mostCommonDef :: Eq a => a -> [a] -> a-mostCommonDef x xs = head $ last $ [x] : sortOn length (group xs)--readBatteriesLinux :: BattOpts -> [Files] -> IO Result-readBatteriesLinux opts bfs =-    do let bfs' = filter (/= NoFiles) bfs-       bats <- mapM (readBattery (scale opts)) (take 3 bfs')-       ac <- haveAc (onlineFile opts)-       let sign = if ac then 1 else -1-           ft = sum (map full bats)-           left = if ft > 0 then sum (map now bats) / ft else 0-           watts = sign * sum (map power bats)-           time = if watts == 0 then 0 else max 0 (sum $ map time' bats)-           mwatts = if watts == 0 then 1 else sign * watts-           time' b = (if ac then full b - now b else now b) / mwatts-           statuses :: [Status]-           statuses = map (fromMaybe Unknown . readMaybe)-                          (sort (map status bats))-           acst = mostCommonDef Unknown $ filter (Unknown/=) statuses-           racst | acst /= Unknown = acst-                 | time == 0 = Idle-                 | ac = Charging-                 | otherwise = Discharging-       unless ac (maybeAlert opts left)-       return $ if isNaN left then NA else Result left watts time racst-#endif- runBatt :: [String] -> Monitor String runBatt = runBatt' ["BAT", "BAT0", "BAT1", "BAT2"]  runBatt' :: [String] -> [String] -> Monitor String runBatt' bfs args = do   opts <- io $ parseOptsWith options defaultOpts args-#ifdef FREEBSD-  c <- io $ readBatteriesFbsd opts-#else-  c <- io $ readBatteriesLinux opts =<< mapM batteryFiles bfs-#endif+  c <- io $ MB.readBatteries opts bfs   formatResult c opts  formatResult :: Result -> BattOpts -> Monitor String
+ src/Xmobar/Plugins/Monitors/Batt/Common.hs view
@@ -0,0 +1,57 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Batt.Common+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega+--                (c) 2010 Andrea Rossato, Petr Rockai+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A battery monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..)+                                           , Result(..)+                                           , Status(..)+                                           , maybeAlert) where++import System.Process (system)+import Control.Monad (unless, void)+import Xmobar.Plugins.Monitors.Common++data Status = Charging | Discharging | Full | Idle | Unknown deriving (Read, Eq)+-- Result perc watts time-seconds Status+data Result = Result Float Float Float Status | NA++data BattOpts = BattOpts+  { onString :: String+  , offString :: String+  , idleString :: String+  , posColor :: Maybe String+  , lowWColor :: Maybe String+  , mediumWColor :: Maybe String+  , highWColor :: Maybe String+  , lowThreshold :: Float+  , highThreshold :: Float+  , onLowAction :: Maybe String+  , actionThreshold :: Float+  , onlineFile :: FilePath+  , scale :: Float+  , onIconPattern :: Maybe IconPattern+  , offIconPattern :: Maybe IconPattern+  , idleIconPattern :: Maybe IconPattern+  , lowString :: String+  , mediumString :: String+  , highString :: String+  , incPerc :: Bool+  }++maybeAlert :: BattOpts -> Float -> IO ()+maybeAlert opts left =+  case onLowAction opts of+    Nothing -> return ()+    Just x -> unless (isNaN left || actionThreshold opts < 100 * left)+                $ void $ system x
+ src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs view
@@ -0,0 +1,46 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Batt.FreeBSD+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega+--                (c) 2010 Andrea Rossato, Petr Rockai+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A battery monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Batt.FreeBSD (readBatteries) where++import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..)+                                           , Result(..)+                                           , Status(..)+                                           , maybeAlert)++import Control.Monad (unless)+import System.BSD.Sysctl (sysctlReadInt)++battStatus :: Int -> Status+battStatus x+  | x == 1 = Discharging+  | x == 2 = Charging+  | otherwise = Unknown++readBatteries :: BattOpts -> [String] -> IO Result+readBatteries opts _ = do+  lf <- sysctlReadInt "hw.acpi.battery.life"+  rt <- sysctlReadInt "hw.acpi.battery.rate"+  tm <- sysctlReadInt "hw.acpi.battery.time"+  st <- sysctlReadInt "hw.acpi.battery.state"+  acline <- sysctlReadInt "hw.acpi.acline"+  let p = fromIntegral lf / 100+      w = fromIntegral rt+      t = fromIntegral tm * 60+      ac = acline == 1+      -- battery full when rate is 0 and on ac.+      sts = if w == 0 && ac then Full else battStatus $ fromIntegral st+  unless ac (maybeAlert opts p)+  return (Result p w t sts)
+ src/Xmobar/Plugins/Monitors/Batt/Linux.hs view
@@ -0,0 +1,130 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Batt.Linux+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega+--                (c) 2010 Andrea Rossato, Petr Rockai+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A battery monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Batt.Linux (readBatteries) where++import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..)+                                           , Result(..)+                                           , Status(..)+                                           , maybeAlert)++import Control.Monad (unless)+import Control.Exception (SomeException, handle)+import System.FilePath ((</>))+import System.IO (IOMode(ReadMode), hGetLine, withFile)+import System.Posix.Files (fileExist)+import Data.List (sort, sortBy, group)+import Data.Maybe (fromMaybe)+import Data.Ord (comparing)+import Text.Read (readMaybe)++data Files = Files+  { fFull :: String+  , fNow :: String+  , fVoltage :: String+  , fCurrent :: String+  , fStatus :: String+  , isCurrent :: Bool+  } | NoFiles deriving Eq++data Battery = Battery+  { full :: !Float+  , now :: !Float+  , power :: !Float+  , status :: !String+  }++sysDir :: FilePath+sysDir = "/sys/class/power_supply"++safeFileExist :: String -> String -> IO Bool+safeFileExist d f = handle noErrors $ fileExist (d </> f)+  where noErrors = const (return False) :: SomeException -> IO Bool++batteryFiles :: String -> IO Files+batteryFiles bat =+  do is_charge <- exists "charge_now"+     is_energy <- if is_charge then return False else exists "energy_now"+     is_power <- exists "power_now"+     plain <- exists (if is_charge then "charge_full" else "energy_full")+     let cf = if is_power then "power_now" else "current_now"+         sf = if plain then "" else "_design"+     return $ case (is_charge, is_energy) of+       (True, _) -> files "charge" cf sf is_power+       (_, True) -> files "energy" cf sf is_power+       _ -> NoFiles+  where prefix = sysDir </> bat+        exists = safeFileExist prefix+        files ch cf sf ip = Files { fFull = prefix </> ch ++ "_full" ++ sf+                                  , fNow = prefix </> ch ++ "_now"+                                  , fCurrent = prefix </> cf+                                  , fVoltage = prefix </> "voltage_now"+                                  , fStatus = prefix </> "status"+                                  , isCurrent = not ip}++haveAc :: FilePath -> IO Bool+haveAc f =+  handle onError $ withFile (sysDir </> f) ReadMode (fmap (== "1") . hGetLine)+  where onError = const (return False) :: SomeException -> IO Bool++readBattery :: Float -> Files -> IO Battery+readBattery _ NoFiles = return $ Battery 0 0 0 "Unknown"+readBattery sc files =+    do a <- grab $ fFull files+       b <- grab $ fNow files+       d <- grab $ fCurrent files+       s <- grabs $ fStatus files+       let sc' = if isCurrent files then sc / 10 else sc+           a' = max a b -- sometimes the reported max charge is lower than+       return $ Battery (3600 * a' / sc') -- wattseconds+                        (3600 * b / sc') -- wattseconds+                        (abs d / sc') -- watts+                        s -- string: Discharging/Charging/Full+    where grab f = handle onError $ withFile f ReadMode (fmap read . hGetLine)+          onError = const (return (-1)) :: SomeException -> IO Float+          grabs f = handle onError' $ withFile f ReadMode hGetLine+          onError' = const (return "Unknown") :: SomeException -> IO String++-- sortOn is only available starting at ghc 7.10+sortOn :: Ord b => (a -> b) -> [a] -> [a]+sortOn f =+  map snd . sortBy (comparing fst) . map (\x -> let y = f x in y `seq` (y, x))++mostCommonDef :: Eq a => a -> [a] -> a+mostCommonDef x xs = head $ last $ [x] : sortOn length (group xs)++readBatteries :: BattOpts -> [String] -> IO Result+readBatteries opts bfs =+    do bfs' <- mapM batteryFiles bfs+       let bfs'' = filter (/= NoFiles) bfs'+       bats <- mapM (readBattery (scale opts)) (take 3 bfs'')+       ac <- haveAc (onlineFile opts)+       let sign = if ac then 1 else -1+           ft = sum (map full bats)+           left = if ft > 0 then sum (map now bats) / ft else 0+           watts = sign * sum (map power bats)+           time = if watts == 0 then 0 else max 0 (sum $ map time' bats)+           mwatts = if watts == 0 then 1 else sign * watts+           time' b = (if ac then full b - now b else now b) / mwatts+           statuses :: [Status]+           statuses = map (fromMaybe Unknown . readMaybe)+                          (sort (map status bats))+           acst = mostCommonDef Unknown $ filter (Unknown/=) statuses+           racst | acst /= Unknown = acst+                 | time == 0 = Idle+                 | ac = Charging+                 | otherwise = Discharging+       unless ac (maybeAlert opts left)+       return $ if isNaN left then NA else Result left watts time racst
src/Xmobar/Plugins/Monitors/Cpu.hs view
@@ -20,23 +20,26 @@   ( startCpu   , runCpu   , cpuConfig-  , CpuDataRef+  , MC.CpuDataRef   , CpuOpts   , CpuArguments-  , parseCpu+  , MC.parseCpu   , getArguments   ) where  import Xmobar.Plugins.Monitors.Common-import qualified Data.ByteString.Lazy.Char8 as B-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-#ifdef FREEBSD-import System.BSD.Sysctl (sysctlPeekArray)-#endif+import Data.IORef (newIORef) import System.Console.GetOpt import Xmobar.App.Timer (doEveryTenthSeconds) import Control.Monad (void)+import Xmobar.Plugins.Monitors.Cpu.Common (CpuData(..)) +#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Cpu.FreeBSD as MC+#else+import qualified Xmobar.Plugins.Monitors.Cpu.Linux as MC+#endif+ newtype CpuOpts = CpuOpts   { loadIconPattern :: Maybe IconPattern   }@@ -94,83 +97,6 @@     , iowaitField     ] -data CpuData = CpuData {-      cpuUser :: !Float,-      cpuNice :: !Float,-      cpuSystem :: !Float,-      cpuIdle :: !Float,-      cpuIowait :: !Float,-      cpuTotal :: !Float-    }--#ifdef FREEBSD--- kern.cp_time data from the previous iteration for computing the difference-type CpuDataRef = IORef [Word]--cpuData :: IO [Word]-cpuData = sysctlPeekArray "kern.cp_time" :: IO [Word]--parseCpu :: CpuDataRef -> IO CpuData-parseCpu cref = do-    prev <- readIORef cref-    curr <- cpuData-    writeIORef cref curr-    let diff = map fromIntegral $ zipWith (-) curr prev-        user = diff !! 0-        nice = diff !! 1-        system = diff !! 2-        intr = diff !! 3-        idle = diff !! 4-        total = user + nice + system + intr + idle-    return CpuData-      { cpuUser = user/total-      , cpuNice = nice/total-      , cpuSystem = (system+intr)/total-      , cpuIdle = idle/total-      , cpuIowait = 0-      , cpuTotal = user/total-      }-#else-type CpuDataRef = IORef [Int]---- Details about the fields here: https://www.kernel.org/doc/Documentation/filesystems/proc.txt-cpuData :: IO [Int]-cpuData = cpuParser <$> B.readFile "/proc/stat"--readInt :: B.ByteString -> Int-readInt bs = case B.readInt bs of-               Nothing -> 0-               Just (i, _) -> i--cpuParser :: B.ByteString -> [Int]-cpuParser = map readInt . tail . B.words . head . B.lines--convertToCpuData :: [Float] -> CpuData-convertToCpuData (u:n:s:ie:iw:_) =-  CpuData-    { cpuUser = u-    , cpuNice = n-    , cpuSystem = s-    , cpuIdle = ie-    , cpuIowait = iw-    , cpuTotal = sum [u, n, s]-    }-convertToCpuData args = error $ "convertToCpuData: Unexpected list" <> show args--parseCpu :: CpuDataRef -> IO CpuData-parseCpu cref =-    do a <- readIORef cref-       b <- cpuData-       writeIORef cref b-       let dif = zipWith (-) b a-           tot = fromIntegral $ sum dif-           safeDiv n = case tot of-                         0 -> 0-                         v -> fromIntegral n / v-           percent = map safeDiv dif-       return $ convertToCpuData percent-#endif- data Field = Field {       fieldName :: !String,       fieldCompute :: !ShouldCompute@@ -235,7 +161,7 @@  data CpuArguments =   CpuArguments-    { cpuDataRef :: !CpuDataRef+    { cpuDataRef :: !MC.CpuDataRef     , cpuParams :: !MonitorConfig     , cpuArgs :: ![String]     , cpuOpts :: !CpuOpts@@ -247,9 +173,9 @@  getArguments :: [String] -> IO CpuArguments getArguments cpuArgs = do-  initCpuData <- cpuData+  initCpuData <- MC.cpuData   cpuDataRef <- newIORef initCpuData-  void $ parseCpu cpuDataRef+  void $ MC.parseCpu cpuDataRef   cpuParams <- computeMonitorConfig cpuArgs cpuConfig   cpuInputTemplate <- runTemplateParser cpuParams   cpuAllTemplate <- runExportParser (pExport cpuParams)@@ -270,7 +196,7 @@  runCpu :: CpuArguments -> IO String runCpu args@CpuArguments {..} = do-  cpuValue <- parseCpu cpuDataRef+  cpuValue <- MC.parseCpu cpuDataRef   temMonitorValues <- formatCpu args cpuValue   let templateInput =         TemplateInput
+ src/Xmobar/Plugins/Monitors/Cpu/Common.hs view
@@ -0,0 +1,25 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Cpu.Common+-- Copyright   :  (c) 2011, 2017 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A cpu monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Cpu.Common (CpuData(..)) where++data CpuData = CpuData {+      cpuUser :: !Float,+      cpuNice :: !Float,+      cpuSystem :: !Float,+      cpuIdle :: !Float,+      cpuIowait :: !Float,+      cpuTotal :: !Float+    }
+ src/Xmobar/Plugins/Monitors/Cpu/FreeBSD.hs view
@@ -0,0 +1,54 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Cpu.FreeBSD+-- Copyright   :  (c) 2011, 2017 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A cpu monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Cpu.FreeBSD (parseCpu+                                           , CpuDataRef+                                           , cpuData) where++import Xmobar.Plugins.Monitors.Cpu.Common (CpuData(..))+import Data.IORef (IORef, readIORef, writeIORef)+import System.BSD.Sysctl (sysctlPeekArray)++-- kern.cp_time data from the previous iteration for computing the difference+type CpuDataRef = IORef [Word]++cpuData :: IO [Word]+cpuData = sysctlPeekArray "kern.cp_time" :: IO [Word]++parseCpu :: CpuDataRef -> IO CpuData+parseCpu cref = do+    prev <- readIORef cref+    curr <- cpuData+    writeIORef cref curr+    let diff = map fromIntegral $ zipWith (-) curr prev+        user = head diff+        nice = diff !! 1+        system = diff !! 2+        intr = diff !! 3+        idle = diff !! 4+        total = user + nice + system + intr + idle+        cpuUserPerc = if total > 0 then user/total else 0+        cpuNicePerc = if total > 0 then nice/total else 0+        cpuSystemPerc = if total > 0 then (system+intr)/total else 0+        cpuIdlePerc = if total > 0 then idle/total else 0++    return CpuData+      { cpuUser = cpuUserPerc+      , cpuNice = cpuNicePerc+      , cpuSystem = cpuSystemPerc+      , cpuIdle = cpuIdlePerc+      , cpuIowait = 0+      , cpuTotal = cpuUserPerc+cpuSystemPerc+      }
+ src/Xmobar/Plugins/Monitors/Cpu/Linux.hs view
@@ -0,0 +1,61 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Cpu.Linux+-- Copyright   :  (c) 2011, 2017 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A cpu monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Cpu.Linux (parseCpu+                                         , CpuDataRef+                                         , cpuData) where++import Xmobar.Plugins.Monitors.Cpu.Common (CpuData(..))+import qualified Data.ByteString.Lazy.Char8 as B+import Data.IORef (IORef, readIORef, writeIORef)++type CpuDataRef = IORef [Int]++-- Details about the fields here: https://www.kernel.org/doc/Documentation/filesystems/proc.txt+cpuData :: IO [Int]+cpuData = cpuParser <$> B.readFile "/proc/stat"++readInt :: B.ByteString -> Int+readInt bs = case B.readInt bs of+               Nothing -> 0+               Just (i, _) -> i++cpuParser :: B.ByteString -> [Int]+cpuParser = map readInt . tail . B.words . head . B.lines++convertToCpuData :: [Float] -> CpuData+convertToCpuData (u:n:s:ie:iw:_) =+  CpuData+    { cpuUser = u+    , cpuNice = n+    , cpuSystem = s+    , cpuIdle = ie+    , cpuIowait = iw+    , cpuTotal = sum [u, n, s]+    }+convertToCpuData args = error $ "convertToCpuData: Unexpected list" <> show args++parseCpu :: CpuDataRef -> IO CpuData+parseCpu cref =+    do a <- readIORef cref+       b <- cpuData+       writeIORef cref b+       let dif = zipWith (-) b a+           tot = fromIntegral $ sum dif+           safeDiv n = case tot of+                         0 -> 0+                         v -> fromIntegral n / v+           percent = map safeDiv dif+       return $ convertToCpuData percent
src/Xmobar/Plugins/Monitors/Mem.hs view
@@ -1,3 +1,4 @@+{-#LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Plugins.Monitors.Mem@@ -15,9 +16,15 @@ module Xmobar.Plugins.Monitors.Mem (memConfig, runMem, totalMem, usedMem) where  import Xmobar.Plugins.Monitors.Common-import qualified Data.Map as M import System.Console.GetOpt +#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Mem.FreeBSD as MM+#else+import qualified Xmobar.Plugins.Monitors.Mem.Linux as MM+#endif++ data MemOpts = MemOpts   { usedIconPattern :: Maybe IconPattern   , freeIconPattern :: Maybe IconPattern@@ -49,27 +56,11 @@         "usedratio", "freeratio", "availableratio",         "total", "free", "buffer", "cache", "available", "used"] -- available replacements -fileMEM :: IO String-fileMEM = readFile "/proc/meminfo"--parseMEM :: IO [Float]-parseMEM =-    do file <- fileMEM-       let content = map words $ take 8 $ lines file-           info = M.fromList $ map (\line -> (head line, (read $ line !! 1 :: Float) / 1024)) content-           [total, free, buffer, cache] = map (info M.!) ["MemTotal:", "MemFree:", "Buffers:", "Cached:"]-           available = M.findWithDefault (free + buffer + cache) "MemAvailable:" info-           used = total - available-           usedratio = used / total-           freeratio = free / total-           availableratio = available / total-       return [usedratio, freeratio, availableratio, total, free, buffer, cache, available, used]- totalMem :: IO Float-totalMem = fmap ((*1024) . (!!1)) parseMEM+totalMem = fmap ((*1024) . (!!1)) MM.parseMEM  usedMem :: IO Float-usedMem = fmap ((*1024) . (!!6)) parseMEM+usedMem = fmap ((*1024) . (!!6)) MM.parseMEM  formatMem :: MemOpts -> [Float] -> Monitor [String] formatMem opts (r:fr:ar:xs) =@@ -84,7 +75,7 @@  runMem :: [String] -> Monitor String runMem argv =-    do m <- io parseMEM+    do m <- io MM.parseMEM        opts <- io $ parseOptsWith options defaultOpts argv        l <- formatMem opts m        parseTemplate l
+ src/Xmobar/Plugins/Monitors/Mem/FreeBSD.hs view
@@ -0,0 +1,45 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Mem.FreeBSD+-- Copyright   :  (c) Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A memory monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Mem.FreeBSD (parseMEM) where++import System.BSD.Sysctl (sysctlReadUInt)++parseMEM :: IO [Float]+parseMEM = do stats <- mapM sysctlReadUInt [+                "vm.stats.vm.v_page_size"+                , "vm.stats.vm.v_page_count"+                , "vm.stats.vm.v_free_count"+                , "vm.stats.vm.v_active_count"+                , "vm.stats.vm.v_inactive_count"+                , "vm.stats.vm.v_wire_count"+                , "vm.stats.vm.v_cache_count"]++              let [ pagesize, totalpages, freepages, activepages, inactivepages, wiredpages, cachedpages ] = fmap fromIntegral stats+                  usedpages = activepages + wiredpages + cachedpages+                  availablepages = inactivepages + cachedpages + freepages+                  bufferedpages = activepages + inactivepages + wiredpages++                  available = availablepages * pagesize+                  used = usedpages * pagesize+                  free = freepages * pagesize+                  cache = cachedpages * pagesize+                  buffer = bufferedpages * pagesize+                  total = totalpages * pagesize++                  usedratio = usedpages / totalpages+                  freeratio = freepages / totalpages+                  availableratio = availablepages / totalpages++              return [usedratio, freeratio, availableratio, total, free, buffer, cache, available, used]
+ src/Xmobar/Plugins/Monitors/Mem/Linux.hs view
@@ -0,0 +1,33 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Mem.Linux+-- Copyright   :  (c) Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A memory monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Mem.Linux (parseMEM) where++import qualified Data.Map as M++fileMEM :: IO String+fileMEM = readFile "/proc/meminfo"++parseMEM :: IO [Float]+parseMEM =+    do file <- fileMEM+       let content = map words $ take 8 $ lines file+           info = M.fromList $ map (\line -> (head line, (read $ line !! 1 :: Float) / 1024)) content+           [total, free, buffer, cache] = map (info M.!) ["MemTotal:", "MemFree:", "Buffers:", "Cached:"]+           available = M.findWithDefault (free + buffer + cache) "MemAvailable:" info+           used = total - available+           usedratio = used / total+           freeratio = free / total+           availableratio = available / total+       return [usedratio, freeratio, availableratio, total, free, buffer, cache, available, used]
src/Xmobar/Plugins/Monitors/Net.hs view
@@ -11,9 +11,10 @@ -- -- A net device monitor for Xmobar --+ ----------------------------------------------------------------------------- -{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}  module Xmobar.Plugins.Monitors.Net (                         startNet@@ -21,19 +22,19 @@                       ) where  import Xmobar.Plugins.Monitors.Common--import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)-import Data.Word (Word64)-import Control.Monad (forM, filterM)-import System.Directory (getDirectoryContents, doesFileExist)-import System.FilePath ((</>))+import Xmobar.Plugins.Monitors.Net.Common (NetDev(..), NetDevInfo(..), NetDevRate, NetDevRef)+import Data.IORef (newIORef, readIORef, writeIORef)+import Data.Time.Clock (getCurrentTime, diffUTCTime) import System.Console.GetOpt-import System.IO.Error (catchIOError)-import System.IO.Unsafe (unsafeInterleaveIO) -import qualified Data.ByteString.Char8 as B+#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Net.FreeBSD as MN+#else+import qualified Xmobar.Plugins.Monitors.Net.Linux as MN+#endif +import Control.Monad (forM)+ type DevList = [String]  parseDevList :: String -> DevList@@ -79,70 +80,11 @@     show MBs = "MB/s"     show GBs = "GB/s" -data NetDev num = N String (NetDevInfo num) | NA deriving (Eq,Show,Read)-data NetDevInfo num = NI | ND num num deriving (Eq,Show,Read)--type NetDevRawTotal = NetDev Word64-type NetDevRate = NetDev Float--type NetDevRef = IORef (NetDevRawTotal, UTCTime)---- The more information available, the better.--- Note that names don't matter. Therefore, if only the names differ,--- a compare evaluates to EQ while (==) evaluates to False.-instance Ord num => Ord (NetDev num) where-    compare NA NA             = EQ-    compare NA _              = LT-    compare _  NA             = GT-    compare (N _ i1) (N _ i2) = i1 `compare` i2--instance Ord num => Ord (NetDevInfo num) where-    compare NI NI                 = EQ-    compare NI ND {}              = LT-    compare ND {} NI              = GT-    compare (ND x1 y1) (ND x2 y2) = x1 `compare` x2 <> y1 `compare` y2- netConfig :: IO MConfig netConfig = mkMConfig     "<dev>: <rx>KB|<tx>KB"      -- template     ["dev", "rx", "tx", "rxbar", "rxvbar", "rxipat", "txbar", "txvbar", "txipat", "up"]     -- available replacements -operstateDir :: String -> FilePath-operstateDir d = "/sys/class/net" </> d </> "operstate"--existingDevs :: IO [String]-existingDevs = getDirectoryContents "/sys/class/net" >>= filterM isDev-  where isDev d | d `elem` excludes = return False-                | otherwise = doesFileExist (operstateDir d)-        excludes = [".", "..", "lo"]--isUp :: String -> IO Bool-isUp d = flip catchIOError (const $ return False) $ do-  operstate <- B.readFile (operstateDir d)-  return $! (head . B.lines) operstate `elem` ["up", "unknown"]--readNetDev :: [String] -> IO NetDevRawTotal-readNetDev ~[d, x, y] = do-  up <- unsafeInterleaveIO $ isUp d-  return $ N d (if up then ND (r x) (r y) else NI)-    where r s | s == "" = 0-              | otherwise = read s--netParser :: B.ByteString -> IO [NetDevRawTotal]-netParser = mapM (readNetDev . splitDevLine) . readDevLines-  where readDevLines = drop 2 . B.lines-        splitDevLine = map B.unpack . selectCols . filter (not . B.null) . B.splitWith (`elem` [' ',':'])-        selectCols cols = map (cols!!) [0,1,9]--findNetDev :: String -> IO NetDevRawTotal-findNetDev dev = do-  nds <- B.readFile "/proc/net/dev" >>= netParser-  case filter isDev nds of-    x:_ -> return x-    _ -> return NA-  where isDev (N d _) = d == dev-        isDev NA = False- formatNet :: Maybe IconPattern -> Float -> Monitor (String, String, String, String) formatNet mipat d = do     s <- getConfigValue useSuffix@@ -169,7 +111,7 @@ parseNet :: NetDevRef -> String -> IO NetDevRate parseNet nref nd = do   (n0, t0) <- readIORef nref-  n1 <- findNetDev nd+  n1 <- MN.findNetDev nd   t1 <- getCurrentTime   writeIORef nref (n1, t1)   let scx = realToFrac (diffUTCTime t1 t0)@@ -213,7 +155,7 @@  startDynNet :: [String] -> Int -> (String -> IO ()) -> IO () startDynNet a r cb = do-  devs <- existingDevs+  devs <- MN.existingDevs   refs <- forM devs $ \d -> do             t <- getCurrentTime             nref <- newIORef (NA, t)
+ src/Xmobar/Plugins/Monitors/Net/Common.hs view
@@ -0,0 +1,50 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Net.Common+-- Copyright   :  (c) 2011, 2012, 2013, 2014, 2017, 2020 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A net device monitor for Xmobar+--++-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Net.Common (+                        NetDev(..)+                      , NetDevInfo(..)+                      , NetDevRawTotal+                      , NetDevRate+                      , NetDevRef+                      ) where++import Data.IORef (IORef)+import Data.Time.Clock (UTCTime)+import Data.Word (Word64)++data NetDev num = N String (NetDevInfo num) | NA deriving (Eq,Show,Read)+data NetDevInfo num = NI | ND num num deriving (Eq,Show,Read)++type NetDevRawTotal = NetDev Word64+type NetDevRate = NetDev Float++type NetDevRef = IORef (NetDevRawTotal, UTCTime)++-- The more information available, the better.+-- Note that names don't matter. Therefore, if only the names differ,+-- a compare evaluates to EQ while (==) evaluates to False.+instance Ord num => Ord (NetDev num) where+    compare NA NA             = EQ+    compare NA _              = LT+    compare _  NA             = GT+    compare (N _ i1) (N _ i2) = i1 `compare` i2++instance Ord num => Ord (NetDevInfo num) where+    compare NI NI                 = EQ+    compare NI ND {}              = LT+    compare ND {} NI              = GT+    compare (ND x1 y1) (ND x2 y2) = x1 `compare` x2 <> y1 `compare` y2
+ src/Xmobar/Plugins/Monitors/Net/FreeBSD.hsc view
@@ -0,0 +1,118 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE CApiFFI #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Net.FreeBSD+-- Copyright   :  (c) 2011, 2012, 2013, 2014, 2017, 2020 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A net device monitor for Xmobar+--++-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Net.FreeBSD (+  existingDevs+  , findNetDev+  ) where++import Xmobar.Plugins.Monitors.Net.Common (NetDevRawTotal, NetDev(..), NetDevInfo(..))+import Control.Exception (catch, SomeException(..))+import Foreign (Int32, plusPtr)+import Foreign.C.Types (CUIntMax, CUChar)+import Foreign.C.String (peekCString)+import Foreign.ForeignPtr ()+import Foreign.Storable (Storable, alignment, sizeOf, peek, poke)+import System.BSD.Sysctl (OID, sysctlPrepareOid, sysctlReadInt, sysctlPeek)++#include <sys/sysctl.h>+#include <net/if.h>+#include <net/if_mib.h>++data IfData = AvailableIfData {+  name :: String+  , txBytes :: CUIntMax+  , rxBytes :: CUIntMax+  , isUp :: Bool+  } | NotAvailableIfData+  deriving (Show, Read, Eq)++instance Storable IfData where+  alignment _ = #{alignment struct ifmibdata}+  sizeOf _    = #{size struct ifmibdata}+  peek ptr    = do+    cname <- peekCString (ptr `plusPtr` (#offset struct ifmibdata, ifmd_name))+    tx <- peek ((ifmd_data_ptr ptr) `plusPtr` (#offset struct if_data, ifi_obytes)) :: IO CUIntMax+    rx <- peek ((ifmd_data_ptr ptr) `plusPtr` (#offset struct if_data, ifi_ibytes)) :: IO CUIntMax+    state <- peek ((ifmd_data_ptr ptr) `plusPtr` (#offset struct if_data, ifi_link_state)) :: IO CUChar+    return $ AvailableIfData {name = cname, txBytes = tx, rxBytes = rx, isUp = up state}+      where+        up state = state == (#const LINK_STATE_UP)+        ifmd_data_ptr p = p `plusPtr` (#offset struct ifmibdata, ifmd_data)++  poke _ _    = pure ()++getNetIfCountOID :: IO OID+getNetIfCountOID = sysctlPrepareOid [+  #const CTL_NET+  , #const PF_LINK+  , #const NETLINK_GENERIC+  , #const IFMIB_SYSTEM+  , #const IFMIB_IFCOUNT]++getNetIfDataOID :: Int32 -> IO OID+getNetIfDataOID i = sysctlPrepareOid [+  #const CTL_NET+  , #const PF_LINK+  , #const NETLINK_GENERIC+  , #const IFMIB_IFDATA+  , i+  , #const IFDATA_GENERAL]++getNetIfCount :: IO Int32+getNetIfCount = do+  oid <- getNetIfCountOID+  sysctlReadInt oid++getNetIfData :: Int32 -> IO IfData+getNetIfData i = do+  oid <- getNetIfDataOID i+  res <- catch (sysctlPeek oid) (\(SomeException _) -> return NotAvailableIfData)+  return res++getAllNetworkData :: IO [IfData]+getAllNetworkData = do+  count <- getNetIfCount+  result <- mapM getNetIfData [1..count]+  return result++existingDevs :: IO [String]+existingDevs = getAllNetworkData >>= (\xs -> return $ filter (/= "lo0") $ fmap name xs)++convertIfDataToNetDev :: IfData -> IO NetDevRawTotal+convertIfDataToNetDev ifData = do+  let up = isUp ifData+      rx = fromInteger . toInteger $ rxBytes ifData+      tx = fromInteger . toInteger $ txBytes ifData+      d = name ifData+  return $ N d (if up then ND rx tx else NI)++netConvertIfDataToNetDev :: [IfData] -> IO [NetDevRawTotal]+netConvertIfDataToNetDev = mapM convertIfDataToNetDev++findNetDev :: String -> IO NetDevRawTotal+findNetDev dev = do+  nds <- getAllNetworkData >>= netConvertIfDataToNetDev+  case filter isDev nds of+    x:_ -> return x+    _ -> return NA+  where isDev (N d _) = d == dev+        isDev NA = False
+ src/Xmobar/Plugins/Monitors/Net/Linux.hs view
@@ -0,0 +1,69 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Net.Linux+-- Copyright   :  (c) 2011, 2012, 2013, 2014, 2017, 2020 Jose Antonio Ortega Ruiz+--                (c) 2007-2010 Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A net device monitor for Xmobar+--++-----------------------------------------------------------------------------++{-# LANGUAGE OverloadedStrings #-}++module Xmobar.Plugins.Monitors.Net.Linux (+  existingDevs+  , findNetDev+  ) where++import Xmobar.Plugins.Monitors.Net.Common (NetDevRawTotal, NetDev(..), NetDevInfo(..))++import Control.Monad (filterM)+import System.Directory (getDirectoryContents, doesFileExist)+import System.FilePath ((</>))+import System.IO.Error (catchIOError)+import System.IO.Unsafe (unsafeInterleaveIO)++import qualified Data.ByteString.Char8 as B+++operstateDir :: String -> FilePath+operstateDir d = "/sys/class/net" </> d </> "operstate"++existingDevs :: IO [String]+existingDevs = getDirectoryContents "/sys/class/net" >>= filterM isDev+  where isDev d | d `elem` excludes = return False+                | otherwise = doesFileExist (operstateDir d)+        excludes = [".", "..", "lo"]++isUp :: String -> IO Bool+isUp d = flip catchIOError (const $ return False) $ do+  operstate <- B.readFile (operstateDir d)+  return $! (head . B.lines) operstate `elem` ["up", "unknown"]++readNetDev :: [String] -> IO NetDevRawTotal+readNetDev ~[d, x, y] = do+  up <- unsafeInterleaveIO $ isUp d+  return $ N d (if up then ND (r x) (r y) else NI)+    where r s | s == "" = 0+              | otherwise = read s++netParser :: B.ByteString -> IO [NetDevRawTotal]+netParser = mapM (readNetDev . splitDevLine) . readDevLines+  where readDevLines = drop 2 . B.lines+        splitDevLine = map B.unpack . selectCols . filter (not . B.null) . B.splitWith (`elem` [' ',':'])+        selectCols cols = map (cols!!) [0,1,9]++findNetDev :: String -> IO NetDevRawTotal+findNetDev dev = do+  nds <- B.readFile "/proc/net/dev" >>= netParser+  case filter isDev nds of+    x:_ -> return x+    _ -> return NA+  where isDev (N d _) = d == dev+        isDev NA = False
src/Xmobar/Plugins/Monitors/Swap.hs view
@@ -1,3 +1,4 @@+{-#LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Plugins.Monitors.Swap@@ -16,31 +17,17 @@  import Xmobar.Plugins.Monitors.Common -import qualified Data.ByteString.Lazy.Char8 as B+#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Swap.FreeBSD as MS+#else+import qualified Xmobar.Plugins.Monitors.Swap.Linux as MS+#endif  swapConfig :: IO MConfig swapConfig = mkMConfig         "Swap: <usedratio>%"                    -- template         ["usedratio", "total", "used", "free"] -- available replacements -fileMEM :: IO B.ByteString-fileMEM = B.readFile "/proc/meminfo"--parseMEM :: IO [Float]-parseMEM =-    do file <- fileMEM-       let li i l-               | l /= [] = head l !! i-               | otherwise = B.empty-           fs s l-               | null l    = False-               | otherwise = head l == B.pack s-           get_data s = flip (/) 1024 . read . B.unpack . li 1 . filter (fs s)-           st   = map B.words . B.lines $ file-           tot  = get_data "SwapTotal:" st-           free = get_data "SwapFree:" st-       return [(tot - free) / tot, tot, tot - free, free]- formatSwap :: [Float] -> Monitor [String] formatSwap (r:xs) = do   d <- getConfigValue decDigits@@ -51,6 +38,6 @@  runSwap :: [String] -> Monitor String runSwap _ =-    do m <- io parseMEM+    do m <- io MS.parseMEM        l <- formatSwap m        parseTemplate l
+ src/Xmobar/Plugins/Monitors/Swap/FreeBSD.hsc view
@@ -0,0 +1,102 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Swap.FreeBSD+-- Copyright   :  (c) Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A  swap usage monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Swap.FreeBSD (parseMEM) where++import System.BSD.Sysctl (sysctlReadUInt)+import Foreign+import Foreign.C.Types+import Foreign.C.String++++#include <unistd.h>+#include <fcntl.h>+#include <kvm.h>+#include <limits.h>+#include <paths.h>+#include <stdlib.h>+++foreign import ccall unsafe "kvm.h kvm_open" c_kvm_open :: CString -> CString -> CString -> CInt -> CString ->  IO (Ptr KVM_T)+foreign import ccall "&kvm_close" c_kvm_close :: FinalizerPtr KVM_T+foreign import ccall unsafe "kvm.h kvm_getswapinfo" c_kvm_getswapinfo :: Ptr KVM_T -> Ptr KVM_SWAP -> CInt -> CInt -> IO CInt++data KVM_T+data KvmT = KvmT !(ForeignPtr KVM_T)+  deriving (Eq, Ord, Show)++data KVM_SWAP+data KvmSwap = KvmSwap !(ForeignPtr KVM_SWAP)+  deriving (Eq, Ord, Show)++getKvmT:: IO KvmT+getKvmT = do+  withCString "/dev/null" $ \dir -> do+    kvm_t_ptr <- c_kvm_open nullPtr dir nullPtr #{const O_RDONLY} nullPtr+    ptr <- newForeignPtr c_kvm_close kvm_t_ptr+    return $ KvmT ptr++getSwapData :: KvmT -> IO SwapData+getSwapData (KvmT kvm_t_fp) = do+  withForeignPtr kvm_t_fp $ \kvm_t_ptr -> do+    allocaBytes #{size struct kvm_swap} $ \swap_ptr -> do+      c_kvm_getswapinfo kvm_t_ptr swap_ptr 1 0+      peek $ castPtr swap_ptr :: IO SwapData++data SwapData = AvailableSwapData {+  used :: Integer+  , total :: Integer+  } | NotAvailableSwapData+  deriving (Show, Read, Eq)++instance Storable SwapData where+  alignment _ = #{alignment struct kvm_swap}+  sizeOf _    = #{size struct kvm_swap}+  peek ptr    = do+    cused <- #{peek struct kvm_swap, ksw_used} ptr :: IO CUInt+    ctotal <- #{peek struct kvm_swap, ksw_total} ptr :: IO CUInt+    return $ AvailableSwapData {used = toInteger cused, total = toInteger ctotal}++  poke _ _    = pure ()+++isEnabled :: IO Bool+isEnabled = do+  enabled <- sysctlReadUInt "vm.swap_enabled"+  return $ enabled == 1++parseMEM' :: Bool -> IO [Float]+parseMEM' False = return []+parseMEM' True = do+  kvm_t <- getKvmT+  swap <- getSwapData kvm_t+  pagesize <- toInteger <$> sysctlReadUInt "vm.stats.vm.v_page_size"++  let+    swapTotal = total swap+    swapUsed = used swap+    tot = swapTotal * pagesize+    fr = tot - swapUsed * pagesize++  return $ res (fromInteger tot) (fromInteger fr)+  where+    res :: Float -> Float -> [Float]+    res _ 0 = []+    res t f = [(t-f)/t, t, t - f, f]++parseMEM :: IO [Float]+parseMEM = do+  enabled <- isEnabled+  parseMEM' enabled
+ src/Xmobar/Plugins/Monitors/Swap/Linux.hs view
@@ -0,0 +1,35 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Swap.Linux+-- Copyright   :  (c) Andrea Rossato+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+-- A  swap usage monitor for Xmobar+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Swap.Linux (parseMEM) where++import qualified Data.ByteString.Lazy.Char8 as B++fileMEM :: IO B.ByteString+fileMEM = B.readFile "/proc/meminfo"++parseMEM :: IO [Float]+parseMEM =+    do file <- fileMEM+       let li i l+               | l /= [] = head l !! i+               | otherwise = B.empty+           fs s l+               | null l    = False+               | otherwise = head l == B.pack s+           get_data s = flip (/) 1024 . read . B.unpack . li 1 . filter (fs s)+           st   = map B.words . B.lines $ file+           tot  = get_data "SwapTotal:" st+           free = get_data "SwapFree:" st+       return [(tot - free) / tot, tot, tot - free, free]
src/Xmobar/Plugins/Monitors/Top.hs view
@@ -1,3 +1,5 @@+{-#LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-} ----------------------------------------------------------------------------- -- | -- Module      :  Plugins.Monitors.Top@@ -12,25 +14,28 @@ -- ----------------------------------------------------------------------------- -{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE BangPatterns #-}- module Xmobar.Plugins.Monitors.Top (startTop, topMemConfig, runTopMem) where  import Xmobar.Plugins.Monitors.Common -import Control.Exception (SomeException, handle)-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.List (sortBy, foldl')+import Data.IORef (newIORef, readIORef, writeIORef)+import Data.List (sortBy) import Data.Ord (comparing)-import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)-import System.Directory (getDirectoryContents)-import System.FilePath ((</>))-import System.IO (IOMode(ReadMode), hGetLine, withFile)-import System.Posix.Unistd (SysVar(ClockTick), getSysVar)+import Data.Time.Clock (getCurrentTime, diffUTCTime) -import Foreign.C.Types+import Xmobar.Plugins.Monitors.Top.Common (+  MemInfo+  , TimeInfo+  , Times+  , TimesRef) +#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Top.FreeBSD as MT+#else+import qualified Xmobar.Plugins.Monitors.Top.Linux as MT+#endif++ maxEntries :: Int maxEntries = 10 @@ -47,41 +52,6 @@                                , k <- [ "name", "cpu", "both"                                       , "mname", "mem", "mboth"]]) -foreign import ccall "unistd.h getpagesize"-  c_getpagesize :: CInt--pageSize :: Float-pageSize = fromIntegral c_getpagesize / 1024--processes :: IO [FilePath]-processes = fmap (filter isPid) (getDirectoryContents "/proc")-  where isPid = (`elem` ['0'..'9']) . head--statWords :: [String] -> [String]-statWords line@(x:pn:ppn:xs) =-  if last pn == ')' then line else statWords (x:(pn ++ " " ++ ppn):xs)-statWords _ = replicate 52 "0"--getProcessData :: FilePath -> IO [String]-getProcessData pidf =-  handle ign $ withFile ("/proc" </> pidf </> "stat") ReadMode readWords-  where readWords = fmap (statWords . words) . hGetLine-        ign = const (return []) :: SomeException -> IO [String]--memPages :: [String] -> String-memPages fs = fs!!23--ppid :: [String] -> String-ppid fs = fs!!3--skip :: [String] -> Bool-skip fs = length fs < 24 || memPages fs == "0" || ppid fs == "0"--handleProcesses :: ([String] -> a) -> IO [a]-handleProcesses f =-  fmap (foldl' (\a p -> if skip p then a else f p : a) [])-       (processes >>= mapM getProcessData)- showInfo :: String -> String -> Float -> Monitor [String] showInfo nm sms mms = do   mnw <- getConfigValue maxWidth@@ -94,20 +64,10 @@   both <- showWithColors' (rnm ++ " " ++ sms) mms   return [nm, mstr, both] -processName :: [String] -> String-processName = drop 1 . init . (!!1)  sortTop :: [(String, Float)] -> [(String, Float)] sortTop =  sortBy (flip (comparing snd)) -type MemInfo = (String, Float)--meminfo :: [String] -> MemInfo-meminfo fs = (processName fs, pageSize * parseFloat (fs!!23))--meminfos :: IO [MemInfo]-meminfos = handleProcesses meminfo- showMemInfo :: Float -> MemInfo -> Monitor [String] showMemInfo scale (nm, rss) =   showInfo nm (showWithUnits 3 1 rss) (100 * rss / sc)@@ -117,30 +77,8 @@ showMemInfos ms = mapM (showMemInfo tm) ms   where tm = sum (map snd ms) -runTopMem :: [String] -> Monitor String-runTopMem _ = do-  mis <- io meminfos-  pstr <- showMemInfos (sortTop mis)-  parseTemplate $ concat pstr--type Pid = Int-type TimeInfo = (String, Float)-type TimeEntry = (Pid, TimeInfo)-type Times = [TimeEntry]-type TimesRef = IORef (Times, UTCTime)--timeMemEntry :: [String] -> (TimeEntry, MemInfo)-timeMemEntry fs = ((p, (n, t)), (n, r))-  where p = parseInt (head fs)-        n = processName fs-        t = parseFloat (fs!!13) + parseFloat (fs!!14)-        (_, r) = meminfo fs--timeMemEntries :: IO [(TimeEntry, MemInfo)]-timeMemEntries = handleProcesses timeMemEntry- timeMemInfos :: IO (Times, [MemInfo], Int)-timeMemInfos = fmap res timeMemEntries+timeMemInfos = fmap res MT.timeMemEntries   where res x = (sortBy (comparing fst) $ map fst x, map snd x, length x)  combine :: Times -> Times -> Times@@ -164,7 +102,7 @@   c1 <- getCurrentTime   let scx = realToFrac (diffUTCTime c1 c0) * scale       !scx' = if scx > 0 then scx else scale-      nts = map (\(_, (nm, t)) -> (nm, min 100 (t / scx'))) (combine t0 t1)+      nts = map (\(_, (nm, t)) -> (nm, t / scx')) (combine t0 t1)       !t1' = take' (length t1) t1       !nts' = take' maxEntries (sortTop nts)       !mis' = take' maxEntries (sortTop mis)@@ -178,6 +116,12 @@ showTimeInfos :: [TimeInfo] -> Monitor [[String]] showTimeInfos = mapM showTimeInfo +runTopMem :: [String] -> Monitor String+runTopMem _ = do+  mis <- io MT.meminfos+  pstr <- showMemInfos (sortTop mis)+  parseTemplate $ concat pstr+ runTop :: TimesRef -> Float -> [String] -> Monitor String runTop tref scale _ = do   (no, ps, ms) <- io $ topProcesses tref scale@@ -187,9 +131,8 @@  startTop :: [String] -> Int -> (String -> IO ()) -> IO () startTop a r cb = do-  cr <- getSysVar ClockTick   c <- getCurrentTime   tref <- newIORef ([], c)-  let scale = fromIntegral cr / 100+  scale <- MT.scale   _ <- topProcesses tref scale   runM a topConfig (runTop tref scale) r cb
+ src/Xmobar/Plugins/Monitors/Top/Common.hs view
@@ -0,0 +1,32 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Top.Common+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2014, 2018 Jose A Ortega Ruiz+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+--  Process activity and memory consumption monitors+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Top.Common (+  MemInfo+  , Pid+  , TimeInfo+  , TimeEntry+  , Times+  , TimesRef+  ) where++import Data.IORef (IORef)+import Data.Time.Clock (UTCTime)++type MemInfo = (String, Float)+type Pid = Int+type TimeInfo = (String, Float)+type TimeEntry = (Pid, TimeInfo)+type Times = [TimeEntry]+type TimesRef = IORef (Times, UTCTime)
+ src/Xmobar/Plugins/Monitors/Top/FreeBSD.hsc view
@@ -0,0 +1,143 @@+{-# LANGUAGE CPP                      #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CApiFFI #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Top.FreeBSD+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2014, 2018 Jose A Ortega Ruiz+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+--  Process activity and memory consumption monitors+--+-----------------------------------------------------------------------------++module Xmobar.Plugins.Monitors.Top.FreeBSD (+  timeMemEntries+  , meminfos+  , scale) where++import Foreign+import Foreign.C.Types+import Foreign.C.String++import Xmobar.Plugins.Monitors.Top.Common (MemInfo, TimeEntry)++#include <unistd.h>+#include <sys/sysctl.h>+#include <sys/user.h>+#include <libprocstat.h>++foreign import ccall "unistd.h getpagesize" c_getpagesize :: CInt+foreign import ccall unsafe "libprocstat.h procstat_open_sysctl" c_procstat_open_sysctl :: IO (Ptr PROCSTAT)+foreign import ccall "&procstat_close" c_procstat_close :: FinalizerPtr PROCSTAT+foreign import ccall "&procstat_freeprocs" c_procstat_freeprocs :: FinalizerEnvPtr PROCSTAT KINFO_PROC+foreign import ccall unsafe "libprocstat.h procstat_getprocs" c_procstat_getprocs :: Ptr PROCSTAT -> CInt -> CInt -> Ptr CUInt -> IO (Ptr KINFO_PROC)++data PROCSTAT+data ProcStat = ProcStat !(ForeignPtr PROCSTAT)+  deriving (Eq, Ord, Show)++data KINFO_PROC+data KinfoProc = KinfoProc [ProcData] Int+  deriving (Eq, Show)++data ProcData = ProcData {+  pname :: String+  , cpu :: Float+  , tdflags :: CULong+  , flag :: CULong+  , stat :: CUChar+  , rss :: Float+  , pid :: Int+  , runtime :: Float+  }+  deriving (Show, Read, Eq)++instance Storable ProcData where+  alignment _ = #{alignment struct kinfo_proc}+  sizeOf _    = #{size struct kinfo_proc}+  peek ptr    = do+       c <- #{peek struct kinfo_proc, ki_pctcpu} ptr+       ctdflags <- #{peek struct kinfo_proc, ki_tdflags} ptr+       cflag <- #{peek struct kinfo_proc, ki_flag} ptr+       cstat <- #{peek struct kinfo_proc, ki_stat} ptr+       cruntime <- #{peek struct kinfo_proc, ki_runtime} ptr :: IO CULong+       crss <- #{peek struct kinfo_proc, ki_rssize} ptr :: IO CULong+       cname <- peekCString (ptr `plusPtr` (#offset struct kinfo_proc, ki_comm))+       cpid <- #{peek struct kinfo_proc, ki_pid} ptr+       let crssf = (fromIntegral . toInteger) crss+       let cruntimef = ((fromIntegral . toInteger) cruntime  + 500000) / 10000+       return $ ProcData {+         pname = cname+         , cpu = (pctdouble c) * 100+         , tdflags = ctdflags+         , stat = cstat+         , flag = cflag+         , rss = crssf * pageSize+         , pid = cpid+         , runtime = cruntimef}++  poke _ _    = pure ()++pctdouble :: Int -> Float+pctdouble p = (fromIntegral p) / #{const FSCALE}+++pageSize :: Float+pageSize = fromIntegral c_getpagesize / 1024+++getProcStat:: IO ProcStat+getProcStat = do+    proc_ptr <- c_procstat_open_sysctl+    ptr <- newForeignPtr c_procstat_close proc_ptr+    return $ ProcStat ptr+++getProcessesInfo :: ProcStat -> IO [ProcData]+getProcessesInfo (ProcStat ps_fp) = do+  withForeignPtr ps_fp $ \ps_ptr -> do+    alloca $ \n_ptr -> do+      kinfo_proc_ptr <- c_procstat_getprocs ps_ptr #{const KERN_PROC_PROC} 0 n_ptr+      newForeignPtrEnv c_procstat_freeprocs ps_ptr kinfo_proc_ptr+      num <- peek (n_ptr :: Ptr CUInt)+      pds <- peekArray (fromIntegral num) $ castPtr kinfo_proc_ptr :: IO [ProcData]++      return $ [p | p <- pds, flag p .&. #{const P_SYSTEM} == 0]+++processes :: IO [ProcData]+processes = do+  proc_stat <- getProcStat+  getProcessesInfo proc_stat++handleProcesses :: (ProcData -> a) -> IO [a]+handleProcesses f = do+  ps <- processes+  return $ fmap (\pd -> f pd) ps++meminfo :: ProcData -> MemInfo+meminfo pd = (pname pd, rss pd)++meminfos :: IO [MemInfo]+meminfos = handleProcesses meminfo++timeMemEntry :: ProcData -> (TimeEntry, MemInfo)+timeMemEntry pd = ((p, (n, t)), (n, r))+  where p = pid pd+        n = pname pd+        t = runtime pd+        (_, r) = meminfo pd++timeMemEntries :: IO [(TimeEntry, MemInfo)]+timeMemEntries = handleProcesses timeMemEntry++scale :: IO Float+scale = return 1
+ src/Xmobar/Plugins/Monitors/Top/Linux.hs view
@@ -0,0 +1,92 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Plugins.Monitors.Top.Linux+-- Copyright   :  (c) 2010, 2011, 2012, 2013, 2014, 2018 Jose A Ortega Ruiz+-- License     :  BSD-style (see LICENSE)+--+-- Maintainer  :  Jose A Ortega Ruiz <jao@gnu.org>+-- Stability   :  unstable+-- Portability :  unportable+--+--  Process activity and memory consumption monitors+--+-----------------------------------------------------------------------------++{-# LANGUAGE ForeignFunctionInterface #-}++module Xmobar.Plugins.Monitors.Top.Linux (+  timeMemEntries+  , meminfos+  , scale) where++import Xmobar.Plugins.Monitors.Common (parseFloat, parseInt)+import Xmobar.Plugins.Monitors.Top.Common (MemInfo, TimeEntry)++import Control.Exception (SomeException, handle)+import Data.List (foldl')+import System.Directory (getDirectoryContents)+import System.FilePath ((</>))+import System.IO (IOMode(ReadMode), hGetLine, withFile)+import System.Posix.Unistd (SysVar(ClockTick), getSysVar)++import Foreign.C.Types++foreign import ccall "unistd.h getpagesize"+  c_getpagesize :: CInt++pageSize :: Float+pageSize = fromIntegral c_getpagesize / 1024++processes :: IO [FilePath]+processes = fmap (filter isPid) (getDirectoryContents "/proc")+  where isPid = (`elem` ['0'..'9']) . head++statWords :: [String] -> [String]+statWords line@(x:pn:ppn:xs) =+  if last pn == ')' then line else statWords (x:(pn ++ " " ++ ppn):xs)+statWords _ = replicate 52 "0"++getProcessData :: FilePath -> IO [String]+getProcessData pidf =+  handle ign $ withFile ("/proc" </> pidf </> "stat") ReadMode readWords+  where readWords = fmap (statWords . words) . hGetLine+        ign = const (return []) :: SomeException -> IO [String]++memPages :: [String] -> String+memPages fs = fs!!23++ppid :: [String] -> String+ppid fs = fs!!3++skip :: [String] -> Bool+skip fs = length fs < 24 || memPages fs == "0" || ppid fs == "0"++handleProcesses :: ([String] -> a) -> IO [a]+handleProcesses f =+  fmap (foldl' (\a p -> if skip p then a else f p : a) [])+       (processes >>= mapM getProcessData)++processName :: [String] -> String+processName = drop 1 . init . (!!1)++meminfo :: [String] -> MemInfo+meminfo fs = (processName fs, pageSize * parseFloat (fs!!23))++meminfos :: IO [MemInfo]+meminfos = handleProcesses meminfo++timeMemEntry :: [String] -> (TimeEntry, MemInfo)+timeMemEntry fs = ((p, (n, t)), (n, r))+  where p = parseInt (head fs)+        n = processName fs+        t = parseFloat (fs!!13) + parseFloat (fs!!14)+        (_, r) = meminfo fs++timeMemEntries :: IO [(TimeEntry, MemInfo)]+timeMemEntries = handleProcesses timeMemEntry+++scale :: IO Float+scale = do+  cr <- getSysVar ClockTick+  return $ fromIntegral cr / 100
src/Xmobar/Plugins/Monitors/Uptime.hs view
@@ -1,3 +1,5 @@+{-#LANGUAGE CPP #-}+ ------------------------------------------------------------------------------ -- | -- Module      : Plugins.Monitors.Uptime@@ -19,22 +21,22 @@  import Xmobar.Plugins.Monitors.Common -import qualified Data.ByteString.Lazy.Char8 as B+#if defined(freebsd_HOST_OS)+import qualified Xmobar.Plugins.Monitors.Uptime.FreeBSD as MU+#else+import qualified Xmobar.Plugins.Monitors.Uptime.Linux as MU+#endif  uptimeConfig :: IO MConfig uptimeConfig = mkMConfig "Up <days>d <hours>h <minutes>m"                          ["days", "hours", "minutes", "seconds"] -readUptime :: IO Float-readUptime =-  fmap (read . B.unpack . head . B.words) (B.readFile "/proc/uptime")- secsPerDay :: Integer secsPerDay = 24 * 3600  uptime :: Monitor [String] uptime = do-  t <- io readUptime+  t <- io MU.readUptime   u <- getConfigValue useSuffix   let tsecs = floor t       secs = tsecs `mod` secsPerDay
+ src/Xmobar/Plugins/Monitors/Uptime/FreeBSD.hsc view
@@ -0,0 +1,52 @@+------------------------------------------------------------------------------+-- |+-- Module      : Plugins.Monitors.Uptime.FreeBSD+-- Copyright   : (c) 2010 Jose Antonio Ortega Ruiz+-- License     : BSD3-style (see LICENSE)+--+-- Maintainer  : jao@gnu.org+-- Stability   : unstable+-- Portability : unportable+-- Created: Sun Dec 12, 2010 20:26+--+--+-- Uptime+--+------------------------------------------------------------------------------+++module Xmobar.Plugins.Monitors.Uptime.FreeBSD (readUptime) where++import Data.Time.Clock.POSIX (getPOSIXTime)+import System.BSD.Sysctl+import Data.Int+import Foreign.C+import Foreign.Storable++#include <sys/types.h>+#include <sys/user.h>+#include <sys/time.h>+#include <sys/sysctl.h>++data TimeVal = TimeVal {sec:: CTime}++instance Storable TimeVal where+  sizeOf _    = #{size struct timeval}+  alignment _ = alignment (undefined::CTime)+  peek ptr    = do cSec  <- #{peek struct timeval, tv_sec} ptr+                   return (TimeVal cSec)+  poke _ _    = pure ()++now :: IO Int64+now = do+    posix <- getPOSIXTime+    return $ round posix++readUptime :: IO Float+readUptime = do+  tv <- sysctlPeek "kern.boottime" :: IO TimeVal+  nowSec <- now+  return $ fromInteger $ toInteger $ (nowSec - (secInt $ sec tv))+  where+    secInt :: CTime -> Int64+    secInt (CTime cSec) = cSec
+ src/Xmobar/Plugins/Monitors/Uptime/Linux.hs view
@@ -0,0 +1,24 @@+------------------------------------------------------------------------------+-- |+-- Module      : Plugins.Monitors.Uptime.Linux+-- Copyright   : (c) 2010 Jose Antonio Ortega Ruiz+-- License     : BSD3-style (see LICENSE)+--+-- Maintainer  : jao@gnu.org+-- Stability   : unstable+-- Portability : unportable+-- Created: Sun Dec 12, 2010 20:26+--+--+-- Uptime+--+------------------------------------------------------------------------------+++module Xmobar.Plugins.Monitors.Uptime.Linux (readUptime) where++import qualified Data.ByteString.Lazy.Char8 as B++readUptime :: IO Float+readUptime =+  fmap (read . B.unpack . head . B.words) (B.readFile "/proc/uptime")
+ src/Xmobar/Plugins/QueueReader.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE RecordWildCards #-}+module Xmobar.Plugins.QueueReader+  (QueueReader (..)+  ) where++import Xmobar.Run.Exec (Exec (..))++import Control.Monad (forever)+import qualified Control.Concurrent.STM as STM++-- | A 'QueueReader' displays data from an 'TQueue a' where+-- the data items 'a' are rendered by a user supplied function.+--+-- Like the 'HandleReader' plugin this is only useful if you are+-- running @xmobar@ from other Haskell code.  You should create a+-- new @TQueue a@ and pass it to this plugin.+--+-- @+-- main :: IO+-- main = do+--   q <- STM.newQueueIO @String+--   bar <- forkIO $ xmobar conf+--     { commands = Run (QueueReader q id "Queue") : commands conf }+--   STM.atomically $ STM.writeTQueue q "Some Message"+-- @+data QueueReader a+  = QueueReader+  { qQueue    :: STM.TQueue a+  , qShowItem :: a -> String+  , qName :: String+  }++-- | This cannot be read back.+instance Show (QueueReader a) where+  -- | Only show the name/alias for the queue reader.+  show q = "QueueReader " <> qName q++-- | WARNING: This read instance will throw an exception if used! It is+-- only implemented, because it is required by 'Xmobar.Run` in 'Xmobar.commands'.+instance Read (QueueReader a) where+  -- | Throws an 'error'!+  readsPrec = error "QueueReader: instance is a stub"++-- | Async queue/channel reading.+instance Exec (QueueReader a) where+  -- | Read from queue as data arrives.+  start QueueReader{..} cb =+    forever (STM.atomically (qShowItem <$> STM.readTQueue qQueue) >>= cb)++  alias = qName
src/Xmobar/X11/Actions.hs view
@@ -18,7 +18,7 @@ import Graphics.X11.Types (Button)  data Action = Spawn [Button] String-                deriving (Eq)+                deriving (Eq, Show)  runAction :: Action -> IO () runAction (Spawn _ s) = void $ system (s ++ "&")
src/Xmobar/X11/Draw.hs view
@@ -60,6 +60,7 @@       getWidth (Text s,cl,i,_) =         textWidth d (safeIndex fs i) s >>= \tw -> return (Text s,cl,i,fi tw)       getWidth (Icon s,cl,i,_) = return (Icon s,cl,i,fi $ iconW s)+      getWidth (Hspace p,cl,i,_) = return (Hspace p,cl,i,fi p)    p <- liftIO $ createPixmap d w wid ht                          (defaultDepthOfScreen (defaultScreenOfDisplay d))@@ -102,6 +103,7 @@ verticalOffset ht (Icon _) _ _ conf   | iconOffset conf > -1 = return $ fi (iconOffset conf)   | otherwise = return $ fi (ht `div` 2) - 1+verticalOffset _ (Hspace _) _ voffs _ = return $ fi voffs  printString :: Display -> Drawable -> XFont -> GC -> String -> String             -> Position -> Position -> Position -> Position -> String -> Int -> IO ()@@ -160,6 +162,7 @@     (Icon p) -> liftIO $ maybe (return ())                            (B.drawBitmap d dr gc fc bc offset valign)                            (lookup p (iconS r))+    (Hspace _) -> liftIO $ return ()   let triBoxes = tBoxes c       dropBoxes = filter (\(_,b) -> b `notElem` triBoxes) boxes       boxes' = map (\((x1,_),b) -> ((x1, offset + l), b)) (filter (\(_,b) -> b `elem` triBoxes) boxes)
src/Xmobar/X11/Parsers.hs view
@@ -28,11 +28,11 @@ import Graphics.X11.Types (Button) import Foreign.C.Types (CInt) -data Widget = Icon String | Text String+data Widget = Icon String | Text String | Hspace Int32 deriving Show -data BoxOffset = BoxOffset Align Int32 deriving Eq +data BoxOffset = BoxOffset Align Int32 deriving (Eq, Show) -- margins: Top, Right, Bottom, Left-data BoxMargins = BoxMargins Int32 Int32 Int32 Int32 deriving Eq+data BoxMargins = BoxMargins Int32 Int32 Int32 Int32 deriving (Eq, Show) data BoxBorder = BBTop                | BBBottom                | BBVBoth@@ -40,14 +40,14 @@                | BBRight                | BBHBoth                | BBFull-                 deriving ( Read, Eq )-data Box = Box BoxBorder BoxOffset CInt String BoxMargins deriving Eq+                 deriving ( Read, Eq, Show )+data Box = Box BoxBorder BoxOffset CInt String BoxMargins deriving (Eq, Show) data TextRenderInfo =     TextRenderInfo { tColorsString   :: String                    , tBgTopOffset    :: Int32                    , tBgBottomOffset :: Int32                    , tBoxes          :: [Box]-                   }+                   } deriving Show type FontIndex   = Int  -- | Runs the string parser@@ -68,6 +68,7 @@            -> Parser [(Widget, TextRenderInfo, FontIndex, Maybe [Action])] allParsers c f a =  textParser c f a                 <|> try (iconParser c f a)+                <|> try (hspaceParser c f a)                 <|> try (rawParser c f a)                 <|> try (actionParser c f a)                 <|> try (fontParser c a)@@ -91,6 +92,7 @@                                      try (string "action=") <|>                                      try (string "/action>") <|>                                      try (string "icon=") <|>+                                     try (string "hspace=") <|>                                      try (string "raw=") <|>                                      try (string "/fn>") <|>                                      try (string "/box>") <|>@@ -132,6 +134,13 @@   string "<icon="   i <- manyTill (noneOf ">") (try (string "/>"))   return [(Icon i, c, f, a)]++hspaceParser :: TextRenderInfo -> FontIndex -> Maybe [Action]+              -> Parser [(Widget, TextRenderInfo, FontIndex, Maybe [Action])]+hspaceParser c f a = do+  string "<hspace="+  pVal <- manyTill digit (try (string "/>"))+  return [(Hspace (fromMaybe 0 $ readMaybe pVal), c, f, a)]  actionParser :: TextRenderInfo -> FontIndex -> Maybe [Action]                 -> Parser [(Widget, TextRenderInfo, FontIndex, Maybe [Action])]
xmobar.cabal view
@@ -1,5 +1,5 @@ name:               xmobar-version:            0.39+version:            0.40 homepage:           http://xmobar.org synopsis:           A Minimalistic Text Based Status Bar description: 	    Xmobar is a minimalistic text based status bar.@@ -140,6 +140,7 @@                    Xmobar.Plugins.Date,                    Xmobar.Plugins.EWMH,                    Xmobar.Plugins.HandleReader,+                   Xmobar.Plugins.QueueReader,                    Xmobar.Plugins.PipeReader,                    Xmobar.Plugins.MarqueePipeReader,                    Xmobar.Plugins.StdinReader,@@ -149,21 +150,25 @@                    Xmobar.Plugins.NotmuchMail,                    Xmobar.Plugins.Monitors,                    Xmobar.Plugins.Monitors.Batt,+                   Xmobar.Plugins.Monitors.Batt.Common,                    Xmobar.Plugins.Monitors.Common.Output,                    Xmobar.Plugins.Monitors.Common.Parsers,                    Xmobar.Plugins.Monitors.Common.Files,                    Xmobar.Plugins.Monitors.CoreTemp,                    Xmobar.Plugins.Monitors.K10Temp,+                   Xmobar.Plugins.Monitors.Cpu.Common,                    Xmobar.Plugins.Monitors.CpuFreq,                    Xmobar.Plugins.Monitors.Disk,                    Xmobar.Plugins.Monitors.Mem,                    Xmobar.Plugins.Monitors.MultiCoreTemp,                    Xmobar.Plugins.Monitors.MultiCpu,                    Xmobar.Plugins.Monitors.Net,+                   Xmobar.Plugins.Monitors.Net.Common,                    Xmobar.Plugins.Monitors.Swap,                    Xmobar.Plugins.Monitors.Thermal,                    Xmobar.Plugins.Monitors.ThermalZone,                    Xmobar.Plugins.Monitors.Top,+                   Xmobar.Plugins.Monitors.Top.Common,                    Xmobar.Plugins.Monitors.Uptime,                    Xmobar.Plugins.Monitors.Bright,                    Xmobar.Plugins.Monitors.CatInt@@ -290,8 +295,24 @@      if os(freebsd)        -- enables freebsd specific code+       extra-libraries: procstat+                      , kvm        build-depends: bsd-sysctl-       cpp-options: -DFREEBSD+       other-modules: Xmobar.Plugins.Monitors.Batt.FreeBSD,+                      Xmobar.Plugins.Monitors.Cpu.FreeBSD,+                      Xmobar.Plugins.Monitors.Mem.FreeBSD,+                      Xmobar.Plugins.Monitors.Net.FreeBSD,+                      Xmobar.Plugins.Monitors.Swap.FreeBSD,+                      Xmobar.Plugins.Monitors.Top.FreeBSD,+                      Xmobar.Plugins.Monitors.Uptime.FreeBSD+    else+       other-modules: Xmobar.Plugins.Monitors.Batt.Linux,+                      Xmobar.Plugins.Monitors.Cpu.Linux,+                      Xmobar.Plugins.Monitors.Mem.Linux,+                      Xmobar.Plugins.Monitors.Net.Linux,+                      Xmobar.Plugins.Monitors.Swap.Linux,+                      Xmobar.Plugins.Monitors.Top.Linux,+                      Xmobar.Plugins.Monitors.Uptime.Linux  executable xmobar     default-language:   Haskell2010@@ -352,6 +373,7 @@                  Xmobar.Plugins.Monitors.Common.Output                  Xmobar.Plugins.Monitors.Common.Files                  Xmobar.Plugins.Monitors.Cpu+                 Xmobar.Plugins.Monitors.Cpu.Common                  Xmobar.Plugins.Monitors.CpuSpec                  Xmobar.Plugins.Monitors.Common.Run                  Xmobar.Run.Exec@@ -367,6 +389,13 @@                      Xmobar.Plugins.Monitors.AlsaSpec        cpp-options: -DALSA++  if os(freebsd)+       -- enables freebsd specific code+      build-depends: bsd-sysctl+      other-modules: Xmobar.Plugins.Monitors.Cpu.FreeBSD+  else+      other-modules: Xmobar.Plugins.Monitors.Cpu.Linux  benchmark xmobarbench   type: exitcode-stdio-1.0