diff --git a/prefs.js b/prefs.js index ce7885c..a27e92a 100644 --- a/prefs.js +++ b/prefs.js @@ -38,6 +38,70 @@ const Settings = new GObject.Class({ this._bind_settings(); }, + _setup_storage_list: function() { + let entryWidget = this.builder.get_object('storage-path'); + let parentBox = entryWidget.get_parent(); + parentBox.remove(entryWidget); + + let listContainer = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 6 }); + parentBox.append(listContainer); + + let paths = this._settings.get_strv('storage-path'); + + let renderRows = () => { + let child = listContainer.get_first_child(); + while (child) { + listContainer.remove(child); + child = listContainer.get_first_child(); + } + + let currentPaths = this._settings.get_strv('storage-path') || []; + currentPaths.forEach((path, index) => { + let row = new Gtk.Box({ spacing: 6 }); + let entry = new Gtk.Entry({ text: path, hexpand: true }); + + entry.connect('activate', (w) => { + let p = this._settings.get_strv('storage-path'); + p[index] = w.get_text(); + this._settings.set_strv('storage-path', p); + }); + let focusController = new Gtk.EventControllerFocus(); + focusController.connect('leave', () => { + let p = this._settings.get_strv('storage-path'); + let text = entry.get_text(); + if (p[index] !== text) { + p[index] = text; + this._settings.set_strv('storage-path', p); + } + }); + entry.add_controller(focusController); + + let delBtn = new Gtk.Button({ icon_name: 'list-remove-symbolic' }); + delBtn.connect('clicked', () => { + let p = this._settings.get_strv('storage-path') || []; + p.splice(index, 1); + this._settings.set_strv('storage-path', p); + renderRows(); + }); + + row.append(entry); + row.append(delBtn); + listContainer.append(row); + }); + + let addBtn = new Gtk.Button({ label: _('Add Path'), icon_name: 'list-add-symbolic' }); + addBtn.connect('clicked', () => { + let p = this._settings.get_strv('storage-path'); + p.push('/'); + this._settings.set_strv('storage-path', p); + renderRows(); + }); + listContainer.append(addBtn); + }; + + renderRows(); + }, + // Bind the gtk window to the schema settings _bind_settings: function() { let widget; @@ -77,7 +141,9 @@ const Settings = new GObject.Class({ this._settings.bind('update-time', this.builder.get_object('update-time'), 'value', Gio.SettingsBindFlags.DEFAULT); // process individual text entry sensor preferences - sensors = [ 'storage-path', 'monitor-cmd' ]; + this._setup_storage_list(); + sensors = [ 'monitor-cmd' ]; + for (let key in sensors) { let sensor = sensors[key]; diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index e60cb0f..e4978b1 100644 Binary files a/schemas/gschemas.compiled and b/schemas/gschemas.compiled differ diff --git a/schemas/org.gnome.shell.extensions.vitals.gschema.xml b/schemas/org.gnome.shell.extensions.vitals.gschema.xml index 72f8f40..3d28bd9 100644 --- a/schemas/org.gnome.shell.extensions.vitals.gschema.xml +++ b/schemas/org.gnome.shell.extensions.vitals.gschema.xml @@ -86,8 +86,8 @@ Network speed format Should speed display in bits or bytes? - - "/" + + ["/"] Storage path Storage path for monitoring diff --git a/sensors.js b/sensors.js index be8df5d..4b94b5a 100644 --- a/sensors.js +++ b/sensors.js @@ -72,8 +72,7 @@ export const Sensors = GObject.registerClass({ this._storageDevice = ''; this._findStorageDevice(); - this._lastRead = 0; - this._lastWrite = 0; + this._storageHistory = {}; } } @@ -349,48 +348,69 @@ export const Sensors = GObject.registerClass({ this._returnValue(callback, 'ARC Current', current, 'storage', 'storage'); }).catch(err => { }); + // skip rest of stats if gtop not available + if (!hasGTop) return; + + let paths = this._settings.get_strv('storage-path') || []; + if (paths.length === 0) return; + // check disk performance stats - new FileModule.File('/proc/diskstats').read("\n").then(lines => { - for (let line of lines) { - let loadArray = line.trim().split(/\s+/); - if ('/dev/' + loadArray[2] == this._storageDevice) { - var read = (loadArray[5] * 512); - var write = (loadArray[9] * 512); - this._returnValue(callback, 'Read total', read, 'storage', 'storage'); - this._returnValue(callback, 'Write total', write, 'storage', 'storage'); - this._returnValue(callback, 'Read rate', (read - this._lastRead) / dwell, 'storage', 'storage'); - this._returnValue(callback, 'Write rate', (write - this._lastWrite) / dwell, 'storage', 'storage'); - this._lastRead = read; - this._lastWrite = write; - break; + Promise.all([ + new FileModule.File('/proc/mounts').read("\n"), + new FileModule.File('/proc/diskstats').read("\n") + ]).then(([mounts, stats]) => { + let pathDeviceMap = {}; + for (let line of mounts) { + let parts = line.trim().split(/\s+/); + if (parts.length >= 2) pathDeviceMap[parts[1]] = parts[0].replace('/dev/', ''); + } + + let deviceData = {}; + for (let line of stats) { + let parts = line.trim().split(/\s+/); + if (parts.length > 10) { + deviceData[parts[2]] = { + read: parseInt(parts[5]) * 512, + write: parseInt(parts[9]) * 512 + }; } } - }).catch(err => { }); - // skip rest of stats if gtop not available - if (!hasGTop) return; + paths.forEach(path => { + if (!path) return; + + GTop.glibtop_get_fsusage(this.storage, path); + let total = this.storage.blocks * this.storage.block_size; + let avail = this.storage.bavail * this.storage.block_size; + let free = this.storage.bfree * this.storage.block_size; + let used = total - free; + let usedPct = Math.round((used / (used + avail)) * 100); + + let suffix = ` (${path})`; + this._returnValue(callback, _('Used %') + suffix, usedPct + '%', 'storage', 'string'); + this._returnValue(callback, _('Free') + suffix, avail, 'storage', 'storage'); + + let dev = pathDeviceMap[path]; + if (dev && deviceData[dev]) { + let currentRead = deviceData[dev].read; + let currentWrite = deviceData[dev].write; + + this._returnValue(callback, _('Read total') + suffix, currentRead, 'storage', 'storage'); + this._returnValue(callback, _('Write total') + suffix, currentWrite, 'storage', 'storage'); + if (this._storageHistory[dev]) { + let rRate = (currentRead - this._storageHistory[dev].read) / dwell; + let wRate = (currentWrite - this._storageHistory[dev].write) / dwell; + this._returnValue(callback, _('Read rate') + suffix, rRate, 'storage', 'storage'); + this._returnValue(callback, _('Write rate') + suffix, wRate, 'storage', 'storage'); + } - GTop.glibtop_get_fsusage(this.storage, this._settings.get_string('storage-path')); - - let total = this.storage.blocks * this.storage.block_size; - let avail = this.storage.bavail * this.storage.block_size; - let free = this.storage.bfree * this.storage.block_size; - let used = total - free; - let reserved = (total - avail) - used; - let freePercent = 0; - let usedPercent = 0; - if (total > 0) { - freePercent = Math.round((free / total) * 100); - usedPercent = Math.round((used / total) * 100); - } + this._storageHistory[dev] = { read: currentRead, write: currentWrite }; + } - this._returnValue(callback, 'Total', total, 'storage', 'storage'); - this._returnValue(callback, 'Used', used, 'storage', 'storage'); - this._returnValue(callback, 'Reserved', reserved, 'storage', 'storage'); - this._returnValue(callback, 'Free', avail, 'storage', 'storage'); - this._returnValue(callback, 'Used %', usedPercent + '%', 'storage', 'string'); - this._returnValue(callback, 'Free %', freePercent + '%', 'storage', 'string'); - this._returnValue(callback, 'storage', avail, 'storage-group', 'storage'); + if (path == paths[0]) + this._returnValue(callback, 'storage', avail, 'storage-group', 'storage'); + }); + }).catch(err => { }); } _queryBattery(callback) {