Post

display-profiles: Generic Display Switching with Named Profiles

display-profiles is now open source: a generic display-switching tool with named profiles, a setup wizard, and DE plugin support built out of a personal Nvidia driver workaround.

display-profiles: Generic Display Switching with Named Profiles

Over the past few days I’ve written two posts about fighting the Nvidia driver bug that keeps dropping display settings on Linux Mint: the initial display switching scripts and adding per-profile Cinnamon panel layouts. What started as a couple of xrandr one-liners has grown into something more general purpose.

The repo is on GitHub: jordan-lee-code/display-profiles.

What grew out of those posts

The original scripts handled the core problem of getting the displays to behave themselves, by applying the right xrandr configuration, setting the correct primary screen, and locking in the refresh rate at 165Hz. That covered the display bug, but it left a few other quiet annoyances unaddressed.

The next thing I wanted, once the displays themselves were holding their settings, was for the panel layout to switch alongside the monitor configuration rather than lagging behind it. Working on one screen wants a single panel sitting in the middle of it, while working on two wants panels spread across both monitors in a configuration that makes sense for that arrangement. Cinnamon stores all of this in dconf, which meant display-save-layout.sh could snapshot the current panel config to a small shell script of dconf write calls, then have that script run automatically on each profile switch from then on.

The next gap was persistence across reboots. xrandr settings are runtime-only, so switching to work mode, shutting down, and coming back the next day would put everything back to the Nvidia driver’s best guess. The fix was a Zenity dialog at shutdown and restart asking which profile to use next, saving the answer to ~/.config/display-mode, and an autostart entry that reads it and calls the right script on every login.

Genericising it

The original scripts had Cinnamon and my specific monitor config baked in throughout. Anyone else using them would need to edit multiple files to change output names, resolution, or DE. That’s not a great experience, so the repo went through a refactor before publishing.

The core is now DE-agnostic. All Cinnamon-specific logic lives in hooks/cinnamon/: a save-panels.sh that takes a profile directory as an argument and snapshots dconf into it, and a restart-de.sh that calls cinnamon --replace. Adding support for another DE means adding a hooks/<de>/ directory with the same two files. The DE is detected from $XDG_CURRENT_DESKTOP.

Profiles are no longer hardcoded. They’re stored in ~/.config/display-profiles/<name>/ and contain three files: xrandr.sh (the generated xrandr command), an optional panel-layout.sh (written by the save hook), and a meta file with the name, description, and creation date. The shutdown and restart scripts list whatever profiles exist in that directory, so adding a new profile makes it appear in the menu automatically.

What’s in the repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
display-profiles/
├── lib/
│   └── common.sh              # DE detection, profile listing, zenity/terminal selector
├── bin/
│   ├── display-setup.sh       # discover outputs and list saved profiles
│   ├── display-new-profile.sh # interactive profile creation wizard
│   ├── display-switch.sh      # apply a named profile
│   ├── display-save-layout.sh # snapshot current DE panel config into a profile
│   ├── display-apply-saved.sh # apply last saved profile (autostart)
│   ├── display-shutdown.sh    # prompt for next profile, power off
│   └── display-restart.sh     # prompt for next profile, reboot
├── hooks/
│   └── cinnamon/
│       ├── save-panels.sh
│       └── restart-de.sh
├── desktop/
│   ├── display-shutdown.desktop
│   └── display-apply.desktop
├── install.sh
└── README.md

install.sh symlinks the scripts into ~/bin/ so edits in the repo take effect immediately without reinstalling. The desktop files use a %%HOME%% placeholder that install.sh substitutes at install time, so they work for any user without hardcoding a path.

Using it

1
2
3
git clone https://github.com/jordan-lee-code/display-profiles.git
cd display-profiles
bash install.sh

Then run the setup script to see what outputs are available and at what modes:

1
display-setup.sh

And create profiles interactively:

1
display-new-profile.sh

The wizard asks for a profile name, walks through each connected output (enable/disable, resolution, refresh rate), then sets the primary and positions the remaining screens.

Positioning works with absolute pixel coordinates rather than xrandr’s relative flags, which is the only way to express layouts that aren’t simple side-by-side arrangements. For each unplaced output the wizard builds a numbered list of options against every screen already placed: left, right, above, below, centered-above, centered-below. Once two or more screens are positioned, centered-above-all and centered-below-all are added, placing the new output centered over the full bounding box of the existing layout. After all outputs are placed, coordinates are normalised so the minimum x and y are both zero.

Before saving, the wizard shows an ASCII block diagram of the layout and a pixel coordinate summary so you can confirm it looks right.

The generated xrandr.sh uses --pos for absolute placement and is plain bash, easy to read and edit by hand if needed.

From that point, switching is one command:

1
display-switch.sh <profile>

Shutdown and restart pick up any profiles in ~/.config/display-profiles/ automatically, no config needed.

Why share it

Nvidia display bugs on Linux are a known long-running annoyance. The xrandr fix is well documented, but the autostart restore, dconf panel snapshots, and DE integration pieces are scattered. The profile wizard also addresses something that’s genuinely fiddly to get right by hand: building a correct multi-monitor xrandr invocation with positions and refresh rates across multiple outputs.

My setup is Linux Mint with Cinnamon and two 1440p DisplayPort monitors, but the core works on any DE. If you’re on GNOME or XFCE and want panel layout support, adding a hook directory is all it takes.

This is also the first thing I have published as open source, and I want to say that out loud rather than skip past it. It started as a personal workaround for an annoying driver bug at my own desk, and over the course of a few quiet evenings it turned into something I would actually want to hand to somebody else. The threshold for publishing felt higher than it should have, in the way it almost always does the first time, and pushing the repo public was a small private moment of letting myself take up a little more space in the world than I usually do.

I am still slightly surprised, looking at the GitHub page, that this is a thing I made. If you hit the same Nvidia bug some evening and the scripts here spare you the afternoon I lost to figuring it out the first time, that is enough reason for the repository to exist, and I would love to know it helped.

This post is licensed under CC BY 4.0 by the author.