From 96d1856a922468f1216d1c8ef5c63828b797e2bf Mon Sep 17 00:00:00 2001 From: Denis Garaev Date: Fri, 17 Jul 2026 13:50:10 +0300 Subject: [PATCH 1/4] DeviceManager: use gala's brightness interface --- src/Services/DBusInterfaces/Screen.vala | 4 ++-- src/Services/DeviceManager.vala | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Services/DBusInterfaces/Screen.vala b/src/Services/DBusInterfaces/Screen.vala index 102faee0..2a61fcac 100644 --- a/src/Services/DBusInterfaces/Screen.vala +++ b/src/Services/DBusInterfaces/Screen.vala @@ -18,8 +18,8 @@ */ namespace Power.Services.DBusInterfaces { - [DBus (name = "org.gnome.SettingsDaemon.Power.Screen")] - interface PowerSettings : GLib.Object { + [DBus (name = "io.elementary.gala.BrightnessManager")] + interface BrightnessManager : GLib.Object { public abstract int brightness { get; set; } } } diff --git a/src/Services/DeviceManager.vala b/src/Services/DeviceManager.vala index 7ce3eb8e..3af36262 100644 --- a/src/Services/DeviceManager.vala +++ b/src/Services/DeviceManager.vala @@ -21,13 +21,13 @@ public class Power.Services.DeviceManager : Object { private const string UPOWER_INTERFACE = "org.freedesktop.UPower"; private const string UPOWER_PATH = "/org/freedesktop/UPower"; - private const string POWER_SETTINGS_INTERFACE = "org.gnome.SettingsDaemon.Power"; - private const string POWER_SETTINGS_PATH = "/org/gnome/SettingsDaemon/Power"; + private const string GALA_INTERFACE = "io.elementary.gala"; + private const string GALA_PATH = "/io/elementary/gala/BrightnessManager"; private static DeviceManager? instance = null; private DBusInterfaces.UPower? upower = null; - private DBusInterfaces.PowerSettings? iscreen = null; + private DBusInterfaces.BrightnessManager? iscreen = null; public Services.Backlight backlight { get; construct; } public Gee.HashMap devices { get; private set; } @@ -92,8 +92,8 @@ public class Power.Services.DeviceManager : Object { iscreen = yield Bus.get_proxy ( BusType.SESSION, - POWER_SETTINGS_INTERFACE, - POWER_SETTINGS_PATH, + GALA_INTERFACE, + GALA_PATH, DBusProxyFlags.GET_INVALIDATED_PROPERTIES ); debug ("Connection to Power Settings bus established"); From 8a12a7ca874ac23fe5f5e51bc9ec4c54eeb89c8e Mon Sep 17 00:00:00 2001 From: Denis Garaev Date: Sun, 30 Aug 2026 18:19:28 +0300 Subject: [PATCH 2/4] Update for interface changes --- src/Indicator.vala | 6 +- .../{Screen.vala => BrightnessManager.vala} | 10 +- src/Services/DeviceManager.vala | 92 +++++++++++++------ src/Utils.vala | 8 +- src/Widgets/PopoverWidget.vala | 6 +- src/Widgets/ScreenBrightness.vala | 86 +++++++++++------ src/meson.build | 2 +- 7 files changed, 140 insertions(+), 70 deletions(-) rename src/Services/DBusInterfaces/{Screen.vala => BrightnessManager.vala} (57%) diff --git a/src/Indicator.vala b/src/Indicator.vala index 954d43a3..46fa5e88 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -87,7 +87,7 @@ public class Power.Indicator : Wingpanel.Indicator { }); display_widget.add_controller (scroll_controller); - dm.brightness_changed.connect (update_tooltip); + dm.monitor_brightness_changed.connect (update_tooltip); } } @@ -191,7 +191,7 @@ public class Power.Indicator : Wingpanel.Indicator { } if (primary_text == null && dm.backlight.present) { - primary_text = _("Screen brightness: %i").printf ((int)(dm.brightness)); + primary_text = _("Screen brightness: %i").printf ((int)(dm.get_monitor_brightness (0))); secondary_text = _("Scroll to change screen brightness"); } @@ -211,7 +211,7 @@ public class Power.Indicator : Wingpanel.Indicator { if (is_in_session) { var notification = new Notify.Notification ("indicator-power", "", "display-brightness-symbolic"); notification.set_hint ("x-canonical-private-synchronous", new Variant.string ("indicator-power")); - notification.set_hint ("value", new Variant.int32 (dm.brightness)); + notification.set_hint ("value", new Variant.int32 ((int) (dm.get_monitor_brightness (0) * 100))); try { notification.show (); return true; diff --git a/src/Services/DBusInterfaces/Screen.vala b/src/Services/DBusInterfaces/BrightnessManager.vala similarity index 57% rename from src/Services/DBusInterfaces/Screen.vala rename to src/Services/DBusInterfaces/BrightnessManager.vala index 2a61fcac..5bd2799e 100644 --- a/src/Services/DBusInterfaces/Screen.vala +++ b/src/Services/DBusInterfaces/BrightnessManager.vala @@ -20,6 +20,14 @@ namespace Power.Services.DBusInterfaces { [DBus (name = "io.elementary.gala.BrightnessManager")] interface BrightnessManager : GLib.Object { - public abstract int brightness { get; set; } + public signal void monitors_changed (); + public signal void monitor_brightness_changed (int index, double value); + + public abstract double get_global_brightness () throws GLib.IOError, GLib.DBusError; + public abstract double get_monitor_brightness (int index) throws GLib.IOError, GLib.DBusError; + public abstract string get_monitor_name (int index) throws GLib.IOError, GLib.DBusError; + public abstract int get_n_monitors () throws GLib.IOError, GLib.DBusError; + public abstract void set_global_brightness (double scale) throws GLib.IOError, GLib.DBusError; + public abstract void set_monitor_brightness (int index, double brightness) throws GLib.IOError, GLib.DBusError; } } diff --git a/src/Services/DeviceManager.vala b/src/Services/DeviceManager.vala index 3af36262..f705172b 100644 --- a/src/Services/DeviceManager.vala +++ b/src/Services/DeviceManager.vala @@ -27,7 +27,7 @@ public class Power.Services.DeviceManager : Object { private static DeviceManager? instance = null; private DBusInterfaces.UPower? upower = null; - private DBusInterfaces.BrightnessManager? iscreen = null; + private DBusInterfaces.BrightnessManager? brightness_manager = null; public Services.Backlight backlight { get; construct; } public Gee.HashMap devices { get; private set; } @@ -36,25 +36,12 @@ public class Power.Services.DeviceManager : Object { public bool has_battery { get; private set; } public bool on_battery { get; private set; } public bool on_low_battery { get; private set; } - public int brightness { - get { - if (backlight.present && iscreen != null) { - return iscreen.brightness; - } else { - return -1; - } - } - - set { - if (backlight.present && iscreen != null) { - iscreen.brightness = value.clamp (0, 100); - } - } - } public signal void battery_registered (string device_path, Device battery); public signal void battery_deregistered (string device_path); - public signal void brightness_changed (int brightness); + + public signal void monitors_changed (); + public signal void monitor_brightness_changed (int index, double value); construct { backlight = new Services.Backlight (); @@ -90,7 +77,7 @@ public class Power.Services.DeviceManager : Object { ); debug ("Connection to UPower bus established"); - iscreen = yield Bus.get_proxy ( + brightness_manager = yield Bus.get_proxy ( BusType.SESSION, GALA_INTERFACE, GALA_PATH, @@ -139,7 +126,7 @@ public class Power.Services.DeviceManager : Object { } } - private void connect_signals () requires (upower != null && iscreen != null) { + private void connect_signals () requires (upower != null && brightness_manager != null) { upower.g_properties_changed.connect (() => { update_properties (); update_batteries (); @@ -148,12 +135,8 @@ public class Power.Services.DeviceManager : Object { upower.DeviceAdded.connect (register_device); upower.DeviceRemoved.connect (deregister_device); - ((DBusProxy)iscreen).g_properties_changed.connect ((changed_properties, invalidated_properties) => { - var changed_brightness = changed_properties.lookup_value ("Brightness", new VariantType ("i")); - if (changed_brightness != null) { - brightness_changed (changed_brightness.get_int32 ()); - } - }); + brightness_manager.monitors_changed.connect (monitors_changed_cb); + brightness_manager.monitor_brightness_changed.connect (monitor_brightness_changed_cb); } private void update_properties () requires (upower != null) { @@ -200,9 +183,62 @@ public class Power.Services.DeviceManager : Object { } } - public void change_brightness (int change) { - if (iscreen != null) { - brightness = iscreen.brightness + change; + private void monitors_changed_cb () { + monitors_changed (); + } + + private void monitor_brightness_changed_cb (int index, double value) { + monitor_brightness_changed (index, value); + } + + public double get_monitor_brightness (int index) { + if (brightness_manager != null) { + try { + return brightness_manager.get_monitor_brightness (index); + } catch (Error e) { + warning ("Couldn't get monitor's brightness: %s", e.message); + } + } + return -1; + } + + public void set_monitor_brightness (int index, double value) { + if (brightness_manager != null) { + try { + brightness_manager.set_monitor_brightness (index, value); + } catch (Error e) { + warning ("Couldn't set monitor's brightness: %s", e.message); + } + } + } + + public string get_monitor_data (int index) { + if (brightness_manager != null) { + try { + return brightness_manager.get_monitor_name (index); + } catch (Error e) { + warning ("Couldn't get monitor's data: %s", e.message); + } + } + return ""; + } + + public int get_monitor_count () { + if (brightness_manager != null) { + try { + return brightness_manager.get_n_monitors (); + } catch (Error e) { + warning ("Couldn't get monitor's count: %s", e.message); + } + } + return 0; + } + + public void change_global_brightness (double change) { + try { + brightness_manager.set_global_brightness ((brightness_manager.get_global_brightness () + change).clamp (0.0, 1.0)); + } catch (Error e) { + warning ("Couldn't set global brightness: %s", e.message); } } } diff --git a/src/Utils.vala b/src/Utils.vala index 3733a088..4fb9fb0f 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -1,6 +1,6 @@ public class Power.Utils { - private const double BRIGHTNESS_STEP = 5.0; + private const double BRIGHTNESS_STEP = 0.005; private static double total_y_delta = 0; private static double total_x_delta = 0; @@ -54,9 +54,9 @@ public class Power.Utils { break; } - if (total_y_delta.abs () * BRIGHTNESS_STEP > 1.0) { + if (total_y_delta.abs () * BRIGHTNESS_STEP > 0.001) { dir = natural_scroll ? total_y_delta : -total_y_delta; - } else if (total_x_delta.abs () * BRIGHTNESS_STEP > 1.0) { + } else if (total_x_delta.abs () * BRIGHTNESS_STEP > 0.001) { dir = natural_scroll ? -total_x_delta : total_x_delta; } @@ -64,7 +64,7 @@ public class Power.Utils { total_y_delta = 0.0; total_x_delta = 0.0; Power.Services.DeviceManager.get_default () - .change_brightness ((int) Math.round (dir * BRIGHTNESS_STEP)); + .change_global_brightness (dir * BRIGHTNESS_STEP); } return Gdk.EVENT_STOP; diff --git a/src/Widgets/PopoverWidget.vala b/src/Widgets/PopoverWidget.vala index 587c11be..8be9f9f7 100644 --- a/src/Widgets/PopoverWidget.vala +++ b/src/Widgets/PopoverWidget.vala @@ -58,7 +58,7 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { }; var last_separator_revealer = new Gtk.Revealer () { - reveal_child = dm.brightness != -1, + reveal_child = dm.get_monitor_count () > 0, child = last_separator, }; @@ -137,8 +137,8 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { } }); - dm.brightness_changed.connect ((brightness) => { - if (brightness != -1) { + dm.monitors_changed.connect (() => { + if (dm.get_monitor_count () > 0) { last_separator_revealer.reveal_child = true; } else { last_separator_revealer.reveal_child = false; diff --git a/src/Widgets/ScreenBrightness.vala b/src/Widgets/ScreenBrightness.vala index 8ea91f1a..19b4f528 100644 --- a/src/Widgets/ScreenBrightness.vala +++ b/src/Widgets/ScreenBrightness.vala @@ -18,8 +18,8 @@ */ public class Power.Widgets.ScreenBrightness : Granite.Bin { - private Gtk.Scale brightness_slider; private Power.Services.DeviceManager dm; + private Gtk.ListBox list_box; public bool natural_scroll_touchpad { get; set; } public bool natural_scroll_mouse { get; set; } @@ -32,54 +32,80 @@ public class Power.Widgets.ScreenBrightness : Granite.Bin { var touchpad_settings = new GLib.Settings ("org.gnome.desktop.peripherals.touchpad"); touchpad_settings.bind ("natural-scroll", this, "natural-scroll-touchpad", SettingsBindFlags.DEFAULT); + var scroll_controller = new Gtk.EventControllerScroll (BOTH_AXES); + scroll_controller.scroll.connect (on_scroll); + add_controller (scroll_controller); + + list_box = new Gtk.ListBox (); + child = list_box; + + populate_list (); + + dm.monitors_changed.connect (() => { + list_box.remove_all (); + populate_list (); + }); + } + + private void populate_list () { + for (int i = 0; i < dm.get_monitor_count (); i++) { + list_box.append (construct_row (i)); + } + } + + private Gtk.Widget construct_row (int index) { var image = new Gtk.Image.from_icon_name ("brightness-display-symbolic") { pixel_size = 48 }; - brightness_slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 10) { - margin_end = 6, + var monitor_label = new Gtk.Label (dm.get_monitor_data (index)) { + halign = Gtk.Align.START + }; + + var brightness_slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 1, 0.1) { + margin_start = 2, + margin_end = 2, hexpand = true, draw_value = false, width_request = 175 }; - var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6) { + var slider_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 2) { hexpand = true, - margin_start = 6, - margin_end = 12 + vexpand = true, + homogeneous = true }; - box.append (image); - box.append (brightness_slider); + slider_box.append (monitor_label); + slider_box.append (brightness_slider); - var show_brightness_slider = new Gtk.Revealer () { - child = box + var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4) { + hexpand = true, + margin_start = 6, + margin_end = 12 }; - child = show_brightness_slider; - - if (dm.brightness != -1) { - brightness_slider.set_value (dm.brightness); - show_brightness_slider.reveal_child = true; - } - - var scroll_controller = new Gtk.EventControllerScroll (BOTH_AXES); - scroll_controller.scroll.connect (on_scroll); - add_controller (scroll_controller); + box.append (image); + box.append (slider_box); - brightness_slider.value_changed.connect ((value) => { - brightness_slider.set_value (value.get_value ()); - dm.brightness = (int) value.get_value (); + ulong slider_signal = 0, dm_signal = 0; + slider_signal = brightness_slider.value_changed.connect ((value) => { + SignalHandler.block (dm, dm_signal); + dm.set_monitor_brightness (index, value.get_value ()); + SignalHandler.unblock (dm, dm_signal); }); - - dm.brightness_changed.connect ((brightness) => { - if (brightness != -1) { - brightness_slider.set_value ((double) brightness); - show_brightness_slider.reveal_child = true; - } else { - show_brightness_slider.reveal_child = false; + dm_signal = dm.monitor_brightness_changed.connect ((ch_index, value) => { + if (index != ch_index) { + return; } + + SignalHandler.block (brightness_slider, slider_signal); + brightness_slider.set_value (value); + SignalHandler.unblock (brightness_slider, slider_signal); }); + + brightness_slider.set_value (dm.get_monitor_brightness (index)); + return box; } private bool on_scroll (Gtk.EventControllerScroll controller, double dx, double dy) { diff --git a/src/meson.build b/src/meson.build index 40051eb4..7b074f87 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,9 +15,9 @@ files = files( 'Indicator.vala', 'Utils.vala', 'Services/Backlight/Backlight.vala', + 'Services/DBusInterfaces/BrightnessManager.vala', 'Services/DBusInterfaces/Device.vala', 'Services/DBusInterfaces/Properties.vala', - 'Services/DBusInterfaces/Screen.vala', 'Services/DBusInterfaces/UPower.vala', 'Services/DBusInterfaces/PowerProfile.vala', 'Services/Device.vala', From 3063d97e6611ac0fff068372b1ec2478ca3ba3b1 Mon Sep 17 00:00:00 2001 From: Denis Garaev Date: Sun, 6 Sep 2026 22:04:54 +0300 Subject: [PATCH 3/4] Adressing comments --- src/Indicator.vala | 59 ++++---- src/Services/Backlight/Backlight.vala | 48 ------- src/Services/BrightnessManager.vala | 128 ++++++++++++++++++ ...anager.vala => GalaBrightnessManager.vala} | 2 +- src/Services/DeviceManager.vala | 85 +----------- src/Utils.vala | 109 +++++++++------ src/Widgets/PopoverWidget.vala | 14 +- src/Widgets/ScreenBrightnessList.vala | 46 +++++++ ...ightness.vala => ScreenBrightnessRow.vala} | 70 ++++------ src/meson.build | 7 +- 10 files changed, 316 insertions(+), 252 deletions(-) delete mode 100644 src/Services/Backlight/Backlight.vala create mode 100644 src/Services/BrightnessManager.vala rename src/Services/DBusInterfaces/{BrightnessManager.vala => GalaBrightnessManager.vala} (96%) create mode 100644 src/Widgets/ScreenBrightnessList.vala rename src/Widgets/{ScreenBrightness.vala => ScreenBrightnessRow.vala} (52%) diff --git a/src/Indicator.vala b/src/Indicator.vala index 46fa5e88..35d4e028 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -30,6 +30,7 @@ public class Power.Indicator : Wingpanel.Indicator { private Services.Device? display_device = null; private Services.DeviceManager dm; + private Services.BrightnessManager brightness_manager; private Settings settings; @@ -46,6 +47,7 @@ public class Power.Indicator : Wingpanel.Indicator { Gtk.IconTheme.get_for_display (Gdk.Display.get_default ()).add_resource_path ("/io/elementary/panel/power"); dm = Power.Services.DeviceManager.get_default (); + brightness_manager = Services.BrightnessManager.get_default (); var mouse_settings = new GLib.Settings ("org.gnome.desktop.peripherals.mouse"); mouse_settings.bind ("natural-scroll", this, "natural-scroll-mouse", SettingsBindFlags.DEFAULT); @@ -60,35 +62,14 @@ public class Power.Indicator : Wingpanel.Indicator { display_widget = new Widgets.DisplayWidget (); /* No need to display the indicator when the device is completely in AC mode */ - if (dm.has_battery || dm.backlight.present) { + if (dm.has_battery || brightness_manager.present) { update_visibility (); } dm.notify["has-battery"].connect (update_visibility); dm.notify["display-device"].connect (update_display_device); settings.changed["show-percentage"].connect (update_tooltip); - - if (dm.backlight.present) { - var scroll_controller = new Gtk.EventControllerScroll (BOTH_AXES); - scroll_controller.scroll.connect ((controller, dx, dy) => { - if (Utils.handle_scroll_event ( - (Gdk.ScrollEvent) controller.get_current_event (), - natural_scroll_mouse, - natural_scroll_touchpad) - ) { - if (popover_widget == null || !popover_widget.is_visible ()) { - show_notification (); - } - - return true; - } - - return false; - }); - display_widget.add_controller (scroll_controller); - - dm.monitor_brightness_changed.connect (update_tooltip); - } + brightness_manager.connected.connect (update_scroll_controller); } return display_widget; @@ -111,7 +92,7 @@ public class Power.Indicator : Wingpanel.Indicator { private void update_visibility () { var dm = Services.DeviceManager.get_default (); - bool should_be_visible = (dm.has_battery || dm.backlight.present); + bool should_be_visible = (dm.has_battery || brightness_manager.present); if (visible != should_be_visible) { /* NOTE: popover closes every time you set visibility, so change property only when needed */ visible = should_be_visible; @@ -190,8 +171,8 @@ public class Power.Indicator : Wingpanel.Indicator { } } - if (primary_text == null && dm.backlight.present) { - primary_text = _("Screen brightness: %i").printf ((int)(dm.get_monitor_brightness (0))); + if (primary_text == null && brightness_manager.present) { + primary_text = _("Screen brightness: %i").printf ((int) (brightness_manager.get_global_brightness () * 100)); secondary_text = _("Scroll to change screen brightness"); } @@ -211,7 +192,7 @@ public class Power.Indicator : Wingpanel.Indicator { if (is_in_session) { var notification = new Notify.Notification ("indicator-power", "", "display-brightness-symbolic"); notification.set_hint ("x-canonical-private-synchronous", new Variant.string ("indicator-power")); - notification.set_hint ("value", new Variant.int32 ((int) (dm.get_monitor_brightness (0) * 100))); + notification.set_hint ("value", new Variant.int32 ((int) (brightness_manager.get_global_brightness () * 100))); try { notification.show (); return true; @@ -223,6 +204,30 @@ public class Power.Indicator : Wingpanel.Indicator { return false; } + + private void update_scroll_controller () { + if (brightness_manager.present) { + var scroll_controller = new Gtk.EventControllerScroll (BOTH_AXES); + scroll_controller.scroll.connect ((controller, dx, dy) => { + if (Utils.handle_global_scroll_event ( + (Gdk.ScrollEvent) controller.get_current_event (), + natural_scroll_mouse, + natural_scroll_touchpad) + ) { + if (popover_widget == null || !popover_widget.is_visible ()) { + show_notification (); + } + + return true; + } + + return false; + }); + display_widget.add_controller (scroll_controller); + + brightness_manager.monitor_brightness_changed.connect (update_tooltip); + } + } } public Wingpanel.Indicator get_indicator (Module module, Wingpanel.IndicatorManager.ServerType server_type) { diff --git a/src/Services/Backlight/Backlight.vala b/src/Services/Backlight/Backlight.vala deleted file mode 100644 index e7fe097e..00000000 --- a/src/Services/Backlight/Backlight.vala +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2011-2015 elementary LLC. (https://elementary.io) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -public class Power.Services.Backlight : GLib.Object { - - private const string BACKLIGHT_NAME = "backlight"; - - public bool present { get; construct set; } - - construct { - present = get_backlight_present (); - debug ("backlight present: %s", present.to_string ()); - } - - private static bool get_backlight_present () { - var context = new UDev.Context (); - var e = context.create_enumerate (); - e.add_match_subsystem (BACKLIGHT_NAME); - e.scan_devices (); - - for (unowned UDev.List d = e.entries; d != null; d = d.next) { - var path = d.name; - var dev = context.open_syspath (path); - - if (dev != null) { - return true; - } - } - - return false; - } -} diff --git a/src/Services/BrightnessManager.vala b/src/Services/BrightnessManager.vala new file mode 100644 index 00000000..c003f6ca --- /dev/null +++ b/src/Services/BrightnessManager.vala @@ -0,0 +1,128 @@ +/* + * Copyright 2026 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Denis Garaev + */ + +public class Power.Services.BrightnessManager : Object { + private const string GALA_INTERFACE = "io.elementary.gala"; + private const string GALA_PATH = "/io/elementary/gala/BrightnessManager"; + + public signal void connected (); + public signal void monitors_changed (); + public signal void monitor_brightness_changed (int index, double value); + + private Power.Services.DBusInterfaces.GalaBrightnessManager? gala_brightness_manager; + + private static BrightnessManager? instance = null; + + public bool present { + get { + if (gala_brightness_manager == null) { + return false; + } + + try { + if (gala_brightness_manager.get_n_monitors () == 0) { + return false; + } + } catch {} + + return true; + } + } + + construct { + init.begin ((obj, res) => { + try { + init.end (res); + connect_signals (); + } catch {} + }); + } + + public static Power.Services.BrightnessManager get_default () { + if (instance == null) { + instance = new BrightnessManager (); + } + + return instance; + } + + public int get_n_monitors () { + try { + return gala_brightness_manager.get_n_monitors (); + } catch (Error e) { + warning ("Couldn't get number of monitors: %s", e.message); + return -1; + } + } + + public double get_global_brightness () { + try { + return gala_brightness_manager.get_global_brightness ().clamp (0.0001, 1.0); + } catch (Error e) { + warning ("Couldn't get global brightness: %s", e.message); + return -1.0f; + } + } + + public void set_global_brightness (double value) { + try { + gala_brightness_manager.set_global_brightness (value.clamp (0.0001, 1.0)); + } catch (Error e) { + warning ("Coulnd't set global brightness: %s", e.message); + } + } + + public string get_monitor_name (int index) { + try { + return gala_brightness_manager.get_monitor_name (index); + } catch (Error e) { + warning ("Couldn't get %n monitor's name: %s", index, e.message); + return ""; + } + } + + public double get_monitor_brightness (int index) { + try { + return gala_brightness_manager.get_monitor_brightness (index).clamp (0.0001, 1); + } catch (Error e) { + warning ("Couldn't get %n monitor's brightness: %s", index, e.message); + return -1.0f; + } + } + + public void set_monitor_brightness (int index, double value) { + try { + gala_brightness_manager.set_monitor_brightness (index, value.clamp (0.0001, 1)); + } catch (Error e) { + warning ("Coulnd't set %n monitor's brightness: %s", index, e.message); + } + } + + + private async void init () throws Error { + try { + gala_brightness_manager = yield Bus.get_proxy (BusType.SESSION, GALA_INTERFACE, GALA_PATH); + connected (); + } catch (Error e) { + warning ("Couldn't connect to Gala's BrightnessManager: %s", e.message); + throw e; + } + } + + private void connect_signals () { + gala_brightness_manager.monitors_changed.connect (on_monitors_changed); + gala_brightness_manager.monitor_brightness_changed.connect (on_monitor_brightness_changed); + } + + private void on_monitors_changed () { + monitors_changed (); + } + + private void on_monitor_brightness_changed (int index, double value) { + monitor_brightness_changed (index, value); + } +} diff --git a/src/Services/DBusInterfaces/BrightnessManager.vala b/src/Services/DBusInterfaces/GalaBrightnessManager.vala similarity index 96% rename from src/Services/DBusInterfaces/BrightnessManager.vala rename to src/Services/DBusInterfaces/GalaBrightnessManager.vala index 5bd2799e..5d1c6229 100644 --- a/src/Services/DBusInterfaces/BrightnessManager.vala +++ b/src/Services/DBusInterfaces/GalaBrightnessManager.vala @@ -19,7 +19,7 @@ namespace Power.Services.DBusInterfaces { [DBus (name = "io.elementary.gala.BrightnessManager")] - interface BrightnessManager : GLib.Object { + interface GalaBrightnessManager : GLib.Object { public signal void monitors_changed (); public signal void monitor_brightness_changed (int index, double value); diff --git a/src/Services/DeviceManager.vala b/src/Services/DeviceManager.vala index f705172b..65e4d106 100644 --- a/src/Services/DeviceManager.vala +++ b/src/Services/DeviceManager.vala @@ -20,16 +20,10 @@ public class Power.Services.DeviceManager : Object { private const string UPOWER_INTERFACE = "org.freedesktop.UPower"; private const string UPOWER_PATH = "/org/freedesktop/UPower"; - - private const string GALA_INTERFACE = "io.elementary.gala"; - private const string GALA_PATH = "/io/elementary/gala/BrightnessManager"; - private static DeviceManager? instance = null; private DBusInterfaces.UPower? upower = null; - private DBusInterfaces.BrightnessManager? brightness_manager = null; - public Services.Backlight backlight { get; construct; } public Gee.HashMap devices { get; private set; } public Gee.Iterator batteries { get; private set; } public Device display_device { get; private set; } @@ -40,12 +34,7 @@ public class Power.Services.DeviceManager : Object { public signal void battery_registered (string device_path, Device battery); public signal void battery_deregistered (string device_path); - public signal void monitors_changed (); - public signal void monitor_brightness_changed (int index, double value); - construct { - backlight = new Services.Backlight (); - connect_to_bus.begin ((obj, res) => { if (connect_to_bus.end (res)) { update_properties (); @@ -77,17 +66,9 @@ public class Power.Services.DeviceManager : Object { ); debug ("Connection to UPower bus established"); - brightness_manager = yield Bus.get_proxy ( - BusType.SESSION, - GALA_INTERFACE, - GALA_PATH, - DBusProxyFlags.GET_INVALIDATED_PROPERTIES - ); - debug ("Connection to Power Settings bus established"); - return true; } catch (Error e) { - critical ("Connecting to UPower or PowerSettings bus failed: %s", e.message); + critical ("Connecting to UPower bus failed: %s", e.message); return false; } @@ -126,7 +107,7 @@ public class Power.Services.DeviceManager : Object { } } - private void connect_signals () requires (upower != null && brightness_manager != null) { + private void connect_signals () requires (upower != null) { upower.g_properties_changed.connect (() => { update_properties (); update_batteries (); @@ -134,9 +115,6 @@ public class Power.Services.DeviceManager : Object { upower.DeviceAdded.connect (register_device); upower.DeviceRemoved.connect (deregister_device); - - brightness_manager.monitors_changed.connect (monitors_changed_cb); - brightness_manager.monitor_brightness_changed.connect (monitor_brightness_changed_cb); } private void update_properties () requires (upower != null) { @@ -182,63 +160,4 @@ public class Power.Services.DeviceManager : Object { battery_deregistered (device_path); } } - - private void monitors_changed_cb () { - monitors_changed (); - } - - private void monitor_brightness_changed_cb (int index, double value) { - monitor_brightness_changed (index, value); - } - - public double get_monitor_brightness (int index) { - if (brightness_manager != null) { - try { - return brightness_manager.get_monitor_brightness (index); - } catch (Error e) { - warning ("Couldn't get monitor's brightness: %s", e.message); - } - } - return -1; - } - - public void set_monitor_brightness (int index, double value) { - if (brightness_manager != null) { - try { - brightness_manager.set_monitor_brightness (index, value); - } catch (Error e) { - warning ("Couldn't set monitor's brightness: %s", e.message); - } - } - } - - public string get_monitor_data (int index) { - if (brightness_manager != null) { - try { - return brightness_manager.get_monitor_name (index); - } catch (Error e) { - warning ("Couldn't get monitor's data: %s", e.message); - } - } - return ""; - } - - public int get_monitor_count () { - if (brightness_manager != null) { - try { - return brightness_manager.get_n_monitors (); - } catch (Error e) { - warning ("Couldn't get monitor's count: %s", e.message); - } - } - return 0; - } - - public void change_global_brightness (double change) { - try { - brightness_manager.set_global_brightness ((brightness_manager.get_global_brightness () + change).clamp (0.0, 1.0)); - } catch (Error e) { - warning ("Couldn't set global brightness: %s", e.message); - } - } } diff --git a/src/Utils.vala b/src/Utils.vala index 4fb9fb0f..9215fcb6 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -4,16 +4,54 @@ public class Power.Utils { private static double total_y_delta = 0; private static double total_x_delta = 0; + public static bool handle_global_scroll_event (Gdk.ScrollEvent e, + bool natural_scroll_mouse, + bool natural_scroll_touchpad) { + if (e.get_device () == null) { + return false; + } + + var dir = calculate_delta (e, natural_scroll_mouse, natural_scroll_touchpad); + + if (dir.abs () > 0.0) { + total_y_delta = 0.0; + total_x_delta = 0.0; + var delta = (Power.Services.BrightnessManager.get_default ().get_global_brightness () + dir * BRIGHTNESS_STEP); + Power.Services.BrightnessManager.get_default () + .set_global_brightness (delta); + } + + return Gdk.EVENT_STOP; + } + + public static bool handle_local_scroll_event (Gdk.ScrollEvent e, + bool natural_scroll_mouse, + bool natural_scroll_touchpad, + int index) { + if (e.get_device () == null) { + return false; + } + + var dir = calculate_delta (e, natural_scroll_mouse, natural_scroll_touchpad); + + if (dir.abs () > 0.0) { + total_y_delta = 0.0; + total_x_delta = 0.0; + var delta = (Power.Services.BrightnessManager.get_default ().get_monitor_brightness (index) + dir * BRIGHTNESS_STEP); + Power.Services.BrightnessManager.get_default () + .set_monitor_brightness (index, delta); + } + + return Gdk.EVENT_STOP; + } + /* Smooth scrolling vertical support. Accumulate delta_y until threshold exceeded before actioning */ - public static bool handle_scroll_event (Gdk.ScrollEvent e, - bool natural_scroll_mouse, - bool natural_scroll_touchpad) { + private static double calculate_delta (Gdk.ScrollEvent e, + bool natural_scroll_mouse, + bool natural_scroll_touchpad) { var dir = 0.0; bool natural_scroll; var event_device = e.get_device (); - if (event_device == null) { - return false; - } if (event_device.source == Gdk.InputSource.MOUSE) { natural_scroll = natural_scroll_mouse; @@ -27,31 +65,31 @@ public class Power.Utils { e.get_deltas (out delta_x, out delta_y); switch (e.get_direction ()) { - case Gdk.ScrollDirection.SMOOTH: - var abs_x = double.max (delta_x.abs (), 0.0001); - var abs_y = double.max (delta_y.abs (), 0.0001); - - if (abs_y / abs_x > 2.0) { - total_y_delta += delta_y; - } else if (abs_x / abs_y > 2.0) { - total_x_delta += delta_x; - } - - break; - case Gdk.ScrollDirection.UP: - total_y_delta = -1.0; - break; - case Gdk.ScrollDirection.DOWN: - total_y_delta = 1.0; - break; - case Gdk.ScrollDirection.LEFT: - total_x_delta = -1.0; - break; - case Gdk.ScrollDirection.RIGHT: - total_x_delta = 1.0; - break; - default: - break; + case Gdk.ScrollDirection.SMOOTH: + var abs_x = double.max (delta_x.abs (), 0.0001); + var abs_y = double.max (delta_y.abs (), 0.0001); + + if (abs_y / abs_x > 2.0) { + total_y_delta += delta_y; + } else if (abs_x / abs_y > 2.0) { + total_x_delta += delta_x; + } + + break; + case Gdk.ScrollDirection.UP: + total_y_delta = -1.0; + break; + case Gdk.ScrollDirection.DOWN: + total_y_delta = 1.0; + break; + case Gdk.ScrollDirection.LEFT: + total_x_delta = -1.0; + break; + case Gdk.ScrollDirection.RIGHT: + total_x_delta = 1.0; + break; + default: + break; } if (total_y_delta.abs () * BRIGHTNESS_STEP > 0.001) { @@ -60,13 +98,6 @@ public class Power.Utils { dir = natural_scroll ? -total_x_delta : total_x_delta; } - if (dir.abs () > 0.0) { - total_y_delta = 0.0; - total_x_delta = 0.0; - Power.Services.DeviceManager.get_default () - .change_global_brightness (dir * BRIGHTNESS_STEP); - } - - return Gdk.EVENT_STOP; + return dir; } } diff --git a/src/Widgets/PopoverWidget.vala b/src/Widgets/PopoverWidget.vala index 8be9f9f7..0085df5e 100644 --- a/src/Widgets/PopoverWidget.vala +++ b/src/Widgets/PopoverWidget.vala @@ -21,6 +21,7 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { public bool is_in_session { get; construct; default = false; } private static Services.DeviceManager dm; + private static Services.BrightnessManager brightness_manager; private Gtk.Revealer device_separator_revealer; @@ -32,6 +33,7 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { static construct { dm = Services.DeviceManager.get_default (); + brightness_manager = Services.BrightnessManager.get_default (); } construct { @@ -58,7 +60,7 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { }; var last_separator_revealer = new Gtk.Revealer () { - reveal_child = dm.get_monitor_count () > 0, + reveal_child = brightness_manager.present, child = last_separator, }; @@ -95,8 +97,8 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { append (device_list_revealer); append (device_separator_revealer); - if (dm.backlight.present) { - var screen_brightness = new ScreenBrightness (); + if (brightness_manager.present) { + var screen_brightness = new ScreenBrightnessList (); append (screen_brightness); } @@ -137,8 +139,8 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { } }); - dm.monitors_changed.connect (() => { - if (dm.get_monitor_count () > 0) { + brightness_manager.monitors_changed.connect (() => { + if (brightness_manager.present) { last_separator_revealer.reveal_child = true; } else { last_separator_revealer.reveal_child = false; @@ -147,6 +149,6 @@ public class Power.Widgets.PopoverWidget : Gtk.Box { } private void update_device_separator_revealer () { - device_separator_revealer.reveal_child = dm.backlight.present && dm.has_battery; + device_separator_revealer.reveal_child = brightness_manager.present && dm.has_battery; } } diff --git a/src/Widgets/ScreenBrightnessList.vala b/src/Widgets/ScreenBrightnessList.vala new file mode 100644 index 00000000..c582d0c8 --- /dev/null +++ b/src/Widgets/ScreenBrightnessList.vala @@ -0,0 +1,46 @@ +/* + * Copyright 2011-2021 elementary, Inc. (https://elementary.io) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +public class Power.Widgets.ScreenBrightnessList : Granite.Bin { + private Power.Services.BrightnessManager brightness_manager; + private Gtk.ListBox list_box; + + public bool natural_scroll_touchpad { get; set; } + public bool natural_scroll_mouse { get; set; } + + construct { + brightness_manager = Power.Services.BrightnessManager.get_default (); + + list_box = new Gtk.ListBox (); + child = list_box; + + populate_list (); + + brightness_manager.monitors_changed.connect (() => { + list_box.remove_all (); + populate_list (); + }); + } + + private void populate_list () { + for (int i = 0; i < brightness_manager.get_n_monitors (); i++) { + list_box.append (new ScreenBrightenssRow (i)); + } + } +} diff --git a/src/Widgets/ScreenBrightness.vala b/src/Widgets/ScreenBrightnessRow.vala similarity index 52% rename from src/Widgets/ScreenBrightness.vala rename to src/Widgets/ScreenBrightnessRow.vala index 19b4f528..08c252e0 100644 --- a/src/Widgets/ScreenBrightness.vala +++ b/src/Widgets/ScreenBrightnessRow.vala @@ -1,31 +1,24 @@ /* - * Copyright 2011-2021 elementary, Inc. (https://elementary.io) + * Copyright 2026 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, - * Boston, MA 02110-1301, USA. + * Authored by: Denis Garaev */ -public class Power.Widgets.ScreenBrightness : Granite.Bin { - private Power.Services.DeviceManager dm; - private Gtk.ListBox list_box; +public class Power.Widgets.ScreenBrightenssRow : Granite.Bin { + private Services.BrightnessManager brightness_manager; + + public int index { get; construct; } public bool natural_scroll_touchpad { get; set; } public bool natural_scroll_mouse { get; set; } + public ScreenBrightenssRow (int index) { + Object (index: index); + } + construct { - dm = Power.Services.DeviceManager.get_default (); + brightness_manager = Services.BrightnessManager.get_default (); var mouse_settings = new GLib.Settings ("org.gnome.desktop.peripherals.mouse"); mouse_settings.bind ("natural-scroll", this, "natural-scroll-mouse", SettingsBindFlags.DEFAULT); @@ -36,32 +29,18 @@ public class Power.Widgets.ScreenBrightness : Granite.Bin { scroll_controller.scroll.connect (on_scroll); add_controller (scroll_controller); - list_box = new Gtk.ListBox (); - child = list_box; - - populate_list (); - - dm.monitors_changed.connect (() => { - list_box.remove_all (); - populate_list (); - }); - } - - private void populate_list () { - for (int i = 0; i < dm.get_monitor_count (); i++) { - list_box.append (construct_row (i)); - } - } - - private Gtk.Widget construct_row (int index) { var image = new Gtk.Image.from_icon_name ("brightness-display-symbolic") { pixel_size = 48 }; - var monitor_label = new Gtk.Label (dm.get_monitor_data (index)) { + var monitor_label = new Gtk.Label (brightness_manager.get_monitor_name (index)) { halign = Gtk.Align.START }; + if (index == 0) { + monitor_label.set_text (monitor_label.get_text () + _(" (Primary)")); + } + var brightness_slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 1, 0.1) { margin_start = 2, margin_end = 2, @@ -88,13 +67,15 @@ public class Power.Widgets.ScreenBrightness : Granite.Bin { box.append (image); box.append (slider_box); + child = box; + ulong slider_signal = 0, dm_signal = 0; slider_signal = brightness_slider.value_changed.connect ((value) => { - SignalHandler.block (dm, dm_signal); - dm.set_monitor_brightness (index, value.get_value ()); - SignalHandler.unblock (dm, dm_signal); + SignalHandler.block (brightness_manager, dm_signal); + brightness_manager.set_monitor_brightness (index, value.get_value ()); + SignalHandler.unblock (brightness_manager, dm_signal); }); - dm_signal = dm.monitor_brightness_changed.connect ((ch_index, value) => { + dm_signal = brightness_manager.monitor_brightness_changed.connect ((ch_index, value) => { if (index != ch_index) { return; } @@ -104,11 +85,10 @@ public class Power.Widgets.ScreenBrightness : Granite.Bin { SignalHandler.unblock (brightness_slider, slider_signal); }); - brightness_slider.set_value (dm.get_monitor_brightness (index)); - return box; + brightness_slider.set_value (brightness_manager.get_monitor_brightness (index)); } private bool on_scroll (Gtk.EventControllerScroll controller, double dx, double dy) { - return Utils.handle_scroll_event ((Gdk.ScrollEvent) controller.get_current_event (), natural_scroll_mouse, natural_scroll_touchpad); + return Utils.handle_local_scroll_event ((Gdk.ScrollEvent) controller.get_current_event (), natural_scroll_mouse, natural_scroll_touchpad, index); } } diff --git a/src/meson.build b/src/meson.build index 7b074f87..e22d8267 100644 --- a/src/meson.build +++ b/src/meson.build @@ -14,8 +14,8 @@ config_in = configure_file( files = files( 'Indicator.vala', 'Utils.vala', - 'Services/Backlight/Backlight.vala', - 'Services/DBusInterfaces/BrightnessManager.vala', + 'Services/BrightnessManager.vala', + 'Services/DBusInterfaces/GalaBrightnessManager.vala', 'Services/DBusInterfaces/Device.vala', 'Services/DBusInterfaces/Properties.vala', 'Services/DBusInterfaces/UPower.vala', @@ -26,7 +26,8 @@ files = files( 'Widgets/DeviceRow.vala', 'Widgets/DisplayWidget.vala', 'Widgets/PopoverWidget.vala', - 'Widgets/ScreenBrightness.vala', + 'Widgets/ScreenBrightnessList.vala', + 'Widgets/ScreenBrightnessRow.vala', 'Widgets/PowerModeList.vala' ) From 0c4c43c31568529cd6fca742d9372bd25344f4ec Mon Sep 17 00:00:00 2001 From: Denis Garaev Date: Sun, 6 Sep 2026 22:26:00 +0300 Subject: [PATCH 4/4] Tweak appearance a bit --- src/Widgets/ScreenBrightnessList.vala | 10 +++++++++- src/Widgets/ScreenBrightnessRow.vala | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Widgets/ScreenBrightnessList.vala b/src/Widgets/ScreenBrightnessList.vala index c582d0c8..8ae88e7f 100644 --- a/src/Widgets/ScreenBrightnessList.vala +++ b/src/Widgets/ScreenBrightnessList.vala @@ -27,7 +27,9 @@ public class Power.Widgets.ScreenBrightnessList : Granite.Bin { construct { brightness_manager = Power.Services.BrightnessManager.get_default (); - list_box = new Gtk.ListBox (); + list_box = new Gtk.ListBox () { + selection_mode = Gtk.SelectionMode.NONE + }; child = list_box; populate_list (); @@ -40,6 +42,12 @@ public class Power.Widgets.ScreenBrightnessList : Granite.Bin { private void populate_list () { for (int i = 0; i < brightness_manager.get_n_monitors (); i++) { + if (i != 0) { + list_box.append (new Gtk.Separator (Gtk.Orientation.HORIZONTAL) { + margin_top = 3, + margin_bottom = 3 + }); + } list_box.append (new ScreenBrightenssRow (i)); } } diff --git a/src/Widgets/ScreenBrightnessRow.vala b/src/Widgets/ScreenBrightnessRow.vala index 08c252e0..ed4e94af 100644 --- a/src/Widgets/ScreenBrightnessRow.vala +++ b/src/Widgets/ScreenBrightnessRow.vala @@ -34,6 +34,8 @@ public class Power.Widgets.ScreenBrightenssRow : Granite.Bin { }; var monitor_label = new Gtk.Label (brightness_manager.get_monitor_name (index)) { + margin_start = 2, + margin_top = 2, halign = Gtk.Align.START };