Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/DBus.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ public class Gala.DBus {
on_name_lost
);

Bus.own_name (
SESSION, "io.elementary.gala.BrightnessManager", NONE, null,
(connection, name) => {
try {
connection.register_object ("/io/elementary/gala/BrightnessManager", new BrightnessManager (wm.get_display ()));
} catch (Error e) {
warning (e.message);
}
},
on_name_lost
);

Bus.own_name (
SESSION, "org.pantheon.gala", NONE, null,
(connection, name) => {
Expand Down
120 changes: 120 additions & 0 deletions src/Misc/Brightness/BrightnessManager.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/

[DBus (name = "io.elementary.gala.BrightnessManager")]
public class Gala.BrightnessManager : Object {
public signal void monitors_changed ();
public signal void monitor_brightness_changed (int index, double brightness);

[DBus (visible = false)]
public Meta.Display display { private get; construct; }

private Gee.ArrayList<MonitorBrightness> supported_monitors;

[DBus (visible = false)]
public BrightnessManager (Meta.Display display) {
Object (display: display);
}

construct {
supported_monitors = new Gee.ArrayList<MonitorBrightness> ();

var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (update_monitors);
update_monitors ();
}

private void update_monitors () {
supported_monitors.clear ();

var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
unowned var logical_monitors = monitor_manager.get_logical_monitors ();

foreach (var logical_monitor in logical_monitors) {
bool has_backlight = false;
foreach (var monitor in logical_monitor.get_monitors ()) {
if (monitor.get_backlight () != null && monitor.is_active ()) {
has_backlight = true;
break;
}
}

if (!has_backlight) {
continue;
}

var brightness = new MonitorBrightness (logical_monitor, supported_monitors.size);
brightness.notify["value"].connect (on_brightness_changed);

supported_monitors.add (brightness);
}

monitors_changed ();
}

private void on_brightness_changed (Object obj, ParamSpec pspec) {
var brightness = (MonitorBrightness) obj;
monitor_brightness_changed (brightness.index, brightness.value);
}

public int get_n_monitors () throws IOError, DBusError {
return supported_monitors.size;
}

public string get_monitor_name (int index) throws IOError, DBusError {
if (index < 0 || index >= supported_monitors.size) {
throw new IOError.INVALID_ARGUMENT ("Invalid monitor index: %d".printf (index));
}

return supported_monitors[index].name;
}

public double get_monitor_brightness (int index) throws IOError, DBusError {
if (index < 0 || index >= supported_monitors.size) {
throw new IOError.INVALID_ARGUMENT ("Invalid monitor index: %d".printf (index));
}

return supported_monitors[index].value;
}

public void set_monitor_brightness (int index, double brightness) throws IOError, DBusError {
if (index < 0 || index >= supported_monitors.size) {
throw new IOError.INVALID_ARGUMENT ("Invalid monitor index: %d".printf (index));
}

if (brightness < 0.0 || brightness > 1.0) {
throw new IOError.INVALID_ARGUMENT ("Invalid brightness value: %f".printf (brightness));
}

supported_monitors[index].value = brightness;
}

public double get_global_brightness () throws IOError, DBusError {
if (supported_monitors.size == 0) {
throw new IOError.INVALID_ARGUMENT ("No supported monitors found.");
}

var max = supported_monitors.max (MonitorBrightness.brightness_compare_func);
return max.value;
}

public void set_global_brightness (double scale) throws IOError, DBusError {
if (supported_monitors.size == 0) {
throw new IOError.INVALID_ARGUMENT ("No supported monitors found.");
}

if (scale < 0.0 || scale > 1.0) {
throw new IOError.INVALID_ARGUMENT ("Invalid scale value: %f".printf (scale));
}

var max = get_global_brightness ();

foreach (var monitor in supported_monitors) {
monitor.value = (monitor.value / max) * scale;
}
}
}
102 changes: 102 additions & 0 deletions src/Misc/Brightness/MonitorBrightness.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/

public class Gala.MonitorBrightness : Object {
// This is really hacky. This causes vala to first include meta/display.h which includes
// the file with the necessary macros that are used in meta/meta-logical-monitor.h and meta/meta-monitor.h
// but not included there. I will upstream fixes for this but for now make it compile
public Meta.Display display;

public Meta.LogicalMonitor logical_monitor { private get; construct; }
public int index { get; construct; }

public string name { get; private set; }

private double _value = -1;
/**
* The brightness of all physical monitors in percent (0.0 - 1.0). This might
* sometimes not actually match the monitors actual brightness, e.g. when it gets dimmed
* because the system is idle, or when an auto target is enabled.
*/
public double value {
get { return _value; }
set {
_value = value;

writing_backlights = true;

foreach (var backlight in backlights) {
set_relative_brightness (backlight, value);
}

writing_backlights = false;
}
}

private Gee.ArrayList<Meta.Backlight> backlights;
private bool writing_backlights = false;

public MonitorBrightness (Meta.LogicalMonitor logical_monitor, int index) {
Object (logical_monitor: logical_monitor, index: index);
}

construct {
backlights = new Gee.ArrayList<Meta.Backlight> ();

unowned var monitors = logical_monitor.get_monitors ();

name = monitors.first ().data.get_display_name ();

foreach (var monitor in monitors) {
var backlight = monitor.get_backlight ();
if (backlight == null || !monitor.is_active ()) {
continue;
}

if (_value == -1) {
_value = get_relative_brightness (backlight);
}

set_relative_brightness (backlight, value);
backlights.add (backlight);
backlight.notify["brightness"].connect (on_brightness_changed);
}
}

private void on_brightness_changed (Object obj, ParamSpec pspec) {
if (writing_backlights) {
return;
}

value = get_relative_brightness ((Meta.Backlight) obj);
}

private static double get_relative_brightness (Meta.Backlight backlight) {
var current = backlight.brightness;
var min = backlight.brightness_min;
var max = backlight.brightness_max;

return (double) (current - min) / (max - min);
}

private static void set_relative_brightness (Meta.Backlight backlight, double value) {
var min = backlight.brightness_min;
var max = backlight.brightness_max;

backlight.brightness = (int) (min + (value * (max - min)));
}

public static int brightness_compare_func (MonitorBrightness a, MonitorBrightness b) {
if (a.value < b.value) {
return -1;
} else if (a.value > b.value) {
return 1;
} else {
return 0;
}
}
}
2 changes: 2 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ gala_bin_sources = files(
'Misc/WindowTracker.vala',
'Misc/WorkspaceManager.vala',
'Misc/Zoom.vala',
'Misc/Brightness/BrightnessManager.vala',
'Misc/Brightness/MonitorBrightness.vala',
'Misc/OSK/OSKManager.vala',
'Misc/OSK/OSKProxy.vala',
'Misc/OSK/OSKReceiver.vala',
Expand Down
6 changes: 3 additions & 3 deletions vapi/libmutter.vapi
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ namespace Meta {
public void purge (GLib.File file);
}
#if HAS_MUTTER49
[CCode (cheader_filename = "meta/main.h", type_id = "meta_backlight_get_type ()")]
[CCode (cheader_filename = "meta/meta-backlight.h", type_id = "meta_backlight_get_type ()")]
public abstract class Backlight : GLib.Object {
[CCode (has_construct_function = false)]
protected Backlight ();
Expand Down Expand Up @@ -647,14 +647,14 @@ namespace Meta {
public Meta.Workspace workspace { owned get; set; }
}
#if HAS_MUTTER49
[CCode (cheader_filename = "meta/main.h", type_id = "meta_logical_monitor_get_type ()")]
[CCode (cheader_filename = "meta/meta-logical-monitor.h", type_id = "meta_logical_monitor_get_type ()")]
public class LogicalMonitor : GLib.Object {
[CCode (has_construct_function = false)]
protected LogicalMonitor ();
public unowned GLib.List<Meta.Monitor> get_monitors ();
public int get_number ();
}
[CCode (cheader_filename = "meta/main.h", type_id = "meta_monitor_get_type ()")]
[CCode (cheader_filename = "meta/meta-monitor.h", type_id = "meta_monitor_get_type ()")]
public class Monitor : GLib.Object {
[CCode (has_construct_function = false)]
protected Monitor ();
Expand Down
Loading