[gnome] Update extensions
This commit is contained in:
@ -11,6 +11,14 @@ import GLib from 'gi://GLib';
|
||||
import GObject from 'gi://GObject';
|
||||
import Gio from 'gi://Gio';
|
||||
|
||||
/*
|
||||
* On some models the start threshold 0 is not allowed, instead 95 is used (Ex: E14 Gen 3)
|
||||
* See issues:
|
||||
* #8: https://gitlab.com/marcosdalvarez/thinkpad-battery-threshold-extension/-/issues/8
|
||||
* #20: https://gitlab.com/marcosdalvarez/thinkpad-battery-threshold-extension/-/issues/20
|
||||
*/
|
||||
const ALTERNATIVE_DISABLED_START = /(Think[pP]ad) (E14 Gen 3|P14s Gen 2a)/;
|
||||
|
||||
// Driver constants
|
||||
const BASE_PATH = '/sys/class/power_supply';
|
||||
const START_FILE_OLD = 'charge_start_threshold'; // kernel 4.17 and newer
|
||||
@ -24,7 +32,7 @@ const END_FILE_NEW = 'charge_control_end_threshold'; // kernel 5.9 and newer
|
||||
* @param {string} path Path of file
|
||||
* @returns {string} File contents
|
||||
*/
|
||||
const readFile = function(path) {
|
||||
const readFile = function (path) {
|
||||
try {
|
||||
const f = Gio.File.new_for_path(path);
|
||||
const [, contents,] = f.load_contents(null);
|
||||
@ -41,7 +49,7 @@ const readFile = function(path) {
|
||||
* @param {string} path Path of file
|
||||
* @returns {number|null} Return a integer or null
|
||||
*/
|
||||
const readFileInt = function(path) {
|
||||
const readFileInt = function (path) {
|
||||
try {
|
||||
const v = readFile(path);
|
||||
if (v) {
|
||||
@ -52,15 +60,15 @@ const readFileInt = function(path) {
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test file/direcory exists
|
||||
* Test file/directory exists
|
||||
*
|
||||
* @param {string} path File/directory path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const fileExists = function(path) {
|
||||
const fileExists = function (path) {
|
||||
try {
|
||||
const f = Gio.File.new_for_path(path);
|
||||
return f.query_exists(null);
|
||||
@ -79,7 +87,7 @@ const Environment = GObject.registerClass({
|
||||
get productVersion() {
|
||||
if (this._productVersion === undefined) {
|
||||
const tmp = readFile('/sys/class/dmi/id/product_version');
|
||||
if (tmp) {
|
||||
if (tmp) {
|
||||
// Remove non-alphanumeric characters
|
||||
const sanitize = /([^ A-Za-z0-9])+/g;
|
||||
this._productVersion = tmp.replace(sanitize, '');
|
||||
@ -109,23 +117,23 @@ const Environment = GObject.registerClass({
|
||||
|
||||
get kernelMajorVersion() {
|
||||
if (this._kernelMajorVersion === undefined) {
|
||||
[this._kernelMajorVersion , this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
|
||||
[this._kernelMajorVersion, this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
|
||||
}
|
||||
return this._kernelMajorVersion;
|
||||
}
|
||||
|
||||
get kernelMinorVersion() {
|
||||
if (this._kernelMinorVersion === undefined) {
|
||||
[this._kernelMajorVersion , this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
|
||||
[this._kernelMajorVersion, this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
|
||||
}
|
||||
return this._kernelMinorVersion;
|
||||
}
|
||||
|
||||
checkMinKernelVersion(major, minor) {
|
||||
return (
|
||||
this.kernelMajorVersion > major ||
|
||||
(this,this.kernelMajorVersion === major && this.kernelMinorVersion >= minor)
|
||||
);
|
||||
this.kernelMajorVersion > major ||
|
||||
(this.kernelMajorVersion === major && this.kernelMinorVersion >= minor)
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
@ -140,7 +148,7 @@ const Runnable = GObject.registerClass({
|
||||
GTypeName: 'Runnable',
|
||||
Signals: {
|
||||
'command-completed': {
|
||||
param_types: [GObject.TYPE_JSOBJECT /* erro */]
|
||||
param_types: [GObject.TYPE_JSOBJECT /* error */]
|
||||
}
|
||||
}
|
||||
}, class Runnable extends GObject.Object {
|
||||
@ -167,8 +175,8 @@ const Runnable = GObject.registerClass({
|
||||
try {
|
||||
GLib.spawn_check_exit_status(status);
|
||||
this.emit('command-completed', null);
|
||||
} catch(e) {
|
||||
if (e.code == 126) {
|
||||
} catch (e) {
|
||||
if (e.code == 126) {
|
||||
// Cancelled
|
||||
} else {
|
||||
logError(e);
|
||||
@ -272,13 +280,6 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
throw Error('Battery name not defined');
|
||||
}
|
||||
|
||||
// Signals handlers IDs
|
||||
this._monitorId = null;
|
||||
this._settingStartId = null;
|
||||
this._settingEndId = null;
|
||||
this._startId = null;
|
||||
this._endId = null;
|
||||
|
||||
// Battery directory
|
||||
this._baseDirectoryPath = `${BASE_PATH}/${this.name}`;
|
||||
this._baseDirectory = Gio.File.new_for_path(this._baseDirectoryPath);
|
||||
@ -334,28 +335,29 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
// Update flags on changes in threshold values
|
||||
this._startId = this.connect(
|
||||
'notify::start-value', () => {
|
||||
this.isActive = this._checkActive();
|
||||
this.isAvailable = this.startValue !== null || this.endValue !== null;
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
this._updateStatuses();
|
||||
}
|
||||
);
|
||||
this._endId = this.connect(
|
||||
'notify::end-value', () => {
|
||||
this.isActive = this._checkActive();
|
||||
this.isAvailable = this.startValue !== null || this.endValue !== null;
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
this._updateStatuses();
|
||||
}
|
||||
);
|
||||
|
||||
// Update pending changes flag on changes to setting values
|
||||
this._settingStartId = this.settings.connect(
|
||||
`changed::start-${this.name.toLowerCase()}`, () => {
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
this._updateStatuses();
|
||||
}
|
||||
);
|
||||
this._settingEndId = this.settings.connect(
|
||||
`changed::end-${this.name.toLowerCase()}`, () => {
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
this._updateStatuses();
|
||||
}
|
||||
);
|
||||
|
||||
this._settingDisabledValueId = this.settings.connect(
|
||||
`changed::disabled-start-${this.name.toLowerCase()}-value`, () => {
|
||||
this._updateStatuses();
|
||||
}
|
||||
);
|
||||
|
||||
@ -366,33 +368,34 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if thresholds is active
|
||||
*
|
||||
* @returns {boolean} Returns true if the battery has active thresholds
|
||||
* Update the statuses
|
||||
*/
|
||||
_checkActive() {
|
||||
// RegExs
|
||||
/* On some models the start threshold 0 is not allowed, instead 95 is used (Ex: E14 Gen 3)
|
||||
* See issue #8: https://gitlab.com/marcosdalvarez/thinkpad-battery-threshold-extension/-/issues/8
|
||||
*/
|
||||
const disableStart95 = /(Think[pP]ad) (E14 Gen 3)/;
|
||||
_updateStatuses() {
|
||||
// Check if is available
|
||||
this.isAvailable = this.startValue !== null || this.endValue !== null;
|
||||
|
||||
if (this.environment.productVersion.search(disableStart95) >= 0) {
|
||||
return (this.startValue !== 95 && this.startValue !== null) || (this.endValue !== 100 && this.endValue !== null);
|
||||
// Check if thresholds is active
|
||||
const disabledStartValue = this.settings.get_int(`disabled-start-${this.name.toLowerCase()}-value`);
|
||||
if (disabledStartValue === -1) {
|
||||
// Use model detection
|
||||
if (this.environment.productVersion.search(ALTERNATIVE_DISABLED_START) >= 0) {
|
||||
this.isActive = (this.startValue !== null && this.startValue !== 95) || (this.endValue !== null && this.endValue !== 100);
|
||||
} else {
|
||||
this.isActive = (this.startValue !== null && this.startValue !== 0) || (this.endValue !== null && this.endValue !== 100);
|
||||
}
|
||||
} else {
|
||||
return (this.startValue !== 0 && this.startValue !== null) || (this.endValue !== 100 && this.endValue !== null);
|
||||
// Use user settings
|
||||
this.isActive = (this.startValue !== null && this.startValue !== disabledStartValue) || (this.endValue !== null && this.endValue !== 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are changes to the settings that need to be applied
|
||||
*
|
||||
* @returns {boolean} Returns true if there are changes to the settings that need to be applied
|
||||
*/
|
||||
_checkPendingChanges() {
|
||||
const startSetting = this.settings.get_int(`start-${this.name.toLowerCase()}`);
|
||||
const endSetting = this.settings.get_int(`end-${this.name.toLowerCase()}`);
|
||||
return ((this.startValue != null && this.startValue !== startSetting) || (this.endValue != null && this.endValue !== endSetting));
|
||||
if (this.isActive) {
|
||||
this.pendingChanges = (this.startValue !== null && this.startValue !== startSetting) || (this.endValue !== null && this.endValue !== endSetting);
|
||||
} else {
|
||||
this.pendingChanges = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -419,7 +422,7 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -446,12 +449,12 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
}
|
||||
|
||||
get isAvailable() {
|
||||
return this._isAvaialble;
|
||||
return this._isAvailable;
|
||||
}
|
||||
|
||||
set isAvailable(value) {
|
||||
if (this.isAvailable !== value) {
|
||||
this._isAvaialble = value;
|
||||
this._isAvailable = value;
|
||||
this.notify('is-available');
|
||||
}
|
||||
}
|
||||
@ -627,6 +630,9 @@ export const ThinkPadBattery = GObject.registerClass({
|
||||
if (this._settingEndId) {
|
||||
this.settings.disconnect(this._settingEndId);
|
||||
}
|
||||
if (this._settingDisabledValueId) {
|
||||
this.settings.disconnect(this._settingDisabledValueId);
|
||||
}
|
||||
if (this._monitorId) {
|
||||
this._baseMonitor.disconnect(this._monitorId);
|
||||
}
|
||||
@ -660,6 +666,13 @@ export const ThinkPad = GObject.registerClass({
|
||||
GObject.ParamFlags.READABLE,
|
||||
false
|
||||
),
|
||||
'pending-changes': GObject.ParamSpec.boolean(
|
||||
'pending-changes',
|
||||
'Pending changes',
|
||||
'Indicates if the current values do not match the configured values',
|
||||
GObject.ParamFlags.READABLE,
|
||||
false
|
||||
),
|
||||
'batteries': GObject.ParamSpec.jsobject(
|
||||
'batteries',
|
||||
'Batteries',
|
||||
@ -706,12 +719,12 @@ export const ThinkPad = GObject.registerClass({
|
||||
this.batteries = [
|
||||
new ThinkPadBattery({
|
||||
'environment': this.environment,
|
||||
'name': 'BAT0',
|
||||
'name': 'BAT0',
|
||||
'settings': this.settings
|
||||
}),
|
||||
new ThinkPadBattery({
|
||||
'environment': this.environment,
|
||||
'name': 'BAT1',
|
||||
'name': 'BAT1',
|
||||
'settings': this.settings
|
||||
}),
|
||||
];
|
||||
@ -728,6 +741,11 @@ export const ThinkPad = GObject.registerClass({
|
||||
this.isActive = this._checkActive();
|
||||
}
|
||||
);
|
||||
const pendingChangesId = battery.connect(
|
||||
'notify::pending-changes', (bat) => {
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
}
|
||||
);
|
||||
const enableCompletedId = battery.connect(
|
||||
'enable-completed', (bat, error) => {
|
||||
this.emit('enable-battery-completed', bat, error);
|
||||
@ -744,6 +762,7 @@ export const ThinkPad = GObject.registerClass({
|
||||
// Load initial values
|
||||
this.isAvailable = this._checkAvailable();
|
||||
this.isActive = this._checkActive();
|
||||
this.pendingChanges = this._checkPendingChanges();
|
||||
}
|
||||
|
||||
get environment() {
|
||||
@ -754,12 +773,12 @@ export const ThinkPad = GObject.registerClass({
|
||||
}
|
||||
|
||||
get isAvailable() {
|
||||
return this._isAvaialble;
|
||||
return this._isAvailable;
|
||||
}
|
||||
|
||||
set isAvailable(value) {
|
||||
if (this.isAvailable !== value) {
|
||||
this._isAvaialble = value;
|
||||
this._isAvailable = value;
|
||||
this.notify('is-available');
|
||||
}
|
||||
}
|
||||
@ -775,6 +794,28 @@ export const ThinkPad = GObject.registerClass({
|
||||
}
|
||||
}
|
||||
|
||||
get pendingChanges() {
|
||||
return this._pendingChanges;
|
||||
}
|
||||
|
||||
set pendingChanges(value) {
|
||||
if (this.pendingChanges !== value) {
|
||||
this._pendingChanges = value;
|
||||
this.notify('pending-changes');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are changes to the settings that need to be applied
|
||||
*
|
||||
* @returns {boolean} Returns true if there are changes to the settings that need to be applied
|
||||
*/
|
||||
_checkPendingChanges() {
|
||||
return this.batteries.some(bat => {
|
||||
return bat.pendingChanges;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if at least one battery has the thresholds active
|
||||
*
|
||||
@ -817,7 +858,7 @@ export const ThinkPad = GObject.registerClass({
|
||||
runnable.connect('command-completed', (obj, error) => {
|
||||
this.emit('enable-all-completed', error);
|
||||
})
|
||||
runnable.run();
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -840,7 +881,7 @@ export const ThinkPad = GObject.registerClass({
|
||||
runnable.connect('command-completed', (obj, error) => {
|
||||
this.emit('disable-all-completed', error);
|
||||
})
|
||||
runnable.run();
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
@ -37,9 +37,6 @@ const BatteryItem = GObject.registerClass({
|
||||
|
||||
this._settings = settings;
|
||||
this._battery = battery;
|
||||
|
||||
// Flag to prevent the PopupImageMenuItem from being activated when the reload icon is clicked
|
||||
this._reloading = false;
|
||||
|
||||
const box = new St.BoxLayout({
|
||||
'opacity': 128,
|
||||
@ -58,9 +55,8 @@ const BatteryItem = GObject.registerClass({
|
||||
});
|
||||
this._reload.connectObject(
|
||||
'button-press-event', () => {
|
||||
this._reloading = true;
|
||||
this._battery.enable();
|
||||
return true; // Does not prevent event propagation???
|
||||
return true;
|
||||
},
|
||||
this
|
||||
);
|
||||
@ -91,10 +87,7 @@ const BatteryItem = GObject.registerClass({
|
||||
// Menu item action
|
||||
this.connectObject(
|
||||
'activate', () => {
|
||||
if (!this._reloading) {
|
||||
this._battery.toggle();
|
||||
}
|
||||
this._reloading = false;
|
||||
this._battery.toggle();
|
||||
},
|
||||
'destroy', () => {
|
||||
this._settings.disconnectObject(this);
|
||||
@ -123,7 +116,11 @@ const BatteryItem = GObject.registerClass({
|
||||
if (this._battery.isActive) {
|
||||
// TRANSLATORS: %s is the name of the battery.
|
||||
this.label.text = _('Disable thresholds (%s)').format(this._battery.name);
|
||||
this.setIcon(getIcon('threshold-active', colorMode));
|
||||
if (this._battery.pendingChanges) {
|
||||
this.setIcon(getIcon('threshold-active-warning', colorMode));
|
||||
} else {
|
||||
this.setIcon(getIcon('threshold-active', colorMode));
|
||||
}
|
||||
// Status text
|
||||
const showCurrentValues = this._settings.get_boolean('show-current-values');
|
||||
if (showCurrentValues) {
|
||||
@ -249,6 +246,9 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
'notify::is-active', () => {
|
||||
this._updateIndicator();
|
||||
},
|
||||
'notify::pending-changes', () => {
|
||||
this._updateIndicator();
|
||||
},
|
||||
'enable-battery-completed', (driver, battery, error) => {
|
||||
if (!error) {
|
||||
this._notifyEnabled(
|
||||
@ -330,6 +330,15 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
this._driver = null;
|
||||
this._extension = null;
|
||||
});
|
||||
|
||||
// Pending changes alert
|
||||
this._driver.batteries.every(battery => {
|
||||
if (battery.pendingChanges && battery.isActive) {
|
||||
this._notify(_('Battery Threshold'), _('The currently set thresholds do not match the configured ones'), 'threshold-active-warning', false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -339,7 +348,11 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
const colorMode = this._settings.get_boolean('color-mode');
|
||||
if (this._driver.isAvailable) {
|
||||
if (this._driver.isActive) {
|
||||
this._indicator.gicon = getIcon('threshold-active', colorMode);
|
||||
if (this._driver.pendingChanges) {
|
||||
this._indicator.gicon = getIcon('threshold-active-warning', colorMode);
|
||||
} else {
|
||||
this._indicator.gicon = getIcon('threshold-active', colorMode);
|
||||
}
|
||||
} else {
|
||||
this._indicator.gicon = getIcon('threshold-inactive', colorMode);
|
||||
}
|
||||
@ -352,14 +365,14 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
case 0: // Active
|
||||
this._indicator.visible = this._driver.isActive;
|
||||
break;
|
||||
case 1: // Inactive
|
||||
this._indicator.visible = !this._driver.isActive;
|
||||
case 1: // Inactive (or pending changes)
|
||||
this._indicator.visible = !this._driver.isActive || this._driver.pendingChanges;
|
||||
break;
|
||||
case 2: // Always
|
||||
this._indicator.visible = true;
|
||||
break;
|
||||
case 3: // Never
|
||||
this._indicator.visible = false;
|
||||
case 3: // Never (or pending changes)
|
||||
this._indicator.visible = this._driver.pendingChanges;
|
||||
break;
|
||||
default:
|
||||
this._indicator.visible = true;
|
||||
@ -368,15 +381,15 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notificaion.
|
||||
* Show notification.
|
||||
*
|
||||
* @param {string} msg Title
|
||||
* @param {string} details Message
|
||||
* @param {string} iconName Icon name
|
||||
* @param {boolean} [transient=true] Transient notification
|
||||
*/
|
||||
_notify(msg, details, iconName) {
|
||||
_notify(msg, details, iconName, transient=true) {
|
||||
if (!this._settings.get_boolean('show-notifications')) return;
|
||||
const colorMode = this._settings.get_boolean('color-mode');
|
||||
if (SHELL_VERSION === 45) {
|
||||
const source = new MessageTray.Source(this._name);
|
||||
Main.messageTray.add(source);
|
||||
@ -384,9 +397,9 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
source,
|
||||
msg,
|
||||
details,
|
||||
{gicon: getIcon(iconName, colorMode)}
|
||||
{gicon: getIcon(iconName, true)}
|
||||
);
|
||||
notification.setTransient(true);
|
||||
notification.setTransient(transient);
|
||||
source.showNotification(notification);
|
||||
} else {
|
||||
const source = new MessageTray.Source({'title': this._name});
|
||||
@ -395,8 +408,8 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
source: source,
|
||||
title: msg,
|
||||
body: details,
|
||||
isTransient: true,
|
||||
gicon: getIcon(iconName, colorMode)
|
||||
isTransient: transient,
|
||||
gicon: getIcon(iconName, true)
|
||||
});
|
||||
source.addNotification(notification);
|
||||
}
|
||||
@ -408,7 +421,7 @@ export const ThresholdIndicator = GObject.registerClass({
|
||||
* @param {string} message Message
|
||||
*/
|
||||
_notifyError(message) {
|
||||
this._notify(_('Battery Threshold'), message, 'threshold-error');
|
||||
this._notify(_('Battery Threshold'), message, 'threshold-error', false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user