A cross-platform desktop system tray app for controlling monitor brightness, contrast, dark mode, volume, keep-awake, window tiling, exposé, and layout presets -- all from one place. Works with both built-in laptop displays and external monitors via DDC/CI. Supports macOS, Windows, and Linux (Linux is BETA — see the Linux section).
- No built-in external display control — no OS offers brightness/contrast for HDMI/USB-C/DisplayPort monitors; you're stuck with the buttons on the back.
- Fragmented experience — every OS scatters brightness, dark mode, and volume across different panels. One place to control it all.
- Unified keyboard shortcuts — one set of global hotkeys that works identically on every OS, regardless of focused app.
- Too many single-purpose apps — brightness, volume, keep awake, tiling, and exposé bundled into one lightweight tray app.
- Small and fast — Rust + Tauri instead of Electron: a fraction of the size and memory footprint, modern React frontend.
| Main View (All Monitors) | Main View (Individual) |
|---|---|
![]() |
![]() |
| Settings - General | Settings - Tiling |
|---|---|
![]() |
![]() |
| Tray Menu - Tiling | Tray Menu - Profiles |
|---|---|
![]() |
![]() |
| Exposé Grid (all windows across 3 monitors) |
|---|
![]() |
On Windows, title-bar double-clicks use native maximize/restore behavior. Display DJ does not intercept global mouse clicks.
- Brightness control -- a single slider to adjust all monitors at once, or expand to control each monitor individually
- Contrast control -- DDC/CI contrast adjustment for external monitors (enable in Settings)
- Dark mode toggle -- system-wide dark/light mode switch
- Volume and output control -- system volume slider with mute indicator, click-to-rename speaker names, reorder arrows in the expanded speaker list, and a Speakers tray submenu for fast output switching plus Mute, 50%, and 100% presets. Settings lists monitors before every speaker and owns Enabled/Disabled/Hidden controls; rename and reorder controls stay in the main popup. Enabled built-in speakers lead the initial order; saved custom order persists afterward, and known Steam Streaming endpoints are hidden automatically. On Windows, Prefer Loudness Equalization (default on) attempts Loudness Equalization on every enabled output at launch and each popup open. This is best effort: driver support and permissions vary, and registry fallback may require administrator rights.
- Keep Awake -- prevent your system from sleeping with a single toggle (macOS, Windows, Linux)
- Night mode schedule -- run a selected night/day profile on a time-based schedule (default Focus at 9 PM, Daylight at 7 AM). A 60-second worker applies the initial phase and later transitions rather than repeating every tick. Custom
nightCommands/dayCommandsoverride the selected profiles; missing profiles fall back to legacy brightness/theme settings. - Profiles -- save and restore preset combinations of brightness, contrast, dark mode, and volume
- Global keyboard shortcuts -- work even when the app isn't focused; fully configurable
- Lock screen action -- a spaced logout-style button left of Settings and
Ctrl+Alt+Qlock the current Windows or Linux session without stopping background work; also bindable ascommand/system/lockScreen - Monitor renaming -- click any display name to give it a custom label
- Window tiling (macOS + Windows + Linux/X11) -- tile windows to halves, thirds, two-thirds, quarters, or maximize via keyboard shortcuts or tray menu
- Tile Snap (macOS + Windows + Linux/X11) -- drag a normal or maximized window to a screen edge to draw DPI-correct drop zones, preview the target on only that display, and snap it into a tiled layout. Enable/disable via the Tile Snap toggle in Settings; Windows defaults it off to avoid conflicting with Windows Snap. Linux support requires X11 and has been tested primarily with XFCE/xfwm4
- Exposé (macOS + Windows + Linux/X11) -- spread all windows into a grid overview, or just the current app's windows. Deterministic alphabetical layout with configurable multi-display strategy (spread evenly or fill each display). Has its own tray submenu with enable/disable toggle and grid size presets (2x2 through 5x5)
- App Exposé -- grids the frontmost app's windows first; other apps may use wholly unconsumed displays but never leftover cells beside the target app
- Layout Presets -- named presets that automatically tile specific apps to specific layouts. Triggered via keyboard shortcuts, profiles, or the tray menu
- Dynamic tray icon -- the system tray icon updates to reflect app state: dark/light mode (border color), keep-awake active (blue fill), and muted (red X overlay)
- Multi-monitor popup placement -- left-click opens on the clicked tray icon's display, including vertically stacked mixed-DPI layouts with overlapping reported bounds; bottom-edge trays place the popup above so it remains fully visible
- Settings panel -- Monitors & Speakers, System, and supported-platform Tiling tabs with grouped cards and ordered auto-save. Configure monitors, speakers, startup, wallpaper, night/day profiles, and tiling. Closing requests a final save; there is no save-status badge. The popup stays screen-bounded with internal scrolling and keyboard-accessible controls.
- DPI scaling (Beta) -- opt-in checkbox in Settings → Monitors (off by default) adds a per-display UI scale dropdown on macOS, Windows, and Linux/X11. macOS and Windows list the scales the OS offers; Linux uses
dpiMinPercent/dpiMaxPercent/dpiStepPercentfrompreferences.json(default 60-250% in 5% steps, hard caps 50-500%). The popup re-anchors to the tray after a scale change - Last-known slider values -- brightness, contrast, and volume show the last requested popup value (50% when unknown), not hardware readback. Launch restores available monitor values, the last enabled/present speaker, and its volume. Hardware failures keep the requested value without a status bar.
- Instant volume -- macOS volume uses CoreAudio directly (~1-4ms per change) and follows the slider live while dragging
- About / Update check -- "About Display DJ" in the tray menu shows current version, latest GitHub release, engine, platform, build date, and homepage. Green "Up to date" or orange "Update available" badge with download link. macOS section includes quarantine fix and Accessibility settings commands
- System tray app -- lives in your menu bar / system tray with no dock or taskbar clutter
Grab the latest release from the Releases page.
| Chip | File |
|---|---|
| Apple Silicon | Display DJ_x.x.x_aarch64.dmg |
| Intel | Display DJ_x.x.x_x64.dmg |
- Download the
.dmgfor your chip - Open the
.dmgand drag Display DJ into your Applications folder - Launch Display DJ from Applications -- it will appear in your menu bar (top-right)
First launch note: macOS Gatekeeper may show "Display DJ is damaged and can't be opened" or "unidentified developer" because the app is not notarized via the App Store. See the macOS Gatekeeper fix below.
| Architecture | File |
|---|---|
| x64 | Display DJ_x.x.x_x64-setup.exe |
| ARM64 | Display DJ_x.x.x_arm64-setup.exe |
Pick ARM64 on Windows-on-ARM devices (Snapdragon X, Surface Pro X and similar) for a native binary; the x64 installer also works there via emulation, but runs slower.
- Download the
.exeinstaller - Run the installer and follow the prompts
- Launch Display DJ -- it will appear in your system tray (bottom-right; click
^if hidden)
Warning
Linux support is in BETA. Arch Linux and KDE Plasma are SUPER ALPHA. Builds ship and the app runs on X11 desktops (verified on Ubuntu 24.04 / Mint 22 with XFCE). The AppImage can run on other glibc-based distributions, but Arch/KDE coverage is diagnostic-first rather than battle-tested. Wayland brightness may work through hardware backends, but window tiling, Tile Snap, gamma dimming, and global shortcuts remain limited or unsupported. Use at your own discretion and report issues.
| Format | File |
|---|---|
| Debian (x64) | Display DJ_x.x.x_amd64.deb |
| Debian (ARM64) | Display DJ_x.x.x_arm64.deb |
| AppImage (x64) | Display DJ_x.x.x_amd64.AppImage |
| AppImage (ARM64) | Display DJ_x.x.x_aarch64.AppImage |
Pick the ARM64 build on arm64 hardware (Raspberry Pi, Ampere, Asahi Linux, arm64 VMs).
Note the suffix differs by format: .deb uses arm64, .AppImage uses aarch64.
-
Install via your preferred format:
# Debian / Ubuntu sudo dpkg -i "Display DJ_x.x.x_amd64.deb" # AppImage (no install needed) chmod +x "Display DJ_x.x.x_amd64.AppImage" ./"Display DJ_x.x.x_amd64.AppImage"
-
Install the required runtime and display-control dependencies:
# Debian / Ubuntu sudo apt install ddcutil brightnessctl i2c-tools # Arch Linux sudo pacman -S --needed fuse2 ddcutil brightnessctl i2c-tools xorg-xrandr sudo modprobe i2c-dev sudo usermod -aG i2c,video $USER
-
The app appears in your top panel. GNOME may need the AppIndicator extension; KDE Plasma provides Status Notifier tray support.
On KDE Plasma, dark/light switching uses plasma-apply-colorscheme, and
wallpaper changes use plasma-apply-wallpaperimage. Wallpaper fit selection is
currently best-effort because Plasma keeps its configured fill mode. Prefer an
X11 Plasma session for tiling and shortcuts during the super-alpha period.
Config files are stored in:
- macOS:
~/Library/Application Support/display-dj/ - Windows:
%APPDATA%\display-dj\ - Linux:
~/.config/display-dj/
The main config file is preferences.json -- it holds keyboard shortcuts, min brightness, night mode schedule, profiles, and per-monitor metadata (labels, sort order).
To diagnose Linux, output-switching, or monitor problems, choose Debug →
Enable Logging. Enabling logging immediately writes a support snapshot with
the distro, desktop/session type, AppImage status, available helper tools,
selected feature backends, display-server output, and DDC detection results.
Each later line includes thread + source context. Reproduce the problem, then
choose Debug → Open Debug Log. Rust panics always write crash.log with a
forced backtrace and recent debug-log tail, even when debug logging was off.
| Keys | Action |
|---|---|
| Shift + Escape | Toggle Dark Mode |
| Shift + F1 / F2 | Activate Focus / Daylight profile |
| Shift + F3 / F4 / F5 | Brightness 0% / 50% / 100% |
| Shift + F10 / F11 / F12 | Volume 0% / 10% / 100% |
macOS maps Ctrl + Super + Shift + Up to Mission Control and Ctrl + Super + Shift + Down to Show Desktop. Windows maps the matching Ctrl + Alt + Shift chords to Task View and Show Desktop. Task View releases the trigger modifiers before sending Win + Tab; Show Desktop calls the native Shell toggle directly and does not synthesize keys.
Window Tiling (macOS + Windows + Linux/X11):
Use Cmd + Ctrl on macOS and Ctrl + Alt on Windows/Linux with each key below.
| Key | Action |
|---|---|
| Left | Left Third |
| C or M | Center Third |
| Right | Right Third |
| Up | Left Two-Thirds |
| Down | Right Two-Thirds |
| / | Maximize |
| , (comma) | Left Half |
| . (dot) | Right Half |
| A | App Exposé |
| E | Exposé |
Window Z-Order (macOS + Windows + Linux/X11):
| Platform | Keys | Action |
|---|---|---|
| macOS | Shift + Ctrl + Super + Left | Send all windows of focused app to back |
| macOS | Shift + Ctrl + Super + Right | Bring all windows of focused app to front |
| Windows/Linux | Shift + Ctrl + Alt + Left | Send all windows of focused app to back |
| Windows/Linux | Shift + Ctrl + Alt + Right | Bring all windows of focused app to front |
Super means Cmd on macOS. More z-order commands (command/window/*, command/app/*) are bindable in preferences.json — see AGENTS.md.
Window tiling lets you snap windows to halves, thirds, two-thirds, quarters, or maximize using keyboard shortcuts or the Tiling submenu in the tray icon's right-click menu. All 19 layouts, restore, Exposé, App Exposé, and Tile Snap (mouse edge snapping with visible drop zones and a live target preview) work on macOS, Windows, and Linux/X11.
Before a focused window is tiled, Windows restores it from maximized state, macOS exits native or browser/video fullscreen, and Linux/X11 removes EWMH maximize/fullscreen state. Display DJ then reads the normal window bounds and applies the requested layout; minimized windows remain minimized outside Exposé.
On Windows, Display DJ normally runs without administrator privileges so Tile Snap can monitor ordinary application drags without a UAC prompt. Windows does not allow a normal-integrity process to move or resize a window opened with Run as administrator; Tiling settings show an advisory warning under Window Tiling at standard integrity and no success marker when elevated. Explicitly launch Display DJ as administrator only when tiling elevated windows is required. If the optional elevation check fails, the app keeps running normally and omits the status.
Tile Snap defaults off on Windows because Windows 11 Snap uses the same screen edges and can draw a second, competing preview. Tiling settings detect the current WindowArrangementActive value: a warning opens System → Multitasking while native Snap is on. No success marker appears when it is off.
Recommended Windows 11 steps:
- Press Win + I to open Settings.
- Open System → Multitasking.
- Turn Snap windows off.
- Enable Tile Snap in Display DJ; no app restart is required.
Advanced command-line alternative (current user only):
reg.exe add "HKCU\Control Panel\Desktop" /v WindowArrangementActive /t REG_SZ /d 0 /fSign out and back in after changing the registry value. To restore Windows Snap later, run the same command with /d 1, then sign out and back in again.
Linux Tile Snap requires an X11 session ($DISPLAY set); Wayland-only sessions remain unsupported. On XFCE/xfwm4, native edge tiling can compete with Display DJ's preview. If both appear:
- Open Settings → Window Manager Tweaks → Accessibility.
- Clear Automatically tile windows when moving toward the screen edge.
Command-line alternative:
xfconf-query -c xfwm4 -p /general/tile_on_move -s falseRestore XFCE edge tiling later with the same command using -s true.
- Exposé (
command/tile/expose) -- spreads all on-screen windows into a deterministic alphabetical grid, filling the first display and overflowing to the next. Windows with minimum size constraints (e.g., Steam, Chrome) that don't fit overflow to displays with larger cells. - App Exposé (
command/tile/exposeApp) -- grids the frontmost app's windows across the first display(s), then uses other apps only on wholly unconsumed displays; it never mixes other apps into leftover cells beside the target app.
Both modes normalize windows first (unminimize, exit fullscreen), then lay out a grid; each invocation re-lays out everything (no toggle/restore). Grid size (1-5 columns/rows, default 2x3) and layout strategy ("spread" evenly across displays vs "fill" each to capacity) are in Settings (Tiling tab). The tray has a separate Exposé submenu: enable/disable, both actions, and grid presets (2x2 through 5x5).
Layout presets let you define named configurations that automatically tile specific apps to specific layouts. Each preset has a name and a list of rules. Each rule specifies:
appMatch-- case-insensitive substring to match against window/app nameslayout-- the tiling layout to apply (e.g.,"leftHalf","rightThird","maximize")displayIndex(optional) -- 0-based display index to place the window on
Presets are triggered via command/layout/{name_or_index} and can be bound to keyboard shortcuts, included in profiles, or triggered from the "Layout Presets" tray submenu. Configure presets by editing preferences.json (accessible via "Open App Preferences" in the tray menu) or browse the config directory via "Open App Folder".
Display DJ can change your desktop wallpaper via commands that work from keyboard shortcuts, profiles, and night/day schedules.
Use the command/wallpaper/change/{path} command in your keyboard shortcuts or profiles. The path must be an absolute path to an image file.
"command/wallpaper/change//Users/jane/Pictures/mountain.jpg" // macOS
"command/wallpaper/change/D:\\Pictures\\mountain.jpg" // Windows
"command/wallpaper/change/\\\\NAS-SERVER\\g\\share\\Wallpapers\\sunset.jpg" // Windows UNCNote: In JSON, backslashes must be doubled (
\\).
Fit modes: add a fit mode before the path: fill (default), fit, stretch, center, tile.
"command/wallpaper/change/fit/D:\\Pictures\\wide-banner.jpg"Per-monitor wallpaper (macOS + Windows only):
"command/wallpaper/change_single/Dell U2723QE/D:\\Pictures\\left-monitor.jpg"
"command/wallpaper/change_single/builtin/fill//Users/jane/Pictures/laptop.jpg"The monitor name is matched by case-insensitive substring (e.g., "Dell" matches "Dell U2723QE").
Start a slideshow that cycles through all images in a folder (interval in minutes, order forward/backward/random):
"command/wallpaper/slideshow//Users/jane/Pictures/rotation"
"command/wallpaper/slideshow/15/random/D:\\Pictures\\Wallpapers"
"command/wallpaper/slideshow_stop"Download a .zip of images from a URL and start a slideshow on them:
"command/wallpaper/slideshow_remote/https://example.com/nature-pack.zip"The zip is downloaded once (max 500 MB), extracted to the config directory, and the slideshow starts automatically. Subsequent calls with the same URL skip the download if images already exist. Extraction uses supported images' final filenames and skips entries whose filenames cannot be decoded.
The Settings panel (System tab) has controls for:
- Enable Slideshow -- checkbox to start/stop
- Wallpaper Fit -- dropdown for fill/fit/stretch/center/tile
- Folder -- path to the image folder
- Interval -- hours, minutes, and seconds dropdowns (minimum total 5 seconds)
- Order -- Forward, Backward, Oldest, Newest, or Random
Slideshow can also be configured in preferences.json under the wallpaper key:
| Setting | Default | Description |
|---|---|---|
wallpaper.fit |
"fill" |
Default fit mode for wallpaper commands |
wallpaper.slideshowEnabled |
false |
Whether the slideshow is active |
wallpaper.slideshowFolder |
"" |
Absolute path to the image folder |
wallpaper.slideshowIntervalMinutes |
30 |
Cycle interval, minutes part |
wallpaper.slideshowIntervalSeconds |
0 |
Extra seconds (0-59); total interval minimum 5s |
wallpaper.slideshowOrder |
"forward" |
"forward" (name A→Z), "backward" (name Z→A), "oldest" / "newest" (date modified), "random" |
wallpaper.slideshowLastPath |
null |
Last image shown; slideshow resumes from it on startup |
- Open the tray menu and go to Tiling > Enable Tiling, or toggle Enable Window Tiling in the Settings panel (Tiling tab)
macOS requires Accessibility permission for tiling to move/resize other apps' windows:
- Open System Settings > Privacy & Security > Accessibility
- Click the + button
- Add Display DJ (from
/Applications/Display DJ.app) - Make sure the toggle next to it is on
- If running in development mode (
npx tauri dev), add your terminal app (e.g., iTerm, Terminal.app) instead
Note: Tiling will not work without Accessibility permission on macOS. The Settings panel shows a warning with a link to these instructions if permission is missing.
No special permissions are needed for normal windows. Tiling works out of the box using Win32 APIs, and a failed elevation-status probe does not block the app. To control windows opened with Run as administrator, close Display DJ and restart it with Run as administrator; Settings shows this guidance while Display DJ runs at standard integrity.
No special permissions are needed on X11. Tiling uses EWMH window manager hints and works out of the box, including XFCE/xfwm4. Before focused tiling, Display DJ exits maximized or fullscreen state, waits for the window manager to confirm normal state, then records the restored bounds; minimized windows remain untouched. Wayland is not supported — get_tiling_supported returns false on Wayland-only sessions (the $DISPLAY env var must be set).
Tiling settings are stored in preferences.json under the tiling key. They can also be configured via the Settings panel (Tiling tab).
- About can show “Up to date” when release lookup fails; that badge alone does not confirm a successful check.
- Soft-overlay brightness currently works only with Windows monitor geometry; macOS/Linux lack the required mapping.
- Inspect and redact logs before sharing: crash records can contain preference paths, aliases, commands, and device identifiers.
- Not every external monitor supports DDC/CI (some budget models and certain HDMI connections)
- Built-in HDMI on base M1/M2 Macs doesn't support DDC/CI -- use USB-C or DisplayPort instead
- Linux global shortcuts may not work under Wayland (X11 works fine)
macOS Gatekeeper quarantines apps downloaded outside the App Store by setting an extended attribute (com.apple.quarantine) on the .app bundle. This causes the "app is damaged and can't be opened" or "unidentified developer" error when you try to launch the app.
To fix this, open Terminal and run:
xattr -cr "/Applications/Display DJ.app"This recursively clears the quarantine flag so macOS allows the app to run. You only need to do this once after the initial install (or after updating to a new version).
Tauri v2 (Rust) + React 19 + TypeScript + Vite 8.
All platform code (DDC/CI, gamma, WMI, DisplayServices, dark mode, volume, audio-output selection, wallpaper) lives in-process inside the Rust backend under src-tauri/src/core/. There is no external sidecar or helper binary -- macOS and Windows need no extra tools installed; Linux needs the system packages listed in the install section above.
See CONTRIBUTING.md for the full development setup, project structure, testing, and platform guides. See ARCHITECTURE.md for layers and state ownership, and DEV.md for development and platform details.
Use the Issues page. Please include your OS version, monitor model(s), and connection type.







