Add project files.

This commit is contained in:
Blade / SCOPEDD
2026-02-22 11:31:35 -05:00
parent 3612bf40db
commit c1a425a7ad
84 changed files with 2323 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
using System;
using System.Runtime.InteropServices;
namespace ClipForge
{
public class TrayIconService : IDisposable
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern bool Shell_NotifyIcon(uint dwMessage, ref NOTIFYICONDATA lpdata);
[DllImport("user32.dll")]
private static extern bool DestroyMenu(IntPtr hMenu);
[DllImport("user32.dll")]
private static extern IntPtr CreatePopupMenu();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern bool AppendMenu(IntPtr hMenu, uint uFlags, uint uIDNewItem, string lpNewItem);
[DllImport("user32.dll")]
private static extern int TrackPopupMenu(IntPtr hMenu, uint uFlags, int x, int y, int nReserved, IntPtr hWnd, IntPtr prcRect);
[DllImport("user32.dll")]
private static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
// This overload loads from a file path
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadImage(IntPtr hInst, string lpszName,
uint uType, int cxDesired, int cyDesired, uint fuLoad);
// This overload loads a system icon by ID
[DllImport("user32.dll")]
private static extern IntPtr LoadImage(IntPtr hInst, IntPtr lpszName,
uint uType, int cxDesired, int cyDesired, uint fuLoad);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, WndProcDelegate newProc);
[DllImport("user32.dll")]
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string? lpModuleName);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NOTIFYICONDATA
{
public uint cbSize;
public IntPtr hWnd;
public uint uID;
public uint uFlags;
public uint uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szTip;
public uint dwState;
public uint dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szInfo;
public uint uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szInfoTitle;
public uint dwInfoFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT { public int x; public int y; }
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private const uint NIM_ADD = 0x00000000;
private const uint NIM_MODIFY = 0x00000001;
private const uint NIM_DELETE = 0x00000002;
private const uint NIF_MESSAGE = 0x00000001;
private const uint NIF_ICON = 0x00000002;
private const uint NIF_TIP = 0x00000004;
private const uint WM_APP = 0x8000;
private const uint WM_TRAYICON = WM_APP + 1;
private const uint WM_RBUTTONUP = 0x0205;
private const uint WM_LBUTTONDBLCLK = 0x0203;
private const uint MF_STRING = 0x00000000;
private const uint MF_SEPARATOR = 0x00000800;
private const uint TPM_RETURNCMD = 0x0100;
private const int IDI_APPLICATION = 32512;
private const uint IMAGE_ICON = 1;
private const uint LR_SHARED = 0x00008000;
private const uint LR_LOADFROMFILE = 0x00000010;
private const int GWLP_WNDPROC = -4;
private const uint CMD_OPEN = 1001;
private const uint CMD_EXIT = 1002;
private NOTIFYICONDATA _iconData;
private IntPtr _hwnd;
private bool _added;
private WndProcDelegate? _wndProc;
private IntPtr _oldWndProc;
private Action? _showWindow;
private Action? _exitApp;
public void Initialize(IntPtr hwnd, Action showWindow, Action exitApp)
{
_hwnd = hwnd;
_showWindow = showWindow;
_exitApp = exitApp;
_wndProc = TrayWndProc;
_oldWndProc = SetWindowLongPtr(_hwnd, GWLP_WNDPROC, _wndProc);
// Try to load our custom icon from the app directory
var iconPath = System.IO.Path.Combine(
AppContext.BaseDirectory, "clipforge.ico");
IntPtr hIcon;
if (System.IO.File.Exists(iconPath))
{
hIcon = LoadImage(IntPtr.Zero, iconPath,
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
}
else
{
// Fallback to generic Windows icon
hIcon = LoadImage(IntPtr.Zero, new IntPtr(IDI_APPLICATION),
IMAGE_ICON, 16, 16, LR_SHARED);
}
_iconData = new NOTIFYICONDATA
{
cbSize = (uint)Marshal.SizeOf<NOTIFYICONDATA>(),
hWnd = _hwnd,
uID = 1,
uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE,
uCallbackMessage = WM_TRAYICON,
hIcon = hIcon,
szTip = "ClipForge — Recording"
};
Shell_NotifyIcon(NIM_ADD, ref _iconData);
_added = true;
}
private IntPtr TrayWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg == WM_TRAYICON)
{
uint mouseEvent = (uint)lParam & 0xFFFF;
if (mouseEvent == WM_LBUTTONDBLCLK)
_showWindow?.Invoke();
if (mouseEvent == WM_RBUTTONUP)
ShowContextMenu();
}
return CallWindowProc(_oldWndProc, hWnd, msg, wParam, lParam);
}
private void ShowContextMenu()
{
var hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING, CMD_OPEN, "Open ClipForge");
AppendMenu(hMenu, MF_SEPARATOR, 0, "");
AppendMenu(hMenu, MF_STRING, CMD_EXIT, "Exit");
GetCursorPos(out POINT pt);
SetForegroundWindow(_hwnd);
int cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD,
pt.x, pt.y, 0, _hwnd, IntPtr.Zero);
DestroyMenu(hMenu);
if (cmd == CMD_OPEN) _showWindow?.Invoke();
if (cmd == CMD_EXIT) _exitApp?.Invoke();
}
public void SetStatus(string status)
{
if (!_added) return;
_iconData.szTip = $"ClipForge — {status}";
Shell_NotifyIcon(NIM_MODIFY, ref _iconData);
}
public void Dispose()
{
if (_added)
{
Shell_NotifyIcon(NIM_DELETE, ref _iconData);
_added = false;
}
}
}
}