[gnome-boxes] Add update CPU graph



commit effa223f1536e0ffc6544720ebafb4f1a0b075c8
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Wed Nov 9 23:32:56 2011 +0100

    Add update CPU graph

 data/gtk-style.css       |    7 +++
 src/libvirt-machine.vala |  132 +++++++++++++++++++++++++++++++++++++++++++---
 src/machine.vala         |   27 +++++++++-
 src/mini-graph.vala      |    2 +-
 src/properties.vala      |   35 ++++++++++--
 5 files changed, 187 insertions(+), 16 deletions(-)
---
diff --git a/data/gtk-style.css b/data/gtk-style.css
index 77f5dab..6b269ac 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -39,6 +39,12 @@
     font-weight: bold;
 }
 
+.boxes-graph-label {
+    font-size: 8;
+    font-family: monospace;
+    color: #989898;
+}
+
 .boxes-source-nb {
     border-radius: 15;
 }
@@ -53,6 +59,7 @@
                                      from (@boxes_selected_color),
                                      to (darker (@boxes_selected_color)));
 }
+
 .boxes-continue:insensitive {
     background-image: none;
 }
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index 14d85b3..13ff4df 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -35,10 +35,10 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
             if (state == DomainState.PAUSED) {
                 started_id = domain.resumed.connect (() => {
-                    domain.disconnect (started_id);
-                    started_id = 0;
-                    connect_display ();
-                });
+                        domain.disconnect (started_id);
+                        started_id = 0;
+                        connect_display ();
+                    });
                 try {
                     domain.resume ();
                 } catch (GLib.Error error) {
@@ -46,10 +46,10 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
                 }
             } else {
                 started_id = domain.started.connect (() => {
-                    domain.disconnect (started_id);
-                    started_id = 0;
-                    connect_display ();
-                });
+                        domain.disconnect (started_id);
+                        started_id = 0;
+                        connect_display ();
+                    });
                 try {
                     domain.start (0);
                 } catch (GLib.Error error) {
@@ -62,6 +62,24 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         update_display ();
     }
 
+    struct MachineStat {
+        int64 timestamp;
+        double cpu_time;
+        double cpu_time_abs;
+        double cpu_guest_percent;
+        double memory_percent;
+        uint disk_read;
+        uint disk_write;
+        uint net_read;
+        uint net_write;
+    }
+
+    static const int STATS_SIZE = 20;
+    private MachineStat[] stats;
+    construct {
+        stats = new MachineStat[STATS_SIZE];
+    }
+
     public LibvirtMachine (CollectionSource source, Boxes.App app,
                            GVir.Connection connection, GVir.Domain domain) {
         base (source, app, domain.get_name ());
@@ -71,6 +89,104 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         this.domain = domain;
 
         set_screenshot_enable (true);
+        set_stats_enable (true);
+    }
+
+    private void update_cpu_stat (DomainInfo info, ref MachineStat stat) {
+        var prev = stats[STATS_SIZE - 1];
+
+        if (info.state == DomainState.CRASHED ||
+            info.state == DomainState.SHUTOFF)
+            return;
+
+        stat.cpu_time = info.cpuTime - prev.cpu_time_abs;
+        stat.cpu_time_abs = info.cpuTime;
+        // hmm, where does this x10 come from?
+        var dt = (stat.timestamp - prev.timestamp) * 10;
+        var percent = stat.cpu_time / dt;
+        percent = percent / info.nrVirtCpu;
+        stat.cpu_guest_percent = percent.clamp (0, 100);
+    }
+
+    private void update_mem_stat (DomainInfo info, ref MachineStat stat) {
+        if (!is_running ())
+            return;
+
+        stat.memory_percent = info.memory * 100.0 / info.maxMem;
+    }
+
+    private void update_io_stat (ref MachineStat stat) {
+    }
+
+
+    public signal void stats_updated ();
+
+    public double[] cpu_stats;
+    public double[] io_stats;
+    public double[] net_stats;
+    private void update_stats () {
+        cpu_stats = {};
+        io_stats = {};
+        net_stats = {};
+
+        foreach (var s in stats) {
+            cpu_stats += s.cpu_guest_percent;
+        }
+
+        stats_updated ();
+    }
+
+    private void update_net_stat (ref MachineStat stat) {
+        try {
+            // var xmldoc = domain.get_config (0).doc;
+            // var target_dev = extract_xpath (xmldoc,
+            //     "string(/domain/devices/interface[ type='network']/target/@dev)", true);
+            // if (target_dev == "")
+            //     return;
+            // var interfaces = domain.get_interfaces ();
+            // foreach (var iface in interfaces)
+            //     message (iface.name);
+        } catch (GLib.Error err) {
+        }
+    }
+
+    private uint stats_id;
+    public void set_stats_enable (bool enable) {
+        if (enable) {
+            if (stats_id != 0)
+                return;
+
+            stats_id = Timeout.add_seconds (1, () => {
+                    try {
+                        var now = get_monotonic_time ();
+                        var stat = MachineStat () { timestamp = now };
+                        var info = domain.get_info ();
+
+                        // message (domain.get_config (0).to_xml ());
+                        // message (domain.get_config (0).get_node_content ("devices"));
+                        // var devices = domain.get_config (0).get_devices ().data;
+                        // foreach (var d in devices) {
+                        //     message (d.to_xml ());
+                        // }
+                        update_cpu_stat (info, ref stat);
+                        update_mem_stat (info, ref stat);
+                        update_io_stat (ref stat);
+                        update_net_stat (ref stat);
+
+                        stats = stats[1:STATS_SIZE];
+                        stats += stat;
+
+                        update_stats ();
+                    } catch (GLib.Error err) {
+                        warning (err.message);
+                    }
+                    return true;
+                });
+        } else {
+            if (stats_id != 0)
+                GLib.Source.remove (stats_id);
+            stats_id = 0;
+        }
     }
 
     public override List<Pair<string, Widget>> get_properties (Boxes.PropertiesPage page) {
diff --git a/src/machine.vala b/src/machine.vala
index 8435c98..fe06859 100644
--- a/src/machine.vala
+++ b/src/machine.vala
@@ -233,6 +233,7 @@ private class Boxes.MachineActor: Boxes.UI {
     public override Clutter.Actor actor { get { return box; } }
     public Clutter.Box box;
 
+    private Clutter.BindConstraint yconstraint;
     private GtkClutter.Texture screenshot;
     private GtkClutter.Actor gtk_vbox;
     private GtkClutter.Actor? display;
@@ -240,6 +241,14 @@ private class Boxes.MachineActor: Boxes.UI {
     private Gtk.VBox vbox; // and the vbox under it
     private Gtk.Entry password_entry;
     private Machine machine;
+    private ulong height_id;
+
+    static const int properties_y = 200;
+
+    ~MachineActor() {
+        machine.app.actor.disconnect (height_id);
+        height_id = 0;
+    }
 
     public MachineActor (Machine machine) {
         this.machine = machine;
@@ -287,6 +296,14 @@ private class Boxes.MachineActor: Boxes.UI {
 
         actor_add (gtk_vbox, box);
         actor.set_reactive (true);
+
+        yconstraint = new Clutter.BindConstraint (machine.app.actor, BindCoordinate.Y,
+                                                  machine.app.actor.height - properties_y);
+        height_id = machine.app.actor.notify["height"].connect (() => {
+            yconstraint.set_offset (machine.app.actor.height - properties_y);
+        });
+
+        yconstraint.enabled = false;
     }
 
     public void scale_screenshot (float scale = 1.5f) {
@@ -320,6 +337,7 @@ private class Boxes.MachineActor: Boxes.UI {
     public override void ui_state_changed () {
         int width, height;
 
+        yconstraint.enabled = false;
         machine.app.window.get_size (out width, out height);
 
         switch (ui_state) {
@@ -376,12 +394,17 @@ private class Boxes.MachineActor: Boxes.UI {
             display.width = (float) width;
             display.height = (float) height;
             actor_add (display, machine.app.stage);
+            display.add_constraint (yconstraint);
 
             display.animate (Clutter.AnimationMode.LINEAR, Boxes.App.duration,
                              "x", 10.0f,
                              "y", height - 200.0f,
-                             "width", 190.0f,
-                             "height", 130.0f);
+                             "width", 180.0f,
+                             "height", 130.0f).completed.connect (() => {
+                                     message ("enabled");
+                                 yconstraint.enabled = true;
+                             });
+
             break;
 
         default:
diff --git a/src/mini-graph.vala b/src/mini-graph.vala
index dbf2ab8..275e1a9 100644
--- a/src/mini-graph.vala
+++ b/src/mini-graph.vala
@@ -44,7 +44,7 @@ private class Boxes.MiniGraph: Gtk.DrawingArea {
         int width = get_allocated_width ();
         int height = get_allocated_height ();
 
-        Gdk.cairo_set_source_color (cr, get_style ().dark[get_state ()]);
+        Gdk.cairo_set_source_rgba (cr, get_boxes_bg_color ());
         cr.rectangle (0, 0, width, height);
         cr.fill ();
 
diff --git a/src/properties.vala b/src/properties.vala
index 9be7d58..6361393 100644
--- a/src/properties.vala
+++ b/src/properties.vala
@@ -53,6 +53,7 @@ private class Boxes.Properties: Boxes.UI {
     private MiniGraph cpu;
     private MiniGraph io;
     private MiniGraph net;
+    private ulong stats_id;
 
     private class PageWidget {
         public Gtk.Widget widget;
@@ -142,6 +143,13 @@ private class Boxes.Properties: Boxes.UI {
         }
 
         tree_view.get_selection ().select_path (new Gtk.TreePath.from_string ("0"));
+
+        var machine = app.current_item as LibvirtMachine;
+        if (machine != null) {
+            stats_id = machine.stats_updated.connect (() => {
+                cpu.points = machine.cpu_stats;
+            });
+        }
     }
 
     private void setup_ui () {
@@ -195,20 +203,32 @@ private class Boxes.Properties: Boxes.UI {
         vbox.pack_start (tree_view, true, true, 0);
 
         var grid = new Gtk.Grid ();
-        vbox.pack_start (grid, true, true, 0);
+        vbox.pack_start (grid, false, false, 0);
         grid.column_homogeneous = true;
-
-        grid.attach (new Gtk.Label (_("CPU:")), 0, 0, 1, 1);
+        grid.column_spacing = 2;
+        grid.margin_left = 10;
+        grid.margin_right = 10;
+        /* this will need to be FIXME */
+        grid.margin_bottom = 30;
+        grid.margin_top = 200;
+
+        var label = new Gtk.Label (_("CPU:"));
+        label.get_style_context ().add_class ("boxes-graph-label");
+        grid.attach (label, 0, 0, 1, 1);
         cpu = new MiniGraph.with_ymax ({}, 100.0, 20);
         cpu.hexpand = true;
         grid.attach (cpu, 1, 0, 1, 1);
 
-        grid.attach (new Gtk.Label (_("I/O:")), 2, 0, 1, 1);
+        label = new Gtk.Label (_("I/O:"));
+        label.get_style_context ().add_class ("boxes-graph-label");
+        grid.attach (label, 2, 0, 1, 1);
         io = new MiniGraph.with_ymax ({}, 100.0, 20);
         io.hexpand = true;
         grid.attach (io, 3, 0, 1, 1);
 
-        grid.attach (new Gtk.Label (_("Net:")), 4, 0, 1, 1);
+        label = new Gtk.Label (_("Net:"));
+        label.get_style_context ().add_class ("boxes-graph-label");
+        grid.attach (label, 4, 0, 1, 1);
         net = new MiniGraph.with_ymax ({}, 100.0, 20);
         net.hexpand = true;
         grid.attach (net, 5, 0, 1, 1);
@@ -218,6 +238,11 @@ private class Boxes.Properties: Boxes.UI {
     }
 
     public override void ui_state_changed () {
+        if (stats_id != 0) {
+            app.current_item.disconnect (stats_id);
+            stats_id = 0;
+        }
+
         switch (ui_state) {
         case UIState.PROPERTIES:
             toolbar_label_bind = app.current_item.bind_property ("name", toolbar_label, "label", BindingFlags.SYNC_CREATE);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]