SpotlightDimmer
A utility that creates semi-transparent overlays to dim inactive displays or regions, creating a "spotlight" effect on the active window. Available for Windows (built with .NET 10 and native Windows APIs) and Linux/Wayland (a shared Rust daemon with adapters for GNOME Shell and KDE Plasma 6).
Features
✅ Multi-monitor support - Automatically detects all connected monitors, including hot-plug
✅ Three dimming modes - FullScreen, Partial, and PartialWithActive on all platforms
✅ Configurable overlays - Customize colors and opacity for both inactive and active regions independently
✅ Click-through overlays - Overlays don't capture mouse input
✅ 100% event-driven - Uses window-manager events instead of polling for zero CPU usage when idle
✅ No admin privileges - Runs as a regular user process
✅ Hot-reloadable configuration - Changes apply instantly without restart
✅ Toggle shortcut - Pause/resume all dimming with a keyboard shortcut
✅ tmux pane spotlight (Linux) - Optionally follow the focused tmux pane inside WezTerm instead of the whole terminal window
Windows-specific:
✅ Multiple rendering backends - Choose between:
- LayeredWindow: Extremely lightweight (< 10MB RAM usage)
- CompositeOverlay: Better visual quality during window dragging (~50MB RAM for dual monitor setup with partial overlays)
✅ Small footprint - Less than 50MB installed, installer under 10MB
✅ Native AOT compilation - Fast startup and minimal runtime dependencies
Installation
Windows
Install via winget:
winget install ThomazMoura.SpotlightDimmer
Or download the installer from the latest GitHub release.
To build from source instead, see Building below.
Linux (Ubuntu / Kubuntu, Wayland)
The Linux version consists of a shared daemon (spotlight-dimmer-daemon) plus a thin adapter for your desktop: a GNOME Shell extension on Ubuntu, or a KWin script on Kubuntu. See docs/LINUX_DAEMON.md for architecture details.
Requirements: a Wayland session (the default on recent Ubuntu and Kubuntu) with GNOME Shell 45–48 (Ubuntu 24.04 or newer) or KDE Plasma 6 (Kubuntu 24.10 or newer).
One-line install or update (recommended)
curl -fsSL https://raw.githubusercontent.com/thomazmoura/spotlight-dimmer/main/SpotlightDimmer.LinuxDaemon/tools/install-release.sh | bash
The script checks whether you're on Ubuntu (GNOME) or Kubuntu (KDE Plasma 6) and on amd64 or arm64. It downloads that desktop's package and the settings window from the latest -linux release, then installs them with apt. Run it again later to update: packages that are already current are skipped. It also seeds config.json when there is none, restarts the daemon and reloads the KWin script. On KDE it binds the default shortcuts if they're unset. On GNOME it enables the extension (log out and back in afterwards). Options: --gnome/--kde to skip detection, --version X.Y.Z, --no-config-gui, --force, --clean-source-install (see below), -y. Run it with --help for the full list.
.deb packages (manual)
Download the package for your desktop and architecture from the newest Linux release on the releases page (Linux release tags end in -linux), then install it with apt so runtime dependencies are resolved automatically:
# Ubuntu (GNOME) — use the _arm64.deb files on ARM devices
sudo apt install ./spotlight-dimmer-gnome__amd64.deb
# Kubuntu (KDE Plasma 6)
sudo apt install ./spotlight-dimmer-kde__amd64.deb
The two packages intentionally conflict with each other — install the one matching your desktop.
Optionally add the settings window, which works on both desktops and installs alongside either package:
sudo apt install ./spotlight-dimmer-config__amd64.deb
It edits the same config.json with a live preview — see docs/LINUX_CONFIG_GUI.md.
On GNOME, log out and back in (Wayland cannot reload GNOME Shell in place), then enable the extension:
gnome-extensions enable spotlightdimmer@thomazmoura.github.io
On KDE, KWin loads the script automatically — no logout needed. To get the dimming toggle shortcut, open System Settings → Shortcuts, add the "SpotlightDimmer Toggle" application and bind it to Meta+Shift+D.
First run: packages cannot write to your home directory, so copy the starter configuration once (dimming uses FullScreen mode otherwise, which shows nothing on a single monitor):
mkdir -p ~/.config/SpotlightDimmer
cp /usr/share/doc/spotlight-dimmer-gnome/examples/config.example.json ~/.config/SpotlightDimmer/config.json
(Use spotlight-dimmer-kde in the path if you installed the KDE package. The tmux integration tools land in /usr/share/spotlight-dimmer/tools/. If you installed spotlight-dimmer-config, you can skip this step and set everything up in the settings window instead.)
To uninstall: sudo apt remove spotlight-dimmer-gnome (or spotlight-dimmer-kde, plus spotlight-dimmer-config if installed).
> Upgrading from a source install? The per-user files from make install-linux-* shadow the packaged ones — a leftover unit in ~/.config/systemd/user/ wins over /usr/lib/systemd/user/, and the extension or KWin script in ~/.local/share wins over the one in /usr/share — so the old build keeps running. Run the one-line installer with --clean-source-install to remove them after the packages are installed, or remove them by hand (see Uninstalling). The tmux tools in ~/.config/SpotlightDimmer/tools/ are left in place.
Installing from source
Building from source additionally requires Rust via rustup. Clone the repository first (the make commands below run from the repository root):
git clone https://github.com/thomazmoura/spotlight-dimmer.git
cd spotlight-dimmer
Ubuntu (GNOME)
On GNOME the daemon computes the overlays and the GNOME Shell extension renders them, so the GTK build dependencies are not needed:
make install-linux-gnome FEATURES=--no-default-features
Then log out and back in (Wayland cannot reload GNOME Shell in place) and enable the extension:
gnome-extensions enable spotlightdimmer@thomazmoura.github.io
Dimming starts as soon as the extension is enabled — the daemon is D-Bus activated automatically; there is nothing to start manually. Toggle dimming on/off with Super+Shift+D (the choice is remembered across restarts); Super+Alt+Shift+D opens or closes the settings window.
> If you also want the daemon-side layer-shell renderer built (not used by GNOME), run sudo apt install libgtk-4-dev libgtk4-layer-shell-dev and use plain make install-linux-gnome.
>
> The GTK4 settings window (spotlight-dimmer-config) is skipped with a warning when libgtk-4-dev is missing; install it and run make install-config-gui to add the window later.
Kubuntu (KDE Plasma 6)
On KDE the daemon renders the overlays itself via the layer-shell protocol, so the GTK development packages are required:
sudo apt install libgtk-4-dev libgtk4-layer-shell-dev
make install-linux-kde
This builds and installs the daemon, installs and enables the KWin script (which feeds focus and monitor events to the daemon), and binds Meta+Shift+D to the dimming toggle and Meta+Alt+Shift+D to open or close the settings window. Everything takes effect immediately — no logout needed. If the shortcut doesn't fire right away, log out/in once or rebind it in System Settings → Shortcuts.
First run and troubleshooting (Linux)
The source-install targets seed a starter configuration at ~/.config/SpotlightDimmer/config.json (PartialWithActive mode) if none exists — edits to it apply instantly (for .deb installs, copy it manually as shown above). Note that FullScreen mode only dims inactive monitors, so on a single-monitor setup it shows nothing; use Partial or PartialWithActive there.
The daemon runs as a systemd user service, so status and logs need the --user flag:
systemctl --user status spotlight-dimmer-daemon
journalctl --user -u spotlight-dimmer-daemon -f
On KDE, qdbus6 org.kde.KWin /Scripting org.kde.kwin.Scripting.isScriptLoaded spotlightdimmer confirms the adapter script is loaded.
Optional: tmux pane spotlight
Source installs copy the tmux integration tools to ~/.config/SpotlightDimmer/tools/; the .deb packages ship them at /usr/share/spotlight-dimmer/tools/ (adjust the paths below accordingly). To have the spotlight follow the focused tmux pane inside WezTerm or Ghostty, two pieces of configuration are needed:
- Map the terminal to the tmux provider by adding an
AppIntegrations section to ~/.config/SpotlightDimmer/config.json (the starter config doesn't include it):
"AppIntegrations": [
{
"WmClass": "org.wezfurlong.wezterm",
"Provider": "tmux",
"ContentOffsetX": 0,
"ContentOffsetY": 0
},
{
"WmClass": "com.mitchellh.ghostty",
"Provider": "tmux",
"TtySource": "title",
"ContentOffsetX": 2,
"ContentOffsetY": 2
}
]
Ghostty has no CLI to ask which pane is focused, so "TtySource": "title" reads the tty that tmux publishes in the window title — which also requires uncommenting the Ghostty block in spotlight-dimmer.tmux.conf.
- Load the reporting hooks by adding to your
~/.tmux.conf (then reload with tmux source-file ~/.tmux.conf):
source-file ~/.config/SpotlightDimmer/tools/spotlight-dimmer.tmux.conf
Without both, dimming falls back to highlighting the whole terminal window. tmux popups (display-popup) need a third, per-binding opt-in — hooks cannot see them — by wrapping the popup's command in spotlight-dimmer-tmux-popup.sh. See docs/TMUX_INTEGRATION.md for the full setup guide, including popups and the content offsets for terminal padding and tab bars.
Running neovim inside tmux? The optional plugin in SpotlightDimmer.NeovimPlugin/ narrows the spotlight further, down to the focused neovim split. See Neovim splits.
Uninstalling (Linux, source installs)
For .deb installs, use sudo apt remove spotlight-dimmer-gnome (or spotlight-dimmer-kde) instead. The commands below undo a make install-linux-* source install:
# Daemon
systemctl --user disable --now spotlight-dimmer-daemon.service 2>/dev/null
rm ~/.local/bin/spotlight-dimmer-daemon
rm ~/.config/systemd/user/spotlight-dimmer-daemon.service
rm ~/.local/share/dbus-1/services/org.spotlightdimmer.Daemon.service
systemctl --user daemon-reload
# Settings window (if installed)
rm ~/.local/bin/spotlight-dimmer-config
rm ~/.local/share/applications/org.spotlightdimmer.Config.desktop
rm ~/.local/share/applications/org.spotlightdimmer.ConfigToggle.desktop
rm ~/.local/share/icons/hicolor/scalable/apps/org.spotlightdimmer.Config.svg
# tmux tools and neovim plugin copy. Remove or repoint the source-file line
# in ~/.tmux.conf first, or tmux reports an error at startup
rm -r ~/.config/SpotlightDimmer/tools
# GNOME extension
gnome-extensions disable spotlightdimmer@thomazmoura.github.io
rm -r ~/.local/share/gnome-shell/extensions/spotlightdimmer@thomazmoura.github.io
# KDE script and shortcut launcher
kpackagetool6 --type KWin/Script --remove spotlightdimmer
rm ~/.local/share/applications/org.spotlightdimmer.toggle.desktop
update-desktop-database ~/.local/share/applications 2>/dev/null
~/.config/SpotlightDimmer/config.json is left in place; the .deb packages read the same file. If you are switching to the packages, your KDE shortcut bindings carry over too, because the packaged launchers have the same names. To move the tmux integration over, point ~/.tmux.conf at /usr/share/spotlight-dimmer/tools/spotlight-dimmer.tmux.conf.
How It Works
Windows
The application uses a dual event hook system for comprehensive window tracking:
Event Hooks (100% Event-Driven - No Polling!)
- EVENT_SYSTEM_FOREGROUND - Instant detection when switching between applications
- EVENT_OBJECT_LOCATIONCHANGE - Real-time detection of window movement:
- Detects windows being dragged between monitors with the mouse
- Detects Win+Arrow and Win+Shift+Arrow keyboard shortcuts
- Filters out cursor/caret events using
OBJID_WINDOW check
Window Management APIs
- EnumDisplayMonitors - Detects all connected monitors
- CreateWindowEx - Creates overlay windows with layered and transparent styles
- SetLayeredWindowAttributes - Sets the semi-transparent appearance with configurable opacity
Linux
Wayland's security model only lets compositor-side components observe other windows, so a small adapter runs inside the compositor and reports focus, geometry, and monitor changes to the shared daemon over D-Bus:
- GNOME: the Shell extension reports events and renders the overlays (GNOME doesn't support the layer-shell protocol), receiving computed overlay rectangles back from the daemon.
- KDE Plasma 6: the KWin script only reports events; the daemon renders click-through overlays itself via
gtk4-layer-shell.
The daemon is D-Bus activated and supervised by a systemd user unit, and everything is event-driven — no polling on any platform. See docs/LINUX_DAEMON.md for the full architecture and D-Bus contract.
Configuration
All platforms share the same JSON configuration schema, hot-reloaded on change:
- Windows:
%AppData%\SpotlightDimmer\config.json
- Linux:
~/.config/SpotlightDimmer/config.json
See CONFIGURATION.md for detailed configuration options.
Building
Windows
# Regular build
dotnet build
# Run
dotnet run
# AOT build (requires Visual Studio C++ tools)
dotnet publish -c Release -r win-x64
Linux
From SpotlightDimmer.LinuxDaemon/:
# Full build (requires libgtk-4-dev libgtk4-layer-shell-dev)
cargo build --release
# Headless build without GTK (no layer-shell renderer; enough for GNOME)
cargo build --release --no-default-features
# Core unit tests
cargo test -p spotlight-dimmer-core
Architecture
Windows (SpotlightDimmer.WindowsClient)
- WinApi.cs - P/Invoke declarations for Windows APIs
- MonitorManager.cs - Multi-monitor detection and management
- OverlayWindow.cs - Semi-transparent, click-through overlay windows
- FocusTracker.cs - Event-driven focus tracking using Windows hooks
- Program.cs - Main application logic and message loop
Linux
- SpotlightDimmer.LinuxDaemon/core - Pure Rust crate: overlay calculator, config parsing, state
- SpotlightDimmer.LinuxDaemon/daemon - Daemon binary: D-Bus services, event loop, tmux/wezterm integration, layer-shell renderer
- SpotlightDimmer.GnomeShellExtension - Thin GNOME adapter + St.Widget renderer
- SpotlightDimmer.KwinScript - KWin (Plasma 6) adapter script
- SpotlightDimmer.NeovimPlugin - Optional neovim plugin: spotlights the focused neovim split inside a tmux pane
Performance Notes
- 100% event-driven - No polling whatsoever!
- Zero CPU usage when idle - Only activates on actual window changes
- Instant response - Event hooks provide immediate notification of window changes
- Efficient movement detection - Tracks window position and focus changes in real-time
- Native Windows API calls for maximum performance (Windows)
- AOT compilation eliminates JIT overhead and reduces startup time (Windows)
- Overlay windows with no visible content are unmapped, handing direct scanout back to fullscreen apps (Linux/KDE)