Skip to content

Commit

Permalink
Merge pull request #407 from corecoding/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
corecoding authored Feb 13, 2024
2 parents a5d0f17 + f26068f commit 6dd6dfa
Show file tree
Hide file tree
Showing 10 changed files with 888 additions and 365 deletions.
112 changes: 87 additions & 25 deletions extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ var VitalsMenuButton = GObject.registerClass({
'icon-rx': 'network-download-symbolic.svg',
'icon-tx': 'network-upload-symbolic.svg' },
'storage' : { 'icon': 'storage-symbolic.svg' },
'battery' : { 'icon': 'battery-symbolic.svg' }
'battery' : { 'icon': 'battery-symbolic.svg' },
'gpu' : { 'icon': 'gpu-symbolic.svg' }
}

this._warnings = [];
Expand All @@ -49,6 +50,9 @@ var VitalsMenuButton = GObject.registerClass({
this._hotIcons = {};
this._groups = {};
this._widths = {};
this._numGpus = 1;
this._newGpuDetected = false;
this._newGpuDetectedCount = 0;
this._last_query = new Date().getTime();

this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons);
Expand All @@ -72,7 +76,7 @@ var VitalsMenuButton = GObject.registerClass({
this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this));
this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this));

let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info' ];
let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info' ];
for (let setting of Object.values(settings))
this._addSettingChangedSignal(setting, this._redrawMenu.bind(this));

Expand All @@ -95,21 +99,14 @@ var VitalsMenuButton = GObject.registerClass({
// groups associated sensors under accordion menu
if (sensor in this._groups) continue;

this._groups[sensor] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(sensor)), true);
this._groups[sensor].icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[sensor]['icon']);
//handle gpus separately.
if (sensor === 'gpu') continue;

// hide menu items that user has requested to not include
if (!this._settings.get_boolean('show-' + sensor))
this._groups[sensor].actor.hide();

if (!this._groups[sensor].status) {
this._groups[sensor].status = this._defaultLabel();
this._groups[sensor].actor.insert_child_at_index(this._groups[sensor].status, 4);
this._groups[sensor].status.text = _('No Data');
}

this.menu.addMenuItem(this._groups[sensor]);
this._initializeMenuGroup(sensor, sensor);
}

for (let i = 1; i <= this._numGpus; i++)
this._initializeMenuGroup('gpu#' + i, 'gpu', (this._numGpus > 1 ? ' ' + i : ''));

// add separator
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
Expand All @@ -135,7 +132,7 @@ var VitalsMenuButton = GObject.registerClass({
refreshButton.connect('clicked', (self) => {
// force refresh by clearing history
this._sensors.resetHistory();
this._values.resetHistory();
this._values.resetHistory(this._numGpus);

// make sure timer fires at next full interval
this._updateTimeChanged();
Expand Down Expand Up @@ -179,6 +176,24 @@ var VitalsMenuButton = GObject.registerClass({
});
}

_initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) {
this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(groupName) + menuSuffix), true);
this._groups[groupName].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(groupName));

// hide menu items that user has requested to not include
if (!this._settings.get_boolean('show-' + optionName))
this._groups[groupName].actor.hide();

if (!this._groups[groupName].status) {
this._groups[groupName].status = this._defaultLabel();
this._groups[groupName].actor.insert_child_at_index(this._groups[groupName].status, 4);
this._groups[groupName].status.text = _('No Data');
}

if(position == -1) this.menu.addMenuItem(this._groups[groupName]);
else this.menu.addMenuItem(this._groups[groupName], position);
}

_createRoundButton(iconName) {
let button = new St.Button({
style_class: 'message-list-clear-button button vitals-button-action'
Expand Down Expand Up @@ -248,7 +263,7 @@ var VitalsMenuButton = GObject.registerClass({
style_class: 'vitals-panel-label',
text: (value)?value:'\u2026', // ...
y_expand: true,
y_align: Clutter.ActorAlign.START
y_align: Clutter.ActorAlign.CENTER
});

// attempt to prevent ellipsizes
Expand All @@ -266,7 +281,13 @@ var VitalsMenuButton = GObject.registerClass({

_showHideSensorsChanged(self, sensor) {
this._sensors.resetHistory();
this._groups[sensor.substr(5)].visible = this._settings.get_boolean(sensor);

const sensorName = sensor.substr(5);
if(sensorName === 'gpu') {
for(let i = 1; i <= this._numGpus; i++)
this._groups[sensorName + '#' + i].visible = this._settings.get_boolean(sensor);
} else
this._groups[sensorName].visible = this._settings.get_boolean(sensor);
}

_positionInPanelChanged() {
Expand Down Expand Up @@ -322,7 +343,7 @@ var VitalsMenuButton = GObject.registerClass({

this._drawMenu();
this._sensors.resetHistory();
this._values.resetHistory();
this._values.resetHistory(this._numGpus);
this._querySensors();
}

Expand Down Expand Up @@ -370,7 +391,8 @@ var VitalsMenuButton = GObject.registerClass({
}
}
}

if(key === "_gpu#1_domain_number_")
console.error('UPDATING: ', key);
// have we added this sensor before?
let item = this._sensorMenuItems[key];
if (item) {
Expand All @@ -394,7 +416,7 @@ var VitalsMenuButton = GObject.registerClass({
let split = sensor.type.split('-');
let type = split[0];
let icon = (split.length == 2)?'icon-' + split[1]:'icon';
let gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[type][icon]);
let gicon = Gio.icon_new_for_string(this._sensorIconPath(type, icon));

let item = new MenuItem.MenuItem(gicon, key, sensor.label, sensor.value, this._hotLabels[key]);
item.connect('toggle', (self) => {
Expand Down Expand Up @@ -460,17 +482,27 @@ var VitalsMenuButton = GObject.registerClass({
});

// second condition prevents crash due to issue #225, which started when _max_ was moved to the end
if (type == 'default' || !(type in this._sensorIcons)) {
icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons['system']['icon']);
// don't use the default system icon if the type is a gpu; use the universal gpu icon instead
if (type == 'default' || (!(type in this._sensorIcons) && !type.startsWith('gpu'))) {
icon.gicon = Gio.icon_new_for_string(this._sensorIconPath('system'));
} else if (!this._settings.get_boolean('hide-icons')) { // support for hide icons #80
let iconObj = (split.length == 2)?'icon-' + split[1]:'icon';
icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[type][iconObj]);
icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(type, iconObj));
}

return icon;
}

_sensorIconPath(sensor, icon = 'icon') {
// If the sensor is a numbered gpu, use the gpu icon. Otherwise use whatever icon associated with the sensor name.
let sensorKey = sensor;
if(sensor.startsWith('gpu')) sensorKey = 'gpu';

return this._extensionObject.path + '/icons/' + this._sensorIcons[sensorKey][icon];
}

_ucFirst(string) {
if(string.startsWith('gpu')) return 'Graphics';
return string.charAt(0).toUpperCase() + string.slice(1);
}

Expand Down Expand Up @@ -524,7 +556,8 @@ var VitalsMenuButton = GObject.registerClass({
this._last_query = now;

this._sensors.query((label, value, type, format) => {
let key = '_' + type.replace('-group', '') + '_' + label.replace(' ', '_').toLowerCase() + '_';
const typeKey = type.replace('-group', '');
let key = '_' + typeKey + '_' + label.replace(' ', '_').toLowerCase() + '_';

// if a sensor is disabled, gray it out
if (key in this._sensorMenuItems) {
Expand All @@ -534,11 +567,39 @@ var VitalsMenuButton = GObject.registerClass({
if (value == 'disabled') return;
}

// add/initialize any gpu groups that we haven't added yet
if(typeKey.startsWith('gpu') && typeKey !== 'gpu#1') {
const split = typeKey.split('#');
if(split.length == 2 && this._numGpus < parseInt(split[1])) {
// occasionally two lines from nvidia-smi will be read at once
// so we only actually update the number of gpus if we have recieved multiple lines at least 3 times in a row
// i.e. we make sure that mutiple queries have detected a new gpu back-to-back
if(this._newGpuDetectedCount < 2) {
this._newGpuDetected = true;
return;
}

this._numGpus = parseInt(split[1]);
this._newGpuDetectedCount = 0;
this._newGpuDetected = false;
// change label for gpu 1 from "Graphics" to "Graphics 1" since we have multiple gpus now
this._groups['gpu#1'].label.text = this._ucFirst('gpu#1') + ' 1';
for(let i = 2; i <= this._numGpus; i++)
if(!('gpu#' + i in this._groups))
this._initializeMenuGroup('gpu#' + i, 'gpu', ' ' + i, Object.keys(this._groups).length);
}
}

let items = this._values.returnIfDifferent(dwell, label, value, type, format, key);
for (let item of Object.values(items))
this._updateDisplay(_(item[0]), item[1], item[2], item[3]);
}, dwell);

//if a new gpu has been detected during the last query, then increment the amount of times we've detected a new gpu
if(this._newGpuDetected) this._newGpuDetectedCount++;
else this._newGpuDetectedCount = 0;
this._newGpuDetected = false;

if (this._warnings.length > 0) {
this._notify('Vitals', this._warnings.join("\n"), 'folder-symbolic');
this._warnings = [];
Expand All @@ -555,6 +616,7 @@ var VitalsMenuButton = GObject.registerClass({

destroy() {
this._destroyTimer();
this._sensors.destroy();

for (let signal of Object.values(this._settingChangedSignals))
this._settings.disconnect(signal);
Expand Down
66 changes: 66 additions & 0 deletions helpers/subprocess.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
const ByteArray = imports.byteArray;

var Decoder;
try {
Decoder = new TextDecoder('utf-8');
} catch(error) {}

// convert Uint8Array into a literal string
function convertUint8ArrayToString(contents) {
// Starting with Gnome 41, we use TextDecoder as ByteArray is deprecated
if (Decoder)
return Decoder.decode(contents).trim();

// Supports ByteArray on Gnome 40
// fixes #304, replaces invalid character
contents[contents.indexOf(208)] = 0;
return ByteArray.toString(contents).trim();
}

export function SubProcess(command) {
this.sub_process = Gio.Subprocess.new(command, Gio.SubprocessFlags.STDOUT_PIPE);
this.stdout = this.sub_process.get_stdout_pipe();
}

SubProcess.prototype.read = function(delimiter = '') {
return new Promise((resolve, reject) => {
this.stdout.read_bytes_async(512, GLib.PRIORITY_LOW, null, function(stdout, res) {
try {
let read_bytes = stdout.read_bytes_finish(res).get_data();

// convert contents to string
let read_str = convertUint8ArrayToString(read_bytes);

// split read_str by delimiter if passed in
if (delimiter) {
if (read_str == '')
read_str = []; // EOF, ''.split(delimiter) would return ['']
else
read_str = read_str.split(delimiter);
}

// return results
resolve(read_str);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING)) {
// previous read attempt is still waiting for something from stdout
// ignore second attempt, return empty data (like EOF)
if (delimiter) resolve([]);
else resolve('');
} else {
reject(e.message);
}
}
});
});
};

SubProcess.prototype.terminate = function() {
const SIGINT = 2;
this.sub_process.send_signal(SIGINT);
this.sub_process = null;
this.stdout.close_async(GLib.PRIORITY_LOW, null, null);
this.stdout = null;
};
15 changes: 15 additions & 0 deletions icons/gpu-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
],
"url": "https://github.com/corecoding/Vitals",
"uuid": "[email protected]",
"version": 63
"version": 64
}
5 changes: 3 additions & 2 deletions prefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ const Settings = new GObject.Class({
'show-network', 'show-storage', 'use-higher-precision',
'alphabetize', 'hide-zeros', 'include-public-ip',
'show-battery', 'fixed-widths', 'hide-icons',
'menu-centered', 'include-static-info' ];
'menu-centered', 'include-static-info',
'show-gpu', 'include-static-gpu-info' ];

for (let key in sensors) {
let sensor = sensors[key];
Expand Down Expand Up @@ -90,7 +91,7 @@ const Settings = new GObject.Class({
}

// makes individual sensor preference boxes appear
sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor' ];
sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor', 'gpu' ];
for (let key in sensors) {
let sensor = sensors[key];

Expand Down
Loading

0 comments on commit 6dd6dfa

Please sign in to comment.