I’d like to call a Bash or Python script from a Gnome extension. From what I read this should be possible, I just can’t find the documentation for it.
Usually you won’t want to do this. Reviewers strongly discourage spawning subprocesses from extensions unless its unavoidable, such as functionality only available in a Python module (eg. pychromecast) or spawning a CLI binary for a program that has no other interface (eg. nordvpn).
If you do determine it’s necessary, we have a guide on spawning subprocesses in GJS on the gjs.guide website: Subprocesses in GJS. Generally that looks something like this:
const {Gio} = imports.gi;
let proc = Gio.Subprocess.new(['python3', '/path/to/script.py'],
Gio.SubprocessFlags.NONE);
proc.wait_check_async(null, (proc, result) => {
try {
if (proc.wait_check_finish(result))
log('the process succeeded');
else
log('the process failed');
} catch (e) {
logError(e);
}
});
I understand, thanks. I’d be okay doing it in JavaScript, but I need a good library to call some dbus functions. For now I could only find a python library to do that, so I’m currently using that. If that also exists for JS directly, I’d be willing to give it a try.
Okay, I think I just found it, but I’m not sure how to translate my python script to JS using Gio.DBus.
Python:
#!/usr/bin/python3
# Toggles the scaling of the primary monitor between 1.0 and 2.0
# 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://ask.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)
What I got so far in JavaScript (which doesn’t work):
const { DBus, DBusProxy } = imports.gi.Gio;
const display_config_well_known_name = "org.gnome.Mutter.DisplayConfig"
const display_config_object_path = "/org/gnome/Mutter/DisplayConfig"
const display_config_interface = `<interface name="${display_config_well_known_name}">
<method name="GetCurrentState" />
<method name="ApplyMonitorsConfig" />
</interface>`;
const display_config_proxy = DBusProxy.makeProxyWrapper(MyIface);
const display_config_interface = new display_config_proxy(DBus.session, display_config_well_known_name, display_config_object_path);
const { serial, physical_monitors, logical_monitors, properties } = display_config_interface.GetCurrentState()
const updated_logical_monitors = []
for (let { x, y, scale, transform, primary, linked_monitors_info, props } of logical_monitors) {
if (primary === 1)
scale = (scale === 1.0) ? 2.0 : 1.0 # toggle scaling between 1.0 and 2.0 for the primary monitor
const physical_monitors_config = []
for (const { linked_monitor_connector, linked_monitor_vendor, linked_monitor_product, linked_monitor_serial } of linked_monitors_info) {
for (const { monitor_info, monitor_modes, monitor_properties } of physical_monitors) {
const { monitor_connector, monitor_vendor, monitor_product, monitor_serial } = monitor_info
if (linked_monitor_connector === monitor_connector) {
for (const { mode_id, mode_width, mode_height, mode_refresh, mode_preferred_scale, mode_supported_scales, mode_properties } of monitor_modes) {
if (mode_properties.get("is-current", False)) { # ( mode_properties provides is-current, is-preferred, is-interlaced, and more)
physical_monitors_config.push(DBus.Struct([monitor_connector, mode_id, {}]))
if (!scale.includes(mode_supported_scales)) {
console.log("Error: " + monitor_properties.get("display-name") + " doesn't support that scaling value! (" + scale + ")")
} else {
console.log("Setting scaling of: " + monitor_properties.get("display-name") + " to " + scale + "!")
}
const 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)
const properties_to_apply = { "layout_mode": properties.get("layout-mode")}
const 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)
Mainly, I don’t know how to call my interface methods and how to create data types like Structs, UInt32 etc., but I can’t find any examples.
but I can’t find any examples.
A code search for makeProxyWrapper
should bring up plenty of examples, not least in gnome-shell itself.
There will also be an extensive gjs.guide section on the topic soon (link goes to the corresponding merge request).
Wait, why are you calling Mutter’s D-Bus API in a GNOME Shell extension? You have access to the internals, you don’t need to use a remote procedure call to get them.
It’s the only way I could find change the display scaling on recent versions of wayland/gnome. How would I call it directly?
You have access to the internals, you don’t need to use a remote procedure call to get them.
In this case the internals are in mutter and not exposed to gnome-shell.
That’s kind of unfortunate that you have to run IPC to the same process to get that data.
Yes, I agree.
But then not everything has to be designed around the possibility that an extension would want to use it. In this case we ended up with a private interface between mutter and gnome-control-center, and that’s OK as far as GNOME itself is concerned
The guide for D-Bus in GJS is now live. Developers may also find the GVariant guide useful when working with D-Bus in GJS.
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.