This commit is contained in:
Trevor Hall 2025-12-09 21:47:37 -05:00
commit ce6d776257
8 changed files with 1 additions and 779 deletions

View file

@ -1,124 +0,0 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace PrivacyGlass
{
public class AcrylicOverlay : Form
{
private int _tintOpacity = 70;
private Color _tintColor = Color.Green;
private bool _isEnabled = false;
public int TintOpacity
{
get { return _tintOpacity; }
set { _tintOpacity = value; }
}
public Color TintColor
{
get { return _tintColor; }
set { _tintColor = value; }
}
public AcrylicOverlay(Rectangle bounds)
{
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.Manual;
Bounds = bounds;
ShowInTaskbar = false;
TopMost = true;
}
public void Toggle()
{
if (_isEnabled)
{
_isEnabled = false;
Hide();
}
else
{
_isEnabled = true;
Show();
EnableAcrylic();
}
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
protected override void OnPaint(PaintEventArgs e)
{
using (SolidBrush brush = new SolidBrush(Color.FromArgb(_tintOpacity, _tintColor)))
{
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
}
private void EnableAcrylic()
{
ACCENT_POLICY accent = new ACCENT_POLICY();
accent.AccentState = (int)AccentState.ACCENT_ENABLE_ACRYLICBLURBEHIND;
accent.AccentFlags = 2; // simplest, most compatible
accent.Color = (_tintOpacity << 24) |
(_tintColor.R << 16) |
(_tintColor.G << 8) |
_tintColor.B;
int size = Marshal.SizeOf(typeof(ACCENT_POLICY));
IntPtr accentPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(accent, accentPtr, false);
WINDOWCOMPOSITIONATTRIBDATA data = new WINDOWCOMPOSITIONATTRIBDATA();
data.Attribute = (int)WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.Data = accentPtr;
data.SizeOfData = size;
SetWindowCompositionAttribute(this.Handle, ref data);
Marshal.FreeHGlobal(accentPtr);
}
// ----------------------------------------------------
// Native
// ----------------------------------------------------
private enum AccentState
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
}
private enum WindowCompositionAttribute
{
WCA_ACCENT_POLICY = 19
}
[StructLayout(LayoutKind.Sequential)]
private struct ACCENT_POLICY
{
public int AccentState;
public int AccentFlags;
public int Color;
public int AnimationId;
}
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWCOMPOSITIONATTRIBDATA
{
public int Attribute;
public IntPtr Data;
public int SizeOfData;
}
[DllImport("user32.dll")]
private static extern int SetWindowCompositionAttribute(
IntPtr hwnd,
ref WINDOWCOMPOSITIONATTRIBDATA data);
}
}

View file

@ -1,41 +0,0 @@

using System.Drawing;
using System.Drawing.Drawing2D;
namespace PrivacyGlass
{
public static class BlurHelper
{
// factor 412 is good; higher = more blurry
public static Bitmap DownscaleBlur(Bitmap source, int factor = 8)
{
if (factor < 2) factor = 2;
int w = source.Width / factor;
int h = source.Height / factor;
if (w < 1) w = 1;
if (h < 1) h = 1;
// downscale
var small = new Bitmap(w, h);
using (var g = Graphics.FromImage(small))
{
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImage(source, new Rectangle(0, 0, w, h));
}
// upscale back
var blurred = new Bitmap(source.Width, source.Height);
using (var g = Graphics.FromImage(blurred))
{
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImage(small, new Rectangle(0, 0, blurred.Width, blurred.Height));
}
small.Dispose();
return blurred;
}
}
}

View file

@ -1,78 +0,0 @@

using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace PrivacyGlass
{
public static class GaussianBlur
{
// 5x5 gaussian kernel (sigma ~1.0)
private static readonly double[,] kernel =
{
{ 1, 4, 7, 4, 1 },
{ 4, 16, 26, 16, 4 },
{ 7, 26, 41, 26, 7 },
{ 4, 16, 26, 16, 4 },
{ 1, 4, 7, 4, 1 }
};
private const double kernelSum = 273;
public static unsafe Bitmap Blur(Bitmap image)
{
int w = image.Width;
int h = image.Height;
Bitmap blurred = new Bitmap(w, h, PixelFormat.Format32bppArgb);
BitmapData srcData =
image.LockBits(new Rectangle(0, 0, w, h),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
BitmapData dstData =
blurred.LockBits(new Rectangle(0, 0, w, h),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
byte* src = (byte*)srcData.Scan0;
byte* dst = (byte*)dstData.Scan0;
for (int y = 2; y < h - 2; y++)
{
for (int x = 2; x < w - 2; x++)
{
double b = 0, g = 0, r = 0;
// apply kernel
for (int ky = -2; ky <= 2; ky++)
{
for (int kx = -2; kx <= 2; kx++)
{
byte* p = src + ((y + ky) * stride) + ((x + kx) * 4);
double wgt = kernel[ky + 2, kx + 2];
b += p[0] * wgt;
g += p[1] * wgt;
r += p[2] * wgt;
}
}
byte* o = dst + (y * stride) + (x * 4);
o[0] = (byte)(b / kernelSum);
o[1] = (byte)(g / kernelSum);
o[2] = (byte)(r / kernelSum);
o[3] = 255;
}
}
image.UnlockBits(srcData);
blurred.UnlockBits(dstData);
return blurred;
}
}
}

View file

@ -1,189 +0,0 @@

using System;
using System.Drawing;
using System.Windows.Forms;
namespace PrivacyGlass
{
namespace PrivacyGlass
{
public class MagnifyOverlay : Form
{
private static bool _magInitialized;
private readonly Rectangle _screenBounds;
private IntPtr _magWindow = IntPtr.Zero;
public bool Blackout { get; set; } = false; // set true if you want pure black instead
public int Darken = 80; // optional extra dark tint 0100
public double blurScale = 0.5;
public MagnifyOverlay(Rectangle bounds)
{
_screenBounds = bounds;
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.Manual;
Bounds = bounds;
ShowInTaskbar = false;
TopMost = true;
DoubleBuffered = true;
BackColor = Color.Black;
Opacity = 1.0;
Visible = false;
}
private void ApplyBlurTransform()
{
// scale down slightly -> blur when drawn full size
float scale = 0.1f; // 0.7 is lighter blur, 0.3 is heavy blur
var m = new Native.MAGTRANSFORM
{
v = new float[9]
};
m.v[0] = scale; // scale X
m.v[4] = scale; // scale Y
m.v[8] = 1.0f; // w
Native.MagSetWindowTransform(_magWindow, ref m);
}
private void RefreshSource()
{
if (_magWindow == IntPtr.Zero)
return;
// full screen size
int fullW = _screenBounds.Width;
int fullH = _screenBounds.Height;
// reduced sampling size
int sampleW = (int)(fullW * blurScale);
int sampleH = (int)(fullH * blurScale);
// center the sampling rect
int left = _screenBounds.Left + (fullW - sampleW) / 2;
int top = _screenBounds.Top + (fullH - sampleH) / 2;
int right = left + sampleW;
int bottom = top + sampleH;
var srcRect = new Native.RECT
{
left = left,
top = top,
right = right,
bottom = bottom
};
Native.MagSetWindowSource(_magWindow, srcRect);
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!_magInitialized)
{
// Magnification API: initialize once per process
_magInitialized = Native.MagInitialize();
}
// Make the host window layered & topmost (we keep it opaque for now)
int ex = (int)Native.GetWindowLong(Handle, Native.GWL_EXSTYLE);
ex |= Native.WS_EX_TOPMOST | Native.WS_EX_LAYERED;
Native.SetWindowLong(Handle, Native.GWL_EXSTYLE, (IntPtr)ex);
if (_magInitialized && !Blackout)
{
// Create the magnifier control as a child of this form
_magWindow = Native.CreateWindowEx(
0,
"Magnifier", // WC_MAGNIFIER class
null,
Native.WS_CHILD | Native.WS_VISIBLE,
0,
0,
Width,
Height,
this.Handle,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
if (_magWindow != IntPtr.Zero)
{
RefreshMagnifierSource();
var fx = Native.CreatePrivacyEffect();
Native.MagSetColorEffect(_magWindow, ref fx);
Native.SetWindowPos(
_magWindow,
IntPtr.Zero,
0,
0,
Width,
Height,
0);
RefreshSource();
}
}
}
private void RefreshMagnifierSource()
{
if (_magWindow == IntPtr.Zero) return;
var src = new Native.RECT
{
left = _screenBounds.Left,
top = _screenBounds.Top,
right = _screenBounds.Right,
bottom = _screenBounds.Bottom
};
Native.MagSetWindowSource(_magWindow, src);
}
public void Toggle()
{
if (Visible)
{
Hide();
}
else
{
if (!Blackout && _magWindow != IntPtr.Zero)
{
RefreshMagnifierSource();
}
Show();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Extra dark tint over whatever Magnifier is doing
if (Darken > 0)
{
int alpha = Math.Max(0, Math.Min(255, (int)(Darken * 2.55)));
using (SolidBrush brush = new SolidBrush(Color.FromArgb(alpha, 0, 0, 0)))
{
e.Graphics.FillRectangle(brush, ClientRectangle);
}
}
}
protected override bool ShowWithoutActivation => true;
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
// We let the process exit clean up MagUninitialize; multiple overlays share the runtime.
}
}
}
}

View file

@ -1,158 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace PrivacyGlass
{
internal static class Native
{
[Flags]
private enum DwmBlurBehindFlags : uint
{
DWM_BB_ENABLE = 0x00000001,
DWM_BB_BLURREGION = 0x00000002,
DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004
}
[StructLayout(LayoutKind.Sequential)]
private struct DWM_BLURBEHIND
{
public DwmBlurBehindFlags dwFlags;
public bool fEnable;
public IntPtr hRgnBlur;
public bool fTransitionOnMaximized;
}
[DllImport("dwmapi.dll")]
private static extern int DwmEnableBlurBehindWindow(
IntPtr hWnd,
ref DWM_BLURBEHIND pBlurBehind);
// Window styles
public const int WS_CHILD = 0x40000000;
public const int WS_VISIBLE = 0x10000000;
// Extended styles
public const int WS_EX_TOPMOST = 0x00000008;
public const int WS_EX_LAYERED = 0x00080000;
public const int WS_EX_TRANSPARENT = 0x00000020;
public const int GWL_EXSTYLE = -20;
// ----- Magnification API -----
[DllImport("Magnification.dll", SetLastError = true)]
internal static extern bool MagInitialize();
[DllImport("Magnification.dll", SetLastError = true)]
internal static extern bool MagUninitialize();
[DllImport("Magnification.dll", SetLastError = true)]
internal static extern bool MagSetWindowSource(IntPtr hwnd, RECT rect);
[DllImport("Magnification.dll", SetLastError = true)]
internal static extern bool MagSetColorEffect(IntPtr hwnd, ref MAGCOLOREFFECT effect);
// ----- User32 stuff -----
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpClassName,
string lpWindowName,
int dwStyle,
int x, int y,
int nWidth, int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll")]
internal static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
internal static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
internal static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X, int Y,
int cx, int cy,
uint uFlags);
// structures
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MAGCOLOREFFECT
{
// 5x5 color matrix (row-major)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
public float[] transform;
}
/// <summary>
/// Strong privacy colour effect: desaturated + darkened.
/// Not a geometric blur, but kills readability hard.
/// </summary>
internal static MAGCOLOREFFECT CreatePrivacyEffect()
{
var fx = new MAGCOLOREFFECT
{
transform = new float[25]
};
// Well map R,G,B all to the same gray value and darken it.
// Gray = (R+G+B)/3 * factor
float factor = 0.25f; // 0..1, lower = darker
float g = factor / 3f;
// Row 0: R'
fx.transform[0] = g; // R
fx.transform[1] = g; // G
fx.transform[2] = g; // B
// Row 1: G'
fx.transform[5] = g;
fx.transform[6] = g;
fx.transform[7] = g;
// Row 2: B'
fx.transform[10] = g;
fx.transform[11] = g;
fx.transform[12] = g;
// Row 3: A' = A
fx.transform[18] = 1.0f;
// Row 4: w' (bias) leave as identity
fx.transform[24] = 1.0f;
return fx;
}
[DllImport("Magnification.dll", SetLastError = true)]
internal static extern bool MagSetWindowTransform(
IntPtr hwnd,
ref MAGTRANSFORM pTransform);
[StructLayout(LayoutKind.Sequential)]
internal struct MAGTRANSFORM
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public float[] v; // 3x3 matrix
}
}
}

View file

@ -1,149 +0,0 @@

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace PrivacyGlass
{
public class OverlayWindow : Form
{
private Rectangle _screenBounds;
private Bitmap _blurred;
// === Customizable settings ===
public int Darken = 20; // 0 = no dark, 100 = full black
public bool BlurEnabled = true; // toggle blur (fast blackout vs frosted)
public OverlayWindow(Rectangle bounds)
{
_screenBounds = bounds;
FormBorderStyle = FormBorderStyle.None;
Bounds = bounds;
StartPosition = FormStartPosition.Manual;
TopMost = true;
ShowInTaskbar = false;
DoubleBuffered = true;
BackgroundImageLayout = ImageLayout.Stretch;
BackColor = Color.Black; // fallback
Opacity = 1.0;
Visible = false;
}
public void Toggle()
{
if (Visible)
{
Hide();
}
else
{
RenderOverlay();
Show();
}
}
private void RenderOverlay()
{
// === FULL BLACKOUT ===
if (Darken >= 100)
{
// pure blackout, no screenshots
if (_blurred != null) { _blurred.Dispose(); _blurred = null; }
BackgroundImage = null;
return;
}
// === otherwise capture the screen ===
var bmp = new Bitmap(_screenBounds.Width, _screenBounds.Height, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(
_screenBounds.X,
_screenBounds.Y,
0,
0,
_screenBounds.Size,
CopyPixelOperation.SourceCopy);
}
// === BLUR if enabled ===
if (BlurEnabled)
{
// dispose old
if (_blurred != null) { _blurred.Dispose(); _blurred = null; }
// simple downscale blur
_blurred = DownscaleBlur(bmp, 8);
bmp.Dispose();
BackgroundImage = _blurred;
}
else
{
// NO BLUR — just display as-is
BackgroundImage = bmp;
}
}
// Cheap fake blur (downscale → upscale)
private Bitmap DownscaleBlur(Bitmap source, int factor)
{
int w = Math.Max(1, source.Width / factor);
int h = Math.Max(1, source.Height / factor);
var small = new Bitmap(w, h);
using (var g = Graphics.FromImage(small))
{
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImage(source, new Rectangle(0, 0, w, h));
}
var blurred = new Bitmap(source.Width, source.Height);
using (var g = Graphics.FromImage(blurred))
{
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImage(small, new Rectangle(0, 0, blurred.Width, blurred.Height));
}
small.Dispose();
source.Dispose();
return blurred;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// === DARKEN OVERLAY ===
if (Darken > 0)
{
// convert 0-100 -> 0-255 alpha
int alpha = (int)(Darken * 2.55);
alpha = Math.Max(0, Math.Min(255, alpha));
using (var brush = new SolidBrush(Color.FromArgb(alpha, 0, 0, 0)))
{
e.Graphics.FillRectangle(brush, ClientRectangle);
}
}
}
protected override bool ShowWithoutActivation => true;
protected override void Dispose(bool disposing)
{
if (disposing && _blurred != null)
{
_blurred.Dispose();
_blurred = null;
}
base.Dispose(disposing);
}
}
}

View file

@ -47,22 +47,10 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AcrylicOverlay.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="BlurHelper.cs" />
<Compile Include="BlurOverlay.cs"> <Compile Include="BlurOverlay.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="GaussianBlur.cs" />
<Compile Include="HotkeyFilter.cs" /> <Compile Include="HotkeyFilter.cs" />
<Compile Include="MagnifyOverlay.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Native.cs" />
<Compile Include="OverlayWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TrayApp.cs" /> <Compile Include="TrayApp.cs" />

View file

@ -1,5 +1,4 @@
using PrivacyGlass.PrivacyGlass; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
@ -75,24 +74,6 @@ namespace PrivacyGlass
return menu; return menu;
} }
//private void CreateOverlays()
//{
// foreach (var scr in Screen.AllScreens)
// overlays.Add(new MagnifyOverlay(scr.Bounds));
//}
//private void CreateOverlays()
//{
// foreach (var screen in Screen.AllScreens)
// {
// var ov = new MagnifyOverlay(screen.Bounds)
// {
// Blackout = false, // or true if you want pure black on this screen
// Darken = 50 // tweak tint strength
// };
// overlays.Add(ov);
// }
//}
private void CreateOverlays() private void CreateOverlays()
{ {
foreach (var screen in Screen.AllScreens) foreach (var screen in Screen.AllScreens)
@ -105,14 +86,6 @@ namespace PrivacyGlass
} }
} }
private void Register()
{
// CTRL+ALT+B
RegisterHotKey(IntPtr.Zero, (int)HOTKEY_ID, 0x2 | 0x4, (uint)Keys.B);
Application.AddMessageFilter(new HotkeyFilter(Toggle));
}
private void Toggle() => private void Toggle() =>
overlays.ForEach(o => o.Toggle()); overlays.ForEach(o => o.Toggle());