162 lines
6.2 KiB
C#
162 lines
6.2 KiB
C#
using System.Drawing;
|
|
using System.Runtime.InteropServices;
|
|
using CursorLang.Core.Interop;
|
|
|
|
namespace CursorLang.Agent.Interop;
|
|
|
|
/// <summary>
|
|
/// Plain GDI: a font, text, and an off-screen bitmap to draw them into.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// GDI rather than GDI+ on purpose. <c>System.Drawing.Common</c> would make the drawing
|
|
/// code shorter, but it is a separate assembly with a native GDI+ library behind it, and
|
|
/// how little this process weighs is the entire reason it exists apart from the window.
|
|
/// Everything the popup needs — one rounded rectangle and one line of text — GDI can do
|
|
/// on its own, and it draws the text with ClearType, exactly as Windows does everywhere else.
|
|
/// </remarks>
|
|
internal static class GdiNative
|
|
{
|
|
internal const int TRANSPARENT = 1;
|
|
|
|
internal const uint DT_SINGLELINE = 0x00000020;
|
|
internal const uint DT_CENTER = 0x00000001;
|
|
internal const uint DT_VCENTER = 0x00000004;
|
|
internal const uint DT_CALCRECT = 0x00000400;
|
|
internal const uint DT_NOPREFIX = 0x00000800;
|
|
internal const uint DT_NOCLIP = 0x00000100;
|
|
|
|
private const int DEFAULT_CHARSET = 1;
|
|
private const int OUT_TT_PRECIS = 4;
|
|
private const int CLIP_DEFAULT_PRECIS = 0;
|
|
private const int CLEARTYPE_QUALITY = 5;
|
|
private const int DEFAULT_PITCH = 0;
|
|
|
|
/// <summary>
|
|
/// The face the popup is written in.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// WPF is asked for "Segoe UI" at FontWeight SemiBold and resolves that to the
|
|
/// seguisb.ttf face. To GDI that face is a family of its own — "Segoe UI Semibold" —
|
|
/// and asking for the "Segoe UI" family at weight 600 lands on Bold instead, which
|
|
/// is visibly heavier. So the family is named outright and the weight is left to the
|
|
/// mapper: the family has one member and no synthetic emboldening happens.
|
|
/// </remarks>
|
|
private const string SemiBoldFace = "Segoe UI Semibold";
|
|
|
|
private const int FW_DONTCARE = 0;
|
|
|
|
/// <summary>
|
|
/// A font of the given size in physical pixels.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The size in the settings is in WPF units, that is 1/96 inch, while GDI counts
|
|
/// pixels — hence the multiplication by the monitor scale. The height is negative:
|
|
/// that asks for the em size rather than the cell height, which is what a font size
|
|
/// means everywhere else.
|
|
/// </remarks>
|
|
internal static IntPtr CreateFont(double wpfFontSize, double scale)
|
|
{
|
|
var height = (int)Math.Round(wpfFontSize * scale);
|
|
|
|
return CreateFontW(
|
|
-height, 0, 0, 0, FW_DONTCARE,
|
|
false, false, false,
|
|
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH,
|
|
SemiBoldFace);
|
|
}
|
|
|
|
/// <summary>The size of a single line of text with the font selected into the context.</summary>
|
|
internal static Size MeasureText(IntPtr deviceContext, string text)
|
|
{
|
|
var bounds = default(PopupWindowNative.Rect);
|
|
DrawText(deviceContext, text, text.Length, ref bounds,
|
|
DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX);
|
|
|
|
return new Size(bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
|
|
}
|
|
|
|
/// <summary>A colour as GDI wants it: 0x00BBGGRR, the alpha carried elsewhere.</summary>
|
|
internal static uint ToColorRef(Color color) =>
|
|
(uint)(color.R | (color.G << 8) | (color.B << 16));
|
|
|
|
[DllImport("gdi32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateFontW")]
|
|
private static extern IntPtr CreateFontW(int cHeight, int cWidth, int cEscapement, int cOrientation,
|
|
int cWeight, bool bItalic, bool bUnderline, bool bStrikeOut,
|
|
int iCharSet, int iOutPrecision, int iClipPrecision, int iQuality, int iPitchAndFamily,
|
|
string pszFaceName);
|
|
|
|
/// <summary>
|
|
/// A 32-bit surface to draw the popup into before anyone can see it.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Top-down — a negative height — so that the first row of <paramref name="bits"/>
|
|
/// is the top row of the picture and the alpha fixing up afterwards can walk the
|
|
/// memory straight through.
|
|
/// </remarks>
|
|
internal static IntPtr CreateSurface(IntPtr deviceContext, int width, int height, out IntPtr bits)
|
|
{
|
|
var header = new BitmapInfoHeader
|
|
{
|
|
biSize = Marshal.SizeOf<BitmapInfoHeader>(),
|
|
biWidth = width,
|
|
biHeight = -height,
|
|
biPlanes = 1,
|
|
biBitCount = 32,
|
|
biCompression = BI_RGB,
|
|
};
|
|
|
|
return CreateDIBSection(deviceContext, ref header, DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
|
|
}
|
|
|
|
private const uint BI_RGB = 0;
|
|
private const uint DIB_RGB_COLORS = 0;
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern bool DeleteDC(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BitmapInfoHeader header, uint usage,
|
|
out IntPtr bits, IntPtr section, uint offset);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern bool DeleteObject(IntPtr ho);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern int SetBkMode(IntPtr hdc, int mode);
|
|
|
|
[DllImport("gdi32.dll")]
|
|
internal static extern uint SetTextColor(IntPtr hdc, uint color);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DrawTextW")]
|
|
internal static extern int DrawText(IntPtr hdc, string lpchText, int cchText,
|
|
ref PopupWindowNative.Rect lprc, uint format);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern IntPtr GetDC(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct BitmapInfoHeader
|
|
{
|
|
public int biSize;
|
|
public int biWidth;
|
|
public int biHeight;
|
|
public short biPlanes;
|
|
public short biBitCount;
|
|
public uint biCompression;
|
|
public uint biSizeImage;
|
|
public int biXPelsPerMeter;
|
|
public int biYPelsPerMeter;
|
|
public uint biClrUsed;
|
|
public uint biClrImportant;
|
|
}
|
|
}
|