91 lines
3.1 KiB
Python
Executable File
91 lines
3.1 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# Note: use system python3, not /usr/bin/env, because whichever python3 is on
|
|
# $PATH may not have dbus, but the system python3 does.
|
|
|
|
"""Toggle display scaling between 100% and 200%.
|
|
|
|
Based on https://gist.github.com/strycore/ca11203fd63cafcac76d4b04235d8759
|
|
|
|
For data structure definitions, see
|
|
https://gitlab.gnome.org/GNOME/mutter/blob/master/src/org.gnome.Mutter.DisplayConfig.xml
|
|
"""
|
|
|
|
import dbus
|
|
import sys
|
|
|
|
namespace = "org.gnome.Mutter.DisplayConfig"
|
|
dbus_path = "/org/gnome/Mutter/DisplayConfig"
|
|
|
|
session_bus = dbus.SessionBus()
|
|
obj = session_bus.get_object(namespace, dbus_path)
|
|
interface = dbus.Interface(obj, dbus_interface=namespace)
|
|
|
|
current_state = interface.GetCurrentState()
|
|
serial = current_state[0]
|
|
connected_monitors = current_state[1]
|
|
logical_monitors = current_state[2]
|
|
|
|
# Multiple monitors are more complicated. For now, since I only use one monitor
|
|
# in Ubuntu, everything is hard-coded so that only info about the first monitor
|
|
# is used, and only it will be connected after running the script.
|
|
#
|
|
# If someday updating this script: a logical monitor may appear on mutiple
|
|
# connected monitors due to mirroring.
|
|
connector = connected_monitors[0][0][0]
|
|
current_mode = None
|
|
# ApplyMonitorsConfig() needs (connector name, mode ID) for each connected
|
|
# monitor of a logical monitor, but GetCurrentState() only returns the
|
|
# connector name for each connected monitor of a logical monitor. So iterate
|
|
# through the globally connected monitors to find the mode ID.
|
|
for mode in connected_monitors[0][1]:
|
|
if mode[6].get("is-current", False):
|
|
current_mode = mode[0]
|
|
|
|
x, y, scale, transform, primary, monitors, props = logical_monitors[0]
|
|
available_resolutions = [
|
|
str(item)
|
|
for item in map(lambda item: f"{item[1]}x{item[2]}", connected_monitors[0][1])
|
|
]
|
|
available_resolutions = sorted(
|
|
set(available_resolutions), key=lambda item: int(item.split("x")[0]), reverse=True
|
|
)
|
|
|
|
try:
|
|
wanted_resolution = str(sys.argv[1])
|
|
except IndexError:
|
|
print("Desired resolution not provided. Example: `./display_scale.py 1920x1080`")
|
|
print("Available resolutions are:\n" + "\n".join(available_resolutions))
|
|
sys.exit(1)
|
|
|
|
wanted_scale = 1
|
|
try:
|
|
wanted_scale = float(sys.argv[2])
|
|
except IndexError:
|
|
print("Desired scale not provided. Example: `./display_scale.py 1920x1080 2`")
|
|
print("Defaulting to a scale of 1")
|
|
|
|
matching_resolutions = [
|
|
item
|
|
for item in filter(
|
|
lambda item: wanted_resolution in item[0], connected_monitors[0][1]
|
|
)
|
|
]
|
|
|
|
try:
|
|
matched_resolution = matching_resolutions[0][0]
|
|
except IndexError:
|
|
print("Desired resolution not available.")
|
|
print("Available resolutions are:\n" + "\n".join(available_resolutions))
|
|
sys.exit(1)
|
|
|
|
print(
|
|
f"Found {len(matching_resolutions)} matching resolutions."
|
|
f"Using {matched_resolution} scaled by {wanted_scale}."
|
|
)
|
|
updated_connected_monitors = [[connector, matched_resolution, {}]]
|
|
|
|
monitor_config = [[x, y, wanted_scale, transform, primary, updated_connected_monitors]]
|
|
|
|
# Change the 1 to a 2 if you want a "Revert Settings / Keep Changes" dialog
|
|
interface.ApplyMonitorsConfig(serial, 1, monitor_config, {})
|