using System; using System.Drawing; using System.Windows.Forms; using System.Threading; using System.Collections.Generic; using System.Runtime.InteropServices; // The 'windowHandle' parameter will contain the window handle for the: // - Active window when run by hotkey // - Window Location target when run by a Window Location rule // - TitleBar Button owner when run by a TitleBar Button // - Jump List owner when run from a Taskbar Jump List // - Currently focused window if none of these match public static class DisplayFusionFunction { private static readonly string SettingName = "ActivlyDimUnfocusedMonitorsWhileWindowIsRunning_TransparentForm_Running"; private const int TransparencyPercent = 40; //40% transparency private const decimal FadeTimeMS = 1000; //1 second public static void Run(IntPtr windowHandle) { //toggle the setting from running to not running, and vice versa ToggleSetting(); //check to see if we should exit bool isExiting = !IsTransparentWindowRunning(); //if we are exiting, exit if(isExiting) return; //add forms for each monitor except the used one List forms = new List(); foreach(uint id in BFS.Monitor.GetMonitorIDs()) { if(BFS.Monitor.GetMonitorIDByWindow(windowHandle) == id) continue; forms.Add(new TransparentForm(TransparencyPercent, FadeTimeMS, id)); } try { //this will open the forms we added to the list by using our custom application context Application.Run(new MultipleFormApplicationContext(windowHandle, forms)); } catch (Exception ex) { BFS.Dialog.ShowMessageError(ex.Message); } } //this function toggles the script settings from running to not running private static void ToggleSetting() { string status = BFS.ScriptSettings.ReadValue(SettingName); if(status.Equals("running", StringComparison.Ordinal)) BFS.ScriptSettings.WriteValue(SettingName, "not"); else BFS.ScriptSettings.WriteValue(SettingName, "running"); } //this function allows us to see the currect state of the script private static bool IsTransparentWindowRunning() { string status = BFS.ScriptSettings.ReadValue(SettingName); return status.Equals("running", StringComparison.Ordinal); } //extend the ApplicationContext class to support opening multiple forms //this class will also listen for active window changes, and play with the form transparency private class MultipleFormApplicationContext : ApplicationContext { //this is the function signature for the hook callback private delegate void WinEventProcDelegate(IntPtr hWinEventHook, uint e, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); //this function will let us listen to windows events [DllImport("User32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventProcDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwflags); //this function will let us stop listening to windows events [DllImport("User32.dll")] private static extern bool UnhookWinEvent(IntPtr hWinEventHook); private IntPtr WindowChangedHookHandle; private WinEventProcDelegate HookDelegateReference; private List Forms; private readonly IntPtr SelectedWindow; private const uint EVENT_SYSTEM_FOREGROUND = 0x0003; internal MultipleFormApplicationContext(IntPtr selectedWindow, List forms) { this.Forms = forms; this.SelectedWindow = selectedWindow; //store the reference to the function so .NET doesn't feel like disposing it this.HookDelegateReference = this.WindowChanged; //start the window hook this.WindowChangedHookHandle = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, this.HookDelegateReference, 0, 0, 0); //open each of the forms, and add our closing event to them foreach(TransparentForm form in forms) { form.FormClosed += this.OnFormClosed; form.Show(); form.FadeIn(); } } //when all the forms close, make sure to exit the application private void OnFormClosed(object sender, EventArgs e) { try { if(Application.OpenForms.Count != 0) return; //release the window hooks and exit the application if(this.WindowChangedHookHandle != IntPtr.Zero) UnhookWinEvent(this.WindowChangedHookHandle); this.HookDelegateReference = null; this.ExitThread(); } catch //ignore any errors { } } private void SetFormsTransparency(IntPtr focusedWindow) { try { //loop through each form and toggle the visibility foreach(TransparentForm form in this.Forms) { if(focusedWindow == this.SelectedWindow) form.FadeIn(); else form.FadeOut(); } } catch //ignore any errors { } } private void WindowChanged(IntPtr hWinEventHook, uint e, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { try { if(BFS.Window.GetText(hwnd).Equals("new notification", StringComparison.OrdinalIgnoreCase)) return; this.SetFormsTransparency(hwnd); } catch //ignore any errors { } } } //extend the Form class to get the behavior we want private class TransparentForm : Form { //this will tell us what transparency to use private readonly decimal Transparency; //variables for fading private const int TimerTickMS = 25; private decimal FadeRate; private decimal TargetTransparency; private readonly decimal FadeTimeMS; private System.Windows.Forms.Timer FadeTimer; //a variable to store the monitor id this form is on public uint MonitorId { get; private set; } //the contructor for our class public TransparentForm(decimal transparency, decimal fadeTimeMS, uint id) { this.Transparency = transparency; this.FadeTimeMS = fadeTimeMS; this.MonitorId = id; this.SuspendLayout(); //setup the layout of this form this.BackColor = Color.Black; this.FormBorderStyle = FormBorderStyle.None; this.ShowInTaskbar = false; this.FadeTimer = new System.Windows.Forms.Timer(); this.FadeTimer.Interval = TimerTickMS; this.FadeTimer.Tick += this.FadeTimer_Tick; this.FadeTimer.Enabled = false; //setup the form load event this.Load += Form_Load; this.ResumeLayout(false); } private void Form_Load(object sender, EventArgs e) { //add a windows style to the current style that will //tell this window to ignore user input uint style = (uint)BFS.Window.GetWindowStyleEx(this.Handle); BFS.Window.SetWindowStyleEx((BFS.WindowEnum.WindowStyleEx)(style | (uint)BFS.WindowEnum.WindowStyleEx.WS_EX_TRANSPARENT | (uint)BFS.WindowEnum.WindowStyleEx.WS_EX_LAYERED), this.Handle); BFS.Window.SetTransparency(this.Handle, 0); //set the form's bounds to the monitor this.Bounds = BFS.Monitor.GetMonitorBoundsByID(this.MonitorId); //set topmost BFS.Window.SetAlwaysOnTop(this.Handle, true); //start up a thread to listen for an exit event new Thread(new ThreadStart(ExitListener)).Start(); } public void FadeIn() { this.FadeTimer.Enabled = false; this.FadeRate = this.Transparency * TimerTickMS / this.FadeTimeMS; if(this.FadeRate < 1) this.FadeRate = 1; this.FadeTimer.Enabled = true; } public void FadeOut() { this.FadeTimer.Enabled = false; this.FadeRate = this.Transparency * TimerTickMS / this.FadeTimeMS * -1; if(this.FadeRate > -1) this.FadeRate = -1; this.FadeTimer.Enabled = true; } private void FadeTimer_Tick(object sender, EventArgs e) { try { bool isStoppingTimer = false; decimal transparency = BFS.Window.GetTransparency(this.Handle); transparency += this.FadeRate; if((this.FadeRate > 0) && (transparency >= this.Transparency)) { transparency = this.Transparency; isStoppingTimer = true; } if((this.FadeRate < 0) && (transparency <= 0)) { transparency = 0; isStoppingTimer = true; } BFS.Window.SetTransparency(this.Handle, transparency); if(isStoppingTimer) this.FadeTimer.Enabled = false; } catch(Exception ex) { } } private void ExitListener() { try { while(true) { //if we should close, tell the main thread to close the form if(!IsTransparentWindowRunning()) { try { this.Invoke((MethodInvoker) delegate { this.Close(); }); } catch //something went wrong, ignore { } break; } //sleep for a quarter of a second BFS.General.ThreadWait(500); } } catch //ignore errors { } } } }