Christians Blog

Stuff about stuff
##########################################################################
Unused Header
##########################################################################

XNA Screensaver Kit

Motivation

I am writing an XNA based screensaver (more to come about that) and needed to bake my XNA code into a screensaver. Some googling sent me to http://forums.xna.com/forums/t/254.aspx?PageIndex=1 where James O’Meara has posted an excellent screensaver starter kit. Unfortunately, the post is some years old and the starter kit not work with my installation of XNA Gamestudio 3.1. So I decided to update the starter kit to support XNA GS 3.1 as I might be using it later anyway and felt that this is something that others might also benefit from. I also decided to add preview, modal configuration dialog and logging since (especially the two first) are necessary for a full blown screensaver.

During the code update, I have been around some different areas: screensaver configuration/preview, starter kit (and how they must be specially created for XNA projects). This article is meant to be an explanation of the problems that I have run into and their solutions and an inspiration to others.

Suggestions and comments are much welcome since I am an infant tech-blogger and need to learn.

Screensaver Preview

The original starter kit did not support preview, but a later post in the thread instructs how to use Win32API for setting the provided HWND as the Game window. One thing that was not mentioned is that during preview, key/mouse events of cause needs to be ignored. The initialization code is very simple:

            // Handle preview mode

            if (this._previewMode == false)

            {

                this.graphics.PreferredBackBufferFormat = this.graphics.GraphicsDevice.DisplayMode.Format;

                this.graphics.PreferredBackBufferWidth = this.graphics.GraphicsDevice.DisplayMode.Width;

                this.graphics.PreferredBackBufferHeight = this.graphics.GraphicsDevice.DisplayMode.Height;

                this.graphics.IsFullScreen = true;

                this.IsMouseVisible = false;

                this.graphics.ApplyChanges();

            }

            else

            {

                this.graphics.IsFullScreen = false;

                this.IsMouseVisible = true;

                //IMPORTANT NOTE: Do not execute the apply changes after the below code or the preview 

                // will not display correctly.

                this.graphics.ApplyChanges();

                if (Natives.IsWindowVisible(this._parentHwnd) == true)

                {

                    Natives.RECT wndRect = new Natives.RECT();

                    Natives.GetClientRect(this._parentHwnd, ref wndRect);

                    Natives.SetParent(this.Window.Handle, this._parentHwnd);

                    Natives.SetWindowLong(this.Window.Handle, Natives.GWL_STYLE, Natives.WS_CHILD);

                    Natives.MoveWindow(this.Window.Handle, wndRect.left, wndRect.top, wndRect.right, wndRect.bottom, false);

                }

            }

Screensaver Configuration Dialog

This was the hardest nut to crack.

Configuration dialog in the original code is not modal to the Screensaver Settings dialog provided by Windows. This means that though the screensavers configuration dialog is active, Windows creensaver Settings dialog can still be e.g. canceled. Other screensavers handle this more gracefully by making their configuration dialog modal to the Screensaver Settings dialog.

Basically, the way it works is that the screensaver is invoked with a /C:< HWND > argument where < HWND > is a handle to the Screensaver Settings dialog (or so I thought).

First of all, the window handle needs to be converted into something else such that a Windows Form can display modal to it. Googling revealed that Ryan Farley has found a simple and effective solution to this (see references). However, this did not work: the dialog was on top of the parent, but the parents buttons could still be clicked.

Lots of Google queries returned nothing about how to make the configuration dialog modal in C#, so I posted a query to stackoverflow and two minutes after it came to my mind that perhaps the supplied handle was not to the parent dialog but one of its controls. So I tried GetParent on the handle and voila, my configuration dialog was fully modal to the Screensaver Settings dialog.

Logging

One thing that I felt was needed in the kit is logging. Since all screensavers are run unattended without user intervention and can hardly be debugged (without running it in its intended environment), logging is a nice way to debug what has happened during the execution. The logging that I wrote is extremely simple: one static class with one static method:

/// <summary>
/// Simple logging class for appending
/// to one file.
/// </summary>
static class Logger
{
/// <summary>
/// File to append to.
/// </summary>
static string FileName = @”c:\users\” + Environment.UserName + @”\screensaver.log”;
/// <summary>
///  Appends <paramref name=”line”/> to the file.
/// </summary>
/// <param name=”line”></param>
public static void Log(string line)
{
TextWriter tw = new StreamWriter(FileName, true);
tw.WriteLine(DateTime.Now.ToString() + “: ” + line);
tw.Close();
}
}
    /// <summary>

    /// Simple logging class for appending

    /// to one file.

    /// </summary>

    static class Logger

    {

        /// <summary>

        /// File to append to.

        /// </summary>

        static string FileName = @"c:\users\" + Environment.UserName + @"\screensaver.log";

        /// <summary>

        /// Appends <paramref name="line"/> to the file.

        /// </summary>

        /// <param name="line"></param>

        public static void Log(string line)

        {

            TextWriter tw = new StreamWriter(FileName, true);

            tw.WriteLine(DateTime.Now.ToString() + ": " + line);

            tw.Close();

        }

    }

In the future, it should support setting the filename using Settings.

Creating the Starter Kit

Creating the starter kit isn’t exactly rocket science, but it involves some steps and it turned out that when creating for an XNA project, manual renaming and export of the Content project  and (however extremely simple) merging of two configuration files is required. See the references section, a splendid step by step guide on MSDN details the process.

Future Improvements

Deployment

Most XNA screensavers will not just compile into just one executable that can be renamed to .scr and be copied into c:\windows, but rather include media like textures or audio usually in the Content folder. This makes it necessary that the generated binary knows how to access the Content folder somewhere else. This can be solved already at starter kit level by e.g. specifying a registry key which is individually named and points to the installation directory where content is stored.

Settings

Settings for the screensaver also needs to be stored somewhere else than c:\windows. Somehow, this must be possible – perhaps also by using registry.

Logging of Uncaught Exceptions

Uncaught exceptions shall be logged using catch-all in main() and using AppDomain.UnhandledException and Application.ThreadException to catch exceptions from other threads.

Download

My XNA 3.1 screensaver starter kit can be downloaded from this location.

References

An excellent article about screensavers in C#: “Useful screensavers”

http://www.i-programmer.info/projects/38-windows/350-useful-screensavers.html

The original post of the original starter kit: “XNA Screensaver Starter Kit”

http://forums.xna.com/forums/t/254.aspx?PageIndex=1

Howto export an XNA 3.1 project as a starter kit:

http://msdn.microsoft.com/en-us/library/dd282481.aspx

Ryan Farley: Creating a IWin32Window from a Win32 Handle

http://ryanfarley.com/blog/archive/2004/03/23/465.aspx

Update me when site is updated
Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Facebook
  • Google Bookmarks
  • LinkedIn

2 Responses to “XNA Screensaver Kit”

  1. Dmitriy says:

    It can be a good idea to disable “screensaver window on top” and “exit on mouse/keyboard action” in debug configuration. Can you change this in next version of template kit?

  2. admin says:

    Yeah. My first comment :)

Leave a Reply

:D :-) :( :o 8O :? 8) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen:

Stats by WP SlimStat