diff --git a/.editorconfig b/.editorconfig
index 2b7e98d..b6c3966 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,6 +1,9 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
+[*]
+guidelines = 120 1px dotted 504CFF00, 150 1px dotted 50FF6A00
+
# Test .cs files
[**/*Tests.cs]
diff --git a/src/samples/Petzold/5th/Clover/Program.cs b/src/samples/Petzold/5th/Clover/Program.cs
index aa5d0f1..3cd5a3f 100644
--- a/src/samples/Petzold/5th/Clover/Program.cs
+++ b/src/samples/Petzold/5th/Clover/Program.cs
@@ -44,9 +44,9 @@ protected override LRESULT WindowProcedure(HWND window, MessageType message, WPA
using HRGN region1 = HRGN.FromEllipse(_cxClient / 2, _cyClient / 3, _cxClient, 2 * _cyClient / 3);
using HRGN region2 = HRGN.FromEllipse(_cxClient / 3, 0, 2 * _cxClient / 3, _cyClient / 2);
using HRGN region3 = HRGN.FromEllipse(_cxClient / 3, _cyClient / 2, 2 * _cxClient / 3, _cyClient);
- using HRGN region4 = HRGN.CombineRegion(region0, region1, RegionCombineMode.Or);
- using HRGN region5 = HRGN.CombineRegion(region2, region3, RegionCombineMode.Or);
- _hrgnClip = HRGN.CombineRegion(region4, region5, RegionCombineMode.Xor);
+ using HRGN region4 = region0.Combine(region1, RegionCombineMode.Or);
+ using HRGN region5 = region2.Combine(region3, RegionCombineMode.Or);
+ _hrgnClip = region4.Combine(region5, RegionCombineMode.Xor);
return (LRESULT)0;
}
diff --git a/src/thirtytwo/Messages/Message.NonClientPaint.cs b/src/thirtytwo/Messages/Message.NonClientPaint.cs
new file mode 100644
index 0000000..107166a
--- /dev/null
+++ b/src/thirtytwo/Messages/Message.NonClientPaint.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows;
+
+public static partial class Message
+{
+ ///
+ /// Wrapper for the frame painting message.
+ ///
+ public readonly unsafe ref struct NonClientPaint(WPARAM wParam)
+ {
+ ///
+ /// Update region clipped to the window frame.
+ ///
+ ///
+ ///
+ /// This may sometimes be to indicate the entire window.
+ ///
+ ///
+ /// This is effectively . If the window has a clipping region
+ /// set via , that will be intersected with the rect.
+ /// This isn't very likely as having a clipping region
+ ///
+ /// prevents theming.
+ ///
+ ///
+ public HRGN UpdateRegion => (HRGN)(nint)wParam;
+
+ public HDC GetDC(HWND window, bool copyRegion = true)
+ {
+ // GetDCEx will take ownership of the region.
+ HRGN updateRegion = UpdateRegion;
+ if (updateRegion.IsFull)
+ {
+ updateRegion = HRGN.Null;
+ }
+
+ HRGN region = copyRegion && !updateRegion.IsNull ? updateRegion.Copy() : updateRegion;
+
+ // GetDC (as opposed to GetDCEx) will call GetDCEx with a special flag that will set flags based
+ // on the window's style. WS_CLIPCHILDREN will set DCX_CLIPCHILDREN for example.
+
+ return Interop.GetDCEx(
+ window,
+ region,
+ region.IsNull ? GET_DCX_FLAGS.DCX_WINDOW : GET_DCX_FLAGS.DCX_WINDOW | GET_DCX_FLAGS.DCX_INTERSECTRGN);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/NativeMethods.txt b/src/thirtytwo/NativeMethods.txt
index 0cb8d22..dd5ba7c 100644
--- a/src/thirtytwo/NativeMethods.txt
+++ b/src/thirtytwo/NativeMethods.txt
@@ -144,6 +144,7 @@ GetCurrentThreadId
GetCursorPos
GetDC
GetDCBrushColor
+GetDCEx
GetDeviceCaps
GetDialogBaseUnits
GetDialogBaseUnits
diff --git a/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs b/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs
index dc22df3..b486336 100644
--- a/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs
+++ b/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs
@@ -10,6 +10,11 @@ public unsafe partial struct HRGN : IDisposable
{
public void Dispose()
{
+ if (IsFull)
+ {
+ return;
+ }
+
if (!IsNull)
{
Interop.DeleteObject(this);
@@ -18,6 +23,18 @@ public void Dispose()
Unsafe.AsRef(in this) = default;
}
+ ///
+ /// Special sent during WM_NCPAINT to indicate the entire window.
+ ///
+ public static HRGN Full { get; } = (HRGN)1;
+
+ ///
+ /// Is special value.
+ ///
+ public bool IsFull => Value == Full.Value;
+
+ // There is also a special HRGN_MONITOR (2) that is used when maximized. Not sure if this escapes.
+
public static HRGN FromRectangle(Rectangle rectangle) =>
Interop.CreateRectRgn(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
@@ -30,13 +47,27 @@ public static HRGN FromEllipse(Rectangle bounds) =>
public static HRGN CreateEmpty() => Interop.CreateRectRgn(0, 0, 0, 0);
- public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode) =>
- CombineRegion(first, second, combineMode, out _);
+ public HRGN Combine(HRGN region, RegionCombineMode combineMode) =>
+ Combine(region, combineMode, out _);
+
+ public HRGN Combine(HRGN region, RegionCombineMode combineMode, out RegionType type)
+ {
+ HRGN hrgn = CreateEmpty();
+ type = (RegionType)Interop.CombineRgn(hrgn, this, region, (RGN_COMBINE_MODE)combineMode);
+ if (type == RegionType.Error)
+ {
+ hrgn.Dispose();
+ }
+
+ return hrgn;
+ }
+
+ public HRGN Copy() => Copy(out _);
- public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode, out RegionType type)
+ public HRGN Copy(out RegionType type)
{
HRGN hrgn = CreateEmpty();
- type = (RegionType)Interop.CombineRgn(hrgn, first, second, (RGN_COMBINE_MODE)combineMode);
+ type = (RegionType)Interop.CombineRgn(hrgn, this, default, (RGN_COMBINE_MODE)RegionCombineMode.Copy);
if (type == RegionType.Error)
{
hrgn.Dispose();