diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4503f449..93d128f4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: run: | apt update apt install -y libgala-dev libgee-0.8-dev libglib2.0-dev libgranite-dev libgtk-3-dev libhandy-1-dev - apt install -y libdbus-glib-1-dev libwnck-3-dev libgtop2-dev libwingpanel-3.0-dev + apt install -y libdbus-glib-1-dev libwnck-3-dev libgtop2-dev libwingpanel-3.0-dev libudisks2-dev apt install -y meson valac sassc git - name: Build env: diff --git a/.github/workflows/push-copr-build.yml b/.github/workflows/release-copr.yml similarity index 96% rename from .github/workflows/push-copr-build.yml rename to .github/workflows/release-copr.yml index 99ad27b0..5a55356f 100644 --- a/.github/workflows/push-copr-build.yml +++ b/.github/workflows/release-copr.yml @@ -1,10 +1,9 @@ --- -name: RPM build in Fedora Copr +name: Release on Copr on: - push: - tags: - - '*' + release: + types: [published] jobs: build: diff --git a/.github/workflows/release-launchpad.yml b/.github/workflows/release-launchpad.yml index 111a02b4..ef48b4a7 100644 --- a/.github/workflows/release-launchpad.yml +++ b/.github/workflows/release-launchpad.yml @@ -1,10 +1,9 @@ --- -name: Launchpad release +name: Release on Launchpad on: - push: - tags: - - '*' + release: + types: [published] # on: [push, pull_request] jobs: @@ -26,9 +25,9 @@ jobs: gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - - name: Check changelog for bumped version - run: | - grep -q ${{ github.event.release.tag_name }} debian/changelog || { echo "Version not bumped!" && exit 1; } + # - name: Check changelog for bumped version + # run: | + # grep -q ${{ github.event.release.tag_name }} debian/changelog || { echo "Version not bumped!" && exit 1; } - name: Install build dependencies run: | diff --git a/README.md b/README.md index fcb846f2..f5cac4e8 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ * libgtop2-dev * libwingpanel-3.0-dev * libhandy-1-dev +* libudisks2-dev * meson * sassc diff --git a/com.github.stsdc.monitor.spec b/com.github.stsdc.monitor.spec index 272f0a50..716d9063 100755 --- a/com.github.stsdc.monitor.spec +++ b/com.github.stsdc.monitor.spec @@ -2,7 +2,7 @@ %global appname com.github.stsdc.monitor Name: com.github.stsdc.monitor -Version: 0.10.1 +Version: 0.11.0 Release: %autorelease Summary: Manage processes and monitor system resources License: GPLv3 @@ -27,6 +27,7 @@ BuildRequires: pkgconfig(libwnck-3.0) BuildRequires: pkgconfig(wingpanel) BuildRequires: pkgconfig(gdk-x11-3.0) BuildRequires: pkgconfig(libhandy-1) +BuildRequires: pkgconfig(libudisks2-1) %description diff --git a/data/com.github.stsdc.monitor.appdata.xml.in b/data/com.github.stsdc.monitor.appdata.xml.in index 8044e3a9..db9e884d 100644 --- a/data/com.github.stsdc.monitor.appdata.xml.in +++ b/data/com.github.stsdc.monitor.appdata.xml.in @@ -28,6 +28,11 @@ https://github.com/stsdc/monitor/issues + +​ +​

Add info about drives (based on Dirli's code)

+​
+​

Adds dark theme 🌚

diff --git a/data/screenshots/monitor-system.png b/data/screenshots/monitor-system.png index 65d77500..ed592480 100644 Binary files a/data/screenshots/monitor-system.png and b/data/screenshots/monitor-system.png differ diff --git a/debian/changelog b/debian/changelog index e5c0fd36..d6cb09c6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +com.github.stsdc.monitor (0.11.0) focal; urgency=low + + * Add info about drives (based on Dirli's code) + + -- Stanisław Dac Sun, 24 Oct 2021 21:38:26 +0200 + com.github.stsdc.monitor (0.10.1) focal; urgency=low * Add support for vdx (vm) and nvme disks diff --git a/debian/control b/debian/control index 541e49de..d5b9e710 100644 --- a/debian/control +++ b/debian/control @@ -13,6 +13,7 @@ Build-Depends: meson, libgtop2-dev, libwingpanel-3.0-dev, libhandy-1-dev, + libudisks2-dev, sassc Standards-Version: 4.1.1 diff --git a/meson.build b/meson.build index 81bd88d6..2582c24b 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('com.github.stsdc.monitor', 'vala', 'c', version: '0.10.1') +project('com.github.stsdc.monitor', 'vala', 'c', version: '0.11.0') # these are Meson modules gnome = import('gnome') @@ -28,6 +28,7 @@ gtop = dependency('libgtop-2.0') wnck = dependency('libwnck-3.0') wingpanel = dependency('wingpanel', version: '>=2.1.0') gdk_x11 = dependency('gdk-x11-3.0') +udisks2 = dependency ('udisks2') handy = dependency('libhandy-1', version: '>=0.90.0') config_data = configuration_data() @@ -150,6 +151,7 @@ executable( 'src/Resources/TemperatureSensor.vala', 'src/Resources/Network.vala', 'src/Resources/Storage.vala', + 'src/Resources/Drive.vala', c_args: c_args, dependencies: [ glib, @@ -161,6 +163,7 @@ executable( gtop, wnck, gdk_x11, + udisks2, handy, livechart_dep, meson.get_compiler('c').find_library('m', required : false), diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 0a0ba986..fa578593 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -23,6 +23,8 @@ public class Monitor.MainWindow : Hdy.ApplicationWindow { setup_window_state (); + title = _("Monitor"); + get_style_context ().add_class ("rounded"); resources = new Resources (); diff --git a/src/Monitor.vala b/src/Monitor.vala index 7aa5b108..e0601378 100644 --- a/src/Monitor.vala +++ b/src/Monitor.vala @@ -34,37 +34,6 @@ namespace Monitor { return; } - var granite_settings = Granite.Settings.get_default (); - var gtk_settings = Gtk.Settings.get_default (); - - bool is_dark = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; - gtk_settings.gtk_application_prefer_dark_theme = is_dark; - - - var provider = new Gtk.CssProvider (); - - if (is_dark) { - provider.load_from_resource ("/com/github/stsdc/monitor/dark.css"); - } else { - provider.load_from_resource ("/com/github/stsdc/monitor/light.css"); - } - - Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - - // We listen to changes in Granite.Settings and update our app if the user changes their preference - granite_settings.notify["prefers-color-scheme"].connect (() => { - is_dark = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; - gtk_settings.gtk_application_prefer_dark_theme = is_dark; - - if (is_dark) { - provider.load_from_resource ("/com/github/stsdc/monitor/dark.css"); - } else { - provider.load_from_resource ("/com/github/stsdc/monitor/light.css"); - } - }); - - - window = new MainWindow (this); // start in background with indicator diff --git a/src/Resources/CPU.vala b/src/Resources/CPU.vala index d27addef..07610701 100644 --- a/src/Resources/CPU.vala +++ b/src/Resources/CPU.vala @@ -55,7 +55,7 @@ public class Monitor.CPU : Object { } // Temperature sensor shouldn't be created here since it - // will provides not only cpu temperature + // will provide not only a cpu temperature temperature_sensor = new TemperatureSensor (); } diff --git a/src/Resources/Drive.vala b/src/Resources/Drive.vala new file mode 100644 index 00000000..2e7bddbf --- /dev/null +++ b/src/Resources/Drive.vala @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 Dirli + * Copyright (c) 2021 stsdc + * + * 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 3 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, see . + * + */ + +public struct Monitor.DriveSmart { + public bool enabled; + public uint64 updated; + public bool failing; + public uint64 power_seconds; + public uint64 power_counts; + public string selftest_status; + public uint life_left; +} + +public struct Monitor.DriveVolume { + public string device; + public string label; + public string type; + public string uuid; + public string mount_point; + public uint64 size; + public uint64 free; + public uint64 offset; +} + +public class Monitor.DiskDrive : GLib.Object { + public string model; + public uint64 size; + public uint64 free; + public string revision; + public string id; + public string device; + public string partition; + public GLib.Icon drive_icon; + + + + private Gee.ArrayList volumes; + + private DriveSmart? smart = null; + public bool has_smart { + get { + return smart != null; + } + } + + public DiskDrive () { + free = 0; + volumes = new Gee.ArrayList (); + } + + public DriveSmart? get_smart () { + return smart; + } + + public void add_smart (DriveSmart _smart) { + smart = _smart; + } + + public void add_volume (DriveVolume vol) { + volumes.add (vol); + free = free + vol.free; + } + + public Gee.ArrayList get_volumes () { + var volumes_arr = new Gee.ArrayList (); + + volumes.foreach ((vol) => { + + volumes_arr.add (vol); + + return true; + }); + + volumes_arr.sort (compare_volumes); + + return volumes_arr; + } + + private int compare_volumes (DriveVolume? vol1, DriveVolume? vol2) { + if (vol1 == null) { + return (vol2 == null) ? 0 : -1; + } + + if (vol2 == null) { + return 1; + } + + return GLib.strcmp (vol1.device, vol2.device); + } +} diff --git a/src/Resources/Storage.vala b/src/Resources/Storage.vala index 3c5bc1ca..55d35c5b 100644 --- a/src/Resources/Storage.vala +++ b/src/Resources/Storage.vala @@ -1,4 +1,24 @@ -public class Monitor.Storage : GLib.Object { +/* + * Copyright (c) 2020 Dirli + * Copyright (c) 2021 stsdc + * + * 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 3 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, see . + * + */ + + public class Monitor.Storage : GLib.Object { + public int bytes_write; private ulong sectors_write_old; @@ -9,12 +29,288 @@ public class Monitor.Storage : GLib.Object { // bc first calculasion is wrong private bool dumb_flag; - public Storage () { + private UDisks.Client? udisks_client; + private GLib.List obj_proxies; + + private Gee.HashMap drives_hash; + + construct { bytes_write = 0; sectors_write_old = 0; bytes_read = 0; sectors_read_old = 0; dumb_flag = true; + + try { + udisks_client = new UDisks.Client.sync (); + var dbus_obj_manager = udisks_client.get_object_manager (); + obj_proxies = dbus_obj_manager.get_objects (); + drives_hash = new Gee.HashMap (); + + init_drives (); + init_volumes (); + + } catch (Error e) { + warning (e.message); + udisks_client = null; + } + } + + + private void init_drives () { + obj_proxies.foreach ((iter) => { + var udisks_obj = udisks_client.peek_object (iter.get_object_path ()); + if (udisks_obj != null) { + var p_table = udisks_obj.get_partition_table (); + if (p_table != null) { + var p_type_display = udisks_client.get_partition_table_type_for_display (p_table.type); + + var block_dev = udisks_obj.get_block (); + if (block_dev != null) { + var obj_icon = udisks_client.get_object_info (udisks_obj).get_icon (); + + var drive_dev = udisks_client.get_drive_for_block (block_dev); + if (drive_dev != null) { + var current_drive = new DiskDrive (); + + current_drive.model = drive_dev.model; + current_drive.size = drive_dev.size; + current_drive.revision = drive_dev.revision; + + if (drive_dev.id == "") { + current_drive.id = ""; + } else { + var dev_id = drive_dev.id.split ("-"); + current_drive.id = dev_id[dev_id.length - 1]; + } + current_drive.device = block_dev.device; + current_drive.partition = p_type_display != null ? p_type_display : "Unknown"; + + if (obj_icon != null) { + current_drive.drive_icon = obj_icon; + } + + drives_hash[current_drive.id] = current_drive; + } + } + } + } + }); + } + + private void init_volumes () { + obj_proxies.foreach ((iter) => { + var udisks_obj = udisks_client.peek_object (iter.get_object_path ()); + + var ata = udisks_obj.get_drive_ata (); + if (ata != null) { + // get_smart (udisks_obj, ata); + } + + var p_table = udisks_obj.get_partition_table (); + if (p_table == null) { + + var block_dev = udisks_obj.get_block (); + if (block_dev != null && block_dev.drive != "/") { + DriveVolume current_volume = {}; + current_volume.device = block_dev.device; + current_volume.label = block_dev.id_label; + current_volume.type = block_dev.id_type; + current_volume.size = block_dev.size; + current_volume.uuid = block_dev.id_uuid; + var partition = udisks_obj.get_partition (); + if (partition != null) { + current_volume.offset = partition.offset; + } + + var block_fs = udisks_obj.get_filesystem (); + if (block_fs != null && block_fs.mount_points[0] != null) { + current_volume.mount_point = block_fs.mount_points[0]; + Posix.statvfs buf; + Posix.statvfs_exec (block_fs.mount_points[0], out buf); + current_volume.free = (uint64) buf.f_bfree * (uint64) buf.f_bsize; + // } else { + // current_volume.mount_point = ""; + } + + var d = udisks_client.get_drive_for_block (block_dev); + if (d != null) { + var dev_id = d.id.split ("-"); + var did = dev_id[dev_id.length - 1]; + if (drives_hash.has_key (did) && block_dev.device.contains (drives_hash[did].device)) { + drives_hash[did].add_volume (current_volume); + } + } + } + } + }); + } + + // public void get_smart (UDisks.Object obj, UDisks.DriveAta ata) { + // if (ata.smart_supported) { + // var d = obj.get_drive (); + + // if (d == null) { + // return; + // } + + // var id_arr = d.id.split("-"); + // var did = id_arr[id_arr.length - 1]; + + // if (!drives_hash.has_key (did)) { + // return; + // } + + // DriveSmart d_smart = {}; + + // d_smart.enabled = ata.smart_enabled; + // d_smart.updated = ata.smart_updated; + // d_smart.failing = ata.smart_failing; + // d_smart.power_seconds = ata.smart_power_on_seconds; + // d_smart.selftest_status = ata.smart_selftest_status; + + // try { + // GLib.Variant var_p = null; + // if (ata.call_smart_get_attributes_sync (new GLib.Variant ("a{sv}"), out var_p)) { + // GLib.VariantIter v_iter = var_p.iterator (); + // uchar id; + // int current, worst, threshold, pretty_unit; + // string name; + // uint16 flags; + // uint64 pretty; + // GLib.Variant expansion; + // while (v_iter.next ("(ysqiiixi@a{sv})", + // out id, + // out name, + // out flags, + // out current, + // out worst, + // out threshold, + // out pretty, + // out pretty_unit, + // out expansion)) { + + // if (id == 231) { + // d_smart.life_left = (uint) Utils.parse_pretty (pretty, pretty_unit); + // } else if (id == 12) { + // d_smart.power_counts = pretty; + // } + // } + + // drives_hash[did].add_smart (d_smart); + // } + // } catch (Error e) { + // warning (e.message); + // } + // } + // } + + public DiskDrive? get_drive (string did) { + if (drives_hash.has_key (did)) { + return drives_hash[did]; + } + + return null; + } + + public Gee.ArrayList get_drives () { + var drives_arr = new Gee.ArrayList (); + drives_hash.values.foreach ((d) => { + drives_arr.add (d); + return true; + }); + + drives_arr.sort (compare_drives); + + return drives_arr; + } + + public Gee.ArrayList get_drive_volumes (string dev_id) { + var volumes_arr = new Gee.ArrayList (); + + if (drives_hash.has_key (dev_id)) { + drives_hash[dev_id].get_volumes ().foreach ((vol) => { + volumes_arr.add (vol); + + return true; + }); + } + + volumes_arr.sort (compare_volumes); + + return volumes_arr; + } + + public Gee.ArrayList get_mounted_volumes () { + var volumes_list = new Gee.ArrayList (); + + if (udisks_client != null) { + obj_proxies.foreach ((iter) => { + var udisks_obj = udisks_client.peek_object (iter.get_object_path ()); + + var p_table = udisks_obj.get_partition_table (); + if (p_table == null) { + + var block_dev = udisks_obj.get_block (); + if (block_dev != null && block_dev.drive != "/") { + var block_fs = udisks_obj.get_filesystem (); + if (block_fs != null && block_fs.mount_points[0] != null) { + DriveVolume current_volume = {}; + current_volume.device = block_dev.device; + current_volume.label = block_dev.id_label; + current_volume.type = block_dev.id_type; + current_volume.size = block_dev.size; + current_volume.uuid = block_dev.id_uuid; + var partition = udisks_obj.get_partition (); + if (partition != null) { + current_volume.offset = partition.offset; + } + + current_volume.mount_point = block_fs.mount_points[0]; + Posix.statvfs buf; + Posix.statvfs_exec (block_fs.mount_points[0], out buf); + current_volume.free = (uint64) buf.f_bfree * (uint64) buf.f_bsize; + + volumes_list.add (current_volume); + // } else { + // current_volume.mount_point = ""; + } + } + } + }); + + volumes_list.sort (compare_volumes); + } + + return volumes_list; + } + + public string size_to_display (uint64 size_to_fmt) { + return udisks_client.get_size_for_display (size_to_fmt, false, false); + } + + private int compare_drives (DiskDrive? drive1, DiskDrive? drive2) { + if (drive1 == null) { + return (drive2 == null) ? 0 : -1; + } + + if (drive2 == null) { + return 1; + } + + return GLib.strcmp (drive1.device, drive2.device); + } + + private int compare_volumes (DriveVolume? vol1, DriveVolume? vol2) { + if (vol1 == null) { + return (vol2 == null) ? 0 : -1; + } + + if (vol2 == null) { + return 1; + } + + return GLib.strcmp (vol1.device, vol2.device); } public void update () { @@ -50,5 +346,4 @@ public class Monitor.Storage : GLib.Object { sectors_read_old = sectors_read_new; sectors_write_old = sectors_write_new; } - } diff --git a/src/Resources/TemperatureSensor.vala b/src/Resources/TemperatureSensor.vala index 3940bad9..acc17927 100644 --- a/src/Resources/TemperatureSensor.vala +++ b/src/Resources/TemperatureSensor.vala @@ -8,6 +8,7 @@ class TemperatureSensor : Object { public double cpu { get { double total_temperature = 0; + // this hsould handle null foreach (var path in cpu_temp_paths) { total_temperature += double.parse (open_file (path)); } diff --git a/src/Views/SystemView/SystemStorageView.vala b/src/Views/SystemView/SystemStorageView.vala index 5ce50ca4..038dfc51 100644 --- a/src/Views/SystemView/SystemStorageView.vala +++ b/src/Views/SystemView/SystemStorageView.vala @@ -6,6 +6,8 @@ public class Monitor.SystemStorageView : Gtk.Grid { private LabelRoundy storage_read_label; private LabelRoundy storage_write_label; + private Gtk.Box drive_cards_container; + construct { margin = 12; column_spacing = 12; @@ -37,9 +39,79 @@ public class Monitor.SystemStorageView : Gtk.Grid { labels_grid.attach (storage_write_label, 0, 0, 1, 1); labels_grid.attach (storage_read_label, 1, 0, 1, 1); + drive_cards_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + + storage.get_drives ().foreach (add_drive_card); + attach (storage_name_label, 0, 0, 1, 1); - attach (labels_grid, 0, 1, 2, 2); - attach (storage_chart, 0, 1, 2, 2); + attach (drive_cards_container, 0, 1, 1, 1); + attach (labels_grid, 0, 2, 2, 2); + attach (storage_chart, 0, 2, 2, 2); + } + + private bool add_drive_card (owned DiskDrive? drive) { + drive_cards_container.add (build_drive_card (drive.model, drive.device, drive.size, drive.free)); + return true; + } + + private Gtk.Box build_drive_card (string model, string device, uint64 size, uint64 free) { + var drive_card = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + drive_card.get_style_context ().add_class ("card"); + drive_card.get_style_context ().add_class ("rounded"); + + drive_card.halign = Gtk.Align.START; + drive_card.margin_end = 12; + drive_card.margin_top = 6; + drive_card.margin_bottom = 12; + + var drive_grid = new Gtk.Grid (); + // drive_grid.row_spacing = 6; + drive_grid.column_spacing = 6; + drive_grid.margin = 6; + + var drive_name_label = new Gtk.Label (model); + drive_name_label.get_style_context ().add_class ("h3"); + drive_name_label.margin = 6; + drive_name_label.margin_bottom = 0; + drive_name_label.halign = Gtk.Align.START; + + string size_string = Utils.HumanUnitFormatter.double_bytes_to_human (size); + string used_string = Utils.HumanUnitFormatter.double_bytes_to_human ((size - free)); + + string drive_block_name_and_size_string = "%s 𐄁 %s / %s".printf (device, used_string, size_string); + + if (free == 0) drive_block_name_and_size_string = "%s 𐄁 %s".printf (device, size_string); + + var drive_block_name_and_size_label = new Gtk.Label (drive_block_name_and_size_string); + drive_block_name_and_size_label.get_style_context ().add_class ("h4"); + drive_block_name_and_size_label.get_style_context ().add_class ("text-secondary"); + drive_block_name_and_size_label.margin = 6; + drive_block_name_and_size_label.margin_top = 0; + drive_block_name_and_size_label.halign = Gtk.Align.START; + + var drive_not_mounted_label = new Gtk.Label ("Not mounted"); + drive_not_mounted_label.halign = Gtk.Align.START; + drive_not_mounted_label.get_style_context ().add_class ("h4"); + drive_not_mounted_label.margin_start = 6; + + var usagebar = new Gtk.LevelBar (); + usagebar.get_style_context ().add_class ("flat"); + usagebar.margin = 6; + usagebar.margin_top = 0; + usagebar.set_max_value (100.0); + usagebar.set_min_value (0.0); + usagebar.set_value (100.0 * (size - free) / size); + + drive_grid.attach (drive_name_label, 0, 0, 1, 1); + drive_grid.attach (drive_block_name_and_size_label, 0, 1, 1, 1); + if (free == 0) { + drive_grid.attach (drive_not_mounted_label, 0, 2, 1, 1); + } else { + drive_grid.attach (usagebar, 0, 2, 1, 1); + } + drive_card.add (drive_grid); + + return drive_card; } public void update () { @@ -52,5 +124,4 @@ public class Monitor.SystemStorageView : Gtk.Grid { storage_chart.update (1, down_bytes); } } - }