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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
{- |
Module : Plugins.Monitors.PacmanUpdates
Copyright : (c) 2024, 2026 Enrico Maria De Angelis
, (c) 2025 Alexander Pankoff
, (c) 2026 Enrico Maria De Angelis
License : BSD-style (see LICENSE)
Maintainer : Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com>
Stability : unstable
Portability : unportable
A Pacman updates availablility plugin for Xmobar. It also informs whether a kernel update is
available (provided the name of the kernel package), and whether the running kernel is older
than the installed one.
-}
module Xmobar.Plugins.PacmanUpdates (
#if __GLASGOW_HASKELL__ >= 908
{-# DEPRECATED "This ctor is DEPRECATED; please use `PacmanUpdates` type and `PacmanUpdatesK`, `PacmanUpdatesPredicateK` and `PacmanUpdatesNoK` constructors instead." #-}
#endif
pattern PacmanUpdates
, PacmanUpdates ()
, PacmanUpdatesKernelCheck (..)
, pattern PacmanUpdatesK
, pattern PacmanUpdatesPredicateK
, pattern PacmanUpdatesNoK) where
import System.Exit (ExitCode (..))
import System.Process (readProcessWithExitCode)
import Xmobar.Plugins.Command (Rate)
import Xmobar.Run.Exec
import Data.Tuple.Extra (fst3)
import Data.Kind (Type)
import Data.Functor ((<&>))
import Data.Void (Void)
import Control.Arrow ((&&&))
import qualified Data.Vector as V
-- | Deprecated plugin ctor (will be deleted in 2027).
-- Use `PacmanUpdatesK`, `PacmanUpdatesPredicateK`, or `PacmanUpdatesNoK` instead.
pattern PacmanUpdates :: (String, String, String, String) -- ^ `String`s to be shown for 0, 1, ≥ 2 updates,
-- and for error respectively (in the 3rd string, for
-- ≥ 2 updates, any occurrence of the '?' character
-- is a placeholder for the number of available updates).
-> Rate -- ^ `Rate` of update (see [Xmobar doc](https://codeberg.org/xmobar/xmobar/src/commit/39fd70308c3aef5402abe7152ade76ff7bb331bb/src/Xmobar/Plugins/Command.hs#L34)).
-> PacmanUpdates NoKernelCheck
pattern PacmanUpdates irrelevant <- (error "PacmanUpdates: PacmanUpdates is a build-only pattern synonym (a ctor synonym)." -> irrelevant)
where PacmanUpdates zome r
= let (z, o, m, e) = zome
printer = const
$ (++ deprecationNote)
. \case Left _ -> e
Right 0 -> z
Right 1 -> o
Right n -> m >>= \c -> if c == '?'
then show n
else pure c
deprecationNote = " <fc=#ff0000>(<action=`xdg-open https://codeberg.org/xmobar/xmobar/pulls/765`>"
++ "deprecated plugin, click here</action>)</fc>"
in PacmanUpdatesNoK r printer
-- | Different types of kernel checks.
data PacmanUpdatesKernelCheck = NoKernelCheck | PredicateKernelCheck
-- | PacmanUpdates plugin parametrized over the `PacmanUpdatesKernelCheck` kind.
data PacmanUpdates (b :: PacmanUpdatesKernelCheck)
= Make -- ^ Constructor.
Rate -- ^ `Rate` of update (see [Xmobar doc](https://codeberg.org/xmobar/xmobar/src/commit/39fd70308c3aef5402abe7152ade76ff7bb331bb/src/Xmobar/Plugins/Command.hs#L34)).
(Arg b) -- ^ Optional further argument. See instances of `Updates`.
(Printer b) -- ^ Printer. See instances of `Updates` for its signature.
instance Show (PacmanUpdates b) where
show = error "PacmanUpdates: Show instance is stub"
instance Read (PacmanUpdates b) where
readsPrec = error "PacmanUpdates: Read instance is stub"
instance Updates b => Exec (PacmanUpdates (b :: PacmanUpdatesKernelCheck)) where
alias = const "pacman"
rate (Make r _ _) = r
run = Xmobar.Plugins.PacmanUpdates.run'
class Updates (b :: PacmanUpdatesKernelCheck) where
-- | See `Updates`'s instances.
type Arg b = (a :: Type) | a -> b
-- | See `Updates`'s instances.
type Printer b = (p :: Type) | p -> b
-- | This is the implementation of `Xmobar.Run.Exec.run`.
run' :: PacmanUpdates b -> IO String
-- | No additional argument required for constructing the plugin;
-- the user-provided printer is fed with a `Bool` telling whether
-- the system is running an outdated kernel, and an `Int` telling
-- the number of available updates (or `Left` if an error occurred
-- when calling `checkupdates`).
instance Updates NoKernelCheck where
type Arg NoKernelCheck = Void
type Printer NoKernelCheck = Bool -> Either String Int -> String
run' (Make _ _ printer)
= printer
<$> kernIsOld
<*> (fmap V.length <$> checkUpdates)
-- | Constructing the plugin requires an additional `String -> Bool` predicate
-- that receives each available package name and returns `True` for kernel
-- packages; the user-provided printer is fed with a `Bool` telling whether the
-- system is running an outdated kernel, and an `(Int, Bool)` pair telling the
-- number of available updates and whether one of these is a kernel update (or
-- `Left` if an error occurred when calling `checkupdates`).
instance Updates PredicateKernelCheck where
type Arg PredicateKernelCheck = String -> Bool
type Printer PredicateKernelCheck = Bool -> Either String (Int, Bool) -> String
run' (Make _ checkKern printer)
= printer
<$> kernIsOld
<*> (fmap (V.length &&& V.any checkKern) <$> checkUpdates)
-- | Pattern synonym to construct a `PacmanUpdates PredicateKernelCheck` that
-- detects updates for packages matched by the given @(String -> Bool)@
-- predicate. This can be used to detect kernel updates for distributions
-- with versioned kernel package names (e.g. Manjaro's @linux618@)
pattern PacmanUpdatesPredicateK :: Rate -> Arg PredicateKernelCheck -> Printer PredicateKernelCheck -> PacmanUpdates PredicateKernelCheck
pattern PacmanUpdatesPredicateK r a p = Make r a p
-- | A convenience wrapper around PacmanUpdatesPredicateK with the predicate @(== kernName)@
-- Construction only: the kernel name cannot be recovered when matching.
pattern PacmanUpdatesK :: Rate -> String -> Printer PredicateKernelCheck -> PacmanUpdates PredicateKernelCheck
pattern PacmanUpdatesK r kernName p <-
(error "PacmanUpdatesK: build-only pattern synonym (a ctor synonym)." -> (r, kernName, p))
where PacmanUpdatesK r kernName p = PacmanUpdatesPredicateK r (== kernName) p
-- | Pattern synonym used to construct a `PacmanUpdates NoKernelCheck`.
pattern PacmanUpdatesNoK :: Rate -> Printer NoKernelCheck -> PacmanUpdates NoKernelCheck
pattern PacmanUpdatesNoK r p <- Make r _ p
where PacmanUpdatesNoK r p = Make r undefined p
checkUpdates :: IO (Either String (V.Vector String))
checkUpdates = readProcessWithExitCode "checkupdates" [] ""
<&> \case (ExitFailure 2, "", "") -> Right V.empty
(ExitSuccess, stdout, "")
-> let pkgName = takeWhile (/= ' ')
pkgs = V.fromList $ fmap pkgName $ lines stdout
in case V.length pkgs of
0 -> impossible
_ -> Right pkgs
(ExitFailure 1, _, _) -> Left "checkupdates: unknown cause of failure."
_ -> impossible
kernIsOld :: IO Bool
kernIsOld = (/= ExitSuccess) . exitCode <$> readProcessWithExitCode "modinfo" ["-n", "i915"] ""
where exitCode = fst3
impossible :: a
impossible = error "This is impossible, according to my knowledge."
|