Processing Ajax...

Title

Message

Confirm

Confirm

Confirm

Confirm

Are you sure you want to delete this item?

Confirm

Are you sure you want to delete this item?

Actively Dim Unfocused Windows

Description
This script will stay running in the background, and dim all windows except for the currently focused window. To stop the script, just run it a second time.
Language
C#.net
Minimum Version
Created By
Thomas Malloch (BFS)
Contributors
-
Date Created
Mar 24, 2017
Date Last Modified
Mar 24, 2017

Scripted Function (Macro) Code

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 = "ActivlyDimUnusedWindows_TransparentForm_Running";
	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<Form> forms = new List<Form>();
		foreach(uint id in BFS.Monitor.GetMonitorIDs())
			forms.Add(new TransparentForm(40m, 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 move the forms around
	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);
	
        [DllImport("User32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
	
        private IntPtr WindowChangedHookHandle;
        private WinEventProcDelegate HookDelegateReference;
        private List<Form> Forms;
	
        private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
        private const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
	
        private const uint SWP_NOACTIVATE = 0x0010;
        private const uint SWP_NOMOVE = 0x0002;
        private const uint SWP_NOSIZE = 0x0001;
	
		internal MultipleFormApplicationContext(IntPtr currentWindow, List<Form> forms)
		{
            this.Forms = forms;
		
            //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(Form form in forms)
			{
				form.FormClosed += this.OnFormClosed;
				form.Show();
			}
			
			this.RepositionForms(currentWindow);
		}
		
		//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 RepositionForms(IntPtr window)
		{
            try
            {
                if(BFS.Window.GetAlwaysOnTop(window))
                {
                    //loop through each form and put them behind the topmost windows
                    foreach(Form form in this.Forms)
                        SetWindowPos(form.Handle, IntPtr.Zero, 0, 0, 0, 0, (SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE));
                }
                else
                {
                    //make sure the focused window is on the top of the z order
                    SetWindowPos(window, IntPtr.Zero, 0, 0, 0, 0, (SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE));
            
                    //loop through each form and put them behind the window
                    foreach(Form form in this.Forms)
                        SetWindowPos(form.Handle, window, 0, 0, 0, 0, (SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE));
                }
            }
            catch //ignore any errors
            {
            }
		}
		
		private void WindowChanged(IntPtr hWinEventHook, uint e, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
		{
            try
            {
                this.RepositionForms(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 decimal Transparency;
		
		//a variable to store the monitor id this form is on
		private uint MonitorId;
		
		//the contructor for our class
		internal TransparentForm(decimal transparency, uint id)
		{		
			this.Transparency = transparency;
			this.MonitorId = id;
			
			this.SuspendLayout();
			
			//setup the layout of this form
			this.BackColor = Color.Black;
			this.FormBorderStyle = FormBorderStyle.None;
			this.ShowInTaskbar = false;
			
			//setup the form load event
			this.Load += Form_Load;			
			
			this.ResumeLayout(false);
		}
		
		private void Form_Load(object sender, EventArgs e)
		{
			//make the window transparent
			BFS.Window.SetTransparency(this.Handle, this.Transparency);
			
			//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);
			
			//set the form's bounds to the monitor
			this.Bounds = BFS.Monitor.GetMonitorBoundsByID(this.MonitorId);
			
			//start up a thread to listen for an exit event
			new Thread(new ThreadStart(ExitListener)).Start();
		}
		
		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
            {
            }
		}
	}
}