1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
------------------------------------------------------------------------------
-- |
-- Module: Xmobar.App.TextEventLoop
-- Copyright: (c) 2022 Jose Antonio Ortega Ruiz
-- License: BSD3-style (see LICENSE)
--
-- Maintainer: jao@gnu.org
-- Stability: unstable
-- Portability: unportable
-- Created: Fri Jan 28, 2022 01:21
--
--
-- Text-only event loop
--
------------------------------------------------------------------------------
module Xmobar.App.TextEventLoop (textLoop) where
import Prelude hiding (lookup)
import System.IO
import Data.List (intercalate)
import Control.Monad.Reader
import Control.Concurrent.Async (Async)
import Control.Concurrent.STM
import Xmobar.System.Signal
import Xmobar.Config.Types (Config, ansiColors)
import Xmobar.X11.Parsers (Segment, Widget(..), parseString, tColorsString, colorComponents)
import Xmobar.App.CommandThreads (initLoop, loop)
-- | Starts the main event loop and threads
textLoop :: Config -> IO ()
textLoop conf = loop conf (startTextLoop' conf)
startTextLoop' :: Config
-> TMVar SignalType
-> TMVar ()
-> [[([Async ()], TVar String)]]
-> IO ()
startTextLoop' cfg sig pauser vs = do
hSetBuffering stdin LineBuffering
hSetBuffering stdout LineBuffering
tv <- initLoop sig pauser vs
eventLoop cfg tv sig
-- | Continuously wait for a signal from a thread or a interrupt handler
eventLoop :: Config -> TVar [String] -> TMVar SignalType -> IO ()
eventLoop cfg tv signal = do
typ <- atomically $ takeTMVar signal
case typ of
Wakeup -> updateString cfg tv >>= putStrLn >> eventLoop cfg tv signal
_ -> eventLoop cfg tv signal
updateString :: Config -> TVar [String] -> IO String
updateString conf v = do
s <- readTVarIO v
let l:c:r:_ = s ++ repeat ""
liftIO $ concat `fmap` mapM (parseStringAsText conf) [l, c, r]
asInt :: String -> String
asInt x = case (reads $ "0x" ++ x) :: [(Integer, String)] of
[(v, "") ] -> show v
_ -> ""
namedColor :: String -> String
namedColor c =
case c of
"black" -> "0"; "red" -> "1"; "green" -> "2"; "yellow" -> "3"; "blue" -> "4";
"magenta" -> "5"; "cyan" -> "6"; "white" -> "7"; _ -> ""
ansiCode :: String -> String
ansiCode ('#':r:g:[b]) = ansiCode ['#', '0', r, '0', g, '0', b]
ansiCode ('#':r0:r1:g0:g1:b0:[b1]) =
"2;" ++ intercalate ";" (map asInt [[r0,r1], [g0,g1], [b0,b1]])
ansiCode ('#':n) = ansiCode n
ansiCode c = "5;" ++ if null i then namedColor c else i where i = asInt c
withAnsiColor :: (String, String) -> String -> String
withAnsiColor (fg, bg) s = wrap "38;" fg (wrap "48;" bg s)
where wrap cd cl w =
if null cl
then w
else "\x1b[" ++ cd ++ ansiCode cl ++ "m" ++ w ++ "\x1b[0m"
asText :: Config -> Segment -> String
asText conf (Text s, info, _, _) =
if null color then s else withAnsiColor (colorComponents conf color) s
where color = if ansiColors conf then tColorsString info else ""
asText colors (Hspace n, i, x, y) =
asText colors (Text $ replicate (fromIntegral n) ' ', i, x, y)
asText _ _ = ""
parseStringAsText :: Config -> String -> IO String
parseStringAsText c s = do
segments <- parseString c s
let txts = map (asText c) segments
return (concat txts)
|