packages feed

taffybar 7.2.7 → 7.3.0

raw patch · 30 files changed

+744/−167 lines, 30 filesdep ~gtk-strutPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: gtk-strut

API changes (from Hackage documentation)

- System.Taffybar.Context.Backend: instance GHC.Internal.Show.Show System.Taffybar.Context.Backend.Backend
- System.Taffybar.Information.ASUS: instance GHC.Internal.Enum.Bounded System.Taffybar.Information.ASUS.ASUSPlatformProfile
- System.Taffybar.Information.ASUS: instance GHC.Internal.Enum.Enum System.Taffybar.Information.ASUS.ASUSPlatformProfile
- System.Taffybar.Information.ASUS: instance GHC.Internal.Show.Show System.Taffybar.Information.ASUS.ASUSInfo
- System.Taffybar.Information.ASUS: instance GHC.Internal.Show.Show System.Taffybar.Information.ASUS.ASUSPlatformProfile
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Base.Monoid System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Base.Semigroup System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthLimit
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthUsage
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthWindow
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageInfo
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageMetadata
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageSnapshot
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageWindow
- System.Taffybar.Information.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.AnthropicUsage.TranscriptUsageEntry
- System.Taffybar.Information.Audio: instance GHC.Internal.Show.Show System.Taffybar.Information.Audio.AudioBackend
- System.Taffybar.Information.Audio: instance GHC.Internal.Show.Show System.Taffybar.Information.Audio.AudioInfo
- System.Taffybar.Information.Backlight: instance GHC.Internal.Show.Show System.Taffybar.Information.Backlight.BacklightInfo
- System.Taffybar.Information.Bluetooth: instance GHC.Internal.Show.Show System.Taffybar.Information.Bluetooth.BluetoothController
- System.Taffybar.Information.Bluetooth: instance GHC.Internal.Show.Show System.Taffybar.Information.Bluetooth.BluetoothDevice
- System.Taffybar.Information.Bluetooth: instance GHC.Internal.Show.Show System.Taffybar.Information.Bluetooth.BluetoothInfo
- System.Taffybar.Information.Bluetooth: instance GHC.Internal.Show.Show System.Taffybar.Information.Bluetooth.BluetoothStatus
- System.Taffybar.Information.CPU2: instance GHC.Internal.Show.Show System.Taffybar.Information.CPU2.CPULoad
- System.Taffybar.Information.CPUFrequency: instance GHC.Internal.Show.Show System.Taffybar.Information.CPUFrequency.CPUFrequencyInfo
- System.Taffybar.Information.Crypto: instance GHC.Internal.Show.Show System.Taffybar.Information.Crypto.CoinGeckoInfo
- System.Taffybar.Information.DiskUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.DiskUsage.DiskUsageInfo
- System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Internal.Read.Read System.Taffybar.Information.EWMHDesktopInfo.WorkspaceId
- System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Internal.Show.Show System.Taffybar.Information.EWMHDesktopInfo.EWMHIcon
- System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Internal.Show.Show System.Taffybar.Information.EWMHDesktopInfo.WorkspaceId
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandClient
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandClientConfig
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandClientEnv
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandCommand
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandError
- System.Taffybar.Information.Hyprland: instance GHC.Internal.Show.Show System.Taffybar.Information.Hyprland.HyprlandSocket
- System.Taffybar.Information.HyprlandWorkspaceHistory: instance GHC.Internal.Show.Show System.Taffybar.Information.HyprlandWorkspaceHistory.HyprlandWorkspaceHistoryMonitor
- System.Taffybar.Information.HyprlandWorkspaceHistory: instance GHC.Internal.Show.Show System.Taffybar.Information.HyprlandWorkspaceHistory.HyprlandWorkspaceHistorySnapshot
- System.Taffybar.Information.Inhibitor: instance GHC.Internal.Enum.Bounded System.Taffybar.Information.Inhibitor.InhibitType
- System.Taffybar.Information.Inhibitor: instance GHC.Internal.Enum.Enum System.Taffybar.Information.Inhibitor.InhibitType
- System.Taffybar.Information.Inhibitor: instance GHC.Internal.Show.Show System.Taffybar.Information.Inhibitor.InhibitType
- System.Taffybar.Information.Inhibitor: instance GHC.Internal.Show.Show System.Taffybar.Information.Inhibitor.InhibitorState
- System.Taffybar.Information.KeyboardState: instance GHC.Internal.Show.Show System.Taffybar.Information.KeyboardState.KeyboardState
- System.Taffybar.Information.Layout.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Layout.Model.LayoutBackend
- System.Taffybar.Information.Layout.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Layout.Model.LayoutSnapshot
- System.Taffybar.Information.MPRIS2: instance GHC.Internal.Show.Show System.Taffybar.Information.MPRIS2.NowPlaying
- System.Taffybar.Information.Memory: instance GHC.Internal.Show.Show System.Taffybar.Information.Memory.MemoryInfo
- System.Taffybar.Information.Network: instance GHC.Internal.Show.Show System.Taffybar.Information.Network.TxSample
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.ActiveConnectionInfo
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.NetworkInfo
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.NetworkState
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.NetworkType
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.WifiInfo
- System.Taffybar.Information.NetworkManager: instance GHC.Internal.Show.Show System.Taffybar.Information.NetworkManager.WifiState
- System.Taffybar.Information.Nvidia: instance GHC.Internal.Show.Show System.Taffybar.Information.Nvidia.NvidiaGpuInfo
- System.Taffybar.Information.Nvidia: instance GHC.Internal.Show.Show System.Taffybar.Information.Nvidia.NvidiaGpuTemperature
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Base.Monoid System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Base.Semigroup System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAITranscriptEntry
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageAdditionalRateLimit
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageCredits
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageInfo
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageRateLimit
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageSnapshot
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
- System.Taffybar.Information.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageWindow
- System.Taffybar.Information.PowerProfiles: instance GHC.Internal.Enum.Bounded System.Taffybar.Information.PowerProfiles.PowerProfile
- System.Taffybar.Information.PowerProfiles: instance GHC.Internal.Enum.Enum System.Taffybar.Information.PowerProfiles.PowerProfile
- System.Taffybar.Information.PowerProfiles: instance GHC.Internal.Show.Show System.Taffybar.Information.PowerProfiles.PowerProfile
- System.Taffybar.Information.PowerProfiles: instance GHC.Internal.Show.Show System.Taffybar.Information.PowerProfiles.PowerProfileInfo
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.NodeType
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PrivacyConfig
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PrivacyInfo
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PrivacyNode
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PwInfo
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PwObject
- System.Taffybar.Information.Privacy: instance GHC.Internal.Generics.Generic System.Taffybar.Information.Privacy.PwProps
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.NodeType
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PrivacyConfig
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PrivacyInfo
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PrivacyNode
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PwInfo
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PwObject
- System.Taffybar.Information.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Information.Privacy.PwProps
- System.Taffybar.Information.PulseAudio: instance GHC.Internal.Show.Show System.Taffybar.Information.PulseAudio.PulseAudioInfo
- System.Taffybar.Information.Systemd: instance GHC.Internal.Show.Show System.Taffybar.Information.Systemd.SystemdInfo
- System.Taffybar.Information.Systemd: instance GHC.Internal.Show.Show System.Taffybar.Information.Systemd.SystemdState
- System.Taffybar.Information.Temperature: instance GHC.Internal.Show.Show System.Taffybar.Information.Temperature.TemperatureInfo
- System.Taffybar.Information.Temperature: instance GHC.Internal.Show.Show System.Taffybar.Information.Temperature.TemperatureUnit
- System.Taffybar.Information.Temperature: instance GHC.Internal.Show.Show System.Taffybar.Information.Temperature.ThermalSensor
- System.Taffybar.Information.WirePlumber: instance GHC.Internal.Show.Show System.Taffybar.Information.WirePlumber.NodeType
- System.Taffybar.Information.WirePlumber: instance GHC.Internal.Show.Show System.Taffybar.Information.WirePlumber.WirePlumberInfo
- System.Taffybar.Information.WirePlumber: instance GHC.Internal.Show.Show System.Taffybar.Information.WirePlumber.WirePlumberSelector
- System.Taffybar.Information.Wlsunset: instance GHC.Internal.Enum.Bounded System.Taffybar.Information.Wlsunset.WlsunsetMode
- System.Taffybar.Information.Wlsunset: instance GHC.Internal.Enum.Enum System.Taffybar.Information.Wlsunset.WlsunsetMode
- System.Taffybar.Information.Wlsunset: instance GHC.Internal.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetConfig
- System.Taffybar.Information.Wlsunset: instance GHC.Internal.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetMode
- System.Taffybar.Information.Wlsunset: instance GHC.Internal.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetState
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WindowEvent
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WindowIdentity
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WindowInfo
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceBackend
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceEvent
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceEventBatch
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceIdentity
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceInfo
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceSnapshot
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceViewState
- System.Taffybar.Information.Workspaces.Model: instance GHC.Internal.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspacesRefreshTarget
- System.Taffybar.Information.X11DesktopInfo: instance GHC.Internal.Generics.Generic System.Taffybar.Information.X11DesktopInfo.DisplayName
- System.Taffybar.Information.X11DesktopInfo: instance GHC.Internal.Read.Read System.Taffybar.Information.X11DesktopInfo.DisplayName
- System.Taffybar.Information.X11DesktopInfo: instance GHC.Internal.Show.Show System.Taffybar.Information.X11DesktopInfo.DisplayName
- System.Taffybar.Information.XDG.Protocol: instance GHC.Internal.Read.Read System.Taffybar.Information.XDG.Protocol.DesktopEntryCondition
- System.Taffybar.Information.XDG.Protocol: instance GHC.Internal.Show.Show System.Taffybar.Information.XDG.Protocol.DesktopEntryCondition
- System.Taffybar.Information.XDG.Protocol: instance GHC.Internal.Show.Show System.Taffybar.Information.XDG.Protocol.XDGLayoutItem
- System.Taffybar.Information.XDG.Protocol: instance GHC.Internal.Show.Show System.Taffybar.Information.XDG.Protocol.XDGMenu
- System.Taffybar.SimpleConfig: instance GHC.Internal.Enum.Bounded System.Taffybar.SimpleConfig.Position
- System.Taffybar.SimpleConfig: instance GHC.Internal.Enum.Enum System.Taffybar.SimpleConfig.Position
- System.Taffybar.SimpleConfig: instance GHC.Internal.Read.Read System.Taffybar.SimpleConfig.Position
- System.Taffybar.SimpleConfig: instance GHC.Internal.Show.Show System.Taffybar.SimpleConfig.Position
- System.Taffybar.Util: instance GHC.Internal.Show.Show d => GHC.Internal.Show.Show (System.Taffybar.Util.VariableDelayConfig d)
- System.Taffybar.Widget.ASUS: instance GHC.Internal.Show.Show System.Taffybar.Widget.ASUS.ASUSWidgetConfig
- System.Taffybar.Widget.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Widget.AnthropicUsage.AnthropicUsageDisplayMode
- System.Taffybar.Widget.AnthropicUsage: instance GHC.Internal.Show.Show System.Taffybar.Widget.AnthropicUsage.AnthropicUsageWindowSelector
- System.Taffybar.Widget.Battery: instance GHC.Internal.Show.Show System.Taffybar.Widget.Battery.BatteryWidgetInfo
- System.Taffybar.Widget.CPUFrequency: instance GHC.Internal.Show.Show System.Taffybar.Widget.CPUFrequency.CPUFrequencyWidgetConfig
- System.Taffybar.Widget.FreedesktopNotifications: instance GHC.Internal.Show.Show System.Taffybar.Widget.FreedesktopNotifications.Notification
- System.Taffybar.Widget.Generic.AutoSizeImage: instance GHC.Internal.Show.Show System.Taffybar.Widget.Generic.AutoSizeImage.ImageScaleStrategy
- System.Taffybar.Widget.Inhibitor: instance GHC.Internal.Show.Show System.Taffybar.Widget.Inhibitor.InhibitorConfig
- System.Taffybar.Widget.KeyboardState: instance GHC.Internal.Show.Show System.Taffybar.Widget.KeyboardState.KeyboardStateConfig
- System.Taffybar.Widget.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Widget.OpenAIUsage.OpenAIUsageDisplayMode
- System.Taffybar.Widget.OpenAIUsage: instance GHC.Internal.Show.Show System.Taffybar.Widget.OpenAIUsage.OpenAIUsageWindowSelector
- System.Taffybar.Widget.PowerProfiles: instance GHC.Internal.Show.Show System.Taffybar.Widget.PowerProfiles.PowerProfilesConfig
- System.Taffybar.Widget.Privacy: instance GHC.Internal.Show.Show System.Taffybar.Widget.Privacy.PrivacyWidgetConfig
- System.Taffybar.Widget.ScreenLock: instance GHC.Internal.Show.Show System.Taffybar.Widget.ScreenLock.ScreenLockConfig
- System.Taffybar.Widget.SimpleClock: instance GHC.Internal.Show.Show System.Taffybar.Widget.SimpleClock.ClockConfig
- System.Taffybar.Widget.SimpleClock: instance GHC.Internal.Show.Show System.Taffybar.Widget.SimpleClock.ClockUpdateStrategy
- System.Taffybar.Widget.Systemd: instance GHC.Internal.Show.Show System.Taffybar.Widget.Systemd.SystemdConfig
- System.Taffybar.Widget.Util: instance GHC.Internal.Show.Show System.Taffybar.Widget.Util.UsageWindowLabelParts
- System.Taffybar.Widget.Util: instance GHC.Internal.Show.Show System.Taffybar.Widget.Util.UsageWindowPosition
- System.Taffybar.Widget.WakeupDebug: instance GHC.Internal.Show.Show System.Taffybar.Widget.WakeupDebug.WakeupDebugWidgetConfig
- System.Taffybar.Widget.Weather: instance GHC.Internal.Show.Show System.Taffybar.Widget.Weather.WeatherInfo
- System.Taffybar.Widget.Wlsunset: instance GHC.Internal.Show.Show System.Taffybar.Widget.Wlsunset.WlsunsetWidgetConfig
- System.Taffybar.Widget.Workspaces: instance GHC.Internal.Show.Show System.Taffybar.Widget.Workspaces.WorkspaceState
- System.Taffybar.Widget.XDGMenu.Menu: instance GHC.Internal.Show.Show System.Taffybar.Widget.XDGMenu.Menu.Menu
- System.Taffybar.Widget.XDGMenu.Menu: instance GHC.Internal.Show.Show System.Taffybar.Widget.XDGMenu.Menu.MenuEntry
- System.Taffybar.WindowIcon: instance GHC.Internal.Show.Show System.Taffybar.WindowIcon.WindowIconCacheKey
+ System.Taffybar.Context: [barCssClasses] :: BarConfig -> [Text]
+ System.Taffybar.Context.Backend: instance GHC.Show.Show System.Taffybar.Context.Backend.Backend
+ System.Taffybar.Information.ASUS: instance GHC.Enum.Bounded System.Taffybar.Information.ASUS.ASUSPlatformProfile
+ System.Taffybar.Information.ASUS: instance GHC.Enum.Enum System.Taffybar.Information.ASUS.ASUSPlatformProfile
+ System.Taffybar.Information.ASUS: instance GHC.Show.Show System.Taffybar.Information.ASUS.ASUSInfo
+ System.Taffybar.Information.ASUS: instance GHC.Show.Show System.Taffybar.Information.ASUS.ASUSPlatformProfile
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Base.Monoid System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Base.Semigroup System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthLimit
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthUsage
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicOAuthWindow
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageInfo
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageMetadata
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageSnapshot
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageTotals
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.AnthropicUsageWindow
+ System.Taffybar.Information.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Information.AnthropicUsage.TranscriptUsageEntry
+ System.Taffybar.Information.Audio: instance GHC.Show.Show System.Taffybar.Information.Audio.AudioBackend
+ System.Taffybar.Information.Audio: instance GHC.Show.Show System.Taffybar.Information.Audio.AudioInfo
+ System.Taffybar.Information.Backlight: instance GHC.Show.Show System.Taffybar.Information.Backlight.BacklightInfo
+ System.Taffybar.Information.Bluetooth: instance GHC.Show.Show System.Taffybar.Information.Bluetooth.BluetoothController
+ System.Taffybar.Information.Bluetooth: instance GHC.Show.Show System.Taffybar.Information.Bluetooth.BluetoothDevice
+ System.Taffybar.Information.Bluetooth: instance GHC.Show.Show System.Taffybar.Information.Bluetooth.BluetoothInfo
+ System.Taffybar.Information.Bluetooth: instance GHC.Show.Show System.Taffybar.Information.Bluetooth.BluetoothStatus
+ System.Taffybar.Information.CPU2: instance GHC.Show.Show System.Taffybar.Information.CPU2.CPULoad
+ System.Taffybar.Information.CPUFrequency: instance GHC.Show.Show System.Taffybar.Information.CPUFrequency.CPUFrequencyInfo
+ System.Taffybar.Information.CPUPower: CPUPowerInfo :: Maybe Double -> Maybe String -> CPUPowerInfo
+ System.Taffybar.Information.CPUPower: EnergySample :: Integer -> Integer -> EnergySample
+ System.Taffybar.Information.CPUPower: [cpuPackagePowerWatts] :: CPUPowerInfo -> Maybe Double
+ System.Taffybar.Information.CPUPower: [cpuPowerDomainName] :: CPUPowerInfo -> Maybe String
+ System.Taffybar.Information.CPUPower: [energySampleMicrojoules] :: EnergySample -> Integer
+ System.Taffybar.Information.CPUPower: [energySampleMonotonicNanoseconds] :: EnergySample -> Integer
+ System.Taffybar.Information.CPUPower: calculateCPUPowerWatts :: Integer -> EnergySample -> EnergySample -> Maybe Double
+ System.Taffybar.Information.CPUPower: data CPUPowerInfo
+ System.Taffybar.Information.CPUPower: data EnergySample
+ System.Taffybar.Information.CPUPower: getCPUPowerInfoChan :: Double -> TaffyIO (TChan CPUPowerInfo)
+ System.Taffybar.Information.CPUPower: getCPUPowerInfoState :: Double -> TaffyIO CPUPowerInfo
+ System.Taffybar.Information.CPUPower: instance GHC.Classes.Eq System.Taffybar.Information.CPUPower.CPUPowerDomain
+ System.Taffybar.Information.CPUPower: instance GHC.Classes.Eq System.Taffybar.Information.CPUPower.CPUPowerInfo
+ System.Taffybar.Information.CPUPower: instance GHC.Classes.Eq System.Taffybar.Information.CPUPower.EnergySample
+ System.Taffybar.Information.CPUPower: instance GHC.Show.Show System.Taffybar.Information.CPUPower.CPUPowerDomain
+ System.Taffybar.Information.CPUPower: instance GHC.Show.Show System.Taffybar.Information.CPUPower.CPUPowerInfo
+ System.Taffybar.Information.CPUPower: instance GHC.Show.Show System.Taffybar.Information.CPUPower.EnergySample
+ System.Taffybar.Information.CPUPower: readCPUPowerInfo :: IO CPUPowerInfo
+ System.Taffybar.Information.Crypto: instance GHC.Show.Show System.Taffybar.Information.Crypto.CoinGeckoInfo
+ System.Taffybar.Information.DiskUsage: forceDiskUsageRefresh :: Double -> FilePath -> TaffyIO ()
+ System.Taffybar.Information.DiskUsage: instance GHC.Show.Show System.Taffybar.Information.DiskUsage.DiskUsageInfo
+ System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Read.Read System.Taffybar.Information.EWMHDesktopInfo.WorkspaceId
+ System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Show.Show System.Taffybar.Information.EWMHDesktopInfo.EWMHIcon
+ System.Taffybar.Information.EWMHDesktopInfo: instance GHC.Show.Show System.Taffybar.Information.EWMHDesktopInfo.WorkspaceId
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandClient
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandClientConfig
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandClientEnv
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandCommand
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandError
+ System.Taffybar.Information.Hyprland: instance GHC.Show.Show System.Taffybar.Information.Hyprland.HyprlandSocket
+ System.Taffybar.Information.HyprlandWorkspaceHistory: instance GHC.Show.Show System.Taffybar.Information.HyprlandWorkspaceHistory.HyprlandWorkspaceHistoryMonitor
+ System.Taffybar.Information.HyprlandWorkspaceHistory: instance GHC.Show.Show System.Taffybar.Information.HyprlandWorkspaceHistory.HyprlandWorkspaceHistorySnapshot
+ System.Taffybar.Information.Inhibitor: instance GHC.Enum.Bounded System.Taffybar.Information.Inhibitor.InhibitType
+ System.Taffybar.Information.Inhibitor: instance GHC.Enum.Enum System.Taffybar.Information.Inhibitor.InhibitType
+ System.Taffybar.Information.Inhibitor: instance GHC.Show.Show System.Taffybar.Information.Inhibitor.InhibitType
+ System.Taffybar.Information.Inhibitor: instance GHC.Show.Show System.Taffybar.Information.Inhibitor.InhibitorState
+ System.Taffybar.Information.KeyboardState: instance GHC.Show.Show System.Taffybar.Information.KeyboardState.KeyboardState
+ System.Taffybar.Information.Layout.Model: instance GHC.Show.Show System.Taffybar.Information.Layout.Model.LayoutBackend
+ System.Taffybar.Information.Layout.Model: instance GHC.Show.Show System.Taffybar.Information.Layout.Model.LayoutSnapshot
+ System.Taffybar.Information.MPRIS2: instance GHC.Show.Show System.Taffybar.Information.MPRIS2.NowPlaying
+ System.Taffybar.Information.Memory: instance GHC.Show.Show System.Taffybar.Information.Memory.MemoryInfo
+ System.Taffybar.Information.Network: instance GHC.Show.Show System.Taffybar.Information.Network.TxSample
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.ActiveConnectionInfo
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.NetworkInfo
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.NetworkState
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.NetworkType
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.WifiInfo
+ System.Taffybar.Information.NetworkManager: instance GHC.Show.Show System.Taffybar.Information.NetworkManager.WifiState
+ System.Taffybar.Information.Nvidia: instance GHC.Show.Show System.Taffybar.Information.Nvidia.NvidiaGpuInfo
+ System.Taffybar.Information.Nvidia: instance GHC.Show.Show System.Taffybar.Information.Nvidia.NvidiaGpuTemperature
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Base.Monoid System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Base.Semigroup System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAITranscriptEntry
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageAdditionalRateLimit
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageCredits
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageInfo
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageRateLimit
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageSnapshot
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageTotals
+ System.Taffybar.Information.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Information.OpenAIUsage.OpenAIUsageWindow
+ System.Taffybar.Information.PowerProfiles: instance GHC.Enum.Bounded System.Taffybar.Information.PowerProfiles.PowerProfile
+ System.Taffybar.Information.PowerProfiles: instance GHC.Enum.Enum System.Taffybar.Information.PowerProfiles.PowerProfile
+ System.Taffybar.Information.PowerProfiles: instance GHC.Show.Show System.Taffybar.Information.PowerProfiles.PowerProfile
+ System.Taffybar.Information.PowerProfiles: instance GHC.Show.Show System.Taffybar.Information.PowerProfiles.PowerProfileInfo
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.NodeType
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PrivacyConfig
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PrivacyInfo
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PrivacyNode
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PwInfo
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PwObject
+ System.Taffybar.Information.Privacy: instance GHC.Generics.Generic System.Taffybar.Information.Privacy.PwProps
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.NodeType
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PrivacyConfig
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PrivacyInfo
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PrivacyNode
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PwInfo
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PwObject
+ System.Taffybar.Information.Privacy: instance GHC.Show.Show System.Taffybar.Information.Privacy.PwProps
+ System.Taffybar.Information.PulseAudio: instance GHC.Show.Show System.Taffybar.Information.PulseAudio.PulseAudioInfo
+ System.Taffybar.Information.Systemd: instance GHC.Show.Show System.Taffybar.Information.Systemd.SystemdInfo
+ System.Taffybar.Information.Systemd: instance GHC.Show.Show System.Taffybar.Information.Systemd.SystemdState
+ System.Taffybar.Information.Temperature: instance GHC.Show.Show System.Taffybar.Information.Temperature.TemperatureInfo
+ System.Taffybar.Information.Temperature: instance GHC.Show.Show System.Taffybar.Information.Temperature.TemperatureUnit
+ System.Taffybar.Information.Temperature: instance GHC.Show.Show System.Taffybar.Information.Temperature.ThermalSensor
+ System.Taffybar.Information.WirePlumber: instance GHC.Show.Show System.Taffybar.Information.WirePlumber.NodeType
+ System.Taffybar.Information.WirePlumber: instance GHC.Show.Show System.Taffybar.Information.WirePlumber.WirePlumberInfo
+ System.Taffybar.Information.WirePlumber: instance GHC.Show.Show System.Taffybar.Information.WirePlumber.WirePlumberSelector
+ System.Taffybar.Information.Wlsunset: instance GHC.Enum.Bounded System.Taffybar.Information.Wlsunset.WlsunsetMode
+ System.Taffybar.Information.Wlsunset: instance GHC.Enum.Enum System.Taffybar.Information.Wlsunset.WlsunsetMode
+ System.Taffybar.Information.Wlsunset: instance GHC.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetConfig
+ System.Taffybar.Information.Wlsunset: instance GHC.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetMode
+ System.Taffybar.Information.Wlsunset: instance GHC.Show.Show System.Taffybar.Information.Wlsunset.WlsunsetState
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WindowEvent
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WindowIdentity
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WindowInfo
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceBackend
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceEvent
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceEventBatch
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceIdentity
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceInfo
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceSnapshot
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspaceViewState
+ System.Taffybar.Information.Workspaces.Model: instance GHC.Show.Show System.Taffybar.Information.Workspaces.Model.WorkspacesRefreshTarget
+ System.Taffybar.Information.X11DesktopInfo: instance GHC.Generics.Generic System.Taffybar.Information.X11DesktopInfo.DisplayName
+ System.Taffybar.Information.X11DesktopInfo: instance GHC.Read.Read System.Taffybar.Information.X11DesktopInfo.DisplayName
+ System.Taffybar.Information.X11DesktopInfo: instance GHC.Show.Show System.Taffybar.Information.X11DesktopInfo.DisplayName
+ System.Taffybar.Information.XDG.Protocol: instance GHC.Read.Read System.Taffybar.Information.XDG.Protocol.DesktopEntryCondition
+ System.Taffybar.Information.XDG.Protocol: instance GHC.Show.Show System.Taffybar.Information.XDG.Protocol.DesktopEntryCondition
+ System.Taffybar.Information.XDG.Protocol: instance GHC.Show.Show System.Taffybar.Information.XDG.Protocol.XDGLayoutItem
+ System.Taffybar.Information.XDG.Protocol: instance GHC.Show.Show System.Taffybar.Information.XDG.Protocol.XDGMenu
+ System.Taffybar.SimpleConfig: SimpleMonitorConfig :: Text -> SimpleTaffyConfig -> SimpleMonitorConfig
+ System.Taffybar.SimpleConfig: [barCssClasses] :: SimpleTaffyConfig -> [Text]
+ System.Taffybar.SimpleConfig: [simpleMonitorConfigKey] :: SimpleMonitorConfig -> Text
+ System.Taffybar.SimpleConfig: [simpleMonitorConfig] :: SimpleMonitorConfig -> SimpleTaffyConfig
+ System.Taffybar.SimpleConfig: data SimpleMonitorConfig
+ System.Taffybar.SimpleConfig: instance GHC.Enum.Bounded System.Taffybar.SimpleConfig.Position
+ System.Taffybar.SimpleConfig: instance GHC.Enum.Enum System.Taffybar.SimpleConfig.Position
+ System.Taffybar.SimpleConfig: instance GHC.Read.Read System.Taffybar.SimpleConfig.Position
+ System.Taffybar.SimpleConfig: instance GHC.Show.Show System.Taffybar.SimpleConfig.Position
+ System.Taffybar.SimpleConfig: toTaffybarConfigPerMonitor :: SimpleTaffyConfig -> (Int -> TaffyIO SimpleMonitorConfig) -> TaffybarConfig
+ System.Taffybar.Util: instance GHC.Show.Show d => GHC.Show.Show (System.Taffybar.Util.VariableDelayConfig d)
+ System.Taffybar.Widget.ASUS: instance GHC.Show.Show System.Taffybar.Widget.ASUS.ASUSWidgetConfig
+ System.Taffybar.Widget.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Widget.AnthropicUsage.AnthropicUsageDisplayMode
+ System.Taffybar.Widget.AnthropicUsage: instance GHC.Show.Show System.Taffybar.Widget.AnthropicUsage.AnthropicUsageWindowSelector
+ System.Taffybar.Widget.Battery: instance GHC.Show.Show System.Taffybar.Widget.Battery.BatteryWidgetInfo
+ System.Taffybar.Widget.CPUFrequency: instance GHC.Show.Show System.Taffybar.Widget.CPUFrequency.CPUFrequencyWidgetConfig
+ System.Taffybar.Widget.CPUMonitor: addHoverRefresh :: CPULoadSource -> Widget -> IO Widget
+ System.Taffybar.Widget.CPUMonitor: cpuMonitorNewWithHoverAndPower :: GraphConfig -> Double -> Double -> Double -> String -> TaffyIO Widget
+ System.Taffybar.Widget.CPUMonitor: powerLabelMarkup :: GraphConfig -> CPUPowerInfo -> Text
+ System.Taffybar.Widget.CPUMonitor: powerTooltip :: CPUPowerInfo -> Maybe Text
+ System.Taffybar.Widget.FreedesktopNotifications: instance GHC.Show.Show System.Taffybar.Widget.FreedesktopNotifications.Notification
+ System.Taffybar.Widget.Generic.AutoSizeImage: instance GHC.Show.Show System.Taffybar.Widget.Generic.AutoSizeImage.ImageScaleStrategy
+ System.Taffybar.Widget.Inhibitor: instance GHC.Show.Show System.Taffybar.Widget.Inhibitor.InhibitorConfig
+ System.Taffybar.Widget.KeyboardState: instance GHC.Show.Show System.Taffybar.Widget.KeyboardState.KeyboardStateConfig
+ System.Taffybar.Widget.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Widget.OpenAIUsage.OpenAIUsageDisplayMode
+ System.Taffybar.Widget.OpenAIUsage: instance GHC.Show.Show System.Taffybar.Widget.OpenAIUsage.OpenAIUsageWindowSelector
+ System.Taffybar.Widget.PowerProfiles: instance GHC.Show.Show System.Taffybar.Widget.PowerProfiles.PowerProfilesConfig
+ System.Taffybar.Widget.Privacy: instance GHC.Show.Show System.Taffybar.Widget.Privacy.PrivacyWidgetConfig
+ System.Taffybar.Widget.ScreenLock: instance GHC.Show.Show System.Taffybar.Widget.ScreenLock.ScreenLockConfig
+ System.Taffybar.Widget.SimpleClock: instance GHC.Show.Show System.Taffybar.Widget.SimpleClock.ClockConfig
+ System.Taffybar.Widget.SimpleClock: instance GHC.Show.Show System.Taffybar.Widget.SimpleClock.ClockUpdateStrategy
+ System.Taffybar.Widget.Systemd: instance GHC.Show.Show System.Taffybar.Widget.Systemd.SystemdConfig
+ System.Taffybar.Widget.Util: instance GHC.Show.Show System.Taffybar.Widget.Util.UsageWindowLabelParts
+ System.Taffybar.Widget.Util: instance GHC.Show.Show System.Taffybar.Widget.Util.UsageWindowPosition
+ System.Taffybar.Widget.Util: manageWidgetResource :: IsWidget widget => widget -> IO resource -> (resource -> IO ()) -> IO ()
+ System.Taffybar.Widget.Util: manageWidgetThreads :: IsWidget widget => widget -> IO [ThreadId] -> IO ()
+ System.Taffybar.Widget.WakeupDebug: instance GHC.Show.Show System.Taffybar.Widget.WakeupDebug.WakeupDebugWidgetConfig
+ System.Taffybar.Widget.Weather: instance GHC.Show.Show System.Taffybar.Widget.Weather.WeatherInfo
+ System.Taffybar.Widget.Wlsunset: instance GHC.Show.Show System.Taffybar.Widget.Wlsunset.WlsunsetWidgetConfig
+ System.Taffybar.Widget.Workspaces: instance GHC.Show.Show System.Taffybar.Widget.Workspaces.WorkspaceState
+ System.Taffybar.Widget.XDGMenu.Menu: instance GHC.Show.Show System.Taffybar.Widget.XDGMenu.Menu.Menu
+ System.Taffybar.Widget.XDGMenu.Menu: instance GHC.Show.Show System.Taffybar.Widget.XDGMenu.Menu.MenuEntry
+ System.Taffybar.WindowIcon: instance GHC.Show.Show System.Taffybar.WindowIcon.WindowIconCacheKey
- System.Taffybar.Context: BarConfig :: StrutConfig -> Int32 -> [TaffyIO Widget] -> [TaffyIO Widget] -> [TaffyIO Widget] -> Maybe [BarLevelConfig] -> Unique -> BarConfig
+ System.Taffybar.Context: BarConfig :: StrutConfig -> [Text] -> Int32 -> [TaffyIO Widget] -> [TaffyIO Widget] -> [TaffyIO Widget] -> Maybe [BarLevelConfig] -> Unique -> BarConfig
- System.Taffybar.SimpleConfig: SimpleTaffyConfig :: TaffyIO [Int] -> StrutSize -> Int -> Position -> Int -> [TaffyIO Widget] -> [TaffyIO Widget] -> [TaffyIO Widget] -> Maybe [BarLevelConfig] -> [FilePath] -> TaffyIO () -> SimpleTaffyConfig
+ System.Taffybar.SimpleConfig: SimpleTaffyConfig :: TaffyIO [Int] -> [Text] -> StrutSize -> Int -> Position -> Int -> [TaffyIO Widget] -> [TaffyIO Widget] -> [TaffyIO Widget] -> Maybe [BarLevelConfig] -> [FilePath] -> TaffyIO () -> SimpleTaffyConfig

Files

CHANGELOG.md view
@@ -1,3 +1,46 @@+# 7.3.0++## Breaking changes++* `BarConfig` and `SimpleTaffyConfig` gained a `barCssClasses` field. Record+  updates against `defaultSimpleTaffyConfig` are unaffected, but positional+  construction and exhaustive pattern matches need updating.++## Features++* Add per-monitor simple bar configuration. `toTaffybarConfigPerMonitor` takes+  a `SimpleMonitorConfig` per monitor, whose key identifies the bar: reusing a+  key reuses the existing bar, and changing it rebuilds only that monitor's bar.+* Apply user-specified CSS classes to bar windows via the new `barCssClasses`+  field, so per-monitor bars can be styled independently.+* Add channel-based CPU package power telemetry in the new+  `System.Taffybar.Information.CPUPower` module, along with+  `cpuMonitorNewWithHoverAndPower` for a CPU monitor that reports package+  wattage in its label and tooltip.+* Refresh the disk usage widget on click via `diskUsageLabelNewWithRefresh` and+  `forceDiskUsageRefresh`, so a stale reading can be updated on demand rather+  than only on the poll interval.+* Add `manageWidgetResource` and `manageWidgetThreads` to+  `System.Taffybar.Widget.Util`, which scope a widget's acquired resources and+  worker threads to its realized lifetime, releasing them on unrealize or+  destroy.++## Fixes++* Fix a segfault in `pollingGraphNew`. The tooltip was set directly from the+  sampling thread, which races GTK's tooltip bookkeeping in+  `gtk_widget_queue_tooltip_query` and eventually corrupts the tooltip window+  list. The update now goes through `postGUIASync` like its sibling widgets.+  Thanks to Isaac Shapira for the diagnosis and fix.+* Stop the MPRIS2 player icon from driving a resize feedback loop. The icon+  used the `ImageResize` path, so a player whose icon lookup fell back+  repeatedly (e.g. Chromium) re-entered size-allocate on every fallback pixbuf+  swap, growing the bar and pegging the CPU. It now routes through the+  strategy-aware `scalingImage`, which renders via a `DrawingArea` and cannot+  retrigger allocation.+* Populate backlight state before starting the monitoring thread, so the widget+  shows a real value immediately instead of waiting for the first sample.+ # 7.2.7  ## Features
app/AppearanceSnap.hs view
@@ -204,6 +204,7 @@     LayoutSingleRow ->       BarConfig         { strutConfig = baseStrutConfig 40,+          barCssClasses = [],           widgetSpacing = 8,           startWidgets = [workspaceWidget],           centerWidgets = [testBoxWidget "test-center-box" 200 20],@@ -218,6 +219,7 @@     LayoutLevels ->       BarConfig         { strutConfig = baseStrutConfig 72,+          barCssClasses = [],           widgetSpacing = 8,           startWidgets = [testBoxWidget "test-ignored-old-left" 40 16],           centerWidgets = [testBoxWidget "test-ignored-old-center" 120 16],@@ -243,6 +245,7 @@     LayoutWindowsTitleStress ->       BarConfig         { strutConfig = baseStrutConfig 55,+          barCssClasses = [],           widgetSpacing = 8,           startWidgets = [workspaceWidget, Windows.windowsNew windowsCfg],           centerWidgets = [],
app/AppearanceSnapHyprland.hs view
@@ -176,6 +176,7 @@     LayoutSingleRow ->       BarConfig         { strutConfig = baseStrutConfig 40,+          barCssClasses = [],           widgetSpacing = 8,           startWidgets = [startWidget],           centerWidgets = [testBoxWidget "test-center-box" 200 20],@@ -190,6 +191,7 @@     LayoutLevels ->       BarConfig         { strutConfig = baseStrutConfig 72,+          barCssClasses = [],           widgetSpacing = 8,           startWidgets = [testBoxWidget "test-ignored-old-left" 40 16],           centerWidgets = [testBoxWidget "test-ignored-old-center" 120 16],
src/System/Taffybar/Context.hs view
@@ -186,6 +186,8 @@ data BarConfig = BarConfig   { -- | The strut configuration to use for the bar     strutConfig :: StrutConfig,+    -- | CSS classes applied to the bar window.+    barCssClasses :: [T.Text],     -- | The amount of spacing in pixels between bar widgets     widgetSpacing :: Int32,     -- | Constructors for widgets that should be placed at the beginning of the bar.@@ -678,6 +680,8 @@       (show $ strutConfig barConfig)    window <- Gtk.windowNew Gtk.WindowTypeToplevel++  mapM_ (widgetSetClassGI window) $ barCssClasses barConfig    void $ Gtk.onWidgetDestroy window $ do     let bId = showBarId barConfig
src/System/Taffybar/Information/Backlight.hs view
@@ -159,7 +159,8 @@     ourWakeupChan <- liftIO $ atomically $ dupTChan wakeupChan     liftIO $ do       chan <- newBroadcastTChanIO-      var <- newMVar Nothing+      initialInfo <- getBacklightInfo deviceOverride+      var <- newMVar initialInfo       _ <- forkIO $ monitorBacklightInfo deviceOverride intervalSeconds ourWakeupChan chan var       pure $ BacklightInfoChanVar (chan, var) 
src/System/Taffybar/Information/CPU2.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TupleSections #-}  -----------------------------------------------------------------------------@@ -169,34 +170,32 @@  acquireFastRefresh :: WakeupManager -> Word64 -> TChan () -> MVar FastRefreshState -> IO (IO ()) acquireFastRefresh manager intervalNs refreshChannel stateVar = do-  modifyMVar_ stateVar $ \state ->-    case state of-      FastRefreshActive count threadId subscription ->-        pure $ FastRefreshActive (count + 1) threadId subscription-      FastRefreshInactive -> do-        logM cpuLoadLogPath DEBUG $ "Acquiring fast CPU refresh lease at " <> show intervalNs <> "ns"-        subscription <- subscribeWakeupInterval manager intervalNs-        ourChannel <- atomically $ dupTChan $ wakeupSubscriptionChannel subscription-        threadId <--          forkIO $-            forever $-              atomically (readTChan ourChannel)-                >> atomically (writeTChan refreshChannel ())-        pure $ FastRefreshActive 1 threadId subscription+  modifyMVar_ stateVar $ \case+    FastRefreshActive count threadId subscription ->+      pure $ FastRefreshActive (count + 1) threadId subscription+    FastRefreshInactive -> do+      logM cpuLoadLogPath DEBUG $ "Acquiring fast CPU refresh lease at " <> show intervalNs <> "ns"+      subscription <- subscribeWakeupInterval manager intervalNs+      ourChannel <- atomically $ dupTChan $ wakeupSubscriptionChannel subscription+      threadId <-+        forkIO $+          forever $+            atomically (readTChan ourChannel)+              >> atomically (writeTChan refreshChannel ())+      pure $ FastRefreshActive 1 threadId subscription   releasedRef <- newIORef False   pure $ do     shouldRelease <- atomicModifyIORef' releasedRef $ \released -> (True, not released)     when shouldRelease $-      modifyMVar_ stateVar $ \state ->-        case state of-          FastRefreshInactive -> pure FastRefreshInactive-          FastRefreshActive count threadId subscription-            | count > 1 -> pure $ FastRefreshActive (count - 1) threadId subscription-            | otherwise -> do-                logM cpuLoadLogPath DEBUG $ "Releasing fast CPU refresh lease at " <> show intervalNs <> "ns"-                killThread threadId-                releaseWakeupSubscription subscription-                pure FastRefreshInactive+      modifyMVar_ stateVar $ \case+        FastRefreshInactive -> pure FastRefreshInactive+        FastRefreshActive count threadId subscription+          | count > 1 -> pure $ FastRefreshActive (count - 1) threadId subscription+          | otherwise -> do+              logM cpuLoadLogPath DEBUG $ "Releasing fast CPU refresh lease at " <> show intervalNs <> "ns"+              killThread threadId+              releaseWakeupSubscription subscription+              pure FastRefreshInactive  drainTChan :: TChan a -> STM () drainTChan chan = do
src/System/Taffybar/Information/CPUFrequency.hs view
@@ -28,6 +28,7 @@ import Control.Monad.IO.Class (liftIO) import Control.Monad.STM (atomically) import qualified Data.ByteString.Char8 as BS8+import Data.Either (fromRight) import Data.List (isPrefixOf, sort) import Data.Maybe (catMaybes, mapMaybe) import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)@@ -76,7 +77,7 @@ readCPUFrequencyInfo :: IO CPUFrequencyInfo readCPUFrequencyInfo = do   result <- try readCPUFrequencyInfoUnsafe-  pure $ either (const emptyCPUFrequencyInfo) id (result :: Either SomeException CPUFrequencyInfo)+  pure $ fromRight emptyCPUFrequencyInfo (result :: Either SomeException CPUFrequencyInfo)  readCPUFrequencyInfoUnsafe :: IO CPUFrequencyInfo readCPUFrequencyInfoUnsafe = do@@ -100,7 +101,7 @@   discoverFrequencyPaths     "/sys/devices/system/cpu/cpufreq"     ("policy" `isPrefixOf`)-    (\base entry -> base </> entry)+    (</>)  discoverCoreFrequencyPaths :: IO [[FilePath]] discoverCoreFrequencyPaths =
+ src/System/Taffybar/Information/CPUPower.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE NumericUnderscores #-}++-- |+-- Module      : System.Taffybar.Information.CPUPower+-- Copyright   : (c) Ivan A. Malison+-- License     : BSD3-style (see LICENSE)+--+-- Linux CPU package-power information derived from the RAPL energy counter.+-- One process-wide sampler feeds all widgets through a broadcast channel.+module System.Taffybar.Information.CPUPower+  ( CPUPowerInfo (..),+    EnergySample (..),+    calculateCPUPowerWatts,+    readCPUPowerInfo,+    getCPUPowerInfoChan,+    getCPUPowerInfoState,+  )+where++import Control.Concurrent (forkIO)+import Control.Concurrent.MVar (MVar, newMVar, readMVar, swapMVar)+import Control.Concurrent.STM.TChan (TChan, dupTChan, newBroadcastTChanIO, readTChan, writeTChan)+import Control.Exception (SomeException, try)+import Control.Monad (filterM, forever, void, when)+import Control.Monad.IO.Class (liftIO)+import Control.Monad.STM (atomically)+import qualified Data.ByteString.Char8 as BS8+import Data.List (isPrefixOf, sort)+import Data.Maybe (catMaybes, listToMaybe)+import GHC.Clock (getMonotonicTimeNSec)+import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)+import System.FilePath ((</>))+import System.Taffybar.Context (TaffyIO, getStateDefault)+import System.Taffybar.Information.Wakeup (getWakeupChannelForDelay)+import Text.Read (readMaybe)++data CPUPowerInfo = CPUPowerInfo+  { cpuPackagePowerWatts :: Maybe Double,+    cpuPowerDomainName :: Maybe String+  }+  deriving (Eq, Show)++data EnergySample = EnergySample+  { energySampleMicrojoules :: Integer,+    energySampleMonotonicNanoseconds :: Integer+  }+  deriving (Eq, Show)++data CPUPowerDomain = CPUPowerDomain+  { cpuPowerDomainPath :: FilePath,+    cpuPowerDomainLabel :: String,+    cpuPowerMaximumEnergyMicrojoules :: Integer+  }+  deriving (Eq, Show)++emptyCPUPowerInfo :: CPUPowerInfo+emptyCPUPowerInfo = CPUPowerInfo Nothing Nothing++-- | Calculate average package power between two energy samples. The maximum+-- energy range is used to account for the kernel counter wrapping around.+calculateCPUPowerWatts :: Integer -> EnergySample -> EnergySample -> Maybe Double+calculateCPUPowerWatts maximumEnergy previous current+  | elapsedNanoseconds <= 0 = Nothing+  | energyDelta < 0 = Nothing+  | otherwise =+      Just $+        fromIntegral energyDelta+          * 1_000+          / fromIntegral elapsedNanoseconds+  where+    previousEnergy = energySampleMicrojoules previous+    currentEnergy = energySampleMicrojoules current+    energyDelta+      | currentEnergy >= previousEnergy = currentEnergy - previousEnergy+      | maximumEnergy > previousEnergy = maximumEnergy - previousEnergy + currentEnergy+      | otherwise = -1+    elapsedNanoseconds =+      energySampleMonotonicNanoseconds current+        - energySampleMonotonicNanoseconds previous++-- | Take one package-power reading. The first reading has no wattage because+-- RAPL exposes accumulated energy; a second sample is needed to derive power.+readCPUPowerInfo :: IO CPUPowerInfo+readCPUPowerInfo = do+  maybeDomain <- discoverCPUPowerDomain+  pure $+    case maybeDomain of+      Nothing -> emptyCPUPowerInfo+      Just domain -> CPUPowerInfo Nothing (Just $ cpuPowerDomainLabel domain)++discoverCPUPowerDomain :: IO (Maybe CPUPowerDomain)+discoverCPUPowerDomain = do+  domains <- discoverDomainsInBases powercapBases+  pure $ listToMaybe domains+  where+    -- Prefer the MSR-backed package counter. The MMIO interface commonly+    -- exposes the same package and would otherwise double-count it.+    powercapBases =+      [ "/sys/devices/virtual/powercap/intel-rapl",+        "/sys/class/powercap"+      ]++discoverDomainsInBases :: [FilePath] -> IO [CPUPowerDomain]+discoverDomainsInBases [] = pure []+discoverDomainsInBases (base : rest) = do+  domains <- discoverDomainsInBase base+  if null domains then discoverDomainsInBases rest else pure domains++discoverDomainsInBase :: FilePath -> IO [CPUPowerDomain]+discoverDomainsInBase base = do+  exists <- doesDirectoryExist base+  if not exists+    then pure []+    else do+      entriesResult <- try $ listDirectory base+      case entriesResult :: Either SomeException [FilePath] of+        Left _ -> pure []+        Right entries -> do+          candidates <- filterM (doesFileExist . (</> "energy_uj")) $ map (base </>) $ sort entries+          catMaybes <$> mapM readPowerDomain candidates++readPowerDomain :: FilePath -> IO (Maybe CPUPowerDomain)+readPowerDomain path = do+  maybeName <- readStringFile $ path </> "name"+  maybeMaximum <- readIntegerFile $ path </> "max_energy_range_uj"+  pure $ case (maybeName, maybeMaximum) of+    (Just name, Just maximumEnergy)+      | "package-" `isPrefixOf` name ->+          Just $+            CPUPowerDomain+              { cpuPowerDomainPath = path,+                cpuPowerDomainLabel = name,+                cpuPowerMaximumEnergyMicrojoules = maximumEnergy+              }+    _ -> Nothing++readEnergySample :: CPUPowerDomain -> IO (Maybe EnergySample)+readEnergySample domain = do+  maybeEnergy <- readIntegerFile $ cpuPowerDomainPath domain </> "energy_uj"+  case maybeEnergy of+    Nothing -> pure Nothing+    Just energy -> do+      timestamp <- fromIntegral <$> getMonotonicTimeNSec+      pure $ Just $ EnergySample energy timestamp++readStringFile :: FilePath -> IO (Maybe String)+readStringFile path = do+  result <- try $ BS8.readFile path+  pure $ case result :: Either SomeException BS8.ByteString of+    Left _ -> Nothing+    Right contents -> Just $ BS8.unpack $ BS8.takeWhile (`notElem` ['\n', '\r']) contents++readIntegerFile :: FilePath -> IO (Maybe Integer)+readIntegerFile path = do+  maybeContents <- readStringFile path+  pure $ maybeContents >>= readMaybe++data CPUPowerSamplerState = CPUPowerSamplerState+  { samplerDomain :: Maybe CPUPowerDomain,+    samplerPreviousSample :: Maybe EnergySample+  }++newtype CPUPowerInfoChanVar+  = CPUPowerInfoChanVar (TChan CPUPowerInfo, MVar CPUPowerInfo)++-- | Return the process-wide package-power stream. The first caller's interval+-- wins; later widgets reuse the same sampler.+getCPUPowerInfoChan :: Double -> TaffyIO (TChan CPUPowerInfo)+getCPUPowerInfoChan interval = do+  CPUPowerInfoChanVar (chan, _) <- setupCPUPowerInfoChanVar interval+  pure chan++-- | Read the latest snapshot from the shared package-power sampler.+getCPUPowerInfoState :: Double -> TaffyIO CPUPowerInfo+getCPUPowerInfoState interval = do+  CPUPowerInfoChanVar (_, infoVar) <- setupCPUPowerInfoChanVar interval+  liftIO $ readMVar infoVar++setupCPUPowerInfoChanVar :: Double -> TaffyIO CPUPowerInfoChanVar+setupCPUPowerInfoChanVar interval = getStateDefault $ do+  wakeupChan <- getWakeupChannelForDelay $ max 0.000_001 interval+  ourWakeupChan <- liftIO $ atomically $ dupTChan wakeupChan+  liftIO $ do+    domain <- discoverCPUPowerDomain+    initialSample <- maybe (pure Nothing) readEnergySample domain+    let initialInfo = CPUPowerInfo Nothing (cpuPowerDomainLabel <$> domain)+        initialSamplerState = CPUPowerSamplerState domain initialSample+    chan <- newBroadcastTChanIO+    infoVar <- newMVar initialInfo+    samplerVar <- newMVar initialSamplerState+    void $ forkIO $ forever $ do+      void $ atomically $ readTChan ourWakeupChan+      oldSamplerState <- readMVar samplerVar+      (info, newSamplerState) <- sampleCPUPower oldSamplerState+      void $ swapMVar samplerVar newSamplerState+      oldInfo <- swapMVar infoVar info+      when (info /= oldInfo) $ atomically $ writeTChan chan info+    pure $ CPUPowerInfoChanVar (chan, infoVar)++sampleCPUPower :: CPUPowerSamplerState -> IO (CPUPowerInfo, CPUPowerSamplerState)+sampleCPUPower oldState = do+  domain <- maybe discoverCPUPowerDomain (pure . Just) $ samplerDomain oldState+  case domain of+    Nothing -> pure (emptyCPUPowerInfo, CPUPowerSamplerState Nothing Nothing)+    Just currentDomain -> do+      sample <- readEnergySample currentDomain+      case sample of+        Nothing ->+          pure+            ( CPUPowerInfo Nothing (Just $ cpuPowerDomainLabel currentDomain),+              CPUPowerSamplerState Nothing Nothing+            )+        Just currentSample -> do+          let watts =+                samplerPreviousSample oldState+                  >>= \previousSample ->+                    calculateCPUPowerWatts+                      (cpuPowerMaximumEnergyMicrojoules currentDomain)+                      previousSample+                      currentSample+          pure+            ( CPUPowerInfo watts (Just $ cpuPowerDomainLabel currentDomain),+              CPUPowerSamplerState (Just currentDomain) (Just currentSample)+            )
src/System/Taffybar/Information/DiskUsage.hs view
@@ -24,22 +24,24 @@ -- directly with 'pollingLabelNew'. module System.Taffybar.Information.DiskUsage   ( DiskUsageInfo (..),+    forceDiskUsageRefresh,     getDiskUsageInfo,     getDiskUsageInfoChan,     getDiskUsageInfoState,   ) where +import Control.Concurrent (forkIO) import Control.Concurrent.MVar import Control.Concurrent.STM.TChan import Control.Exception.Enclosed (catchAny)-import Control.Monad (void)+import Control.Monad (forever, void) import Control.Monad.IO.Class (liftIO)-import Control.Monad.STM (atomically)+import Control.Monad.STM (atomically, orElse) import System.DiskSpace (diskAvail, diskFree, diskTotal, getDiskUsage) import System.Log.Logger (Priority (..)) import System.Taffybar.Context (TaffyIO, getStateDefault)-import System.Taffybar.Information.Wakeup (taffyForeverWithDelay)+import System.Taffybar.Information.Wakeup (getWakeupChannelForDelay) import System.Taffybar.Util (logPrintF)  -- | Disk usage statistics for a single filesystem.@@ -89,37 +91,62 @@ -- Shared polling channel  newtype DiskUsageChanVar-  = DiskUsageChanVar (TChan DiskUsageInfo, MVar DiskUsageInfo)+  = DiskUsageChanVar+      ( TChan DiskUsageInfo,+        MVar DiskUsageInfo,+        TChan ()+      )  -- | Get a broadcast channel that is updated by a shared polling thread. -- The first call starts the poller; subsequent calls return the same channel. getDiskUsageInfoChan :: Double -> FilePath -> TaffyIO (TChan DiskUsageInfo) getDiskUsageInfoChan interval path = do-  DiskUsageChanVar (chan, _) <- setupDiskUsageChanVar interval path+  DiskUsageChanVar (chan, _, _) <- setupDiskUsageChanVar interval path   pure chan  -- | Read the latest cached 'DiskUsageInfo' from the shared poller. getDiskUsageInfoState :: Double -> FilePath -> TaffyIO DiskUsageInfo getDiskUsageInfoState interval path = do-  DiskUsageChanVar (_, var) <- setupDiskUsageChanVar interval path+  DiskUsageChanVar (_, var, _) <- setupDiskUsageChanVar interval path   liftIO $ readMVar var +-- | Request an immediate disk usage refresh.+forceDiskUsageRefresh :: Double -> FilePath -> TaffyIO ()+forceDiskUsageRefresh interval path = do+  DiskUsageChanVar (_, _, refreshChan) <- setupDiskUsageChanVar interval path+  liftIO $ atomically $ writeTChan refreshChan ()+ setupDiskUsageChanVar :: Double -> FilePath -> TaffyIO DiskUsageChanVar setupDiskUsageChanVar interval path = getStateDefault $ do   chan <- liftIO newBroadcastTChanIO+  refreshChan <- liftIO newTChanIO   info <- liftIO $ getDiskUsageInfo path   var <- liftIO $ newMVar info+  wakeupChan <- getWakeupChannelForDelay interval+  ourWakeupChan <- liftIO $ atomically $ dupTChan wakeupChan   void $-    taffyForeverWithDelay interval $-      liftIO $-        catchAny-          ( do-              newInfo <- getDiskUsageInfo path-              void $ swapMVar var newInfo-              atomically $ writeTChan chan newInfo-          )-          (logPrintF logName WARNING "DiskUsage poll failed: %s")-  pure $ DiskUsageChanVar (chan, var)+    liftIO $+      forkIO $+        forever $ do+          atomically $+            void (readTChan refreshChan)+              `orElse` void (readTChan ourWakeupChan)+          refreshDiskUsageState path chan var+  pure $ DiskUsageChanVar (chan, var, refreshChan)++refreshDiskUsageState ::+  FilePath ->+  TChan DiskUsageInfo ->+  MVar DiskUsageInfo ->+  IO ()+refreshDiskUsageState path chan var =+  catchAny+    ( do+        newInfo <- getDiskUsageInfo path+        void $ swapMVar var newInfo+        atomically $ writeTChan chan newInfo+    )+    (logPrintF logName WARNING "DiskUsage poll failed: %s")  logName :: String logName = "System.Taffybar.Information.DiskUsage"
src/System/Taffybar/SimpleConfig.hs view
@@ -18,6 +18,7 @@ -- offered in "System.Taffybar.Context". module System.Taffybar.SimpleConfig   ( SimpleTaffyConfig (..),+    SimpleMonitorConfig (..),     BarLevelConfig (..),     Position (..),     defaultSimpleTaffyConfig,@@ -25,6 +26,7 @@     simpleTaffybar,     toTaffyConfig,     toTaffybarConfig,+    toTaffybarConfigPerMonitor,     useAllMonitors,     usePrimaryMonitor,     withPriority,@@ -41,6 +43,7 @@ import Data.Default (Default (..)) import Data.List import Data.Maybe+import qualified Data.Text as T import Data.Unique import GI.Gdk import qualified GI.Gtk as Gtk@@ -49,7 +52,6 @@ import System.Taffybar import System.Taffybar.Context hiding (BarConfig (..), TaffybarConfig (..)) import qualified System.Taffybar.Context as BC (BarConfig (..), TaffybarConfig (..))-import System.Taffybar.Util import System.Taffybar.WidgetPriority (setWidgetPriority, withPriority)  -- | Size of a bar strut reservation. This is an alias for@@ -79,6 +81,8 @@ data SimpleTaffyConfig = SimpleTaffyConfig   { -- | The monitor number to put the bar on (default: 'usePrimaryMonitor')     monitorsAction :: TaffyIO [Int],+    -- | CSS classes applied to each bar window.+    barCssClasses :: [T.Text],     -- | Number of pixels to reserve for the bar (default: 30)     barHeight :: StrutSize,     -- | Number of additional pixels to reserve for the bar strut (default: 0)@@ -109,6 +113,7 @@ defaultSimpleTaffyConfig =   SimpleTaffyConfig     { monitorsAction = useAllMonitors,+      barCssClasses = [],       barHeight = ScreenRatio $ 1 / 27,       barPadding = 0,       barPosition = Top,@@ -153,6 +158,7 @@   return     BC.BarConfig       { BC.strutConfig = strutConfig,+        BC.barCssClasses = barCssClasses config,         BC.widgetSpacing = fromIntegral $ widgetSpacing config,         BC.startWidgets = startWidgets config,         BC.centerWidgets = centerWidgets config,@@ -161,8 +167,22 @@         BC.barId = barId       } -newtype SimpleBarConfigs = SimpleBarConfigs (MV.MVar [(Int, BC.BarConfig)])+-- | A monitor-specific simple configuration and the key that identifies it.+-- Returning the same key lets Taffybar reuse the existing bar. Changing the+-- key causes that monitor's bar to be rebuilt.+data SimpleMonitorConfig = SimpleMonitorConfig+  { simpleMonitorConfigKey :: T.Text,+    simpleMonitorConfig :: SimpleTaffyConfig+  } +data SimpleBarConfigEntry = SimpleBarConfigEntry+  { simpleBarConfigMonitor :: Int,+    simpleBarConfigKey :: T.Text,+    simpleBarConfigValue :: BC.BarConfig+  }++newtype SimpleBarConfigs = SimpleBarConfigs (MV.MVar [SimpleBarConfigEntry])+ {-# DEPRECATED toTaffyConfig "Use toTaffybarConfig instead" #-}  -- | Deprecated alias for 'toTaffybarConfig'.@@ -173,6 +193,19 @@ -- with 'startTaffybar' or 'dyreTaffybar'. toTaffybarConfig :: SimpleTaffyConfig -> BC.TaffybarConfig toTaffybarConfig conf =+  toTaffybarConfigPerMonitor conf $ \_ ->+    pure $ SimpleMonitorConfig T.empty conf++-- | Convert a simple configuration using a monitor-specific configuration+-- builder. The base configuration supplies monitor selection, CSS paths, and+-- the startup hook. Per-monitor configurations supply bar geometry, classes,+-- and widgets. Bars are reused while their key is unchanged and rebuilt when+-- it changes.+toTaffybarConfigPerMonitor ::+  SimpleTaffyConfig ->+  (Int -> TaffyIO SimpleMonitorConfig) ->+  BC.TaffybarConfig+toTaffybarConfigPerMonitor conf configForMonitor =   def     { BC.getBarConfigsParam = configGetter,       BC.cssPaths = cssPaths conf,@@ -181,22 +214,32 @@   where     configGetter = do       SimpleBarConfigs configsVar <--        getStateDefault $ lift (SimpleBarConfigs <$> MV.newMVar [])+        getStateDefault $ lift $ SimpleBarConfigs <$> MV.newMVar []       monitorNumbers <- monitorsAction conf--      let lookupWithIndex barConfigs monitorNumber =-            (monitorNumber, lookup monitorNumber barConfigs)--          lookupAndUpdate barConfigs = do-            let (alreadyPresent, toCreate) =-                  partition (isJust . snd) $-                    map (lookupWithIndex barConfigs) monitorNumbers-                alreadyPresentConfigs = mapMaybe snd alreadyPresent+      requestedConfigs <-+        forM monitorNumbers $ \monitorNumber -> do+          monitorConfig <- configForMonitor monitorNumber+          pure (monitorNumber, monitorConfig) -            newlyCreated <--              mapM (forkM return (toBarConfig conf) . fst) toCreate-            let result = map snd newlyCreated ++ alreadyPresentConfigs-            return (barConfigs ++ newlyCreated, result)+      let lookupAndUpdate existingEntries = do+            activeEntries <-+              forM requestedConfigs $ \(monitorNumber, monitorConfig) ->+                case find+                  ( \entry ->+                      simpleBarConfigMonitor entry == monitorNumber+                        && simpleBarConfigKey entry == simpleMonitorConfigKey monitorConfig+                  )+                  existingEntries of+                  Just entry -> pure entry+                  Nothing -> do+                    barConfig <- toBarConfig (simpleMonitorConfig monitorConfig) monitorNumber+                    pure $+                      SimpleBarConfigEntry+                        { simpleBarConfigMonitor = monitorNumber,+                          simpleBarConfigKey = simpleMonitorConfigKey monitorConfig,+                          simpleBarConfigValue = barConfig+                        }+            pure (activeEntries, map simpleBarConfigValue activeEntries)        lift $ MV.modifyMVar configsVar lookupAndUpdate 
src/System/Taffybar/Widget/AnthropicUsage.hs view
@@ -44,6 +44,7 @@   ( UsageWindowLabelParts (..),     UsageWindowPosition (..),     buildIconLabelBox,+    manageWidgetThreads,     widgetSetClassGI,   ) import Text.Printf (printf)@@ -213,7 +214,7 @@     _ <- widgetSetClassGI label (anthropicUsageLabelClass config)     updateLabelFromState config label displayState initialSnapshot -    void $ Gtk.onWidgetRealize label $ do+    manageWidgetThreads label $ do       -- The usage listener only runs while the label is realized (e.g. the       -- visible page of a GtkStack), so broadcasts that arrived while the       -- widget was hidden were missed. Re-sync from the shared state before@@ -223,9 +224,7 @@       updateLabelFromState config label displayState currentSnapshot       usageThread <- forkUsageListener config label displayState snapshotVar usageChan       modeThread <- forkDisplayModeListener config label displayState snapshotVar-      void $ Gtk.onWidgetUnrealize label $ do-        killThread usageThread-        killThread modeThread+      return [usageThread, modeThread]      Gtk.widgetShowAll label     widget <- Gtk.toWidget label
src/System/Taffybar/Widget/CPUFrequency.hs view
@@ -18,6 +18,7 @@ import Control.Monad.IO.Class (liftIO) import Data.Default (Default (..)) import Data.IORef (newIORef, readIORef, writeIORef)+import Data.Maybe (fromMaybe) import qualified Data.Text as T import qualified GI.Gtk as Gtk import System.Taffybar.Context (TaffyIO)@@ -82,7 +83,7 @@           printf             "CPU clock average: %.2f GHz\nRange: %.2f-%.2f GHz\n%d frequency policies sampled"             average-            (maybe average id $ cpuFrequencyMinimumGHz info)-            (maybe average id $ cpuFrequencyMaximumGHz info)+            (fromMaybe average $ cpuFrequencyMinimumGHz info)+            (fromMaybe average $ cpuFrequencyMaximumGHz info)             (cpuFrequencySampleCount info)       )
src/System/Taffybar/Widget/CPUMonitor.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}  -------------------------------------------------------------------------------- @@ -21,19 +22,30 @@ import Control.Monad (void) import Control.Monad.IO.Class (liftIO) import Data.IORef (atomicModifyIORef', newIORef)+import Data.Maybe (fromMaybe)+import qualified Data.Text as T import qualified GI.Gdk as Gdk import qualified GI.Gtk import System.Taffybar.Context (TaffyIO) import System.Taffybar.Information.CPU2   ( CPULoad (..),+    CPULoadSource,     acquireCPULoadFastRefresh,     cpuLoadSourceChannel,     forceCPULoadRefresh,     getCPULoadSource,   )+import System.Taffybar.Information.CPUPower+  ( CPUPowerInfo (..),+    getCPUPowerInfoChan,+    getCPUPowerInfoState,+  )+import System.Taffybar.Util (postGUIASync) import System.Taffybar.Widget.Generic.ChannelGraph+import System.Taffybar.Widget.Generic.ChannelWidget (channelWidgetNew) import System.Taffybar.Widget.Generic.Graph import System.Taffybar.Widget.Util (widgetSetClassGI)+import Text.Printf (printf)  -- | Creates a new CPU monitor. This is a channel-driven graph fed by CPU load -- samples for one core (or all cores when "cpu" is selected).@@ -66,36 +78,93 @@   source <- getCPULoadSource cpu interval hoverInterval   liftIO $ do     graphWidget <- channelGraphNew cfg (cpuLoadSourceChannel source) toSample-    eventBox <- GI.Gtk.eventBoxNew-    GI.Gtk.eventBoxSetVisibleWindow eventBox False-    GI.Gtk.eventBoxSetAboveChild eventBox True-    GI.Gtk.containerAdd eventBox graphWidget-    widget <- GI.Gtk.toWidget eventBox >>= (`widgetSetClassGI` "cpu-monitor")-    releaseRef <- newIORef Nothing+    addHoverRefresh source graphWidget -    let beginFastRefresh = do-          currentRelease <- atomicModifyIORef' releaseRef $ \current -> (current, current)-          case currentRelease of-            Just _ -> pure ()-            Nothing -> do-              release <- acquireCPULoadFastRefresh source-              previous <- atomicModifyIORef' releaseRef $ \current -> (Just release, current)-              maybe (pure ()) id previous+-- | Create a hover-responsive CPU graph with a live RAPL package-power label.+-- The power sampler is shared across all bars through the Taffybar context.+cpuMonitorNewWithHoverAndPower ::+  -- | Configuration data for the graph.+  GraphConfig ->+  -- | Normal CPU polling period, in seconds.+  Double ->+  -- | CPU polling period while hovered, in seconds.+  Double ->+  -- | Package-power polling period, in seconds.+  Double ->+  -- | Name of the core to watch (for example, @"cpu"@ or @"cpu0"@).+  String ->+  TaffyIO GI.Gtk.Widget+cpuMonitorNewWithHoverAndPower cfg interval hoverInterval powerInterval cpu = do+  source <- getCPULoadSource cpu interval hoverInterval+  powerChan <- getCPUPowerInfoChan powerInterval+  initialPower <- getCPUPowerInfoState powerInterval+  liftIO $ do+    graphWidget <- channelGraphNew (cfg {graphLabel = Nothing}) (cpuLoadSourceChannel source) toSample+    overlay <- GI.Gtk.overlayNew+    powerLabel <- GI.Gtk.labelNew Nothing+    void $ widgetSetClassGI powerLabel "graph-label"+    GI.Gtk.containerAdd overlay graphWidget+    GI.Gtk.overlayAddOverlay overlay powerLabel+    GI.Gtk.overlaySetOverlayPassThrough overlay powerLabel True -        endFastRefresh = do-          release <- atomicModifyIORef' releaseRef $ \current -> (Nothing, current)-          maybe (pure ()) id release+    let updatePowerLabel info = postGUIASync $ do+          GI.Gtk.labelSetMarkup powerLabel $ powerLabelMarkup cfg info+          GI.Gtk.widgetSetTooltipText overlay $ powerTooltip info -    GI.Gtk.widgetAddEvents-      widget-      [ Gdk.EventMaskEnterNotifyMask,-        Gdk.EventMaskLeaveNotifyMask-      ]-    void $ GI.Gtk.onWidgetEnterNotifyEvent widget $ \_ -> beginFastRefresh >> pure False-    void $ GI.Gtk.onWidgetLeaveNotifyEvent widget $ \_ -> endFastRefresh >> pure False-    void $ GI.Gtk.onWidgetRealize graphWidget $ forceCPULoadRefresh source-    void $ GI.Gtk.onWidgetUnrealize widget endFastRefresh-    pure widget+    void $ GI.Gtk.onWidgetRealize powerLabel $ updatePowerLabel initialPower+    powerWidget <- channelWidgetNew overlay powerChan updatePowerLabel+    GI.Gtk.toWidget powerWidget >>= addHoverRefresh source++addHoverRefresh ::+  CPULoadSource ->+  GI.Gtk.Widget ->+  IO GI.Gtk.Widget+addHoverRefresh source graphWidget = do+  eventBox <- GI.Gtk.eventBoxNew+  GI.Gtk.eventBoxSetVisibleWindow eventBox False+  GI.Gtk.eventBoxSetAboveChild eventBox True+  GI.Gtk.containerAdd eventBox graphWidget+  widget <- GI.Gtk.toWidget eventBox >>= (`widgetSetClassGI` "cpu-monitor")+  releaseRef <- newIORef Nothing++  let beginFastRefresh = do+        currentRelease <- atomicModifyIORef' releaseRef $ \current -> (current, current)+        case currentRelease of+          Just _ -> pure ()+          Nothing -> do+            release <- acquireCPULoadFastRefresh source+            previous <- atomicModifyIORef' releaseRef (Just release,)+            fromMaybe (pure ()) previous++      endFastRefresh = do+        release <- atomicModifyIORef' releaseRef (Nothing,)+        fromMaybe (pure ()) release++  GI.Gtk.widgetAddEvents+    widget+    [ Gdk.EventMaskEnterNotifyMask,+      Gdk.EventMaskLeaveNotifyMask+    ]+  void $ GI.Gtk.onWidgetEnterNotifyEvent widget $ \_ -> beginFastRefresh >> pure False+  void $ GI.Gtk.onWidgetLeaveNotifyEvent widget $ \_ -> endFastRefresh >> pure False+  void $ GI.Gtk.onWidgetRealize graphWidget $ forceCPULoadRefresh source+  void $ GI.Gtk.onWidgetUnrealize widget endFastRefresh+  pure widget++powerLabelMarkup :: GraphConfig -> CPUPowerInfo -> T.Text+powerLabelMarkup cfg info =+  T.intercalate "\n" $+    filter (not . T.null) [fromMaybe "" $ graphLabel cfg, powerText]+  where+    powerText = maybe "--W" (T.pack . printf "%.0fW") $ cpuPackagePowerWatts info++powerTooltip :: CPUPowerInfo -> Maybe T.Text+powerTooltip info =+  Just $+    maybe+      "CPU package power unavailable"+      (T.pack . printf "CPU package power: %.1f W")+      (cpuPackagePowerWatts info)  toSample :: CPULoad -> IO [Double] toSample CPULoad {cpuTotalLoad = totalLoad, cpuSystemLoad = systemLoad} =
src/System/Taffybar/Widget/CoordinatedClock.hs view
@@ -11,7 +11,7 @@   ) where -import Control.Concurrent (forkIO, killThread)+import Control.Concurrent (forkIO) import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TChan (dupTChan, readTChan) import Control.Exception.Enclosed (catchAny)@@ -94,12 +94,12 @@         _ <- widgetSetClassGI label "text-clock-label"         void $ refreshClockLabel label -        _ <- onWidgetRealize label $ do+        manageWidgetThreads label $ do           ourWakeupChan <- atomically $ dupTChan wakeupChan           threadId <- forkIO $ forever $ do             void $ atomically $ readTChan ourWakeupChan             void $ refreshClockLabel label-          void $ onWidgetUnrealize label $ killThread threadId+          return [threadId]          ebox <- eventBoxNew         _ <- widgetSetClassGI ebox "text-clock"
src/System/Taffybar/Widget/DiskUsage.hs view
@@ -34,8 +34,10 @@   ) where +import Control.Concurrent (forkIO) import Control.Monad (void) import Control.Monad.IO.Class+import Control.Monad.Trans.Reader (ask, runReaderT) import Data.Default (Default (..)) import qualified Data.Text as T import qualified GI.Gtk as Gtk@@ -83,24 +85,35 @@ -- | Create a disk usage label with the given configuration. diskUsageLabelNewWith :: DiskUsageWidgetConfig -> TaffyIO Gtk.Widget diskUsageLabelNewWith config = do+  (labelWidget, refreshNow) <- diskUsageLabelNewWithRefresh config+  liftIO $ wrapDiskUsageRefresh "disk-usage-label" labelWidget refreshNow++diskUsageLabelNewWithRefresh ::+  DiskUsageWidgetConfig ->+  TaffyIO (Gtk.Widget, IO ())+diskUsageLabelNewWithRefresh config = do   let path = diskUsagePath config       interval = diskUsagePollInterval config   chan <- getDiskUsageInfoChan interval path   initialInfo <- getDiskUsageInfoState interval path+  ctx <- ask    liftIO $ do     label <- Gtk.labelNew Nothing     _ <- widgetSetClassGI label "disk-usage-label" -    let updateLabel info = postGUIASync $ do+    let refreshNow = runReaderT (forceDiskUsageRefresh interval path) ctx+        updateLabel info = postGUIASync $ do           let (labelText, tooltipText) = formatDiskUsage config info           Gtk.labelSetText label labelText           Gtk.widgetSetTooltipText label tooltipText      void $ Gtk.onWidgetRealize label $ updateLabel initialInfo     Gtk.widgetShowAll label-    (Gtk.toWidget =<< channelWidgetNew label chan updateLabel)-      >>= (`widgetSetClassGI` "disk-usage-label")+    labelWidget <-+      (Gtk.toWidget =<< channelWidgetNew label chan updateLabel)+        >>= (`widgetSetClassGI` "disk-usage-label")+    pure (labelWidget, refreshNow)  -- | Create a disk usage icon widget with default configuration. diskUsageIconNew :: TaffyIO Gtk.Widget@@ -122,10 +135,28 @@ diskUsageNewWith :: DiskUsageWidgetConfig -> TaffyIO Gtk.Widget diskUsageNewWith config = do   iconWidget <- diskUsageIconNewWith config-  labelWidget <- diskUsageLabelNewWith config-  liftIO $-    buildIconLabelBox iconWidget labelWidget-      >>= (`widgetSetClassGI` "disk-usage")+  (labelWidget, refreshNow) <- diskUsageLabelNewWithRefresh config+  liftIO $ do+    widget <-+      buildIconLabelBox iconWidget labelWidget+        >>= (`widgetSetClassGI` "disk-usage")+    wrapDiskUsageRefresh "disk-usage" widget refreshNow++wrapDiskUsageRefresh ::+  T.Text ->+  Gtk.Widget ->+  IO () ->+  IO Gtk.Widget+wrapDiskUsageRefresh klass child refreshNow = do+  eventBox <- Gtk.eventBoxNew+  _ <- widgetSetClassGI eventBox klass+  Gtk.containerAdd eventBox child+  void $+    Gtk.onWidgetButtonPressEvent eventBox $ \_event -> do+      void $ forkIO refreshNow+      pure True+  Gtk.widgetShowAll eventBox+  Gtk.toWidget eventBox  -- -------------------------------------------------------------------------- -- Formatting
src/System/Taffybar/Widget/Generic/ChannelGraph.hs view
@@ -8,6 +8,7 @@ import Control.Monad.STM (atomically) import GI.Gtk import System.Taffybar.Widget.Generic.Graph+import System.Taffybar.Widget.Util (manageWidgetThreads)  -- | Given a broadcast 'TChan' and an action to consume that broadcast chan and -- turn it into graphable values, build a graph that will update as values are@@ -17,12 +18,12 @@   GraphConfig -> TChan a -> (a -> IO [Double]) -> m GI.Gtk.Widget channelGraphNew config chan sampleBuilder = do   (graphWidget, graphHandle) <- graphNew config-  _ <- onWidgetRealize graphWidget $ do+  liftIO $ manageWidgetThreads graphWidget $ do     ourChan <- atomically $ dupTChan chan     sampleThread <-       forkIO $         forever $           atomically (readTChan ourChan)             >>= (graphAddSample graphHandle <=< sampleBuilder)-    void $ onWidgetUnrealize graphWidget $ killThread sampleThread+    return [sampleThread]   return graphWidget
src/System/Taffybar/Widget/Generic/ChannelWidget.hs view
@@ -7,6 +7,7 @@ import Control.Monad.IO.Class import Control.Monad.STM (atomically) import GI.Gtk+import System.Taffybar.Widget.Util (manageWidgetThreads)  -- | Given a widget, a broadcast 'TChan' and a function that consumes the values -- yielded by the channel that is in 'IO', connect the function to the@@ -15,12 +16,12 @@   (MonadIO m, IsWidget w) =>   w -> TChan a -> (a -> IO ()) -> m w channelWidgetNew widget channel updateWidget = do-  void $ onWidgetRealize widget $ do+  liftIO $ manageWidgetThreads widget $ do     ourChan <- atomically $ dupTChan channel     processingThreadId <-       forkIO $         forever $           atomically (readTChan ourChan) >>= updateWidget-    void $ onWidgetUnrealize widget $ killThread processingThreadId+    return [processingThreadId]   widgetShowAll widget   return widget
src/System/Taffybar/Widget/Generic/Icon.hs view
@@ -8,7 +8,6 @@   ) where -import Control.Concurrent (killThread) import Control.Exception as E import Control.Monad (void) import Control.Monad.IO.Class (MonadIO, liftIO)@@ -18,6 +17,7 @@ import GI.Gtk import System.Taffybar.Context (TaffyIO) import System.Taffybar.Information.Wakeup (taffyForeverWithDelay)+import System.Taffybar.Widget.Util (manageWidgetThreads)  -- | Create a new widget that displays a static image --@@ -106,8 +106,8 @@ pollingIcon interval doUpdateName doInitImage doSetImage = do   context <- ask   image <- liftIO doInitImage-  liftIO $ do-    _ <- onWidgetRealize image $ do+  liftIO $+    manageWidgetThreads image $ do       sampleThread <-         runReaderT           ( taffyForeverWithDelay interval $@@ -120,8 +120,7 @@                   ignoreIOException           )           context-      void $ onWidgetUnrealize image $ killThread sampleThread-    return ()+      return [sampleThread]   liftIO $ putInBox image  putInBox :: (IsWidget child) => child -> IO Widget
src/System/Taffybar/Widget/Generic/PollingBar.hs view
@@ -15,7 +15,7 @@  import Control.Concurrent import Control.Exception.Enclosed (tryAny)-import Control.Monad (void)+import Control.Monad (forever) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Reader (ask, runReaderT) import Data.Foldable (traverse_)@@ -23,7 +23,7 @@ import System.Taffybar.Context (TaffyIO) import System.Taffybar.Information.Wakeup (taffyForeverWithDelay) import System.Taffybar.Widget.Generic.VerticalBar-import System.Taffybar.Widget.Util (backgroundLoop)+import System.Taffybar.Widget.Util (manageWidgetThreads)  -- | Construct a bar widget driven directly by a sample callback. verticalBarFromCallback ::@@ -31,9 +31,11 @@   BarConfig -> IO Double -> m GI.Gtk.Widget verticalBarFromCallback cfg action = liftIO $ do   (drawArea, h) <- verticalBarNew cfg-  _ <- GI.Gtk.onWidgetRealize drawArea $ backgroundLoop $ do-    esample <- tryAny action-    traverse (verticalBarSetPercent h) esample+  manageWidgetThreads drawArea $ do+    sampleThread <- forkIO $ forever $ do+      esample <- tryAny action+      traverse (verticalBarSetPercent h) esample+    return [sampleThread]   return drawArea  -- | Construct a polling bar with a fixed polling interval (seconds).@@ -43,8 +45,8 @@   context <- ask   (drawArea, h) <- verticalBarNew cfg -  liftIO $ do-    _ <- GI.Gtk.onWidgetRealize drawArea $ do+  liftIO $+    manageWidgetThreads drawArea $ do       sampleThread <-         runReaderT           ( taffyForeverWithDelay pollSeconds $@@ -53,7 +55,6 @@                 traverse_ (verticalBarSetPercent h) esample           )           context-      void $ GI.Gtk.onWidgetUnrealize drawArea $ killThread sampleThread-    return ()+      return [sampleThread]    return drawArea
src/System/Taffybar/Widget/Generic/PollingGraph.hs view
@@ -14,16 +14,16 @@   ) where -import Control.Concurrent import qualified Control.Exception.Enclosed as E-import Control.Monad import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Reader (ask, runReaderT) import qualified Data.Text as T import GI.Gtk import System.Taffybar.Context (TaffyIO) import System.Taffybar.Information.Wakeup (taffyForeverWithDelay)+import System.Taffybar.Util (postGUIASync) import System.Taffybar.Widget.Generic.Graph+import System.Taffybar.Widget.Util (manageWidgetThreads)  -- | Construct a polling graph whose callback also supplies tooltip text. pollingGraphNewWithTooltip ::@@ -32,8 +32,8 @@   context <- ask   (graphWidget, graphHandle) <- graphNew cfg -  liftIO $ do-    _ <- onWidgetRealize graphWidget $ do+  liftIO $+    manageWidgetThreads graphWidget $ do       sampleThread <-         runReaderT           ( taffyForeverWithDelay pollSeconds $@@ -43,11 +43,10 @@                   Left _ -> return ()                   Right (sample, tooltipStr) -> do                     graphAddSample graphHandle sample-                    widgetSetTooltipMarkup graphWidget tooltipStr+                    postGUIASync $ widgetSetTooltipMarkup graphWidget tooltipStr           )           context-      void $ onWidgetUnrealize graphWidget $ killThread sampleThread-    return ()+      return [sampleThread]    return graphWidget 
src/System/Taffybar/Widget/Generic/PollingLabel.hs view
@@ -123,12 +123,12 @@         updateLabelHandlingErrors =           E.tryAny action >>= either (const $ return 1) updateLabel -    _ <- onWidgetRealize label $ do+    manageWidgetThreads label $ do       sampleThread <-         foreverWithVariableDelayWithConfig           (pollingLabelVariableDelayConfig config)           updateLabelHandlingErrors-      void $ onWidgetUnrealize label $ killThread sampleThread+      return [sampleThread]      vFillCenter label     vFillCenter grid
src/System/Taffybar/Widget/MPRIS2.hs view
@@ -45,7 +45,7 @@ import qualified System.Taffybar.DBus.Client.MPRIS2 as MPRIS2DBus import System.Taffybar.Information.MPRIS2 import System.Taffybar.Util-import System.Taffybar.Widget.Generic.AutoSizeImage+import System.Taffybar.Widget.Generic.ScalingImage (scalingImage) import System.Taffybar.Widget.Util import System.Taffybar.WindowIcon import Text.Printf@@ -250,9 +250,12 @@   np@(Just NowPlaying {npBusName = busName}) = do     ctx <- ask     client <- asks sessionDBusClient+    (image, _) <-+      scalingImage+        (fmap Just . loadIconAtSize client busName)+        Gtk.OrientationHorizontal     lift $ do       mprisLog DEBUG "Building widget for %s" busName-      image <- autoSizeImageNew (loadIconAtSize client busName) Gtk.OrientationHorizontal       playerBox <- Gtk.gridNew       label <- Gtk.labelNew Nothing       setupPlayerLabel c label@@ -316,9 +319,12 @@   np@(Just nowPlaying@NowPlaying {npBusName = busName}) = do     ctx <- ask     client <- asks sessionDBusClient+    (image, _) <-+      scalingImage+        (fmap Just . loadIconAtSize client busName)+        Gtk.OrientationHorizontal     lift $ do       mprisLog DEBUG "Building widget for %s" busName-      image <- autoSizeImageNew (loadIconAtSize client busName) Gtk.OrientationHorizontal       playerBox <- Gtk.boxNew Gtk.OrientationHorizontal 0       clickArea <- Gtk.boxNew Gtk.OrientationHorizontal 0       controlsBox <- Gtk.boxNew Gtk.OrientationHorizontal 0@@ -476,15 +482,17 @@             busName <- parseBusName name             when (busName `M.member` playerWidgets) doUpdate -      _ <- Gtk.onWidgetRealize grid $ do-        updateHandler <--          DBus.registerForPropertiesChanged client propMatcher signalCallback-        nameHandler <--          DBus.registerForNameOwnerChanged client matchAny handleNameOwnerChanged-        doUpdate-        void $-          Gtk.onWidgetUnrealize grid $-            removeMatch client updateHandler >> removeMatch client nameHandler+      manageWidgetResource+        grid+        ( do+            updateHandler <-+              DBus.registerForPropertiesChanged client propMatcher signalCallback+            nameHandler <-+              DBus.registerForNameOwnerChanged client matchAny handleNameOwnerChanged+            doUpdate+            return (updateHandler, nameHandler)+        )+        (\(updateHandler, nameHandler) -> removeMatch client updateHandler >> removeMatch client nameHandler)        Gtk.widgetShow grid       setPlayingClass
src/System/Taffybar/Widget/OpenAIUsage.hs view
@@ -25,7 +25,7 @@   ) where -import Control.Concurrent (ThreadId, forkIO, killThread)+import Control.Concurrent (ThreadId, forkIO) import Control.Concurrent.MVar (MVar, newMVar, readMVar, swapMVar) import Control.Concurrent.STM (atomically, newTVarIO, readTVarIO, swapTVar) import Control.Concurrent.STM.TChan (TChan, dupTChan, newBroadcastTChanIO, readTChan, writeTChan)@@ -44,6 +44,7 @@   ( UsageWindowLabelParts (..),     UsageWindowPosition (..),     buildIconLabelBox,+    manageWidgetThreads,     widgetSetClassGI,   ) import Text.Printf (printf)@@ -205,12 +206,10 @@     _ <- widgetSetClassGI label (openAIUsageLabelClass config)     updateLabelFromState config label displayState initialSnapshot -    void $ Gtk.onWidgetRealize label $ do+    manageWidgetThreads label $ do       usageThread <- forkUsageListener config label displayState snapshotVar usageChan       modeThread <- forkDisplayModeListener config label displayState snapshotVar-      void $ Gtk.onWidgetUnrealize label $ do-        killThread usageThread-        killThread modeThread+      return [usageThread, modeThread]      Gtk.widgetShowAll label     widget <- Gtk.toWidget label@@ -262,13 +261,13 @@         -- every usage snapshot.         Gtk.widgetDestroy oldMenu -  void $ Gtk.onWidgetRealize ebox $ do+  manageWidgetThreads ebox $ do     menuThread <-       forkCachedMenuUpdater         rebuildMenu         (openAIUsageLabelSnapshotVar parts)         (openAIUsageLabelUsageChan parts)-    void $ Gtk.onWidgetUnrealize ebox $ killThread menuThread+    return [menuThread]    void $     Gtk.onWidgetButtonPressEvent ebox $ \_event -> do
src/System/Taffybar/Widget/Privacy.hs view
@@ -51,7 +51,7 @@  import Control.Concurrent (forkIO) import Control.Concurrent.STM.TChan-import Control.Monad (void, when)+import Control.Monad (when) import Control.Monad.IO.Class (liftIO) import Control.Monad.STM (atomically) import Data.Default (Default (..))@@ -71,7 +71,7 @@     getPrivacyInfoState,   ) import System.Taffybar.Util (postGUIASync)-import System.Taffybar.Widget.Util (widgetSetClassGI)+import System.Taffybar.Widget.Util (manageWidgetThreads, widgetSetClassGI)  -- | Configuration for the privacy widget. data PrivacyWidgetConfig = PrivacyWidgetConfig@@ -165,16 +165,14 @@     updateWidget initialInfo      -- Connect to channel updates-    void $ Gtk.onWidgetRealize revealer $ do+    manageWidgetThreads revealer $ do       ourChan <- atomically $ dupTChan chan-      void $ Gtk.onWidgetUnrealize revealer $ return ()-      -- Start update thread       let loop = do             info <- atomically $ readTChan ourChan             updateWidget info             loop-      _ <- forkIO loop-      return ()+      listenerThread <- forkIO loop+      return [listenerThread]      Gtk.widgetShowAll revealer     Gtk.toWidget revealer
src/System/Taffybar/Widget/Util.hs view
@@ -18,7 +18,7 @@ -- Utility functions to facilitate building GTK interfaces. module System.Taffybar.Widget.Util where -import Control.Concurrent (forkIO)+import Control.Concurrent (ThreadId, forkIO, killThread) import qualified Control.Concurrent.MVar as MV import Control.Exception.Enclosed (catchAny) import Control.Monad@@ -248,6 +248,35 @@ -- | Run an action forever on a background thread. backgroundLoop :: IO a -> IO () backgroundLoop = void . forkIO . forever++-- | Acquire a resource whenever a widget is realized and release it when the+-- widget is unrealized or destroyed. At most one resource is retained.+manageWidgetResource ::+  (Gtk.IsWidget widget) =>+  widget ->+  IO resource ->+  (resource -> IO ()) ->+  IO ()+manageWidgetResource widget acquire release = do+  resourceVar <- MV.newMVar Nothing+  let releaseCurrent =+        MV.swapMVar resourceVar Nothing >>= mapM_ release+      acquireCurrent = do+        releaseCurrent+        resource <- acquire+        MV.swapMVar resourceVar (Just resource) >>= mapM_ release+  void $ Gtk.onWidgetRealize widget acquireCurrent+  void $ Gtk.onWidgetUnrealize widget releaseCurrent+  void $ Gtk.onWidgetDestroy widget releaseCurrent++-- | Keep worker threads scoped to a widget's realized lifetime.+manageWidgetThreads ::+  (Gtk.IsWidget widget) =>+  widget ->+  IO [ThreadId] ->+  IO ()+manageWidgetThreads widget startThreads =+  manageWidgetResource widget startThreads (mapM_ killThread)  -- | Register an action on widget realization and return the widget. drawOn :: (Gtk.IsWidget object) => object -> IO () -> IO object
src/System/Taffybar/Widget/WakeupDebug.hs view
@@ -9,10 +9,10 @@   ) where -import Control.Concurrent (forkIO, killThread, threadDelay)+import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TChan (TChan, readTChan)-import Control.Monad (forever, void, when)+import Control.Monad (forever, when) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Reader (ask, runReaderT) import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef)@@ -30,7 +30,7 @@     getWakeupSchedulerEvents,   ) import System.Taffybar.Util (postGUIASync)-import System.Taffybar.Widget.Util (onClick, widgetSetClassGI)+import System.Taffybar.Widget.Util (manageWidgetThreads, onClick, widgetSetClassGI)  -- | Configuration for 'wakeupDebugWidgetNewWithConfig'. data WakeupDebugWidgetConfig = WakeupDebugWidgetConfig@@ -96,9 +96,9 @@                in (nextState, nextState)           refreshWidget stateRef label eventBox -    _ <- Gtk.onWidgetRealize eventBox $ do+    manageWidgetThreads eventBox $ do       listenerThread <- forkIO $ wakeupDebugLoop schedulerEvents refreshRegisteredIntervals stateRef label eventBox flashTokenRef config-      void $ Gtk.onWidgetUnrealize eventBox $ killThread listenerThread+      return [listenerThread]      Gtk.widgetShowAll eventBox     Gtk.toWidget eventBox
taffybar.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: taffybar-version: 7.2.7+version: 7.3.0 synopsis: A desktop bar similar to xmobar, but with more GUI description: Taffybar is a desktop status bar with GTK widgets for window   manager state, system information, tray icons, and custom user modules.@@ -153,6 +153,7 @@                  , System.Taffybar.Information.Bluetooth                  , System.Taffybar.Information.CPU2                  , System.Taffybar.Information.CPUFrequency+                 , System.Taffybar.Information.CPUPower                  , System.Taffybar.Information.ChromeWindowInfo                  , System.Taffybar.Information.Crypto                  , System.Taffybar.Information.DiskIO@@ -402,6 +403,7 @@                , System.Taffybar.ContextSpec                , System.Taffybar.Information.ASUSSpec                , System.Taffybar.Information.CPUFrequencySpec+               , System.Taffybar.Information.CPUPowerSpec                , System.Taffybar.Information.CryptoSpec                , System.Taffybar.Information.LayoutSpec                , System.Taffybar.Information.NvidiaSpec@@ -427,6 +429,7 @@                , filepath                , text                , gi-gtk3+               , gtk-strut                , hspec                , hspec-core                , hspec-golden
test/unit/System/Taffybar/ContextSpec.hs view
@@ -283,6 +283,7 @@ toSimpleConfig GenSimpleConfig {..} =   SimpleTaffyConfig     { monitorsAction = toMonitorsAction monitors,+      barCssClasses = [],       barHeight = size,       barPadding = unGenSpace padding,       barPosition = position,
+ test/unit/System/Taffybar/Information/CPUPowerSpec.hs view
@@ -0,0 +1,22 @@+module System.Taffybar.Information.CPUPowerSpec (spec) where++import System.Taffybar.Information.CPUPower+import Test.Hspec++spec :: Spec+spec =+  describe "CPU package power calculation" $ do+    it "converts an energy delta over time to watts" $ do+      let previous = EnergySample 10_000_000 1_000_000_000+          current = EnergySample 55_000_000 2_000_000_000+      calculateCPUPowerWatts 100_000_000 previous current `shouldBe` Just 45++    it "accounts for a wrapping energy counter" $ do+      let previous = EnergySample 95_000_000 1_000_000_000+          current = EnergySample 5_000_000 2_000_000_000+      calculateCPUPowerWatts 100_000_000 previous current `shouldBe` Just 10++    it "rejects samples without elapsed time" $ do+      let previous = EnergySample 10_000_000 1_000_000_000+          current = EnergySample 20_000_000 1_000_000_000+      calculateCPUPowerWatts 100_000_000 previous current `shouldBe` Nothing
test/unit/System/Taffybar/SimpleConfigSpec.hs view
@@ -2,10 +2,18 @@  module System.Taffybar.SimpleConfigSpec (spec) where -import Data.Maybe (maybeToList)+import Control.Monad.IO.Class (liftIO)+import Control.Monad.Trans.Reader (runReaderT)+import Data.Default (def)+import Data.IORef+import Data.Maybe (fromMaybe, maybeToList)+import Data.Text qualified as T+import Graphics.UI.GIGtkStrut (strutYPadding)+import System.Taffybar.Context qualified as BC import System.Taffybar.ContextSpec (runTaffyDefault) import System.Taffybar.Information.X11DesktopInfo (DisplayName (..)) import System.Taffybar.SimpleConfig+import System.Taffybar.Test.DBusSpec (withTestDBus) import System.Taffybar.Test.XvfbSpec (RROutput (..), RROutputSettings (..), RRSetup (..), setDefaultDisplay_, withRandrSetup, withXdummy) import Test.Hspec import Test.Hspec.QuickCheck@@ -13,10 +21,71 @@ import Test.QuickCheck.Monadic  spec :: Spec-spec = aroundAll_ (withXdummy . flip setDefaultDisplay_) $ do-  -- Pending: Can't run properties without cleaning up buildContext-  xprop "useAllMonitors" prop_useAllMonitors-  xprop "usePrimaryMonitor" prop_usePrimaryMonitor+spec = do+  aroundAll_ withTestDBus $ describe "toTaffybarConfigPerMonitor" $ do+    it "builds distinct monitor geometry and CSS classes" $ do+      let base = defaultSimpleTaffyConfig {monitorsAction = pure [0, 1]}+          config =+            toTaffybarConfigPerMonitor base $ \monitorNumber ->+              pure $+                SimpleMonitorConfig+                  (T.pack $ show monitorNumber)+                  base+                    { barPadding = monitorNumber,+                      barCssClasses = [T.pack $ "monitor-" <> show monitorNumber]+                    }+      barConfigs <- runTaffyWayland $ BC.getBarConfigsParam config+      map BC.barCssClasses barConfigs `shouldBe` [["monitor-0"], ["monitor-1"]]+      map (strutYPadding . BC.strutConfig) barConfigs `shouldBe` [0, 1]++    it "reuses bars while monitor configuration keys are unchanged" $ do+      let base = defaultSimpleTaffyConfig {monitorsAction = pure [0, 1]}+          config =+            toTaffybarConfigPerMonitor base $ \monitorNumber ->+              pure $ SimpleMonitorConfig (T.pack $ show monitorNumber) base+      (first, second) <- runTaffyWayland $ do+        first <- BC.getBarConfigsParam config+        second <- BC.getBarConfigsParam config+        pure (first, second)+      map BC.barId second == map BC.barId first `shouldBe` True++    it "rebuilds only the monitor whose configuration key changed" $ do+      keysRef <- newIORef [(0, "zero"), (1, "one")]+      let base = defaultSimpleTaffyConfig {monitorsAction = pure [0, 1]}+          monitorConfig monitorNumber = do+            keys <- liftIO $ readIORef keysRef+            pure $ SimpleMonitorConfig (fromMaybe "" $ lookup monitorNumber keys) base+          config = toTaffybarConfigPerMonitor base monitorConfig+      (first, second) <- runTaffyWayland $ do+        first <- BC.getBarConfigsParam config+        liftIO $ writeIORef keysRef [(0, "changed"), (1, "one")]+        second <- BC.getBarConfigsParam config+        pure (first, second)+      zipWith (/=) (map BC.barId second) (map BC.barId first) `shouldBe` [True, False]++    it "purges removed monitors from the bar cache" $ do+      monitorsRef <- newIORef [0, 1]+      let base = defaultSimpleTaffyConfig {monitorsAction = liftIO $ readIORef monitorsRef}+          config =+            toTaffybarConfigPerMonitor base $ \monitorNumber ->+              pure $ SimpleMonitorConfig (T.pack $ show monitorNumber) base+      (first, third) <- runTaffyWayland $ do+        first <- BC.getBarConfigsParam config+        liftIO $ writeIORef monitorsRef [0]+        _ <- BC.getBarConfigsParam config+        liftIO $ writeIORef monitorsRef [0, 1]+        third <- BC.getBarConfigsParam config+        pure (first, third)+      zipWith (/=) (map BC.barId third) (map BC.barId first) `shouldBe` [False, True]++  aroundAll_ (withXdummy . flip setDefaultDisplay_) $ do+    -- Pending: Can't run properties without cleaning up buildContext+    xprop "useAllMonitors" prop_useAllMonitors+    xprop "usePrimaryMonitor" prop_usePrimaryMonitor++runTaffyWayland :: BC.TaffyIO a -> IO a+runTaffyWayland action =+  BC.buildContextWithBackend BC.BackendWayland def >>= runReaderT action  prop_useAllMonitors :: RRSetup -> Property prop_useAllMonitors rr = monadicIO $ do