#region Usage notice /* * Screensaver.cs * * (c) Rei Miyasaka 2006 * rei@thefraser.com * * Last updated 2006.05.16 * * You may use this code for any purpose, in part or in whole, on two conditions: * 1. I cannot be held legally responsible for any damage or problems caused by this code. * 2. If you make something cool using this code, give me a shout and tell me about it. * */ #endregion using System; using System.Windows.Forms; using System.Drawing; using System.Collections; using System.Text; using System.Runtime.InteropServices; using System.Threading; using System.Diagnostics; using System.Collections.Generic; namespace Screensavers { /// /// Provides initialization, timing and windowing facilities for screensavers. /// public abstract class Screensaver { /// /// Creates a new with the given fullscreen mode. /// /// A value indicating the fullscreen windowing mode. protected Screensaver(FullscreenMode fullscreenMode) { this.fullscreenMode = fullscreenMode; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Framerate = 30; framerateTimer.Elapsed += new System.Timers.ElapsedEventHandler(framerateTimer_Elapsed); framerateTimer.Start(); } /// /// Creates a new that runs one window per screen. /// protected Screensaver() : this(FullscreenMode.MultipleWindows) { } void framerateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { achievedFramerate = updatesThisSec; updatesThisSec = 0; if (OneSecondTick != null) OneSecondTick(this, new EventArgs()); } /// /// Occurs before the screensaver windows close. /// public event EventHandler Exit; #region Multimedia Timer [DllImport("winmm.dll")] static extern int timeSetEvent(int delay, int resolution, TimeCallback callback, int user, int mode); [DllImport("winmm.dll")] static extern int timeKillEvent(int id); [DllImport("winmm.dll")] static extern int timeGetTime(); [DllImport("user32.dll")] public static extern bool LockWorkStation(); delegate void TimeCallback(uint id, uint msg, IntPtr user, IntPtr param1, IntPtr param2); TimeCallback timerCallback; int timerId; void StartUpdating() { timerCallback = new TimeCallback(TimerCallback); //TIME_KILL_SYNCHRONOUS = 0x0100 //TIME_PERIODIC = 0x0001 timerId = timeSetEvent((int)(1000/(double)framerate), 0, timerCallback, 0, 0x0101); while (timerCallback != null) { updateEvent.WaitOne(); DoUpdate(); Application.DoEvents(); updateEvent.Reset(); } } void StopUpdating() { timerCallback = null; timeKillEvent(timerId); updateEvent.WaitOne(); } System.Threading.ManualResetEvent updateEvent = new System.Threading.ManualResetEvent(false); void TimerCallback(uint id, uint msg, IntPtr user, IntPtr param1, IntPtr param2) { updateEvent.Set(); } System.Timers.Timer framerateTimer = new System.Timers.Timer(1000); /// /// Occurs once each second on a thread separate from the window thread. /// public event EventHandler OneSecondTick; int framerate; /// /// Gets or sets the target framerate. /// public int Framerate { get { return framerate; } set { if (value < 0) throw new ArgumentOutOfRangeException(); if (timerCallback != null) { StopUpdating(); framerate = value; StartUpdating(); } else framerate = value; } } #endregion [StructLayout(LayoutKind.Sequential)] struct RECT { public int left, top, right, bottom; } [DllImport("user32.dll")] static extern bool GetClientRect(IntPtr handle, out RECT rect); [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr handle); static Rectangle GetClientRect(IntPtr handle) { RECT rect; GetClientRect(handle, out rect); return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); } /// /// Occurs when the screensaver should process its logic and render. /// public event EventHandler Update; event EventHandler PreUpdate; event EventHandler PostUpdate; int achievedFramerate; int updatesThisSec; /// /// Actual framerate achieved. This value is updated once each second. /// public int AchievedFramerate { get { return achievedFramerate; } } void DoUpdate() { if (screensaverMode == ScreensaverMode.Preview && !IsWindowVisible(windowHandle)) { StopUpdating(); if (Exit != null) Exit(this, new EventArgs()); previewShutdownEvent.Set(); return; } if (PreUpdate != null) PreUpdate(this, new EventArgs()); if (Update != null) Update(this, new EventArgs()); if (PostUpdate != null) PostUpdate(this, new EventArgs()); updatesThisSec++; } ScreensaverMode ProcessCommandLine() { string[] args = Environment.GetCommandLineArgs(); if (args.Length == 1 && IsScr) return ScreensaverMode.Settings; if (args.Length < 2) throw new FormatException(); if (args[1].ToLower().StartsWith("/c")) { return ScreensaverMode.Settings; } switch (args[1].ToLower()) { case "w": return ScreensaverMode.Windowed; case "/s": return ScreensaverMode.Normal; case "/p": if (args.Length < 3) { throw new FormatException(); } try { windowHandle = (IntPtr)uint.Parse(args[2]); return ScreensaverMode.Preview; } catch (FormatException) { throw new FormatException(); } default: throw new FormatException(); } } bool IsScr { get { return System.IO.Path.GetExtension(System.Reflection.Assembly.GetExecutingAssembly().CodeBase). Equals(".scr", StringComparison.InvariantCultureIgnoreCase); } } /// /// Start the screensaver in windowed mode if the file extension is not scr, unless a mode is specified in the command line. /// Otherwise, if the file extension is scr, start the screensaver in config mode. /// public void Run() { Run(ScreensaverMode.Windowed); } /// /// Start the screensaver in the specified mode unless one is specified in the command line. /// /// The mode in which to run the screensaver. This value cannot be . public void Run(ScreensaverMode mode) { if (mode == ScreensaverMode.Preview && windowHandle == IntPtr.Zero) throw new ArgumentException("Cannot explicity run in preview mode", "mode"); if (isEnded) throw new Exception("This screensaver has already finished running"); try { this.screensaverMode = ProcessCommandLine(); } catch (FormatException) { this.screensaverMode = mode; } try { switch (screensaverMode) { case ScreensaverMode.Windowed: RunWindowed(); break; case ScreensaverMode.Settings: ShowSettingsDialog(); break; case ScreensaverMode.Normal: if (!closeOnMouseMoveOverride) closeOnMouseMove = true; if (!closeOnClickOverride) closeOnClick = true; if (!closeOnKeyboardInputOverride) closeOnKeyboardInput = true; RunNormal(); break; case ScreensaverMode.Preview: RunPreview(); break; } } finally { isEnded = true; } } FullscreenMode fullscreenMode = FullscreenMode.SingleWindow; ScreensaverMode screensaverMode; /// /// Gets the current running mode of the screensaver. /// public ScreensaverMode Mode { get { return screensaverMode; } } IntPtr windowHandle = IntPtr.Zero; /// /// Occurs after the windows are created, before the screensaver runs. /// public event EventHandler Initialize; bool closeOnMouseMove; bool closeOnMouseMoveOverride; /// /// Gets or sets a value indicating whether or not the screensaver should close when the user moves the mouse. /// /// This value is true by default in all modes except . public bool CloseOnMouseMove { get { return closeOnMouseMove; } set { closeOnMouseMove = value; closeOnMouseMoveOverride = true; } } bool closeOnClick; bool closeOnClickOverride; /// /// Gets or sets a value indicating whether or not the screensaver should close when the user clicks the mouse. /// /// This value is true by default in all modes except . public bool CloseOnClick { get { return closeOnClick; } set { closeOnClick = value; closeOnClickOverride = true; } } bool closeOnKeyboardInput; bool closeOnKeyboardInputOverride; /// /// Gets or sets a value indicating whether or not the screensaver should close when the user presses a key. /// /// This value is true by default in all modes except . public bool CloseOnKeyboardInput { get { return closeOnKeyboardInput; } set { closeOnKeyboardInput = value; closeOnKeyboardInputOverride = true; } } WindowCollection windows; /// /// Gets a collection of all of the running screensaver windows. /// public WindowCollection Windows { get { return windows; } } Window window0; /// /// Gets the primary screensaver window. /// public Window Window0 { get { if(window0 != null) return window0; if (windows == null || windows.Count == 0) return null; window0 = windows[0]; return window0; } } Graphics graphics0; /// /// Gets the GDI graphics object for the primary window. /// public Graphics Graphics0 { get { if (graphics0 != null) return graphics0; if (Window0 == null) return null; graphics0 = Window0.Graphics; return graphics0; } } string settingsText; /// /// Gets or sets text to be displayed in the default settings message box. /// public string SettingsText { get { return settingsText; } set { settingsText = value; } } /// /// Shows the settings dialog, or, by default, shows a message box indicating the assembly name, version and copyright information. /// protected virtual void ShowSettingsDialog() { System.IO.StringWriter sw = new System.IO.StringWriter(); System.Reflection.AssemblyName name = System.Reflection.Assembly.GetExecutingAssembly().GetName(); sw.WriteLine(name.Name); sw.WriteLine("Version " + name.Version); object[] attribs = System.Reflection.Assembly.GetExecutingAssembly(). GetCustomAttributes(typeof(System.Reflection.AssemblyDescriptionAttribute), false); if (attribs != null && attribs.Length != 0) { System.Reflection.AssemblyDescriptionAttribute desc = attribs[0] as System.Reflection.AssemblyDescriptionAttribute; if (desc.Description != string.Empty) { sw.WriteLine(desc.Description); } } attribs = System.Reflection.Assembly.GetExecutingAssembly(). GetCustomAttributes(typeof(System.Reflection.AssemblyCopyrightAttribute), false); if (attribs != null && attribs.Length != 0) { System.Reflection.AssemblyCopyrightAttribute copyright = attribs[0] as System.Reflection.AssemblyCopyrightAttribute; if (copyright.Copyright != string.Empty) { sw.WriteLine(); sw.WriteLine(copyright.Copyright); } } if (settingsText != null && settingsText != string.Empty) { sw.WriteLine(); sw.WriteLine(SettingsText); } MessageBox.Show(sw.ToString(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, MessageBoxButtons.OK); } System.Threading.AutoResetEvent previewShutdownEvent = new System.Threading.AutoResetEvent(false); private void RunPreview() { #if DEBUG System.Diagnostics.Debugger.Launch(); #endif windows = new WindowCollection(new Window[] { new Window(this, windowHandle) }); InitializeAndStart(); previewShutdownEvent.WaitOne(); } string windowTitle = ""; /// /// Gets or sets text to be displayed in the taskbar window title. /// public string WindowTitle { get { return windowTitle; } set { windowTitle = value; } } private void RunNormal() { Cursor.Hide(); switch (fullscreenMode) { case FullscreenMode.SingleWindow: RunNormalSingleWindow(); break; case FullscreenMode.MultipleWindows: RunNormalMultipleWindows(); break; } } private void RunNormalMultipleWindows() { //List windows = new List(); ArrayList windows = new ArrayList(); Form primary = new Form(); primary.StartPosition = FormStartPosition.Manual; primary.Location = Screen.PrimaryScreen.Bounds.Location; primary.Size = Screen.PrimaryScreen.Bounds.Size; primary.BackColor = Color.Black; #if !DEBUG primary.TopMost = true; #endif primary.FormBorderStyle = FormBorderStyle.None; primary.Text = WindowTitle; primary.Icon = InvisbleLockScreen.Properties.Resources.icon; foreach (Screen screen in Screen.AllScreens) { if (screen == Screen.PrimaryScreen) continue; Form form = new Form(); form.Owner = primary; form.BackColor = Color.Black; #if !DEBUG form.TopMost = true; #endif form.StartPosition = FormStartPosition.Manual; form.Location = screen.Bounds.Location; form.Size = screen.Bounds.Size; form.FormBorderStyle = FormBorderStyle.None; form.Text = primary.Text; form.Icon = InvisbleLockScreen.Properties.Resources.icon; windows.Add(new Window(this, form)); } windows.Insert(0, new Window(this, primary)); primary.Load += delegate(object sender, EventArgs e) { foreach (Window window in this.windows) { if (window.Form.Owner == null) continue; window.Form.Show(); } }; this.windows = new WindowCollection(windows.ToArray(typeof(Window)) as Window[]); primary.Show(); InitializeAndStart(); } private void RunNormalSingleWindow() { Form form = new Form(); Rectangle rect = GetVirtualScreenRect(); form.Location = rect.Location; form.Size = rect.Size; form.BackColor = Color.Black; #if !DEBUG form.TopMost = true; #endif form.FormBorderStyle = FormBorderStyle.None; form.StartPosition = FormStartPosition.Manual; form.Text = WindowTitle; form.Icon = InvisbleLockScreen.Properties.Resources.icon; windows = new WindowCollection(new Window[] { new Window(this, form) }); form.Show(); InitializeAndStart(); } static Rectangle GetVirtualScreenRect() { Screen[] screens = Screen.AllScreens; Rectangle rect = Rectangle.Empty; foreach (Screen screen in Screen.AllScreens) rect = Rectangle.Union(rect, screen.Bounds); return rect; } private void RunWindowed() { Form form = new Form(); form.FormBorderStyle = FormBorderStyle.FixedSingle; form.Text = WindowTitle; form.Icon = InvisbleLockScreen.Properties.Resources.icon; form.StartPosition = FormStartPosition.CenterScreen; form.BackColor = Color.Black; #if !DEBUG form.TopMost = true; #endif form.MaximizeBox = false; form.ClientSize = new Size((int)(Screen.PrimaryScreen.WorkingArea.Width * 0.9), (int)(Screen.PrimaryScreen.WorkingArea.Height * 0.9)); windows = new WindowCollection(new Window[] { new Window(this, form) }); form.Show(); InitializeAndStart(); } void InitializeAndStart() { if (Initialize != null) Initialize(this, new EventArgs()); if (Window0 != null && Window0.Form != null) Window0.Form.FormClosing += new FormClosingEventHandler(Form_FormClosing); // enable keyboard hook toggleKeyboardHook(true); StartUpdating(); } void Form_FormClosing(object sender, FormClosingEventArgs e) { if (!ctrlLPressed) { if (e.CloseReason == CloseReason.UserClosing) e.Cancel = true; } else { StopUpdating(); LockWorkStation(); if (Exit != null) Exit(this, new EventArgs()); e.Cancel = false; } } #region Low Level Keyboard Hook private bool ctrlLPressed; private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { int vkCode = Marshal.ReadInt32(lParam); List acceptedKeys = new List { Keys.LControlKey, Keys.RControlKey, Keys.L }; if (acceptedKeys.Contains((Keys)vkCode)) return CallNextHookEx(_hookID, nCode, wParam, lParam); else return (IntPtr)1; } [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); public void toggleKeyboardHook(bool enable) { if (enable) _hookID = SetHook(_proc); else UnhookWindowsHookEx(_hookID); } #endregion #region IDisposable Members bool isEnded = false; #endregion void OnMouseMove() { //if (closeOnMouseMove) //{ // if (Window0.Form != null) // Window0.Form.Close(); // else // Application.Exit(); //} } void OnKeyboardInput() { // disable keyboard hook toggleKeyboardHook(false); if (Window0.Form != null) Window0.Form.Close(); else Application.Exit(); } void OnMouseClick() { /* if (closeOnMouseMove) { if (Window0.Form != null) Window0.Form.Close(); else { Application.Exit(); } } */ } /// /// Represents a screensaver window. /// public class Window { internal Window(Screensaver screensaver, Form form) { this.screensaver = screensaver; this.form = form; this.size = form.ClientSize; this.graphics = form.CreateGraphics(); this.handle = form.Handle; form.MouseMove += new MouseEventHandler(form_MouseMove); form.MouseClick += new MouseEventHandler(form_MouseClick); form.MouseDoubleClick += new MouseEventHandler(form_MouseDoubleClick); form.MouseDown += new MouseEventHandler(form_MouseDown); form.MouseUp += new MouseEventHandler(form_MouseUp); form.MouseWheel += new MouseEventHandler(form_MouseWheel); form.KeyDown += new KeyEventHandler(form_KeyDown); form.KeyUp += new KeyEventHandler(form_KeyUp); form.KeyPress += new KeyPressEventHandler(form_KeyPress); //form.BackColor = Color.Lime; //form.TransparencyKey = Color.Lime; form.Opacity = 0.01; this.screensaver.PreUpdate += new EventHandler(screensaver_PreUpdate); this.screensaver.PostUpdate += new EventHandler(screensaver_PostUpdate); } internal Window(Screensaver screensaver, IntPtr handle) { this.screensaver = screensaver; this.handle = handle; this.graphics = Graphics.FromHwnd(handle); this.size = GetClientRect(handle).Size; this.screensaver.PreUpdate += new EventHandler(screensaver_PreUpdate); this.screensaver.PostUpdate += new EventHandler(screensaver_PostUpdate); } bool doubleBuffer = false; bool doubleBufferSet = false; /// /// Gets or sets a value indicating whether or not the Graphics object should be double buffered. /// Set to false if the Graphics object will not be used. /// public bool DoubleBuffer { get { if (!doubleBufferSet) DoubleBuffer = true; return doubleBuffer; } set { doubleBufferSet = true; if (doubleBuffer != value) { doubleBuffer = value; if (doubleBuffer) SetDoubleBuffer(); else UnsetDoubleBuffer(); } else doubleBuffer = value; } } private void SetDoubleBuffer() { graphicsSwap = graphics; BufferedGraphicsManager.Current.MaximumBuffer = this.Size; buffer = BufferedGraphicsManager.Current.Allocate(graphicsSwap, new Rectangle(0, 0, Size.Width, Size.Height)); graphics = buffer.Graphics; } private void UnsetDoubleBuffer() { buffer.Dispose(); graphics = graphicsSwap; buffer = null; graphicsSwap = null; } BufferedGraphics buffer; Graphics graphicsSwap; void screensaver_PreUpdate(object sender, EventArgs e) { } void screensaver_PostUpdate(object sender, EventArgs e) { if(doubleBuffer) { buffer.Render(graphicsSwap); } } #region Keyboard and Mouse Events void form_KeyPress(object sender, KeyPressEventArgs e) { if (KeyPress != null) KeyPress(this, e); //screensaver.OnKeyboardInput(); } void form_KeyUp(object sender, KeyEventArgs e) { if (KeyUp != null) KeyUp(this, e); if (e.Control && e.KeyCode == Keys.L) { screensaver.ctrlLPressed = true; screensaver.OnKeyboardInput(); } } void form_KeyDown(object sender, KeyEventArgs e) { if (KeyDown != null) KeyDown(this, e); if (e.Control && e.KeyCode == Keys.L) { screensaver.ctrlLPressed = true; screensaver.OnKeyboardInput(); } } void form_MouseWheel(object sender, MouseEventArgs e) { if (MouseWheel != null) MouseWheel(this, e); screensaver.OnMouseClick(); } void form_MouseUp(object sender, MouseEventArgs e) { if (MouseUp != null) MouseUp(this, e); screensaver.OnMouseClick(); } void form_MouseDown(object sender, MouseEventArgs e) { if (MouseDown!= null) MouseDown(this, e); screensaver.OnMouseClick(); } void form_MouseDoubleClick(object sender, MouseEventArgs e) { if (MouseDoubleClick != null) MouseDoubleClick(this, e); screensaver.OnMouseClick(); } void form_MouseClick(object sender, MouseEventArgs e) { if (MouseClick != null) MouseClick(this, e); screensaver.OnMouseClick(); } //Keep track of the initial mouse position since we want to ignore the MouseMove messages that are fired right when the form is created. Point mousePosition = Point.Empty; void form_MouseMove(object sender, MouseEventArgs e) { if (MouseMove != null) MouseMove(this, e); if (mousePosition == Point.Empty) mousePosition = e.Location; else if (mousePosition != e.Location) screensaver.OnMouseMove(); } /// /// Occurs when the mouse is moved over this window. /// public event MouseEventHandler MouseMove; /// /// Occurs when the mouse is clicked inside this window. /// public event MouseEventHandler MouseClick; /// /// Occurs when the mouse is double clicked inside this window. /// public event MouseEventHandler MouseDoubleClick; /// /// Occurs when the mouse wheel is moved inside this window. /// public event MouseEventHandler MouseWheel; /// /// Occurs when a mouse button goes up inside this window. /// public event MouseEventHandler MouseUp; /// /// Occurs when a mouse button goes down inside this window. /// public event MouseEventHandler MouseDown; /// /// Occurs when a key goes down. /// public event KeyEventHandler KeyDown; /// /// Occurs when a key is released. /// public event KeyEventHandler KeyUp; /// /// Occurs when a key is pressed. /// public event KeyPressEventHandler KeyPress; #endregion object tag; /// /// Gets or sets a tag value. /// public object Tag { get { return tag; } set { tag = value; } } Screensaver screensaver; /// /// Gets the for which this window was created. /// public Screensaver Screensaver { get { return screensaver; } } Form form; /// /// Gets the form encapsulating this window. /// This property is null if the screensaver is running in preview mode. /// public Form Form { get { return form; } } IntPtr handle; /// /// Gets the native handle of the window. /// public IntPtr Handle { get { return handle; } } Size size; /// /// Gets the size of the window. /// public Size Size { get { return size; } } Graphics graphics; /// /// Gets the GDI graphics object for this window. /// public Graphics Graphics { get { //Only set double buffering if the Graphics object is being used. if (!doubleBufferSet) DoubleBuffer = true; return graphics; } } /// /// Gets the screen on which this window resides /// public Screen Screen { get { return Screen.FromHandle(handle); } } /// /// Gets the display device index to use with this window. /// public int DeviceIndex { get { Screen thisScreen = Screen; for (int i = 0; i < Screen.AllScreens.Length; i++) if (Screen.AllScreens[i].Equals(thisScreen)) return i; throw new ApplicationException(); } } } /// /// Represents a collection of screensaver windows. /// public class WindowCollection : IEnumerable { internal WindowCollection(Window[] windows) { this.windows = windows; } Window[] windows; /// /// Gets the window at the given index. /// /// The zero-based index of the screensaver window. /// The window at the given index. public Window this[int index] { get { return windows[index]; } } /// /// Gets the number of screensaver windows available. /// public int Count { get { return windows.Length; } } #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return windows.GetEnumerator(); } #endregion } } /// /// Specifies the types of multiple monitor support to make available. /// public enum FullscreenMode { /// /// Single window covering all monitors. /// SingleWindow, /// /// Multiple windows, one for each monitor. /// MultipleWindows } /// /// Specifies the mode in which to run the screensaver. /// public enum ScreensaverMode { /// /// Show a the settings dialog. /// Settings, /// /// Render inside the preview box of the Windows Display Properties. /// Preview, /// /// Run the screensaver in full screen mode. /// Normal, /// /// Run the screensaver inside a fixed-sized window. /// Windowed } }