Skip to content

Instantly share code, notes, and snippets.

@ustun
Last active March 31, 2026 08:02
Show Gist options
  • Select an option

  • Save ustun/e6a9b2073854fc4a35352daa169c72a4 to your computer and use it in GitHub Desktop.

Select an option

Save ustun/e6a9b2073854fc4a35352daa169c72a4 to your computer and use it in GitHub Desktop.
Set display scale to 200% from command line in latest Ubuntu (Wayland)

fix_scale.py

A small Python script to set display scaling on GNOME/Mutter via D-Bus — no GUI needed.

Why

If you run Ubuntu in a VM (particularly UTM/QEMU on macOS), spice-vdagent likes to reset your display scaling whenever the resolution changes — e.g. when you resize the VM window. This script sets it back to 200% from the command line by calling Mutter's DisplayConfig D-Bus API directly, the same API that GNOME Settings uses.

Usage

python3 fix_scale.py

No dependencies beyond python3-dbus, which ships with Ubuntu by default.

Make it quick to run

# Add an alias
echo 'alias fixdpi="python3 ~/fix_scale.py"' >> ~/.bashrc

Run on login

mkdir -p ~/.config/autostart
cat > ~/.config/autostart/fix-scale.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=Fix HiDPI Scale
Exec=/bin/bash -c "sleep 3 && python3 /path/to/fix_scale.py"
X-GNOME-Autostart-enabled=true
EOF

The sleep 3 gives spice-vdagent time to do its thing before we override it.

How it works

  1. Connects to Mutter's org.gnome.Mutter.DisplayConfig D-Bus interface
  2. Reads the current display state (resolution, mode, connected monitors)
  3. Reapplies the current configuration with scale forced to 2.0

The script reads the current resolution dynamically, so it works regardless of what resolution UTM/SPICE has set.

Changing the scale factor

Edit this line in fix_scale.py:

dbus.Double(2.0),  # change to 1.0, 1.5, etc.

Supported values depend on your display — run the debug snippet below to check.

Debugging

To inspect your current display state:

#!/usr/bin/env python3
import dbus

bus = dbus.SessionBus()
proxy = bus.get_object("org.gnome.Mutter.DisplayConfig",
                       "/org/gnome/Mutter/DisplayConfig")
iface = dbus.Interface(proxy, "org.gnome.Mutter.DisplayConfig")

serial, physical_monitors, logical_monitors, properties = iface.GetCurrentState()

for pm in physical_monitors:
    connector = pm[0][0]
    print(f"Connector: {connector}")
    for mode in pm[1]:
        if mode[6].get("is-current", False):
            print(f"  Current mode: {mode[0]} ({mode[1]}x{mode[2]} @ {mode[3]:.2f}Hz)")
            print(f"  Supported scales: {list(mode[5])}")

for x, y, scale, transform, primary, linked, props in logical_monitors:
    print(f"  Scale: {scale}, Primary: {primary}")

Tested on

  • Ubuntu 24.04 (GNOME/Wayland) running in UTM on macOS
#!/usr/bin/env python3
import dbus
bus = dbus.SessionBus()
proxy = bus.get_object("org.gnome.Mutter.DisplayConfig",
"/org/gnome/Mutter/DisplayConfig")
iface = dbus.Interface(proxy, "org.gnome.Mutter.DisplayConfig")
serial, physical_monitors, logical_monitors, properties = iface.GetCurrentState()
# Find current mode for each connector
current_modes = {}
for pm in physical_monitors:
connector = pm[0][0]
for mode in pm[1]:
if mode[6].get("is-current", False):
current_modes[connector] = mode[0] # mode ID like "3024x1898@60.003"
updated = []
for x, y, scale, transform, primary, linked, props in logical_monitors:
monitors_config = []
for connector, vendor, product, serial_num in linked:
mode_id = current_modes.get(connector, "")
monitors_config.append(dbus.Struct([
connector, mode_id, dbus.Dictionary({}, signature="sv")
]))
updated.append(dbus.Struct([
dbus.Int32(x), dbus.Int32(y), dbus.Double(2.0),
dbus.UInt32(transform), dbus.Boolean(primary), monitors_config
]))
iface.ApplyMonitorsConfig(
dbus.UInt32(serial), dbus.UInt32(1),
updated, {"layout_mode": properties.get("layout-mode")}
)
print("Scale set to 2.0")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment