[gnome-boxes] Update screenshot handling
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes] Update screenshot handling
- Date: Thu, 21 Jun 2012 09:12:20 +0000 (UTC)
commit a5539fd063d581422795d615b4cca8cc650a54e2
Author: Alexander Larsson <alexl redhat com>
Date: Wed Jun 20 10:48:03 2012 +0200
Update screenshot handling
The current screenshot handling is pretty bad, doing sync libvirt
calls on the mainloop and constantly doing (sometimes sync) i/o to
disk to access the screenshot data. This is a restructuring of
the screenshot handling with the following features:
* No sync libvirt calls
* Read the screenshots from libvirt directly into a pixbuf
* Save the screenshot caches more rarely
* Only load the screenshot caches on start
* Always get a screenshot of the last frame when closing the
connection
* Only read the boxes-grid.png file once
* scale down screenshots using gdk-pixbuf (much better quality downscaling)
* Work around bug in libvirt screenshot handling after disconnect
https://bugzilla.gnome.org/show_bug.cgi?id=678455
src/app.vala | 1 -
src/libvirt-machine.vala | 29 ++++-------
src/machine.vala | 121 +++++++++++++++++++++++++++++++++-------------
src/remote-machine.vala | 29 +----------
4 files changed, 101 insertions(+), 79 deletions(-)
---
diff --git a/src/app.vala b/src/app.vala
index 4eaddc9..732cfcd 100644
--- a/src/app.vala
+++ b/src/app.vala
@@ -493,7 +493,6 @@ private class Boxes.App: Boxes.UI {
var machine = current_item as Machine;
machine.disconnect_display ();
- machine.update_screenshot.begin ();
}
fullscreen = false;
view.visible = true;
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index 7599d6e..69256d3 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -13,15 +13,6 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
set { source.set_boolean ("source", "save-on-quit", value); }
}
- public override void disconnect_display () {
- if (display == null)
- return;
-
- App.app.display_page.remove_display ();
- display.disconnect_it ();
- display = null;
- }
-
private ulong started_id;
public override void connect_display () {
if (display != null)
@@ -139,6 +130,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
title = domain_config.get_title () ?? name;
domain.updated.connect (update_domain_config);
+ load_screenshot ();
set_screenshot_enable (true);
set_stats_enable (true);
}
@@ -340,7 +332,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
return domain.get_uuid ();
}
- public override async bool take_screenshot () throws GLib.Error {
+ public override async Gdk.Pixbuf? take_screenshot () throws GLib.Error {
var state = DomainState.NONE;
try {
state = (yield domain.get_info_async (null)).state;
@@ -349,23 +341,24 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
}
if (state != DomainState.RUNNING && state != DomainState.PAUSED)
- return true;
+ return null;
var stream = connection.get_stream (0);
- var file_name = get_screenshot_filename ();
- var file = File.new_for_path (file_name);
- var output_stream = yield file.replace_async (null, false, FileCreateFlags.REPLACE_DESTINATION);
- var input_stream = stream.get_input_stream ();
- domain.screenshot (stream, 0, 0);
+ yield run_in_thread (()=> {
+ domain.screenshot (stream, 0, 0);
+ });
+ var loader = new Gdk.PixbufLoader ();
+ var input_stream = stream.get_input_stream ();
var buffer = new uint8[65535];
ssize_t length = 0;
do {
length = yield input_stream.read_async (buffer);
- yield output_stream_write (output_stream, buffer[0:length]);
+ loader.write (buffer[0:length]);
} while (length > 0);
+ loader.close ();
- return true;
+ return loader.get_pixbuf ();
}
public override void delete (bool by_user = true) {
diff --git a/src/machine.vala b/src/machine.vala
index cb1d39d..6ba9921 100644
--- a/src/machine.vala
+++ b/src/machine.vala
@@ -19,6 +19,8 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
private uint screenshot_id;
public static const int SCREENSHOT_WIDTH = 180;
public static const int SCREENSHOT_HEIGHT = 134;
+ private static Cairo.Surface grid_surface;
+ private bool updating_screenshot;
public enum MachineState {
UNKNOWN,
@@ -94,6 +96,10 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
}
}
+ static construct {
+ grid_surface = new Cairo.ImageSurface.from_png (get_pixmap ("boxes-grid.png"));
+ }
+
public Machine (Boxes.CollectionSource source, string name) {
this.name = name;
this.source = source;
@@ -107,13 +113,22 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
else
set_screenshot_enable (true);
});
+
+ }
+
+ public void load_screenshot () {
+ try {
+ var screenshot = new Gdk.Pixbuf.from_file (get_screenshot_filename ());
+ set_screenshot (screenshot, false);
+ } catch (GLib.Error error) {
+ }
}
public void set_screenshot_enable (bool enable) {
if (enable) {
if (screenshot_id != 0)
return;
- update_screenshot.begin ();
+ update_screenshot.begin (false, true);
var interval = App.app.settings.get_int ("screenshot-interval");
screenshot_id = Timeout.add_seconds (interval, () => {
update_screenshot.begin ();
@@ -127,12 +142,12 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
}
}
- public virtual string get_screenshot_filename (string ext = "ppm") {
- return get_user_pkgcache (get_screenshot_prefix () + "-screenshot." + ext);
+ public string get_screenshot_filename () {
+ return get_user_pkgcache (get_screenshot_prefix () + "-screenshot.png");
}
- public virtual async bool take_screenshot () throws GLib.Error {
- return false;
+ public async virtual Gdk.Pixbuf? take_screenshot () throws GLib.Error {
+ return null;
}
public abstract List<Pair<string, Widget>> get_properties (Boxes.PropertiesPage page);
@@ -140,51 +155,91 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
public abstract string get_screenshot_prefix ();
public abstract void connect_display ();
- public abstract void disconnect_display ();
+
+ public virtual void disconnect_display () {
+ if (display == null)
+ return;
+
+ try {
+ var pixbuf = display.get_pixbuf (0);
+ if (pixbuf != null)
+ set_screenshot (pixbuf, true);
+ } catch (GLib.Error error) {
+ warning (error.message);
+ }
+
+ App.app.display_page.remove_display ();
+ display.disconnect_it ();
+ display = null;
+ }
public bool is_running () {
return state == MachineState.RUNNING;
}
- public async void update_screenshot (int width = SCREENSHOT_WIDTH, int height = SCREENSHOT_HEIGHT) {
+ public void set_screenshot (Gdk.Pixbuf? large_screenshot, bool save) {
+ if (large_screenshot != null) {
+ var pw = large_screenshot.get_width ();
+ var ph = large_screenshot.get_height ();
+ var s = double.min ((double)SCREENSHOT_WIDTH / pw, (double)SCREENSHOT_HEIGHT / ph);
+ int w = (int) (pw * s);
+ int h = (int) (ph * s);
+
+ var small_screenshot = new Gdk.Pixbuf (Gdk.Colorspace.RGB, large_screenshot.has_alpha, 8, w, h);
+ large_screenshot.scale (small_screenshot, 0, 0, w, h, 0, 0, s, s, Gdk.InterpType.HYPER);
+
+ pixbuf = draw_vm (small_screenshot, SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT);
+ machine_actor.set_screenshot (large_screenshot); // high resolution
+
+ if (save) {
+ try {
+ pixbuf.save (get_screenshot_filename (), "png");
+ } catch (GLib.Error error) {
+ }
+ }
+ } else if (pixbuf == null) {
+ pixbuf = draw_fallback_vm (SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT);
+ machine_actor.set_screenshot (pixbuf);
+ }
+ }
+
+ int screenshot_counter;
+ public async void update_screenshot (bool force_save = false, bool first_check = false) {
+ if (updating_screenshot)
+ return;
+
+ updating_screenshot = true;
+
+ Gdk.Pixbuf? large_screenshot = null;
try {
- yield take_screenshot ();
- pixbuf = new Gdk.Pixbuf.from_file (get_screenshot_filename ());
- machine_actor.set_screenshot (pixbuf); // high resolution
- pixbuf = draw_vm (pixbuf, width, height);
+ large_screenshot = yield take_screenshot ();
+ // There is some kind of bug in libvirt, so the first time we
+ // take a screenshot after displaying the box we get the old
+ // screenshot from before connecting to the box
+ if (first_check)
+ large_screenshot = yield take_screenshot ();
} catch (GLib.Error error) {
- if (!(error is FileError.NOENT))
- warning ("%s: %s".printf (name, error.message));
}
+ // Save the screenshot first time and every 60 sec
+ set_screenshot (large_screenshot, force_save || screenshot_counter++ % 12 == 0);
- if (pixbuf == null) {
- pixbuf = draw_fallback_vm (width, height);
- machine_actor.set_screenshot (pixbuf);
- }
+ updating_screenshot = false;
}
private Gdk.Pixbuf draw_vm (Gdk.Pixbuf pixbuf, int width, int height) {
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
var context = new Cairo.Context (surface);
- var pw = (double)pixbuf.get_width ();
- var ph = (double)pixbuf.get_height ();
- var sw = width / pw;
- var sh = height / ph;
- var x = 0.0;
- var y = 0.0;
-
- if (pw > ph) {
- y = (height - (ph * sw)) / 2;
- sh = sw;
- }
+ var pw = pixbuf.get_width ();
+ var ph = pixbuf.get_height ();
+ var x = (width - pw) / 2;
+ var y = (height - ph) / 2;
- context.rectangle (x, y, width - x * 2, height - y * 2);
+ context.rectangle (x, y, pw, ph);
context.clip ();
- context.scale (sw, sh);
- Gdk.cairo_set_source_pixbuf (context, pixbuf, x / sw, y / sh);
- context.get_source ().set_filter (Cairo.Filter.BEST); // FIXME: cairo scaling is crap
+ Gdk.cairo_set_source_pixbuf (context, pixbuf, 0, 0);
+ context.set_operator (Cairo.Operator.SOURCE);
context.paint ();
if (!is_running ()) {
@@ -194,7 +249,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
context.identity_matrix ();
context.scale (0.1875 / SCREENSHOT_WIDTH * width, 0.1875 / SCREENSHOT_HEIGHT * height);
- var grid = new Cairo.Pattern.for_surface (new Cairo.ImageSurface.from_png (get_pixmap ("boxes-grid.png")));
+ var grid = new Cairo.Pattern.for_surface (grid_surface);
grid.set_extend (Cairo.Extend.REPEAT);
context.set_source_rgba (0, 0, 0, 1);
context.set_operator (Cairo.Operator.OVER);
diff --git a/src/remote-machine.vala b/src/remote-machine.vala
index 77196b9..87ac3cb 100644
--- a/src/remote-machine.vala
+++ b/src/remote-machine.vala
@@ -11,7 +11,8 @@ private class Boxes.RemoteMachine: Boxes.Machine, Boxes.IPropertiesProvider {
config = new DisplayConfig (source);
source.bind_property ("name", this, "name", BindingFlags.DEFAULT);
- update_screenshot.begin ();
+
+ load_screenshot ();
}
public override void connect_display () {
@@ -30,32 +31,6 @@ private class Boxes.RemoteMachine: Boxes.Machine, Boxes.IPropertiesProvider {
}
}
- public override string get_screenshot_filename (string ext = "jpg") {
- return base.get_screenshot_filename (ext);
- }
-
- public override void disconnect_display () {
- if (display == null)
- return;
-
- App.app.display_page.remove_display ();
-
- if (display != null) {
- try {
- var pixbuf = display.get_pixbuf (0);
- if (pixbuf != null) {
- pixbuf.save (get_screenshot_filename (), "jpeg");
- update_screenshot ();
- }
- } catch (GLib.Error err) {
- warning (err.message);
- }
-
- display.disconnect_it ();
- display = null;
- }
- }
-
public override List<Pair<string, Widget>> get_properties (Boxes.PropertiesPage page) {
var list = new List<Pair<string, Widget>> ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]