From 6c141f1efc67166518d17cf71497b31ea1fbbed3 Mon Sep 17 00:00:00 2001 From: jao Date: Sun, 24 Jul 2022 01:23:43 +0100 Subject: documentation: mini-tutorial on writting your own xmobar.hs --- doc/write-your-own-plugin.org | 132 +++++++++++++++++++++--------------------- examples/xmobar.hs | 2 +- readme.org | 50 +++++++++++++++- 3 files changed, 115 insertions(+), 69 deletions(-) diff --git a/doc/write-your-own-plugin.org b/doc/write-your-own-plugin.org index a877ad8..fb1ca85 100644 --- a/doc/write-your-own-plugin.org +++ b/doc/write-your-own-plugin.org @@ -1,75 +1,73 @@ #+title: Writing your own plugin -Writing a plugin for xmobar is very simple! - -First, you need to create a data type with at least one constructor. -Next you must declare this data type an instance of the =Exec= class, by -defining the one needed method (alternatively =start= or =run=) and 3 -optional ones (=alias=, =rate=, and =trigger=): - -#+begin_src haskell - start :: e -> (String -> IO ()) -> IO () - run :: e -> IO String - rate :: e -> Int - alias :: e -> String - trigger :: e -> (Maybe SignalType -> IO ()) -> IO () -#+end_src - -=start= must receive a callback to be used to display the =String= -produced by the plugin. This method can be used for plugins that need to -perform asynchronous actions. See =src/Xmobar/Plugins/PipeReader.hs= for -an example. - -=run= can be used for simpler plugins. If you define only =run= the -plugin will be run every second. To overwrite this default you just need -to implement =rate=, which must return the number of tenth of seconds -between every successive runs. See [[../examples/xmobar.hs][examples/xmobar.hs]] for an example of -a plugin that runs just once, and [[../src/Xmobar/Plugins/Date.hs][src/Xmobar/Plugins/Date.hs]] for one -that implements =rate=. - -Notice that Date could be implemented as: - -#+begin_src haskell - instance Exec Date where - alias (Date _ a _) = a - start (Date f _ r) = date f r - - date :: String -> Int -> (String -> IO ()) -> IO () - date format r callback = do go - where go = do - t <- toCalendarTime =<< getClockTime - callback $ formatCalendarTime defaultTimeLocale format t - tenthSeconds r >> go -#+end_src - -Modulo some technicalities like refreshing the time-zone in a clever -way, this implementation is equivalent to the one you can read in -=Plugins/Date.hs=. - -=alias= is the name to be used in the output template. Default alias -will be the data type constructor. - -After that your type constructor can be used as an argument for the -Runnable type constructor =Run= in the =commands= list of the -configuration options. - -If your plugin only implements =alias= and =start=, then it is advisable -to put it into the =Xmobar/Plugins/Monitors= directory and use one of -the many =run*= functions in [[../src/Xmobar/Plugins/Monitors/Common/Run.hs][Xmobar.Plugins.Monitors.Run]] in order to -define =start=. The =Exec= instance should then live in -[[../src/Xmobar/Plugins/Monitors.hs][Xmobar.Plugins.Monitors]]. +*** Writing a plugin + Writing a plugin for xmobar is very simple! + + First, you need to create a data type with at least one constructor. Next + you must declare this data type an instance of the =Exec= class, by defining + the one needed method (alternatively =start= or =run=) and 3 optional ones + (=alias=, =rate=, and =trigger=): + + #+begin_src haskell + start :: e -> (String -> IO ()) -> IO () + run :: e -> IO String + rate :: e -> Int + alias :: e -> String + trigger :: e -> (Maybe SignalType -> IO ()) -> IO () + #+end_src + + =start= must receive a callback to be used to display the =String= produced by + the plugin. This method can be used for plugins that need to perform + asynchronous actions. See =src/Xmobar/Plugins/PipeReader.hs= for an example. + + =run= can be used for simpler plugins. If you define only =run= the plugin + will be run every second. To overwrite this default you just need to + implement =rate=, which must return the number of tenth of seconds between + every successive runs. See [[../examples/xmobar.hs][examples/xmobar.hs]] for an example of a plugin + that runs just once, and [[../src/Xmobar/Plugins/Date.hs][src/Xmobar/Plugins/Date.hs]] for one that + implements =rate=. + + Notice that Date could be implemented as: + + #+begin_src haskell + instance Exec Date where + alias (Date _ a _) = a + start (Date f _ r) = date f r + + date :: String -> Int -> (String -> IO ()) -> IO () + date format r callback = do go + where go = do + t <- toCalendarTime =<< getClockTime + callback $ formatCalendarTime defaultTimeLocale format t + tenthSeconds r >> go + #+end_src + + Modulo some technicalities like refreshing the time-zone in a clever way, + this implementation is equivalent to the one you can read in + =Plugins/Date.hs=. + + =alias= is the name to be used in the output template. Default alias will be + the data type constructor. + + After that your type constructor can be used as an argument for the + Runnable type constructor =Run= in the =commands= list of the configuration + options. + + If your plugin only implements =alias= and =start=, then it is advisable to + put it into the =Xmobar/Plugins/Monitors= directory and use one of the many + =run*= functions in [[../src/Xmobar/Plugins/Monitors/Common/Run.hs][Xmobar.Plugins.Monitors.Run]] in order to define + =start=. The =Exec= instance should then live in [[../src/Xmobar/Plugins/Monitors.hs][Xmobar.Plugins.Monitors]]. *** Using a Plugin - To use your new plugin, you need to use a pure Haskell - configuration for xmobar, and load your definitions there. You can - see an example in [[../examples/xmobar.hs][examples/xmobar.hs]] showing you how to write a - Haskell configuration that uses a new plugin, all in one file. + To use your new plugin, you need to use a pure Haskell configuration for + xmobar (as explained [[../readme.org#xmobar-in-haskell][here)]] and load your definitions in your =xmobar.hs= + file. You can see an example in [[../examples/xmobar.hs][examples/xmobar.hs]] showing you how to + write a Haskell configuration that uses a new plugin, all in one file. - When xmobar runs with the full path to that Haskell file as its - argument (or if you put it in =~/.config/xmobar/xmobar.hs=), and - with the xmobar library installed (e.g., with =cabal install --lib - xmobar=), the Haskell code will be compiled as needed, and the new - executable spawned for you. + When xmobar runs with the full path to that Haskell file as its argument + (or if you put it in =~/.config/xmobar/xmobar.hs=), and with the xmobar + library installed (e.g., with =cabal install --lib xmobar=), the Haskell + code will be compiled as needed, and the new executable spawned for you. That's it! diff --git a/examples/xmobar.hs b/examples/xmobar.hs index 791d3af..f8434fe 100644 --- a/examples/xmobar.hs +++ b/examples/xmobar.hs @@ -74,4 +74,4 @@ config = defaultConfig { } main :: IO () -main = xmobar config +main = configFromArgs config >>= xmobar diff --git a/readme.org b/readme.org index 25a11eb..23758fa 100644 --- a/readme.org +++ b/readme.org @@ -89,7 +89,7 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. See [[file:doc/compiling.org][compiling]]. * Running xmobar - +*** Running xmobar with a configuration file You can run xmobar with: #+begin_src shell @@ -106,6 +106,54 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. =$XDG_CONFIG_HOME/xmobar/xmobarrc= (defaulting to =~/.config/xmobar/xmobarrc=), or =~/.xmobarrc=. +*** Writing your own xmobar in Haskell + :PROPERTIES: + :CUSTOM_ID: xmobar-in-haskell + :END: + + It is possible to install xmobar as a library and use it to write your own + xmobar using Haskell instead of using a configuration file. (This is very + similar to how [[http://xmonad.org][xmonad]] works.) + + Make sure that ~ghc~ will be able to locate the xmobar library, e.g. with + + #+begin_src shell + cabal install --lib xmobar + #+end_src + + and then write your Haskell configuration and main function using the + functions and types exported in the library, which closely resemble those + used in configuration files. Here's a small example: + + #+begin_src haskell + import Xmobar + + config :: Config + config = + defaultConfig + { font = "xft:Terminus-8", + allDesktops = True, + alpha = 200, + commands = + [ Run XMonadLog, + Run $ Memory ["t", "Mem: %"] 10, + Run $ Kbd [], + Run $ Date "%a %_d %b %Y %H:%M:%S" "date" 10 + ], + template = "%XMonadLog% }{ %kbd% | %date% | %memory%", + alignSep = "}{" + } + + main :: IO () + main = xmobar config + #+end_src + + You can then for instance run =ghc --make xmobar.hs= to create a new xmobar + executable running exactly the monitors defined above. Or put your + =xmobar.hs= program in =~/.config/xmobar/xmobar.hs= and, when running the + system-wide xmobar, it will notice that you have your own implementation + and (re)compile and run it as needed. See also this [[./examples/xmobar.hs][extended example]]. + *** Running xmobar in text mode By default, xmobar will run as an X11 application, in a docked -- cgit v1.2.3