GitHub - synle/display-dj: 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. · GitHub
Skip to content

About

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.

Resources

Contributing

Stars

6 stars

Watchers

1 watching

Forks

Latest commit

 

History

879 Commits

Folders and files

Repository files navigation

Build

Display DJ

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).

Why I Built This

  • 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.

Screenshots

Main View (All Monitors) Main View (Individual)
Main - All Monitors Main - Individual Monitors
Settings - General Settings - Tiling
Settings General Settings Tiling
Tray Menu - Tiling Tray Menu - Profiles
Tray Menu Tiling Tray Menu Profiles
Exposé Grid (all windows across 3 monitors)
Exposé Grid

Features

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/dayCommands override 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+Q lock the current Windows or Linux session without stopping background work; also bindable as command/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/dpiStepPercent from preferences.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

Download & Install

Grab the latest release from the Releases page.

macOS

Chip File
Apple Silicon Display DJ_x.x.x_aarch64.dmg
Intel Display DJ_x.x.x_x64.dmg
  1. Download the .dmg for your chip
  2. Open the .dmg and drag Display DJ into your Applications folder
  3. 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.

Windows

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.

  1. Download the .exe installer
  2. Run the installer and follow the prompts
  3. Launch Display DJ -- it will appear in your system tray (bottom-right; click ^ if hidden)

Linux

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.

  1. 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"
  2. 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
  3. 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.

Configuration

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.

Default Keyboard Shortcuts

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 (macOS + Windows + Linux/X11)

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.

Windows Tile Snap setup

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:

  1. Press Win + I to open Settings.
  2. Open System → Multitasking.
  3. Turn Snap windows off.
  4. 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 /f

Sign 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/X11 Tile Snap setup

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:

  1. Open Settings → Window Manager Tweaks → Accessibility.
  2. Clear Automatically tile windows when moving toward the screen edge.

Command-line alternative:

xfconf-query -c xfwm4 -p /general/tile_on_move -s false

Restore XFCE edge tiling later with the same command using -s true.

Exposé

  • 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

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 names
  • layout -- 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".

Wallpaper

Display DJ can change your desktop wallpaper via commands that work from keyboard shortcuts, profiles, and night/day schedules.

Setting a Wallpaper

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 UNC

Note: 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").

Slideshow

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"

Remote Wallpaper Packs

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.

Slideshow Settings

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

Enabling Tiling

  1. Open the tray menu and go to Tiling > Enable Tiling, or toggle Enable Window Tiling in the Settings panel (Tiling tab)

macOS: Accessibility Permission

macOS requires Accessibility permission for tiling to move/resize other apps' windows:

  1. Open System Settings > Privacy & Security > Accessibility
  2. Click the + button
  3. Add Display DJ (from /Applications/Display DJ.app)
  4. Make sure the toggle next to it is on
  5. 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.

Windows

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.

Linux (X11)

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 Preferences

Tiling settings are stored in preferences.json under the tiling key. They can also be configured via the Settings panel (Tiling tab).

Setting Default Range Description
enabled true -- Master toggle for all tiling features
halfRatio 50 -- Percentage for half splits (affects halves and quarters)
thirdRatio 33 -- Percentage for third splits (center = 100 - 2 x third)
gap 0 -- Padding in points around the tiling area
tileSnapEnabled platform -- Enable/disable Tile Snap mouse edge snapping. Default: true on macOS/Linux, false on Windows to avoid Windows Snap conflicts
sideEdgeTrigger 18 1-500 px Tile Snap: width of left/right/bottom edge zone
topEdgeTrigger 18 1-500 px Tile Snap: height of top edge zone (maximize)
cornerTrigger 30 1-500 px Tile Snap: size of corner zone (quarter tiles)
snap*Enabled true -- Nine toggles for top/left/right edges, four corners, bottom thirds, and bottom two-thirds
exposeEnabled true -- Master toggle for Exposé features
exposeColumns 2 1-5 Number of columns in the Exposé grid per display
exposeRows 3 1-5 Number of rows in the Exposé grid per display
exposeLayoutStrategy "fill" -- "spread" distributes windows evenly across displays; "fill" packs each display to capacity before using the next
exposeMinWidth 400 100-800 px Minimum grid cell width in logical pixels. Cells smaller than this cause overflow to less-crowded displays. Scaled by DPI on Windows
exposeMinHeight 300 100-600 px Minimum grid cell height in logical pixels. Same DPI scaling as width

Known Issues

  • 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 Fix

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).

Tech Stack

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.

Contributing

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.

Bug Reports & Suggestions

Use the Issues page. Please include your OS version, monitor model(s), and connection type.

About

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.

Resources

Contributing

Stars

6 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages