diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,26 @@
+# 4.1.1
+
+## Improvements
+
+ * Add [`pollingLabelWithVariableDelayAndRefresh`][pollingLabelWithVariableDelayAndRefresh]. The [`WttrIn`][WttrIn] widget uses this.
+
+[pollingLabelWithVariableDelayAndRefresh]: https://hackage.haskell.org/package/taffybar-4.1.1/docs/System-Taffybar-Widget-Generic-PollingLabel.html#v:pollingLabelWithVariableDelayAndRefresh
+
+[WttrIn]: https://hackage.haskell.org/package/taffybar-4.1.1/docs/System-Taffybar-Widget-WttrIn.html
+
+## Breaking Changes
+
+ * Use version-named package build dependencies. Users building their
+   configurations with Stack or Nix may need to do likewise. That is,
+   change:
+    - `gi-gtk` → `gi-gtk3`
+    - `gi-gdk` → `gi-gdk3`
+    - `gi-gdkx11` → `gi-gdkx113`
+
+ * Taffybar is tested with GHC versions 9.8 and 9.10.
+   Other versions may or may not work.
+
+
 # 4.1.0
 
 ## Breaking Changes
diff --git a/src/System/Taffybar/DBus/Client/Util.hs b/src/System/Taffybar/DBus/Client/Util.hs
--- a/src/System/Taffybar/DBus/Client/Util.hs
+++ b/src/System/Taffybar/DBus/Client/Util.hs
@@ -3,9 +3,7 @@
 {-# LANGUAGE TemplateHaskellQuotes #-}
 module System.Taffybar.DBus.Client.Util where
 
-#if !MIN_VERSION_base(4,18,0)
-import           Control.Applicative (liftA2)
-#endif
+import Control.Monad (forM)
 import           DBus.Generation
 import qualified DBus.Internal.Types as T
 import qualified DBus.Introspection as I
@@ -78,13 +76,13 @@
 generateClientFromFile :: RecordGenerationParams -> GenerationParams -> Bool -> FilePath -> Q [Dec]
 generateClientFromFile recordGenerationParams params useObjectPath filepath = do
   object <- getIntrospectionObjectFromFile filepath "/"
-  let interface = head $ I.objectInterfaces object
-      actualObjectPath = I.objectPath object
+  let actualObjectPath = I.objectPath object
       realParams =
         if useObjectPath
           then params {genObjectPath = Just actualObjectPath}
           else params
       (<++>) = liftA2 (++)
-  generateGetAllRecord recordGenerationParams params interface <++>
-    generateClient realParams interface <++>
-    generateSignalsFromInterface realParams interface
+  fmap concat $ forM (I.objectInterfaces object) $ \interface -> do
+    generateGetAllRecord recordGenerationParams params interface <++>
+      generateClient realParams interface <++>
+      generateSignalsFromInterface realParams interface
diff --git a/src/System/Taffybar/Information/CPU.hs b/src/System/Taffybar/Information/CPU.hs
--- a/src/System/Taffybar/Information/CPU.hs
+++ b/src/System/Taffybar/Information/CPU.hs
@@ -12,7 +12,7 @@
   return (procParser firstLine)
 
 procParser :: String -> [Double]
-procParser = map read . tail . words
+procParser = map read . drop 1 . words
 
 truncVal :: Double -> Double
 truncVal v
diff --git a/src/System/Taffybar/Information/XDG/Protocol.hs b/src/System/Taffybar/Information/XDG/Protocol.hs
--- a/src/System/Taffybar/Information/XDG/Protocol.hs
+++ b/src/System/Taffybar/Information/XDG/Protocol.hs
@@ -155,9 +155,7 @@
                           $ elChildren e
           "Or"       -> Just $ Or  $ mapMaybe parseSingleItem
                           $ elChildren e
-          "Not"      -> case parseSingleItem (head (elChildren e)) of
-                          Nothing   -> Nothing
-                          Just rule -> Just $ Not rule
+          "Not"      -> Not <$> (parseSingleItem =<< listToMaybe (elChildren e))
           unknown    -> D.trace ("Unknown Condition item: " ++  unknown) Nothing
 
 -- | Combinable conditions for Include and Exclude statements.
@@ -202,8 +200,8 @@
     Just l -> return $
       let woEncoding      = takeWhile (/= '.') l
           (language, _cm) = span (/= '_') woEncoding
-          (country, _m)   = span (/= '@') (if null _cm then "" else tail _cm)
-          modifier        = if null _m then "" else tail _m
+          (country, _m)   = span (/= '@') (drop 1 _cm)
+          modifier        = drop 1 _m
                        in dgl language country modifier
     where dgl "" "" "" = []
           dgl l  "" "" = [l]
diff --git a/src/System/Taffybar/Widget/Crypto.hs b/src/System/Taffybar/Widget/Crypto.hs
--- a/src/System/Taffybar/Widget/Crypto.hs
+++ b/src/System/Taffybar/Widget/Crypto.hs
@@ -28,7 +28,6 @@
 import           Data.Aeson.Types
 import qualified Data.Aeson.Key as Key
 import qualified Data.ByteString.Lazy as LBS
-import           Data.List.Split
 import           Data.Maybe
 import           Data.Proxy
 import qualified Data.Text
@@ -58,7 +57,7 @@
 cryptoPriceLabelWithIcon = do
   label <- cryptoPriceLabel @a
   let symbolPair = symbolVal (Proxy :: Proxy a)
-      symbol = head $ splitOn "-" symbolPair
+      symbol = takeWhile (/= '-') symbolPair
   hbox <- Gtk.boxNew Gtk.OrientationHorizontal 0
 
   ctx <- ask
diff --git a/src/System/Taffybar/Widget/FreedesktopNotifications.hs b/src/System/Taffybar/Widget/FreedesktopNotifications.hs
--- a/src/System/Taffybar/Widget/FreedesktopNotifications.hs
+++ b/src/System/Taffybar/Widget/FreedesktopNotifications.hs
@@ -207,9 +207,9 @@
   }
 
 defaultFormatter :: [Notification] -> T.Text
-defaultFormatter ns =
-  let count = length ns
-      n = head ns
+defaultFormatter [] = ""
+defaultFormatter (n:ns) =
+  let count = length ns + 1
       prefix = if count == 1
                then ""
                else "(" <> T.pack (show count) <> ") "
diff --git a/src/System/Taffybar/Widget/Generic/PollingLabel.hs b/src/System/Taffybar/Widget/Generic/PollingLabel.hs
--- a/src/System/Taffybar/Widget/Generic/PollingLabel.hs
+++ b/src/System/Taffybar/Widget/Generic/PollingLabel.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 -- | This is a simple text widget that updates its contents by calling
 -- a callback at a set interval.
 module System.Taffybar.Widget.Generic.PollingLabel where
@@ -8,6 +10,7 @@
 import           Control.Monad.IO.Class
 import qualified Data.Text as T
 import           GI.Gtk
+import qualified GI.Gdk as Gdk
 import           System.Log.Logger
 import           System.Taffybar.Util
 import           System.Taffybar.Widget.Util
@@ -48,10 +51,28 @@
   => IO (T.Text, Maybe T.Text, Double)
   -> m GI.Gtk.Widget
 pollingLabelWithVariableDelay action =
+  pollingLabelWithVariableDelayAndRefresh action False
+
+-- TODO: Customize the delay and message on mouse click
+pollingLabelWithVariableDelayAndRefresh
+  :: MonadIO m
+  => IO (T.Text, Maybe T.Text, Double)
+  -> Bool -- ^ Whether to refresh the label on mouse click
+  -> m GI.Gtk.Widget
+pollingLabelWithVariableDelayAndRefresh action refreshOnClick =
   liftIO $ do
     grid <- gridNew
     label <- labelNew Nothing
+    ebox <- eventBoxNew
 
+    when refreshOnClick $ void $ onWidgetButtonPressEvent ebox $ onClick [Gdk.EventTypeButtonPress] $ do
+      postGUIASync $ labelSetMarkup label "Refreshing..."
+      forkIO $ do
+        newLavelStr <- E.tryAny action >>= \case
+          Left _                  -> return "Error"
+          Right (_labelStr, _, _) -> return _labelStr
+        postGUIASync $ labelSetMarkup label newLavelStr
+
     let updateLabel (labelStr, tooltipStr, delay) = do
           postGUIASync $ do
              labelSetMarkup label labelStr
@@ -69,5 +90,6 @@
     vFillCenter label
     vFillCenter grid
     containerAdd grid label
-    widgetShowAll grid
-    toWidget grid
+    containerAdd ebox grid
+    widgetShowAll ebox
+    toWidget ebox
diff --git a/src/System/Taffybar/Widget/Weather.hs b/src/System/Taffybar/Widget/Weather.hs
--- a/src/System/Taffybar/Widget/Weather.hs
+++ b/src/System/Taffybar/Widget/Weather.hs
@@ -145,10 +145,10 @@
 
 parseData :: Parser WeatherInfo
 parseData = do
-  st <- getAllBut ","
+  st <- getAllBut ','
   _ <- space
-  ss <- getAllBut "("
-  _ <- skipRestOfLine >> getAllBut "/"
+  ss <- getAllBut '('
+  _ <- skipRestOfLine >> getAllBut '/'
   (y,m,d,h) <- pTime
   w <- getAfterString "Wind: "
   v <- getAfterString "Visibility: "
@@ -163,9 +163,8 @@
   _ <- manyTill skipRestOfLine eof
   return $ WI st ss y m d h w v sk tC tF dp rh p
 
-getAllBut :: String -> Parser String
-getAllBut s =
-    manyTill (noneOf s) (char $ head s)
+getAllBut :: Char -> Parser String
+getAllBut c = manyTill (noneOf [c]) (char c)
 
 getAfterString :: String -> Parser String
 getAfterString s = pAfter <|> return ("<" ++ s ++ " not found!>")
diff --git a/src/System/Taffybar/Widget/WttrIn.hs b/src/System/Taffybar/Widget/WttrIn.hs
--- a/src/System/Taffybar/Widget/WttrIn.hs
+++ b/src/System/Taffybar/Widget/WttrIn.hs
@@ -28,7 +28,7 @@
   )
 import Network.HTTP.Types.Status (statusIsSuccessful)
 import System.Log.Logger (Priority (ERROR), logM)
-import System.Taffybar.Widget.Generic.PollingLabel (pollingLabelNew)
+import System.Taffybar.Widget.Generic.PollingLabel (pollingLabelWithVariableDelayAndRefresh)
 import Text.Regex (matchRegex, mkRegex)
 
 -- | Creates a GTK Label widget that polls the requested wttr.in url for weather
@@ -47,7 +47,10 @@
   -- | Update Interval (in seconds)
   Double ->
   m Widget
-textWttrNew url interval = pollingLabelNew interval (callWttr url)
+textWttrNew url interval = pollingLabelWithVariableDelayAndRefresh action True
+  where action = do
+          rsp <- callWttr url
+          return (rsp, Nothing, interval)
 
 -- | IO Action that calls wttr.in as per the user's request.
 callWttr :: String -> IO T.Text
diff --git a/taffybar.cabal b/taffybar.cabal
--- a/taffybar.cabal
+++ b/taffybar.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name: taffybar
-version: 4.1.0
+version: 4.1.1
 synopsis: A desktop bar similar to xmobar, but with more GUI
 license: BSD-3-Clause
 license-file: LICENSE
@@ -8,7 +8,7 @@
 maintainer: IvanMalison@gmail.com
 category: System
 build-type: Simple
-tested-with: GHC == 9.2.8, GHC == 9.4.8, GHC == 9.6.6, GHC == 9.8.2
+tested-with: GHC == 9.8.4, GHC == 9.10.3
 homepage: http://github.com/taffybar/taffybar
 data-files:
   taffybar.css
@@ -65,12 +65,12 @@
                , fsnotify >= 0.4 && < 0.5
                , gi-cairo-connector
                , gi-cairo-render
-               , gi-gdk >=3.0.6 && <3.1
+               , gi-gdk3 >=3.0.30 && <3.1
                , gi-gdkpixbuf >=2.0.6 && <2.1
-               , gi-gdkx11
+               , gi-gdkx113 >=3.0.17 && < 4
                , gi-glib
-               , gi-gtk >= 3.0.26 && < 4
-               , gi-gtk-hs >= 0.3.9 && < 0.4
+               , gi-gtk3 >= 3.0.44 && < 4
+               , gi-gtk-hs >= 0.3.17 && < 0.4
                , gi-pango
                , gtk-sni-tray >= 0.1.8.0
                , gtk-strut >= 0.1.2.1
@@ -243,7 +243,7 @@
                , System.Taffybar.SimpleConfigSpec
   build-depends: data-default
                , filepath
-               , gi-gtk
+               , gi-gtk3
                , hspec
                , hspec-core
                , hspec-golden
@@ -255,4 +255,4 @@
 
 source-repository head
   type: git
-  location: git://github.com/taffybar/taffybar.git
+  location: http://github.com/taffybar/taffybar.git
diff --git a/test/lib/System/Taffybar/Test/UtilSpec.hs b/test/lib/System/Taffybar/Test/UtilSpec.hs
--- a/test/lib/System/Taffybar/Test/UtilSpec.hs
+++ b/test/lib/System/Taffybar/Test/UtilSpec.hs
@@ -292,7 +292,7 @@
 
 makeServiceDefaults :: FilePath -> [String] -> IO (ProcessConfig () () ())
 makeServiceDefaults prog args =
-  ($ proc prog args) . setServiceDefaults <$> getSpecLogPriority
+  flip setServiceDefaults (proc prog args) <$> getSpecLogPriority
 
 setServiceDefaults :: Priority -> ProcessConfig i o e -> ProcessConfig () () ()
 setServiceDefaults logLevel = setCloseFds True
