The stackexchange post above seems to have some other arguments before the string starts too. Really not sure how this all works.
Maybe worth a question on the Gnome channels to get the specifics? They have their own Discourse instance too:
The stackexchange post above seems to have some other arguments before the string starts too. Really not sure how this all works.
Maybe worth a question on the Gnome channels to get the specifics? They have their own Discourse instance too:
Okay, I gave up on doing it with bash. There appears to be a decent python library for dbus which I think works pretty well. I think I’m almost there now, but for some reason it always changes scaling on all my monitors, not just the primary one and I don’t understand why.
#!/usr/bin/python3
# https://dbus.freedesktop.org/doc/dbus-python/tutorial.html
# https://github.com/GNOME/mutter/blob/b5f99bd12ebc483e682e39c8126a1b51772bc67d/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml
# https://discussion.fedoraproject.org/t/change-scaling-resolution-of-primary-monitor-from-bash-terminal/19892
import dbus
bus = dbus.SessionBus()
display_config_well_known_name = "org.gnome.Mutter.DisplayConfig"
display_config_object_path = "/org/gnome/Mutter/DisplayConfig"
display_config_proxy = bus.get_object(display_config_well_known_name, display_config_object_path)
display_config_interface = dbus.Interface(display_config_proxy, dbus_interface=display_config_well_known_name)
serial, physical_monitors, logical_monitors, properties = display_config_interface.GetCurrentState()
updated_logical_monitors=[]
for x, y, scale, transform, primary, linked_monitors_info, props in logical_monitors:
if primary == 1:
scale = 2.0 if (scale == 1.0) else 1.0 # toggle scaling between 1.0 and 2.0 for the primary monitor
physical_monitors_config = []
for linked_monitor_connector, linked_monitor_vendor, linked_monitor_product, linked_monitor_serial in linked_monitors_info:
for monitor_info, monitor_modes, monitor_properties in physical_monitors:
monitor_connector, monitor_vendor, monitor_product, monitor_serial = monitor_info
if linked_monitor_connector == monitor_connector:
for mode_id, mode_width, mode_height, mode_refresh, mode_preferred_scale, mode_supported_scales, mode_properties in monitor_modes:
if mode_properties.get("is-current", False): # ( mode_properties provides is-current, is-preferred, is-interlaced, and more)
physical_monitors_config.append([monitor_connector, mode_id, {}])
updated_logical_monitors.append([x, y, scale, transform, primary, physical_monitors_config])
properties_to_apply = { "layout_mode": properties.get("layout-mode")}
method = 2 # 2 means show a prompt before applying settings; 1 means instantly apply settings without prompt
display_config_interface.ApplyMonitorsConfig(serial, method, updated_logical_monitors, properties_to_apply)
Any ideas what I might be doing wrong here?
I made some notes on the method signatures and return formats which helped me a lot to not get confused by the structures: GetCurrentState () ↦ ( UInt32 serial, Array of monitors, Array - Pastebin.com
That seems to work for me; maybe print out updated_logical_monitors
to see what is changing?
Oh, actually, are you running X11 instead of Wayland? I don’t think it supports mixed scaling.
No, I’m on Wayland and I manually changing the scaling of individual monitors through the GUI works just fine for me.
Checking updated_logical_monitors
, I noticed that some of the dbus data types may have been detected incorrectly. I Have specified them explicitly now, but the result is the same unfortunately.
Here is the output of my current updated_logical_monitors
:
[
dbus.Struct(
(
dbus.Int32(0),
dbus.Int32(0),
dbus.Double(2.0),
dbus.UInt32(0),
dbus.Boolean(True),
[
dbus.Struct(
(
dbus.String('DP-5'),
dbus.String('3840x2160@59.996623992919922'),
{}
),
signature=None
)
]
),
signature=None
),
dbus.Struct(
(
dbus.Int32(3840),
dbus.Int32(544),
dbus.Double(1.0),
dbus.UInt32(0),
dbus.Boolean(False),
[
dbus.Struct(
(
dbus.String('DP-2'),
dbus.String('1920x1080@60'),
{}
),
signature=None
)
]
),
signature=None
)
]
For reference:
Array of [
Struct of (
Int32, layout x position
Int32, layout y position
Double, scale
UInt32, transform (see GetCurrentState)
Boolean, primary: true if this is the primary logical monitor
Array of [
Struct of (
String, connector
String, monitor mode ID
Dict of {
String,
Variant
} monitor properties ("enable_underscanning" (b))
)
] a list of monitors
)
] logical_monitors,
and my updated script:
#!/usr/bin/python3
# https://dbus.freedesktop.org/doc/dbus-python/tutorial.html
# https://github.com/GNOME/mutter/blob/b5f99bd12ebc483e682e39c8126a1b51772bc67d/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml
# https://discussion.fedoraproject.org/t/change-scaling-resolution-of-primary-monitor-from-bash-terminal/19892
import dbus
bus = dbus.SessionBus()
display_config_well_known_name = "org.gnome.Mutter.DisplayConfig"
display_config_object_path = "/org/gnome/Mutter/DisplayConfig"
display_config_proxy = bus.get_object(display_config_well_known_name, display_config_object_path)
display_config_interface = dbus.Interface(display_config_proxy, dbus_interface=display_config_well_known_name)
serial, physical_monitors, logical_monitors, properties = display_config_interface.GetCurrentState()
updated_logical_monitors=[]
for x, y, scale, transform, primary, linked_monitors_info, props in logical_monitors:
if primary == 1:
scale = 2.0 if (scale == 1.0) else 1.0 # toggle scaling between 1.0 and 2.0 for the primary monitor
physical_monitors_config = []
for linked_monitor_connector, linked_monitor_vendor, linked_monitor_product, linked_monitor_serial in linked_monitors_info:
for monitor_info, monitor_modes, monitor_properties in physical_monitors:
monitor_connector, monitor_vendor, monitor_product, monitor_serial = monitor_info
if linked_monitor_connector == monitor_connector:
for mode_id, mode_width, mode_height, mode_refresh, mode_preferred_scale, mode_supported_scales, mode_properties in monitor_modes:
if mode_properties.get("is-current", False): # ( mode_properties provides is-current, is-preferred, is-interlaced, and more)
physical_monitors_config.append(dbus.Struct([monitor_connector, mode_id, {}]))
if scale not in mode_supported_scales:
print("Error: " + monitor_properties.get("display-name") + " doesn't support that scaling value! (" + str(scale) + ")")
else:
print("Setting scaling of: " + monitor_properties.get("display-name") + " to " + str(scale) + "!")
updated_logical_monitor_struct = dbus.Struct([dbus.Int32(x), dbus.Int32(y), dbus.Double(scale), dbus.UInt32(transform), dbus.Boolean(primary), physical_monitors_config])
updated_logical_monitors.append(updated_logical_monitor_struct)
properties_to_apply = { "layout_mode": properties.get("layout-mode")}
method = 2 # 2 means show a prompt before applying settings; 1 means instantly apply settings without prompt
display_config_interface.ApplyMonitorsConfig(dbus.UInt32(serial), dbus.UInt32(method), updated_logical_monitors, properties_to_apply)
Ohhhh… I think it actually works and may have worked all along…
What’s got me confused is that my taskbar (Dash to Panel extension) doubles in height when I scale up my primary monitor. I mean that alone is to be expected, but the taskbar height of the primary monitor automatically gets applied to the secondary monitor as well. So it looked like the second monitor was scaling along the the primary one.
Sorry for the confusion. I’m really happy it works now.
Thanks a lot to all of you for your help, especially to @ankursinha
Being able to programmatically talk with dbus is a tremendously helpful skill that I learned now.
Glad that worked! Can you mark the one that works as the solution for the benefit of others too please?
I made a Gnome Extension out of this now:
I already submitted it, but I guess it needs to be reviewed before becoming visible:
https://extensions.gnome.org/extension/4794/scaletoggle/