Issues covered
This article explains:
- the basics of a screensaver
- loading a screensaver
- filling the whole screen
- handling multiple monitors
- handling events
- some little intricacies
Additionally, the code provides a basic screensaver framework which you could use easily to create screensavers on your own.
Introduction
A screensaver in Windows� is simply an executable file with the extension .scr. The only difference between a normal executable and a screensaver is that a screensaver does some specific things, viz:
- parses the command line to find out what Windows� wants it to do
- loads the screensaver appropriate to that request
- ends the screensaver (usually), when the user uses the mouse or the keyboard
The arguments Windows� passes to a screensaver are:
/s
- load the screensaver
/c
- load the configuration screen
/p
- load the preview
Once we determine the argument passed, we load the screensaver appropriately. When there is some kind of activity, you end the screensaver or do something else.
The code explained
Step 1: Making the Main method accept command-line arguments
The first step in developing a screensaver in C# is to modify the Main
method so that it could accept command-line arguments. The default Main
method is like this:
static void Main()
{
...
}
As you can see, this Main
method accepts no parameters. So, we add a string
array parameter like this:
static void Main(string[] args)
{
...
}
Step 2: Checking the arguments
Now we need to find out what arguments were passed.
First, we check whether Windows did pass some arguments, by checking whether the length of the string
array is zero or not. If it's zero, there were no arguments. A typical scenario where no arguments are passed to a screensaver is when users just double click on your screensaver file.
Secondly, if there indeed were arguments, we catch them. Windows may pass arguments in either the lower case or the upper case. So it's a good idea to induce generalization of some sort. We do this by changing the arguments to lower case (or upper case) first and then checking them. The complete Main
method would look something like:
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0].ToLower().Trim().Substring(0,2) == "/c")
{
...
}
else if (args[0].ToLower() == "/s")
{
...
}
else if (args[0].ToLower() == "/p")
{
...
}
}
else
{
...
}
}
Quite straight forward, isn't it? The only code you need to add here is what you would like to do for each argument.
Step 3: Invoking the screensaver
To run the screensaver, you call the System.Windows.Forms.Application.Run
method. You pass a new form object as the argument. The code will look like:
System.Windows.Forms.Application.Run(new frmScreenSaver());
where frmScreenSaver
is the main screensaver form.
When the screensaver loads, we want it to fit the entire screen. For this, we need to figure out which screen our screensaver is running in right now and its dimensions. To access screens, we use the Screen
class. Using the PrimaryScreen
property of this class, we could access the primary screen of the system as well as its properties. Among those properties is Bounds
, representing the size of the screen. What we do is set the form's Bounds
property to that of this screen so that the form would be resized to occupy the entire screen. This procedure is usually executed in the form's Load
event, for example:
private void frmScreenSaver_Load(object sender, System.EventArgs e)
{
...
Bounds = Screen.PrimaryScreen.Bounds;
...
}
We need to address one possibility though - the user may be using more than one screen. If that's true, and if we load the screensaver only on one screen, the whole point of using this screensaver is lost. Hence, we find out the number of screens available, and load the screensaver in each of them. To do this, we iterate through all the available screens in the Main
method, where we repeatedly call the form's constructor passing the screen's index. The set of all screens available in the system is listed in the AllScreens
property of the Screen
class. Thus the code in the Main
method invoking the screensaver will have to be modified as:
...
...
for (int i = Screen.AllScreens.GetLowerBound(0);
i <= Screen.AllScreens.GetUpperBound(0); i++)
System.Windows.Forms.Application.Run(new frmScreenSaver(i));
...
...
Note the change in the main screensaver form's constructor - it accepts an int
parameter, which represents the screen's index onto which the screensaver is to be run. In response to this, we modify the form's class by adding an int
member variable which will store the screen index. The form's constructor will then be modified to initialize this variable, like this:
public frmScreenSaver(int scrn)
{
...
ScreenNumber = scrn;
...
}
where ScreenNumber
is the int
member variable.
After this, the Load
event handler will have to be modified to resize the form to the bounds of not the primary screen, but that of the screen represented by the index, like this:
private void frmScreenSaver_Load(object sender, System.EventArgs e)
{
...
Bounds = Screen.AllScreens[ScreenNumber].Bounds;
...
}
Once this is done, we have added multiple screen display functionality to our screensaver.
You will want to make the form topmost and hide the cursor. Just call the additional methods in the Load
handler like this:
private void frmScreenSaver_Load(object sender, System.EventArgs e)
{
...
Bounds = Screen.AllScreens[ScreenNumber].Bounds;
Cursor.Hide();
TopMost = true;
...
}
Step 4: Handling events
Once you have loaded your screensaver, you have to handle events. This is because when the user uses the mouse or hits the keyboard, you probably want to do something, like ending the screensaver. To do this, you add event handlers to the KeyDown
, MouseMove
and MoveDown
events of the screensaver form. In the event handlers, you could use the form's Close
method to end the screensaver.
There is one point to note here. When an application is executed, the current mouse parameters are passed to it by Windows. This naturally triggers a mouse event, which would close the screensaver. To handle this, add a Point
variable in the class, initialize it when the application executes, and check the mouse position and mouse clicks when mouse events occur. If there happens to be any change, then call Close
. The code would look something like:
private void OnMouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
if (!MouseXY.IsEmpty)
{
if (MouseXY != new Point(e.X, e.Y))
Close();
}
MouseXY = new Point(e.X, e.Y);
}
where MouseXY
is the Point
variable we use to store the initial mouse position temporarily. Initially, MouseXY
is null; and thus is assigned the current mouse position through this code. In the next call of this event handler, MouseXY
is not empty; so we check whether the position has changed; if yes, close the form.
As mentioned earlier, you should handle both MouseMove
and MouseDown
events in a screensaver. But since both have the same arguments, you could use the same event handler for handling both events. Such a code would look like:
private void OnMouseEvent(object sender,
System.Windows.Forms.MouseEventArgs e)
{
if (!MouseXY.IsEmpty)
{
if (MouseXY != new Point(e.X, e.Y))
Close();
if (e.Clicks > 0)
Close();
}
MouseXY = new Point(e.X, e.Y);
}
where OnMouseEvent
is the event handler which is called on mouse move as well as mouse down events.
Step 5: Finally...
The last step of developing a screensaver is short and sweet...rename the file to an .scr extension. :)
You are done!
Using the bundled code
The code provided just loads a black form. To change it and implement your own features, follow these simple steps:
- Change the code in the
Main
method to handle different arguments
- Add a configuration form if necessary, and add the code to load it in the
Main
method
- Modify the main screensaver form so as to display what you want it to
- Rename the extension from .exe to .scr.
- Go!