Propagate taffybar redraw fix

This commit is contained in:
2026-05-04 08:13:17 -07:00
parent 07fb87ddbb
commit df36fe2d12
5 changed files with 146 additions and 46 deletions

View File

@@ -8,6 +8,28 @@ run:
restart: restart:
@scripts/taffybar-restart @scripts/taffybar-restart
# Restart with one of the bisect widget profiles:
# minimal, workspaces, workspaces-windows, or full.
restart-widget-profile profile:
@TAFFYBAR_WIDGET_PROFILE='{{profile}}' scripts/taffybar-restart
# Restart a widget profile with draw and workspace update logging enabled.
# Inspect /tmp/taffybar.log after switching workspaces.
restart-redraw-debug profile="full":
@TAFFYBAR_WIDGET_PROFILE='{{profile}}' TAFFYBAR_DRAW_DEBUG=1 TAFFYBAR_WORKSPACE_DEBUG=1 scripts/taffybar-restart
# Minimal widget profile with all taffybar CSS disabled.
restart-redraw-minimal:
@TAFFYBAR_WIDGET_PROFILE='minimal' TAFFYBAR_DRAW_DEBUG=1 TAFFYBAR_WORKSPACE_DEBUG=1 TAFFYBAR_DISABLE_VENDOR_CSS=1 TAFFYBAR_DISABLE_USER_CSS=1 scripts/taffybar-restart
# Workspace-only profile with all taffybar CSS disabled.
restart-redraw-workspaces-empty-css:
@TAFFYBAR_WIDGET_PROFILE='workspaces' TAFFYBAR_DRAW_DEBUG=1 TAFFYBAR_WORKSPACE_DEBUG=1 TAFFYBAR_DISABLE_VENDOR_CSS=1 TAFFYBAR_DISABLE_USER_CSS=1 scripts/taffybar-restart
# Workspace-only profile with the normal configured CSS.
restart-redraw-workspaces-user-css:
@TAFFYBAR_WIDGET_PROFILE='workspaces' TAFFYBAR_DRAW_DEBUG=1 TAFFYBAR_WORKSPACE_DEBUG=1 scripts/taffybar-restart
# Restart with no vendor CSS (taffybar's built-in stylesheet) loaded. # Restart with no vendor CSS (taffybar's built-in stylesheet) loaded.
restart-no-vendor-css: restart-no-vendor-css:
@TAFFYBAR_DISABLE_VENDOR_CSS=1 scripts/taffybar-restart @TAFFYBAR_DISABLE_VENDOR_CSS=1 scripts/taffybar-restart

View File

@@ -37,6 +37,7 @@ executable taffybar
, time , time
, taffybar>=3.2.0 , taffybar>=3.2.0
, transformers , transformers
, unix
, xdg-basedir , xdg-basedir
default-language: Haskell2010 default-language: Haskell2010

View File

@@ -11,6 +11,7 @@ import Control.Monad.Trans.Reader (asks)
import Data.Char (toLower) import Data.Char (toLower)
import Data.Foldable (for_) import Data.Foldable (for_)
import Data.GI.Base (castTo) import Data.GI.Base (castTo)
import Data.IORef (newIORef, readIORef, writeIORef)
import Data.Int (Int32) import Data.Int (Int32)
import Data.List (nub) import Data.List (nub)
import qualified Data.Map as M import qualified Data.Map as M
@@ -26,7 +27,9 @@ import Network.HostName (getHostName)
import qualified StatusNotifier.Tray as SNITray import qualified StatusNotifier.Tray as SNITray
import System.Environment (lookupEnv) import System.Environment (lookupEnv)
import System.Environment.XDG.BaseDir (getUserConfigFile) import System.Environment.XDG.BaseDir (getUserConfigFile)
import System.IO (hFlush, stdout)
import System.Log.Logger (Priority (WARNING), rootLoggerName, setLevel, updateGlobalLogger) import System.Log.Logger (Priority (WARNING), rootLoggerName, setLevel, updateGlobalLogger)
import System.Posix.Process (getProcessID)
import System.Process (spawnCommand) import System.Process (spawnCommand)
import System.Taffybar (startTaffybar) import System.Taffybar (startTaffybar)
import System.Taffybar.Context import System.Taffybar.Context
@@ -83,6 +86,40 @@ import System.Taffybar.WindowIcon (pixBufFromColor)
import Text.Printf (printf) import Text.Printf (printf)
import Text.Read (readMaybe) import Text.Read (readMaybe)
envFlag :: String -> IO Bool
envFlag name = do
value <- lookupEnv name
pure $
case fmap (map toLower) value of
Just "1" -> True
Just "true" -> True
Just "yes" -> True
Just "on" -> True
_ -> False
envTextDefault :: String -> Text -> IO Text
envTextDefault name fallback =
maybe fallback T.pack <$> lookupEnv name
attachDrawProbe :: Text -> Gtk.Widget -> TaffyIO Gtk.Widget
attachDrawProbe name widget = do
enabled <- liftIO $ envFlag "TAFFYBAR_DRAW_DEBUG"
when enabled $ liftIO $ do
counter <- newIORef (0 :: Int)
pid <- getProcessID
_ <- Gtk.onWidgetDraw widget $ \_ -> do
count <- (+ 1) <$> readIORef counter
writeIORef counter count
printf "taffybar-draw pid=%s widget=%s count=%d\n" (show pid) (T.unpack name) count
hFlush stdout
pure False
pure ()
pure widget
withDrawProbe :: Text -> TaffyIO Gtk.Widget -> TaffyIO Gtk.Widget
withDrawProbe name builder =
builder >>= attachDrawProbe name
-- | Wrap the widget in a "TaffyBox" (via 'buildContentsBox') and add a CSS class. -- | Wrap the widget in a "TaffyBox" (via 'buildContentsBox') and add a CSS class.
decorateWithClassAndBox :: (MonadIO m) => Text -> Gtk.Widget -> m Gtk.Widget decorateWithClassAndBox :: (MonadIO m) => Text -> Gtk.Widget -> m Gtk.Widget
decorateWithClassAndBox klass widget = do decorateWithClassAndBox klass widget = do
@@ -320,13 +357,19 @@ windowsWidget =
) )
workspacesWidget :: TaffyIO Gtk.Widget workspacesWidget :: TaffyIO Gtk.Widget
workspacesWidget = Workspaces.workspacesNew cfg workspacesWidget = do
iconProfile <- liftIO $ envTextDefault "TAFFYBAR_WORKSPACE_ICON_PROFILE" "custom"
Workspaces.workspacesNew (cfg iconProfile)
where where
cfg = iconGetter "default" = Workspaces.defaultGetWindowIconPixbuf
iconGetter "fallback" = workspaceFallbackIcon
iconGetter "none" = \_ _ -> return Nothing
iconGetter _ = workspaceWindowIconGetter
cfg iconProfile =
Workspaces.defaultWorkspacesConfig Workspaces.defaultWorkspacesConfig
{ Workspaces.widgetGap = 0, { Workspaces.widgetGap = 0,
Workspaces.minIcons = 1, Workspaces.minIcons = 1,
Workspaces.getWindowIconPixbuf = workspaceWindowIconGetter, Workspaces.getWindowIconPixbuf = iconGetter iconProfile,
Workspaces.labelSetter = workspaceLabelSetter, Workspaces.labelSetter = workspaceLabelSetter,
Workspaces.showWorkspaceFn = Workspaces.showWorkspaceFn =
\workspace -> \workspace ->
@@ -700,68 +743,101 @@ sniTrayWidget = do
-- ** Layout -- ** Layout
startWidgetsForBackend :: Backend -> [TaffyIO Gtk.Widget] probeWidgets :: [(Text, TaffyIO Gtk.Widget)] -> [TaffyIO Gtk.Widget]
startWidgetsForBackend backend = probeWidgets =
case backend of map (uncurry withDrawProbe)
BackendX11 -> [omniMenuWidget, workspacesWidget, layoutWidget, windowsWidget]
-- These Wayland widgets are Hyprland-specific.
BackendWayland -> [omniMenuWidget, workspacesWidget, windowsWidget]
endWidgetsForHost :: String -> [TaffyIO Gtk.Widget] startWidgetsForBackend :: Text -> Backend -> [TaffyIO Gtk.Widget]
endWidgetsForHost hostName = startWidgetsForBackend "minimal" _ =
[]
startWidgetsForBackend "workspaces" _ =
probeWidgets [("workspaces", workspacesWidget)]
startWidgetsForBackend "workspaces-windows" _ =
probeWidgets [("workspaces", workspacesWidget), ("windows", windowsWidget)]
startWidgetsForBackend _ backend =
case backend of
BackendX11 ->
probeWidgets
[ ("omni-menu", omniMenuWidget),
("workspaces", workspacesWidget),
("layout", layoutWidget),
("windows", windowsWidget)
]
-- These Wayland widgets are Hyprland-specific.
BackendWayland ->
probeWidgets
[ ("omni-menu", omniMenuWidget),
("workspaces", workspacesWidget),
("windows", windowsWidget)
]
centerWidgetsForProfile :: Text -> [TaffyIO Gtk.Widget]
centerWidgetsForProfile "workspaces" = []
centerWidgetsForProfile _ = probeWidgets [("clock", clockWidget)]
endWidgetsForHost :: Text -> String -> [TaffyIO Gtk.Widget]
endWidgetsForHost "minimal" _ =
[]
endWidgetsForHost "workspaces" _ =
[]
endWidgetsForHost "workspaces-windows" _ =
[]
endWidgetsForHost _ hostName =
-- NOTE: end widgets are packed with Gtk.boxPackEnd, so the list order is -- NOTE: end widgets are packed with Gtk.boxPackEnd, so the list order is
-- right-to-left on screen. Make the tray appear at the far right by placing -- right-to-left on screen. Make the tray appear at the far right by placing
-- it first in the list. (On laptops: the battery/wifi stack is far right, -- it first in the list. (On laptops: the battery/wifi stack is far right,
-- tray immediately left of it.) -- tray immediately left of it.)
let baseEndWidgets = let baseEndWidgets =
[ sniTrayWidget, probeWidgets
audioWidget, [ ("sni-tray", sniTrayWidget),
anthropicUsageWidget, ("audio", audioWidget),
openAIUsageWidget, ("anthropic-usage", anthropicUsageWidget),
cpuWidget, ("openai-usage", openAIUsageWidget),
ramSwapWidget, ("cpu", cpuWidget),
diskUsageWidget, ("ram-swap", ramSwapWidget),
networkWidget, ("disk-usage", diskUsageWidget),
screensaverWidget, ("network", networkWidget),
sunLockWidget, ("screensaver", screensaverWidget),
mprisWidget ("sun-lock", sunLockWidget),
] ("mpris", mprisWidget)
]
laptopEndWidgets = laptopEndWidgets =
[ batteryNetworkWidget, probeWidgets
sniTrayWidget, [ ("battery-network", batteryNetworkWidget),
asusDiskUsageWidget, ("sni-tray", sniTrayWidget),
audioBacklightWidget, ("asus-disk-usage", asusDiskUsageWidget),
anthropicUsageWidget, ("audio-backlight", audioBacklightWidget),
openAIUsageWidget, ("anthropic-usage", anthropicUsageWidget),
cpuWidget, ("openai-usage", openAIUsageWidget),
ramSwapWidget, ("cpu", cpuWidget),
screensaverWidget, ("ram-swap", ramSwapWidget),
sunLockWidget, ("screensaver", screensaverWidget),
mprisWidget ("sun-lock", sunLockWidget),
] ("mpris", mprisWidget)
]
in if hostName `elem` laptopHosts in if hostName `elem` laptopHosts
then laptopEndWidgets then laptopEndWidgets
else baseEndWidgets else baseEndWidgets
mkSimpleTaffyConfig :: String -> Backend -> [FilePath] -> SimpleTaffyConfig mkSimpleTaffyConfig :: String -> Text -> Backend -> [FilePath] -> SimpleTaffyConfig
mkSimpleTaffyConfig hostName backend cssFiles = mkSimpleTaffyConfig hostName widgetProfile backend cssFiles =
defaultSimpleTaffyConfig defaultSimpleTaffyConfig
{ startWidgets = startWidgetsForBackend backend, { startWidgets = startWidgetsForBackend widgetProfile backend,
centerWidgets = [clockWidget], centerWidgets = centerWidgetsForProfile widgetProfile,
endWidgets = endWidgetsForHost hostName, endWidgets = endWidgetsForHost widgetProfile hostName,
barLevels = Nothing, barLevels = Nothing,
barPosition = Top, barPosition = Top,
widgetSpacing = 0, widgetSpacing = 0,
barPadding = barPadding =
if hostName `elem` smallBarHosts if hostName `elem` smallBarHosts
then 1 then 0
else else
if hostName `elem` compactBarHosts if hostName `elem` compactBarHosts
then 2 then 2
else 4, else 4,
barHeight = barHeight =
if hostName `elem` smallBarHosts if hostName `elem` smallBarHosts
then ScreenRatio $ 1 / 48 then ScreenRatio $ 1 / 64
else else
if hostName `elem` compactBarHosts if hostName `elem` compactBarHosts
then ScreenRatio $ 1 / 40 then ScreenRatio $ 1 / 40
@@ -777,9 +853,10 @@ main = do
hostName <- getHostName hostName <- getHostName
backend <- detectBackend backend <- detectBackend
widgetProfile <- envTextDefault "TAFFYBAR_WIDGET_PROFILE" "full"
cssFiles <- mapM (getUserConfigFile "taffybar") (cssFilesForHost hostName) cssFiles <- mapM (getUserConfigFile "taffybar") (cssFilesForHost hostName)
let simpleTaffyConfig = mkSimpleTaffyConfig hostName backend cssFiles let simpleTaffyConfig = mkSimpleTaffyConfig hostName widgetProfile backend cssFiles
startTaffybar $ startTaffybar $
withLogServer $ withLogServer $
withLogLevels $ withLogLevels $

4
nixos/flake.lock generated
View File

@@ -1934,8 +1934,8 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777790216, "lastModified": 1777906173,
"narHash": "sha256-0rnLX5oPIsxzypq6t1dI6uLkkMhFYqO5N0ql9BgmX6c=", "narHash": "sha256-g0mvCQ1erIjaNnNerr8AAjdwY9PTcas6c0iOJukilOY=",
"path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar", "path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar",
"type": "path" "type": "path"
}, },