5
using System.Runtime.InteropServices;
10
public class XScreenSaverSlide : Gtk.Window {
11
public const string ScreenSaverEnviroment = "XSCREENSAVER_WINDOW";
13
public XScreenSaverSlide () : base (String.Empty)
17
protected override void OnRealized ()
19
string env = Environment.GetEnvironmentVariable (ScreenSaverEnviroment);
25
if (env.StartsWith ("0x"))
26
env = env.Substring (2);
28
uint xid = UInt32.Parse (env, System.Globalization.NumberStyles.HexNumber);
30
GdkWindow = Gdk.Window.ForeignNew (xid);
31
Style.Attach (GdkWindow);
32
GdkWindow.Events = EventMask.ExposureMask
33
| EventMask.StructureMask
34
| EventMask.EnterNotifyMask
35
| EventMask.LeaveNotifyMask
36
| EventMask.FocusChangeMask;
38
Style.SetBackground (GdkWindow, Gtk.StateType.Normal);
39
GdkWindow.SetDecorations ((Gdk.WMDecoration) 0);
40
GdkWindow.UserData = this.Handle;
41
SetFlag (WidgetFlags.Realized);
45
GdkWindow.GetGeometry (out geom.X, out geom.Y, out geom.Width, out geom.Height, out depth);
46
SizeAllocate (new Gdk.Rectangle (geom.X, geom.Y, geom.Width, geom.Height));
47
Resize (geom.Width, geom.Height);
49
} catch (System.Exception e) {
50
System.Console.WriteLine (e);
53
System.Console.WriteLine ("{0} not set, falling back to window", ScreenSaverEnviroment);
56
SetSizeRequest (640, 480);
61
public class FullSlide : Gtk.Window {
62
private SlideView slideview;
63
private Gdk.Pixbuf screenshot;
65
private Gdk.Cursor busy;
66
private Gdk.Cursor none;
68
public FullSlide (Gtk.Window parent, IBrowsableItem [] items) : base ("Slideshow")
70
screenshot = PixbufUtils.LoadFromScreen (parent.GdkWindow);
72
this.Destroyed += HandleDestroyed;
74
this.TransientFor = parent;
76
this.ButtonPressEvent += HandleSlideViewButtonPressEvent;
77
this.KeyPressEvent += HandleSlideViewKeyPressEvent;
78
this.AddEvents ((int) (EventMask.ButtonPressMask | EventMask.KeyPressMask | EventMask.PointerMotionMask));
79
slideview = new SlideView (screenshot, items, 2.0);
81
this.Decorated = false;
85
busy = new Gdk.Cursor (Gdk.CursorType.Watch);
86
this.GdkWindow.Cursor = busy;
87
none = GdkUtils.CreateEmptyCursor (GdkWindow.Display);
89
hide = new Delay (2000, new GLib.IdleHandler (HideCursor));
94
Gdk.GCValues values = new Gdk.GCValues ();
95
values.SubwindowMode = SubwindowMode.IncludeInferiors;
96
Gdk.GC fillgc = new Gdk.GC (this.GdkWindow, values, Gdk.GCValuesMask.Subwindow);
99
this.GdkWindow.SetBackPixmap (null, false);
101
screenshot.RenderToDrawable (this.GdkWindow, fillgc,
102
0, 0, 0, 0, -1, -1, RgbDither.Normal, 0, 0);
109
private void HandleSlideViewKeyPressEvent (object sender, KeyPressEventArgs args)
115
protected override bool OnMotionNotifyEvent (Gdk.EventMotion args)
117
base.OnMotionNotifyEvent (args);
118
this.GdkWindow.Cursor = busy;
123
private bool HideCursor ()
125
this.GdkWindow.Cursor = none;
129
private void HandleDestroyed (object sender, System.EventArgs args)
134
private void HandleSlideViewButtonPressEvent (object sender, ButtonPressEventArgs args)
141
public class SlideView : Gtk.Image {
142
IBrowsableItem [] photos;
147
Pixbuf [] tweens = new Pixbuf [10];
156
uint transition_timer = 0;
160
uint animate_max = 200;
163
uint flip_interval = 2000;
164
uint transition_interval = 75;
166
public bool Running {
168
return flip_timer != 0 || transition_timer != 0;
172
public bool Animate {
173
get { return animate; }
174
set { animate = value; }
179
if (photos.Length < 1)
183
if (current_idx >= 0) {
184
Pixbuf frame = GetScaled (photos[current_idx]);
189
if (PreloadNextImage (current_idx + 1))
195
StopTranstionTimer ();
202
StopTranstionTimer ();
206
public void Forward ()
208
if (PreloadNextImage (current_idx + 1))
214
if (PreloadNextImage (current_idx - 1))
218
private void ShowNext ()
222
if (current_idx != next_idx && next != null)
225
current_idx = next_idx;
230
private bool PreloadNextImage (int idx)
233
if (idx < photos.Length && idx >= 0) {
237
next = GetScaled (photos [idx]);
239
next = GetScaled (PixbufUtils.ShallowCopy (PixbufUtils.ErrorPixbuf));
249
next = GetScaled (photos [0]);
251
next = GetScaled (PixbufUtils.ShallowCopy (PixbufUtils.ErrorPixbuf));
257
} catch (GLib.GException e) {
258
System.Console.WriteLine (e);
259
idx = (idx + 1) % photos.Length;
260
return PreloadNextImage (idx);
264
private Pixbuf CrossFade (Pixbuf current, Pixbuf prev, Pixbuf next, double percent)
266
Rectangle area = new Rectangle (0, 0, Allocation.Width, Allocation.Height);
267
BlockProcessor proc = new BlockProcessor (area, 256);
270
while (proc.Step (out subarea)) {
272
GdkWindow.ProcessUpdates (false);
274
prev.CopyArea (subarea.X, subarea.Y, subarea.Width, subarea.Height, current, subarea.X, subarea.Y);
275
next.Composite (current, subarea.X, subarea.Y, subarea.Width, subarea.Height, 0, 0, 1, 1,
276
Gdk.InterpType.Nearest, (int) System.Math.Round (255 * percent));
281
private Pixbuf BlackFade (Pixbuf current, Pixbuf prev, Pixbuf next, double percent)
283
int width = Allocation.Width;
284
int height = Allocation.Height;
289
prev.Composite (current, 0,0, width, height, 0, 0, 1, 1,
290
Gdk.InterpType.Nearest, (int)System.Math.Round (255 * (1 - percent * 2)));
292
next.Composite (current, 0,0, width, height, 0, 0, 1, 1,
293
Gdk.InterpType.Nearest, (int)System.Math.Round (255 * (percent * 2 - 1)));
297
private Pixbuf Blend (Pixbuf current, Pixbuf prev, Pixbuf next, double percent)
300
return BlackFade (current, prev, next, percent);
302
return CrossFade (current, prev, next, percent);
306
private Pixbuf GetScaled (Pixbuf orig)
309
int width = Allocation.Width;
310
int height = Allocation.Height;
311
double scale = PixbufUtils.Fit (orig, width, height, false, out pos.Width, out pos.Height);
312
pos.X = (width - pos.Width) / 2;
313
pos.Y = (height - pos.Height) / 2;
315
Pixbuf scaled = new Pixbuf (Colorspace.Rgb, false, 8, width, height);
316
scaled.Fill (0x000000);
318
Rectangle rect = new Rectangle (pos.X, pos.Y, 256, 256);
321
while (rect.Top < pos.Bottom) {
322
while (rect.X < pos.Right) {
324
GdkWindow.ProcessUpdates (false);
326
rect.Intersect (pos, out subarea);
327
orig.Composite (scaled, subarea.X, subarea.Y,
328
subarea.Width, subarea.Height,
329
pos.X, pos.Y, scale, scale,
330
Gdk.InterpType.Bilinear,
332
rect.X += rect.Width;
335
rect.Y += rect.Height;
342
private Pixbuf GetScaled (IBrowsableItem photo)
346
orig = FSpot.PhotoLoader.LoadAtMaxSize (photo, Allocation.Width, Allocation.Height);
354
Pixbuf result = GetScaled (orig);
361
private bool HandleFlipTimer ()
365
StartTransitionTimer ();
371
private bool HandleTransitionTimer ()
373
System.DateTime start_time = System.DateTime.Now;
374
transition_timer = 0;
375
if (current_tween-- > 0) {
376
StartTransitionTimer ();
377
this.Pixbuf = tweens[current_tween];
378
GdkWindow.ProcessUpdates (false);
379
System.TimeSpan span = System.DateTime.Now - start_time;
382
if (span.TotalMilliseconds > animate_max) {
385
if (fail_count > 3) {
387
System.Console.WriteLine ("Disabling slide animation due to 3 consecutive excessive frame intervals {0}ms",
388
span.TotalMilliseconds);
398
PreloadNextImage (current_idx + 1);
406
private bool HandleTweenIdle ()
408
using (Pixbuf prev = this.Pixbuf) {
414
if (photos.Length < 2) { // Only one photo. Nothing to do
419
if (current_tween >= tweens.Length) {
424
if (current_tween < tweens.Length && tweens[current_tween] == null) {
425
tweens[current_tween] = new Pixbuf (Colorspace.Rgb, false, 8,
426
Allocation.Width, Allocation.Height);
431
double blend_t = (-10 * current_tween) / ((double)tweens.Length - 1);
432
blend_val = 1.0 - (.01 / (.01 + (.99 * Math.Exp(blend_t))));
434
double [] blends = new double [] { .99, .97, .9, .8, .7, .6, .5, .4, .3, .15};
435
blend_val = blends [current_tween];
437
tweens[current_tween] = Blend (tweens[current_tween], prev, next, blend_val);
443
private void StartTweenIdle ()
445
if (tween_idle == 0) {
447
tween_idle = GLib.Idle.Add (new GLib.IdleHandler (HandleTweenIdle));
451
private void StopTweenIdle ()
453
if (tween_idle != 0) {
454
GLib.Source.Remove (tween_idle);
460
private void StartTransitionTimer ()
462
if (transition_timer == 0)
463
transition_timer = GLib.Timeout.Add (transition_interval,
464
new TimeoutHandler (HandleTransitionTimer));
467
private void StopTranstionTimer ()
469
if (transition_timer != 0)
470
GLib.Source.Remove (transition_timer);
472
transition_timer = 0;
475
private void StartFlipTimer ()
478
flip_timer = GLib.Timeout.Add (flip_interval,
479
new TimeoutHandler (HandleFlipTimer));
482
private void StopFlipTimer ()
485
GLib.Source.Remove (flip_timer);
491
private void HandleSizeAllocate (object sender, SizeAllocatedArgs args)
493
Pixbuf current = this.Pixbuf;
499
// The size has changed so we need to reload the images.
501
if (current.Width != Allocation.Width || current.Height != Allocation.Height) {
502
bool playing = (flip_timer != 0 || transition_timer != 0);
504
if (current_idx < 0) {
505
using (Gdk.Pixbuf old = this.Pixbuf) {
506
this.Pixbuf = GetScaled (old);
510
using (Pixbuf frame = GetScaled (photos[current_idx])) {
520
if (playing && current_idx != next_idx)
527
private void ClearTweens () {
528
for (int i = 0; i < tweens.Length; i++) {
529
if (tweens[i] != null)
530
tweens[i].Dispose ();
535
private void HandleDestroyed (object sender, EventArgs args)
541
public SlideView (Pixbuf background, IBrowsableItem [] photos, double delay) : base ()
543
this.photos = photos;
545
if (background != null) {
546
this.Pixbuf = background;
547
background.Dispose ();
551
flip_interval = (uint)(delay * 1000);
554
SizeAllocated += new SizeAllocatedHandler (HandleSizeAllocate);
555
Destroyed += new EventHandler (HandleDestroyed);