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
{
}
}
}
}