12 Replies - 4629 Views - Last Post: 03 September 2009 - 06:21 AM Rate Topic: -----

#1 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Multi Monitor Screen Saver

Posted 01 September 2009 - 10:18 AM

Hullo,

I started messing around with the Visual Studio 2005 C# Screen Saver Starter Kit as I wanted to make my own customized screen saver. The provided starter kit works great on a single monitor, however I have two monitors at my workstation and the screen saver from the kit only seems to show up on whichever monitor is flagged as the primary display.

I've got one video card that has one DVI output with some kind of Y connector attaching the two monitors.

The monitors are listed as 'Plug and Play Monitor on 256MB Radeon X600' and 'Plug and Play Monitor on 256MB Radeon X600 Secondary' in the Display section of the Settings tab of the display properties window thingy.

There is what appears to be an excellent tutorial here at Dreamincode.net but the sample screen saver provided only appears to run on the primary monitor as well...

Nevertheless, after reading over the tutorial it seemed simple enough to at least try converting the Screen Saver Starter Kit to support multi monitors. This is of course didn't work either. Here's the code I have now in the project:

In Program.cs
using System;
using System.Windows.Forms;
using System.Globalization;

namespace MultiMonitorScreenSaver
{
	static class Program
	{
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			if (args.Length > 0)
			{
				// Get the 2 character command line argument
				string arg = args[0].ToLower(CultureInfo.InvariantCulture).Trim().Substring(0, 2);
				switch (arg)
				{
					case "/c":
						// Show the options dialog
						ShowOptions();
						break;
					case "/p":
						// Don't do anything for preview
						break;
					case "/s":
						// Show screensaver form
						ShowScreenSaver();
						Application.Run();
						break;
					default:
						MessageBox.Show("Invalid command line argument :" + arg, "Invalid Command Line Argument", MessageBoxButtons.OK, MessageBoxIcon.Error);
						break;
				}
			}
			else
			{
				// If no arguments were passed in, show the screensaver
				ShowScreenSaver();
				Application.Run();
			}
		}


		static void ShowOptions()
		{
			OptionsForm optionsForm = new OptionsForm();
			Application.Run(optionsForm);
		}

		static void ShowScreenSaver()
		{
			foreach (Screen screen in Screen.AllScreens)
			{
				ScreenSaverForm screenSaver = new ScreenSaverForm(screen.Bounds);
				screenSaver.Show();
				//Application.Run(screenSaver);
			}
		}
	}
}



In ScreenSaverForm.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using MultiMonitorScreenSaver.UI;
using MultiMonitorScreenSaver.Rss;

namespace MultiMonitorScreenSaver
{
	partial class ScreenSaverForm : Form
	{
		// The RssFeed to display articles from
		private RssFeed rssFeed;

		// Objects for displaying RSS contents
		private ItemListView<RssItem> rssView;
		private ItemDescriptionView<RssItem> rssDescriptionView;

		// The images to display in the background
		private List<Image> backgroundImages;
		private int currentImageIndex;

		// Keep track of whether the screensaver has become active.
		private bool isActive = false;

		// Keep track of the location of the mouse
		private Point mouseLocation;

		private List<string> imageExtensions = new List<string>(new string[] { "*.bmp", "*.gif", "*.png", "*.jpg", "*.jpeg" });

		/* Parameter 'Rectangle Bounds' added  by me for Multi-Display */
		public ScreenSaverForm(Rectangle Bounds)
		{
			InitializeComponent();
			this.Bounds = Bounds;
			SetupScreenSaver();
			LoadBackgroundImage();
			LoadRssFeed();

			// Initialize the ItemListView to display the list of items in the 
			// RssItem.  It is placed on the left side of the screen.			
			rssView = new ItemListView<RssItem>(rssFeed.MainChannel.Title, rssFeed.MainChannel.Items);
			InitializeRssView();

			// Initialize the ItemDescriptionView to display the description of the 
			// RssItem.  It is placed on the right side of the screen.
			rssDescriptionView = new ItemDescriptionView<RssItem>();
			InitializeRssDescriptionView();
		}
		public ScreenSaverForm()
		{
			InitializeComponent();

			SetupScreenSaver();
			LoadBackgroundImage();
			LoadRssFeed();

			// Initialize the ItemListView to display the list of items in the 
			// RssItem.  It is placed on the left side of the screen.			
			rssView = new ItemListView<RssItem>(rssFeed.MainChannel.Title, rssFeed.MainChannel.Items);
			InitializeRssView();

			// Initialize the ItemDescriptionView to display the description of the 
			// RssItem.  It is placed on the right side of the screen.
			rssDescriptionView = new ItemDescriptionView<RssItem>();
			InitializeRssDescriptionView();
		}


		/// <summary>
		/// Set up the main form as a full screen screensaver.
		/// </summary>
		private void SetupScreenSaver()
		{
			// Use double buffering to improve drawing performance
			this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
			// Capture the mouse
			this.Capture = true;

			// Set the application to full screen mode and hide the mouse
			Cursor.Hide();
			//Bounds = Screen.PrimaryScreen.Bounds;
			WindowState = FormWindowState.Maximized;
			ShowInTaskbar = false;
			DoubleBuffered = true;
			BackgroundImageLayout = ImageLayout.Stretch;
		}

		private void LoadBackgroundImage()
		{
			// Initialize the background images.
			backgroundImages = new List<Image>();
			currentImageIndex = 0;

			if (Directory.Exists(Properties.Settings.Default.BackgroundImagePath))
			{
				try
				{
					// Try to load the images given by the users.
					LoadImagesFromFolder();
				}
				catch
				{
					// If this fails, load the default images.
					LoadDefaultBackgroundImages();
				}
			}

			// If no images were loaded, load the defaults
			if (backgroundImages.Count == 0)
			{
				LoadDefaultBackgroundImages();
			}
		}

		private void LoadImagesFromFolder()
		{
			DirectoryInfo backgroundImageDir = new DirectoryInfo(Properties.Settings.Default.BackgroundImagePath);
			// For each image extension (.jpg, .bmp, etc.)
			foreach (string imageExtension in imageExtensions)
			{
				// For each file in the directory provided by the user
				foreach (FileInfo file in backgroundImageDir.GetFiles(imageExtension))
				{
					// Try to load the image
					try
					{
						Image image = Image.FromFile(file.FullName);
						backgroundImages.Add(image);
					}
					catch (OutOfMemoryException)
					{
						// If the image can't be loaded, move on.
						continue;
					}
				}
			}
		}


		private void LoadDefaultBackgroundImages()
		{
			// If the background images could not be loaded for any reason
			// use the image stored in the resources 
			backgroundImages.Add(Properties.Resources.SSaverBackground);
			backgroundImages.Add(Properties.Resources.SSaverBackground2);
		}

		private void LoadRssFeed()
		{
			try
			{
				// Try to get it from the users settings
				rssFeed = RssFeed.FromUri(Properties.Settings.Default.RssFeedUri);
			}
			catch
			{
				// If there is any problem loading the RSS load an error message RSS feed
				rssFeed = RssFeed.FromText(Properties.Resources.DefaultRSSText);
			}
		}

		/// <summary>
		/// Initialize display properties of the rssView.
		/// </summary>
		private void InitializeRssView()
		{
			rssView.BackColor = Color.FromArgb(120, 240, 234, 232);
			rssView.BorderColor = Color.White;
			rssView.ForeColor = Color.FromArgb(255, 40, 40, 40);
			rssView.SelectedBackColor = Color.FromArgb(200, 105, 61, 76);
			rssView.SelectedForeColor = Color.FromArgb(255, 204, 184, 163);
			rssView.TitleBackColor = Color.Empty;
			rssView.TitleForeColor = Color.FromArgb(255, 240, 234, 232);
			rssView.MaxItemsToShow = 20;
			rssView.MinItemsToShow = 15;
			rssView.Location = new Point(Width / 10, Height / 10);
			rssView.Size = new Size(Width / 2, Height / 2);
		}

		/// <summary>
		/// Initialize display properties of the rssDescriptionView.
		/// </summary>
		private void InitializeRssDescriptionView()
		{
			rssDescriptionView.DisplayItem = rssView.SelectedItem;
			rssDescriptionView.ForeColor = Color.FromArgb(255, 240, 234, 232);
			rssDescriptionView.TitleFont = rssView.TitleFont;
			rssDescriptionView.LineColor = Color.FromArgb(120, 240, 234, 232);
			rssDescriptionView.LineWidth = 2f;
			rssDescriptionView.FadeTimer.Tick += new EventHandler(FadeTimer_Tick);
			rssDescriptionView.FadeTimer.Interval = 40;
			rssDescriptionView.Location = new Point(3 * Width / 4, Height / 3);
			rssDescriptionView.Size = new Size(Width / 4, Height / 2);
			rssDescriptionView.FadingComplete += new EventHandler(rssItemView_FadingComplete);
		}

		private void ScreenSaverForm_MouseMove(object sender, MouseEventArgs e)
		{
			// Set IsActive and MouseLocation only the first time this event is called.
			if (!isActive)
			{
				mouseLocation = MousePosition;
				isActive = true;
			}
			else
			{
				// If the mouse has moved significantly since first call, close.
				if ((Math.Abs(MousePosition.X - mouseLocation.X) > 10) ||
					(Math.Abs(MousePosition.Y - mouseLocation.Y) > 10))
				{
					Close();
				}
			}
		}

		private void ScreenSaverForm_KeyDown(object sender, KeyEventArgs e)
		{
			Close();

		}

		private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)
		{
			Close();
		}

		protected override void OnPaintBackground(PaintEventArgs e)
		{
			// Draw the current background image stretched to fill the full screen
			e.Graphics.DrawImage(backgroundImages[currentImageIndex], 0, 0, this.Width, this.Height);
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			rssView.Paint(e);
			rssDescriptionView.Paint(e);
		}

		private void backgroundChangeTimerTick(object sender, EventArgs e)
		{
			// Change the background image to the next image.
			currentImageIndex = (currentImageIndex + 1) % backgroundImages.Count;
		}

		void FadeTimer_Tick(object sender, EventArgs e)
		{
			this.Refresh();
		}

		void rssItemView_FadingComplete(object sender, EventArgs e)
		{
			rssView.NextArticle();
			rssDescriptionView.DisplayItem = rssView.SelectedItem;
		}
	}
}



Interestingly enough, when I try to debug this project using the Visual Studio debugger, I can step through line by line until it hits the Application.Run() method after running the ShowScreenSaver() method. At this point the Visual Studio window seems to freeze (in fact everything on that monitor seems frozen) and the only way to 'unfreeze' is to kill the screensaver vshost process using the task manager. Just for fun I tried renaming the compiled executable to a .scr file and set that as my screen saver and it works, but once again only for the primary monitor.

Is there something inherently wrong with the way I have things set up? The default Microsoft screensavers that come stock with Windows XP work fine with multiple monitors, why won't mine?

Is This A Good Question/Topic? 0
  • +

Replies To: Multi Monitor Screen Saver

#2 djkitt  Icon User is offline

  • D.I.C Head

Reputation: 29
  • View blog
  • Posts: 189
  • Joined: 22-May 08

Re: Multi Monitor Screen Saver

Posted 01 September 2009 - 10:31 AM

So,

This is just a W.A.G but maybe it is because you set WindowState = FormWindowState.Maximized;

Maximizing a form tends to fill the area of one screen.

Hope this helps,

Kitt
Was This Post Helpful? 0
  • +
  • -

#3 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 01 September 2009 - 10:54 AM

Thanks for the speedy reply, unfortunately, commenting out the line:
WindowState = FormWindowState.Maximized;



does not seem to fix the issue. In fact, there appears to be no change at all. The saver shows up fine on the primary and nothing at all happens to the secondary screen.

This post has been edited by SamuelHenderson: 01 September 2009 - 10:55 AM

Was This Post Helpful? 0
  • +
  • -

#4 djkitt  Icon User is offline

  • D.I.C Head

Reputation: 29
  • View blog
  • Posts: 189
  • Joined: 22-May 08

Re: Multi Monitor Screen Saver

Posted 01 September 2009 - 03:10 PM

View PostSamuelHenderson, on 1 Sep, 2009 - 11:54 AM, said:

Thanks for the speedy reply, unfortunately, commenting out the line:
WindowState = FormWindowState.Maximized;



does not seem to fix the issue. In fact, there appears to be no change at all. The saver shows up fine on the primary and nothing at all happens to the secondary screen.


It was just a guess...

Maybe you could try:
WindowState = FormWindowState.Normal;


Was This Post Helpful? 0
  • +
  • -

#5 djkitt  Icon User is offline

  • D.I.C Head

Reputation: 29
  • View blog
  • Posts: 189
  • Joined: 22-May 08

Re: Multi Monitor Screen Saver

Posted 01 September 2009 - 04:09 PM

I played around with the demo code.

I noticed that value of this.Left seemed to be changing between

public MainForm(Rectangle Bounds)

and

private void MainForm_Shown(object sender, EventArgs e)

so I added a 'private int holdLeft = 0' to MainForm and set it to this.Left in the MainForm(Rectangle Bounds) constructor

then I set this.Left equal to 'holdLeft' in MainForm_Shown


		private int holdLeft = 0;



		public MainForm(Rectangle Bounds)
		{
			InitializeComponent();
			this.Bounds = Bounds;
			this.holdLeft = Bounds.Left;
			//hide the cursor
			Cursor.Hide();
		}

		private void MainForm_Shown(object sender, EventArgs e)
		{
			this.Left = holdLeft;
 


Worked like a charm. (note: I did not test it to see what happens in preview mode)

Hope this helps.


-Kitt

This post has been edited by djkitt: 01 September 2009 - 04:11 PM

Was This Post Helpful? 0
  • +
  • -

#6 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 01 September 2009 - 06:54 PM

Thanks djkitt! I'll try this out tomorrow and post the results :)
Was This Post Helpful? 0
  • +
  • -

#7 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 05:49 AM

Ok, unfortunately that didn't work either. I've attached a screenshot of what it looks like when the screen saver is running: Attached Image

So the face you see on the right is actually my desktop background, same with the thin vertical bar on the far left. The thin vertical bar on the far left is where my taskbar usually is (I prefer the vertical task bar for space reasons. :))

When the WindowState was set to Maximized the saver covered the first screen completely.

The picture attached looks weird because it is GIF image, the png format is too big to be attached :)

Once again, thanks for your help so far! I know we can figure this out :D
Was This Post Helpful? 0
  • +
  • -

#8 djkitt  Icon User is offline

  • D.I.C Head

Reputation: 29
  • View blog
  • Posts: 189
  • Joined: 22-May 08

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 07:51 AM

So SamuelHenderson,

I started C# Express 2005.

File -> New Project -> Screen Saver Starter Kit

inScreenSaverForm.cs:
	partial class ScreenSaverForm : Form
	{


		// I added this constructor
		public ScreenSaverForm(Rectangle Bounds){  
			InitializeComponent();  


			SetupScreenSaver();
			LoadBackgroundImage();
			LoadRssFeed();

			// Initialize the ItemListView to display the list of items in the 
			// RssItem.  It is placed on the left side of the screen.			
			rssView = new ItemListView<RssItem>(rssFeed.MainChannel.Title, rssFeed.MainChannel.Items);
			InitializeRssView();

			// Initialize the ItemDescriptionView to display the description of the 
			// RssItem.  It is placed on the right side of the screen.
			rssDescriptionView = new ItemDescriptionView<RssItem>();
			InitializeRssDescriptionView();

			this.Bounds = Bounds;  
			Cursor.Hide();
		}

		private void SetupScreenSaver()
		{
			// Use double buffering to improve drawing performance
			this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
			// Capture the mouse
			this.Capture = true;

			// Set the application to full screen mode and hide the mouse
			Cursor.Hide();
			Bounds = Screen.PrimaryScreen.Bounds;
//			WindowState = FormWindowState.Maximized;

// Changed WindowState
			WindowState = FormWindowState.Normal;



			ShowInTaskbar = false;
			DoubleBuffered = true;
			BackgroundImageLayout = ImageLayout.Stretch;
		}




in Program.cs:


		[STAThread]
		static void Main(string[] args)
		{
			if (args.Length > 0)
			{
						 [snip]
			}	
			else
			{
				// If no arguments were passed in, show the screensaver
				ShowScreenSaver();
				Application.Run();
			}
		}
		static void ShowScreenSaver()
		{
			foreach (Screen screen in Screen.AllScreens)
			{

				ScreenSaverForm screenSaver = new ScreenSaverForm(screen.Bounds);
				screenSaver.Show();
				//Application.Run(screenSaver);
			}
		}




It works on both screens after this. This is an amalgamation of the Dream.In.Code sample and the Windows Project.

Note: Because of the way the screens are started and the Application.Run is called the program does not stop running after the screen saver is cleared.

Anyway, hopefully this will work for you too.

-Kitt

This post has been edited by djkitt: 02 September 2009 - 07:54 AM

Was This Post Helpful? 1
  • +
  • -

#9 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 01:18 PM

Thanks djkitt! The screen saver is now displaying on both monitors which is awesome. I think my problem was that I commented this out of the SetupScreenSaverMethod()

Bounds = Screen.PrimaryScreen.Bounds;




Quote

Note: Because of the way the screens are started and the Application.Run is called the program does not stop running after the screen saver is cleared.


I see. I also notice also that moving the mouse/keyboard only clears one screen at a time. So I move the mouse a bit and the secondary display's saver disapears, and then I move the mouse some more and then the primary display is cleared. I'm assuming that is also due to the way the the forms are shown along with Application.Run()

Is there any way of working around this so that the application exits when either the mouse or keyboard are pressed?
Was This Post Helpful? 0
  • +
  • -

#10 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 01:26 PM

Quote

Is there any way of working around this so that the application exits when either the mouse or keyboard are pressed?


Durrr...

In ScreenSaverForm.cs
private void ScreenSaverForm_KeyDown(object sender, KeyEventArgs e)
{
		Application.Exit();

}

private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)
{
		Application.Exit();
}



Does the trick! Thanks very much djkitt!

This post has been edited by SamuelHenderson: 02 September 2009 - 01:27 PM

Was This Post Helpful? 0
  • +
  • -

#11 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 01:47 PM

Spoke too soon, no, Application.Exit does not quit the entire application.

How can I kill the entire application so that it's no longer listed in the task manager?
Was This Post Helpful? 0
  • +
  • -

#12 djkitt  Icon User is offline

  • D.I.C Head

Reputation: 29
  • View blog
  • Posts: 189
  • Joined: 22-May 08

Re: Multi Monitor Screen Saver

Posted 02 September 2009 - 02:10 PM

View PostSamuelHenderson, on 2 Sep, 2009 - 02:47 PM, said:

Spoke too soon, no, Application.Exit does not quit the entire application.

How can I kill the entire application so that it's no longer listed in the task manager?



Hello again Samuel,

I added an Application.Exit (like you said) to the following after the Form.Close() event in each.

		private void ScreenSaverForm_MouseMove(object sender, MouseEventArgs e)
		{
			// Set IsActive and MouseLocation only the first time this event is called.
			if (!isActive)
			{
				mouseLocation = MousePosition;
				isActive = true;
			}
			else
			{
				// If the mouse has moved significantly since first call, close.
				if ((Math.Abs(MousePosition.X - mouseLocation.X) > 10) ||
					(Math.Abs(MousePosition.Y - mouseLocation.Y) > 10))
				{
					Close();
					Application.Exit();
				}
			}
		}

		private void ScreenSaverForm_KeyDown(object sender, KeyEventArgs e)
		{
			Close();
			Application.Exit();

		}

		private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)
		{
			Close();
			Application.Exit();
		}



Seems to work.

Later,

Kitt
Was This Post Helpful? 1
  • +
  • -

#13 SamuelHenderson  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 31-August 09

Re: Multi Monitor Screen Saver

Posted 03 September 2009 - 06:21 AM

Thanks djkitt. I forgot to add the Application.Exit() to the mouse movement event, that is why it did not appear to be working.

I believe the screen saver now meets all the needs here at work. I can begin creating my own nifty-er screen saver using this one as framework.

Thanks again :D :^:
Was This Post Helpful? 1

Page 1 of 1