From 85ea1738dcdf7baa73ac741f5e6019c2a825469a Mon Sep 17 00:00:00 2001
From: Jeremy Kuhne <jeremy.kuhne@microsoft.com>
Date: Sat, 3 Feb 2024 20:20:00 -0800
Subject: [PATCH] Port RandRect, Bezier, and Blokout2 samples from WInterop

---
 src/samples/Petzold/5th/Bezier/Bezier.csproj  |  10 ++
 src/samples/Petzold/5th/Bezier/Program.cs     |  97 ++++++++++++++
 .../Petzold/5th/Blokout2/Blokout2.csproj      |  10 ++
 src/samples/Petzold/5th/Blokout2/Program.cs   |  98 ++++++++++++++
 src/samples/Petzold/5th/RandRect/Program.cs   | 121 ++++++++++++++++++
 .../Petzold/5th/RandRect/RandRect.csproj      |  10 ++
 src/thirtytwo/DeviceContextExtensions.cs      |  28 ++++
 src/thirtytwo/NativeMethods.txt               |  23 ++--
 .../Win32/UI/WindowsAndMessaging/HCURSOR.cs   |   1 +
 src/thirtytwo/WrapperEnums/PenMixMode.cs      |  93 ++++++++++++++
 .../WrapperEnums/WrapperEnumExtensions.cs     |   1 +
 thirtytwo.sln                                 |  21 +++
 12 files changed, 504 insertions(+), 9 deletions(-)
 create mode 100644 src/samples/Petzold/5th/Bezier/Bezier.csproj
 create mode 100644 src/samples/Petzold/5th/Bezier/Program.cs
 create mode 100644 src/samples/Petzold/5th/Blokout2/Blokout2.csproj
 create mode 100644 src/samples/Petzold/5th/Blokout2/Program.cs
 create mode 100644 src/samples/Petzold/5th/RandRect/Program.cs
 create mode 100644 src/samples/Petzold/5th/RandRect/RandRect.csproj
 create mode 100644 src/thirtytwo/WrapperEnums/PenMixMode.cs

diff --git a/src/samples/Petzold/5th/Bezier/Bezier.csproj b/src/samples/Petzold/5th/Bezier/Bezier.csproj
new file mode 100644
index 0000000..265b404
--- /dev/null
+++ b/src/samples/Petzold/5th/Bezier/Bezier.csproj
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/samples/Petzold/5th/Bezier/Program.cs b/src/samples/Petzold/5th/Bezier/Program.cs
new file mode 100644
index 0000000..176f0d0
--- /dev/null
+++ b/src/samples/Petzold/5th/Bezier/Program.cs
@@ -0,0 +1,97 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using Windows;
+using Windows.Win32.Foundation;
+
+namespace Bezier;
+
+/// <summary>
+///  Sample from Programming Windows, 5th Edition.
+///  Original (c) Charles Petzold, 1998
+///  Figure 5-16, Pages 156-159.
+/// </summary>
+internal static class Program
+{
+    [STAThread]
+    private static void Main() => Application.Run(new Bezier("Bezier Splines"));
+}
+
+internal class Bezier : MainWindow
+{
+    private readonly Point[] _apt = new Point[4];
+
+    public Bezier(string title) : base(title)
+    {
+    }
+
+    protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
+    {
+        switch (message)
+        {
+            case MessageType.Size:
+                int cxClient = lParam.LOWORD;
+                int cyClient = lParam.HIWORD;
+
+                _apt[0].X = cxClient / 4;
+                _apt[0].Y = cyClient / 2;
+                _apt[1].X = cxClient / 2;
+                _apt[1].Y = cyClient / 4;
+                _apt[2].X = cxClient / 2;
+                _apt[2].Y = 3 * cyClient / 4;
+                _apt[3].X = 3 * cxClient / 4;
+                _apt[3].Y = cyClient / 2;
+
+                return (LRESULT)0;
+
+            case MessageType.LeftButtonDown:
+            case MessageType.RightButtonDown:
+            case MessageType.MouseMove:
+                MouseKey mk = (MouseKey)wParam.LOWORD;
+                if ((mk & (MouseKey.LeftButton | MouseKey.RightButton)) != 0)
+                {
+                    using DeviceContext dc = window.GetDeviceContext();
+                    dc.SelectObject(StockPen.White);
+                    DrawBezier(dc, _apt);
+
+                    if ((mk & MouseKey.LeftButton) != 0)
+                    {
+                        _apt[1].X = lParam.LOWORD;
+                        _apt[1].Y = lParam.HIWORD;
+                    }
+
+                    if ((mk & MouseKey.RightButton) != 0)
+                    {
+                        _apt[2].X = lParam.LOWORD;
+                        _apt[2].Y = lParam.HIWORD;
+                    }
+
+                    dc.SelectObject(StockPen.Black);
+                    DrawBezier(dc, _apt);
+                }
+
+                return (LRESULT)0;
+
+            case MessageType.Paint:
+                window.Invalidate(true);
+                using (DeviceContext dc = window.BeginPaint())
+                {
+                    DrawBezier(dc, _apt);
+                }
+
+                return (LRESULT)0;
+        }
+
+        static void DrawBezier(DeviceContext dc, Point[] apt)
+        {
+            dc.PolyBezier(apt);
+            dc.MoveTo(apt[0]);
+            dc.LineTo(apt[1]);
+            dc.MoveTo(apt[2]);
+            dc.LineTo(apt[3]);
+        }
+
+        return base.WindowProcedure(window, message, wParam, lParam);
+    }
+}
diff --git a/src/samples/Petzold/5th/Blokout2/Blokout2.csproj b/src/samples/Petzold/5th/Blokout2/Blokout2.csproj
new file mode 100644
index 0000000..265b404
--- /dev/null
+++ b/src/samples/Petzold/5th/Blokout2/Blokout2.csproj
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/samples/Petzold/5th/Blokout2/Program.cs b/src/samples/Petzold/5th/Blokout2/Program.cs
new file mode 100644
index 0000000..6b7bb6e
--- /dev/null
+++ b/src/samples/Petzold/5th/Blokout2/Program.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using Windows;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+
+namespace Blokout2;
+
+/// <summary>
+///  Sample from Programming Windows, 5th Edition.
+///  Original (c) Charles Petzold, 1998
+///  Figure 7-11, Pages 314-317.
+/// </summary>
+internal static class Program
+{
+    [STAThread]
+    private static void Main() => Application.Run(new Blockout2("Mouse Button & Capture Demo"));
+}
+
+internal class Blockout2 : MainWindow
+{
+    private bool _fBlocking, _fValidBox;
+    private Point _ptBeg, _ptEnd, _ptBoxBeg, _ptBoxEnd;
+
+    public Blockout2(string title) : base(title)
+    {
+    }
+
+    protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
+    {
+        switch (message)
+        {
+            case MessageType.LeftButtonDown:
+                _ptBeg.X = _ptEnd.X = lParam.LOWORD;
+                _ptBeg.Y = _ptEnd.Y = lParam.HIWORD;
+                DrawBoxOutline(window, _ptBeg, _ptEnd);
+                Interop.SetCapture(window);
+                CursorId.Cross.SetCursor();
+                _fBlocking = true;
+                return (LRESULT)0;
+            case MessageType.MouseMove:
+                if (_fBlocking)
+                {
+                    CursorId.Cross.SetCursor();
+                    DrawBoxOutline(window, _ptBeg, _ptEnd);
+                    _ptEnd.X = lParam.LOWORD;
+                    _ptEnd.Y = lParam.HIWORD;
+                    DrawBoxOutline(window, _ptBeg, _ptEnd);
+                }
+
+                return (LRESULT)0;
+            case MessageType.LeftButtonUp:
+                if (_fBlocking)
+                {
+                    DrawBoxOutline(window, _ptBeg, _ptEnd);
+                    _ptBoxBeg = _ptBeg;
+                    _ptBoxEnd.X = lParam.LOWORD;
+                    _ptBoxEnd.Y = lParam.HIWORD;
+                    Interop.ReleaseCapture();
+                    CursorId.Arrow.SetCursor();
+                    _fBlocking = false;
+                    _fValidBox = true;
+                    window.Invalidate(true);
+                }
+
+                return (LRESULT)0;
+            case MessageType.Paint:
+                using (DeviceContext dc = window.BeginPaint())
+                {
+                    if (_fValidBox)
+                    {
+                        dc.SelectObject(StockBrush.Black);
+                        dc.Rectangle(_ptBoxBeg.X, _ptBoxBeg.Y, _ptBoxEnd.X, _ptBoxEnd.Y);
+                    }
+                    if (_fBlocking)
+                    {
+                        dc.SetRasterOperation(PenMixMode.Not);
+                        dc.SelectObject(StockBrush.Null);
+                        dc.Rectangle(_ptBeg.X, _ptBeg.Y, _ptEnd.X, _ptEnd.Y);
+                    }
+                }
+
+                return (LRESULT)0;
+        }
+
+        static void DrawBoxOutline(HWND window, Point ptBeg, Point ptEnd)
+        {
+            using DeviceContext dc = window.GetDeviceContext();
+            dc.SetRasterOperation(PenMixMode.Not);
+            dc.SelectObject(StockBrush.Null);
+            dc.Rectangle(Rectangle.FromLTRB(ptBeg.X, ptBeg.Y, ptEnd.X, ptEnd.Y));
+        }
+
+        return base.WindowProcedure(window, message, wParam, lParam);
+    }
+}
diff --git a/src/samples/Petzold/5th/RandRect/Program.cs b/src/samples/Petzold/5th/RandRect/Program.cs
new file mode 100644
index 0000000..10a9e8e
--- /dev/null
+++ b/src/samples/Petzold/5th/RandRect/Program.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using System.Runtime.InteropServices;
+using Windows;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.Graphics.Gdi;
+using Windows.Win32.UI.WindowsAndMessaging;
+
+namespace RandRect;
+
+/// <summary>
+///  Sample from Programming Windows, 5th Edition.
+///  Original (c) Charles Petzold, 1998
+///  Figure 5-26, Pages 200-202.
+/// </summary>
+internal unsafe static class Program
+{
+    [STAThread]
+    private static void Main()
+    {
+        const string szAppName = "RandRect";
+
+        WindowProcedure wndProc = WindowProcedure;
+        HMODULE module;
+        Interop.GetModuleHandleEx(0, (PCWSTR)null, &module);
+
+        HWND hwnd;
+
+        fixed (char* appName = szAppName)
+        fixed (char* title = "Random Rectangles")
+        {
+            WNDCLASSEXW wndClass = new()
+            {
+                cbSize = (uint)sizeof(WNDCLASSEXW),
+                style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW,
+                lpfnWndProc = (WNDPROC)Marshal.GetFunctionPointerForDelegate(wndProc),
+                hInstance = module,
+                hIcon = Interop.LoadIcon(default, Interop.IDI_APPLICATION),
+                hCursor = Interop.LoadCursor(default, Interop.IDC_ARROW),
+                hbrBackground = (HBRUSH)Interop.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH),
+                lpszClassName = appName
+            };
+
+            ATOM atom = Interop.RegisterClassEx(&wndClass);
+
+            hwnd = Interop.CreateWindowEx(
+                WINDOW_EX_STYLE.WS_EX_OVERLAPPEDWINDOW,
+                appName,
+                title,
+                WINDOW_STYLE.WS_OVERLAPPEDWINDOW,
+                Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT,
+                HWND.Null,
+                HMENU.Null,
+                module,
+                null);
+
+
+        }
+
+        Interop.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_SHOWDEFAULT);
+        Interop.UpdateWindow(hwnd);
+
+        while (true)
+        {
+            if (Interop.PeekMessage(out MSG message, HWND.Null, 0, uint.MaxValue, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
+            {
+                if (message.message == Interop.WM_QUIT)
+                {
+                    break;
+                }
+
+                Interop.TranslateMessage(message);
+                Interop.DispatchMessage(message);
+            }
+
+            // We're crazy fast over 25 years past the source sample,
+            // sleeping to make this a bit more interesting.
+            Thread.Sleep(100);
+            DrawRectangle(hwnd);
+        }
+    }
+
+    private static int s_cxClient, s_cyClient;
+    private static readonly Random s_rand = new();
+
+    private static LRESULT WindowProcedure(HWND window, uint message, WPARAM wParam, LPARAM lParam)
+    {
+        switch ((MessageType)message)
+        {
+            case MessageType.Size:
+                s_cxClient = lParam.LOWORD;
+                s_cyClient = lParam.HIWORD;
+                return (LRESULT)0;
+            case MessageType.Destroy:
+                Interop.PostQuitMessage(0);
+                return (LRESULT)0;
+        }
+
+        return Interop.DefWindowProc(window, message, wParam, lParam);
+    }
+
+    private static void DrawRectangle(HWND window)
+    {
+        if (s_cxClient == 0 || s_cyClient == 0)
+            return;
+
+        Rectangle rect = Rectangle.FromLTRB(
+            s_rand.Next() % s_cxClient,
+            s_rand.Next() % s_cyClient,
+            s_rand.Next() % s_cxClient,
+            s_rand.Next() % s_cyClient);
+
+        using HBRUSH brush = HBRUSH.CreateSolid(
+            Color.FromArgb((byte)(s_rand.Next() % 256), (byte)(s_rand.Next() % 256), (byte)(s_rand.Next() % 256)));
+        using DeviceContext dc = window.GetDeviceContext();
+        dc.FillRectangle(rect, brush);
+    }
+}
diff --git a/src/samples/Petzold/5th/RandRect/RandRect.csproj b/src/samples/Petzold/5th/RandRect/RandRect.csproj
new file mode 100644
index 0000000..265b404
--- /dev/null
+++ b/src/samples/Petzold/5th/RandRect/RandRect.csproj
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/thirtytwo/DeviceContextExtensions.cs b/src/thirtytwo/DeviceContextExtensions.cs
index d2bad6d..c6db3a5 100644
--- a/src/thirtytwo/DeviceContextExtensions.cs
+++ b/src/thirtytwo/DeviceContextExtensions.cs
@@ -306,6 +306,19 @@ public static bool Ellipse<T>(this T context, int left, int top, int right, int
         return success;
     }
 
+    public static unsafe bool PolyBezier<T>(this T context, params Point[] points) where T : IHandle<HDC> =>
+        PolyBezier(context, points.AsSpan());
+
+    public static unsafe bool PolyBezier<T>(this T context, ReadOnlySpan<Point> points) where T : IHandle<HDC>
+    {
+        fixed (Point* p = points)
+        {
+            bool success = Interop.PolyBezier(context.Handle, p, (uint)points.Length);
+            GC.KeepAlive(context.Wrapper);
+            return success;
+        }
+    }
+
     public static bool FillRectangle<T>(this T context, Rectangle rectangle, HBRUSH hbrush)
         where T : IHandle<HDC>
     {
@@ -314,4 +327,19 @@ public static bool FillRectangle<T>(this T context, Rectangle rectangle, HBRUSH
         GC.KeepAlive(context.Wrapper);
         return success;
     }
+
+    public static PenMixMode SetRasterOperation<T>(this T context, PenMixMode foregroundMixMode)
+        where T : IHandle<HDC>
+    {
+        PenMixMode result = (PenMixMode)Interop.SetROP2(context.Handle, (R2_MODE)foregroundMixMode);
+        GC.KeepAlive(context.Wrapper);
+        return result;
+    }
+
+    public static PenMixMode GetRasterOperation<T>(this T context) where T : IHandle<HDC>
+    {
+        PenMixMode result = (PenMixMode)Interop.GetROP2(context.Handle);
+        GC.KeepAlive(context.Wrapper);
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/src/thirtytwo/NativeMethods.txt b/src/thirtytwo/NativeMethods.txt
index 6f8b5d9..935a7e3 100644
--- a/src/thirtytwo/NativeMethods.txt
+++ b/src/thirtytwo/NativeMethods.txt
@@ -32,6 +32,7 @@ CreateErrorInfo
 CreateFont
 CreateFontIndirect
 CreateRectRgn
+CreateSolidBrush
 CreateStdAccessibleObject
 CreateStdAccessibleProxy
 CreateStdDispatch
@@ -64,6 +65,7 @@ E_INVALIDARG
 E_NOINTERFACE
 E_NOTIMPL
 E_POINTER
+Ellipse
 EM_*
 EmptyClipboard
 EnableWindow
@@ -84,6 +86,7 @@ FONT_PITCH
 FONT_QUALITY
 FONT_WEIGHT
 FormatMessage
+FrameRect
 FVE_E_LOCKED_VOLUME
 GET_MODULE_HANDLE_EX_FLAG*
 GET_STOCK_OBJECT_FLAGS
@@ -115,6 +118,7 @@ GetParent
 GetProcAddress
 GetProcessDpiAwareness
 GetRgnBox
+GetROP2
 GetStockObject
 GetSysColorBrush
 GetThreadDpiAwarenessContext
@@ -188,6 +192,7 @@ IUnknown
 KEY_INFORMATION_CLASS
 KEY_NAME_INFORMATION
 KF_*
+KillTimer
 LIBFLAGS
 LineTo
 LoadCursor
@@ -200,6 +205,7 @@ LPtoDP
 LsaNtStatusToWinError
 MapWindowPoints
 MEMBERID_NIL
+MessageBeep
 MessageBoxEx
 METHODDATA
 MODIFIERKEYS_FLAGS
@@ -220,11 +226,13 @@ PARAMDATA
 PeekMessage
 PlaySound
 POINTS
+PolyBezier
 Polygon
 PostMessage
 PostQuitMessage
 PROPVARIANT
 PropVariantClear
+Rectangle
 RegCloseKey
 RegEnumValue
 RegisterClassEx
@@ -232,9 +240,11 @@ RegisterClipboardFormat
 RegOpenKeyEx
 RegQueryInfoKey
 RegQueryValueEx
+ReleaseCapture
 ReleaseDC
 RemoveClipboardFormatListener
 ROLE_SYSTEM_*
+RoundRect
 S_FALSE
 S_OK
 SAFEARRAY
@@ -248,13 +258,16 @@ SelectObject
 SELFLAG_*
 SendMessage
 SetActiveWindow
+SetCapture
 SetClipboardData
+SetCoalescableTimer
 SetCursor
 SetFocus
 SetGraphicsMode
 SetLayeredWindowAttributes
 SetMapMode
 SetPolyFillMode
+SetROP2
 SetThreadDpiAwarenessContext
 SetViewportOrgEx
 SetWindowLong
@@ -300,12 +313,4 @@ VIRTUAL_KEY
 WIN32_ERROR
 WINDOWPOS
 WM_*
-XFORMCOORDS
-FrameRect
-Rectangle
-Ellipse
-RoundRect
-MessageBeep
-CreateSolidBrush
-SetCoalescableTimer
-KillTimer
\ No newline at end of file
+XFORMCOORDS
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/UI/WindowsAndMessaging/HCURSOR.cs b/src/thirtytwo/Win32/UI/WindowsAndMessaging/HCURSOR.cs
index b5cc8ff..80eeea1 100644
--- a/src/thirtytwo/Win32/UI/WindowsAndMessaging/HCURSOR.cs
+++ b/src/thirtytwo/Win32/UI/WindowsAndMessaging/HCURSOR.cs
@@ -12,6 +12,7 @@ public unsafe partial struct HCURSOR : IDisposable
     public static implicit operator HCURSOR(CursorId id) => Interop.LoadCursor(default, (PCWSTR)(char*)(uint)id);
 
     public SetScope SetCursorScope() => new(this);
+    public HCURSOR SetCursor() => Interop.SetCursor(this);
 
     public void Dispose()
     {
diff --git a/src/thirtytwo/WrapperEnums/PenMixMode.cs b/src/thirtytwo/WrapperEnums/PenMixMode.cs
new file mode 100644
index 0000000..c45ab70
--- /dev/null
+++ b/src/thirtytwo/WrapperEnums/PenMixMode.cs
@@ -0,0 +1,93 @@
+// 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;
+
+/// <summary>
+///  Pen pixel mix modes (ROP2)
+/// </summary>
+public enum PenMixMode : int
+{
+    // https://learn.microsoft.com/openspecs/windows_protocols/ms-mnpr/1f681fd4-8379-4312-8bf9-138e69f4f12b
+    // https://learn.microsoft.com/windows/win32/gdi/binary-raster-operations?redirectedfrom=MSDN
+
+    /// <summary>
+    ///  Pixel is always drawn black. [R2_BLACK]
+    /// </summary>
+    Black = R2_MODE.R2_BLACK,
+
+    /// <summary>
+    ///  Inverse of result of MergePen. [R2_NOTMERGEPEN]
+    /// </summary>
+    NotMergePen = R2_MODE.R2_NOTMERGEPEN,
+
+    /// <summary>
+    ///  The pixel is a combination of the colors that are common to both the screen and the inverse of the pen. [R2_MASKNOTPEN]
+    /// </summary>
+    MaskNotPen = R2_MODE.R2_MASKNOTPEN,
+
+    /// <summary>
+    ///  The pixel is the inverse of the pen color. [R2_NOTCOPYPEN]
+    /// </summary>
+    NotCopyPen = R2_MODE.R2_NOTCOPYPEN,
+
+    /// <summary>
+    ///  The pixel is a combination of the colors that are common to both the pen and the inverse of the screen. [R2_MASKPENNOT]
+    /// </summary>
+    MaskPenNot = R2_MODE.R2_MASKPENNOT,
+
+    /// <summary>
+    ///  The pixel is the inverse of the screen color. [R2_NOT]
+    /// </summary>
+    Not = R2_MODE.R2_NOT,
+
+    /// <summary>
+    ///  The pixel is a combination of the colors in the pen and in the screen, but not in both. [R2_XORPEN]
+    /// </summary>
+    XOrPen = R2_MODE.R2_XORPEN,
+
+    /// <summary>
+    ///  Inverse of the result of MaskPen. [R2_NOTMASKPEN]
+    /// </summary>
+    NotMaskPen = R2_MODE.R2_NOTMASKPEN,
+
+    /// <summary>
+    ///  The pixel is a combination of the colors that are common to both the pen and the screen. [R2_MASKPEN]
+    /// </summary>
+    MaskPen = R2_MODE.R2_MASKPEN,
+
+    /// <summary>
+    ///  Inverse of the result of XOrPen. [R2_NOTXORPEN]
+    /// </summary>
+    NotXOrPen = R2_MODE.R2_NOTXORPEN,
+
+    /// <summary>
+    ///  The pixel remains unchanged. [R2_NOP]
+    /// </summary>
+    Nop = R2_MODE.R2_NOP,
+
+    /// <summary>
+    ///  The pixel is a combination of the screen color and the inverse of the pen color. [R2_MERGENOTPEN]
+    /// </summary>
+    MergeNotPen = R2_MODE.R2_MERGENOTPEN,
+
+    /// <summary>
+    ///  The pixel always has the color of the pen. [R2_COPYPEN]
+    /// </summary>
+    CopyPen = R2_MODE.R2_COPYPEN,
+
+    /// <summary>
+    ///  The pixel is a combination of the pen color and the inverse of the screen color. [R2_MERGEPENNOT]
+    /// </summary>
+    MergePenNot = R2_MODE.R2_MERGEPENNOT,
+
+    /// <summary>
+    ///  The pixel is a combination of the pen color and the screen color. [R2_MERGEPEN]
+    /// </summary>
+    MergePen = R2_MODE.R2_MERGEPEN,
+
+    /// <summary>
+    ///  The pixel is always drawn as white. [R2_WHITE]
+    /// </summary>
+    White = R2_MODE.R2_WHITE
+}
\ No newline at end of file
diff --git a/src/thirtytwo/WrapperEnums/WrapperEnumExtensions.cs b/src/thirtytwo/WrapperEnums/WrapperEnumExtensions.cs
index 237c48e..5f9aabd 100644
--- a/src/thirtytwo/WrapperEnums/WrapperEnumExtensions.cs
+++ b/src/thirtytwo/WrapperEnums/WrapperEnumExtensions.cs
@@ -6,4 +6,5 @@ namespace Windows;
 public static class WrapperEnumExtensions
 {
     public static HCURSOR.SetScope SetCursorScope(this CursorId cursor) => ((HCURSOR)cursor).SetCursorScope();
+    public static HCURSOR SetCursor(this CursorId cursor) => ((HCURSOR)cursor).SetCursor();
 }
\ No newline at end of file
diff --git a/thirtytwo.sln b/thirtytwo.sln
index d89f076..4dc4f72 100644
--- a/thirtytwo.sln
+++ b/thirtytwo.sln
@@ -43,6 +43,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LineDemo", "src\samples\Pet
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beeper", "src\samples\Petzold\5th\Beeper\Beeper.csproj", "{8AD31AA4-0997-4338-93EF-691E948F7FA8}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RandRect", "src\samples\Petzold\5th\RandRect\RandRect.csproj", "{1EABEAF9-CEE7-4415-98B9-466982727848}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bezier", "src\samples\Petzold\5th\Bezier\Bezier.csproj", "{751D8704-4AF9-46E4-AD24-B43DD011ED11}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blokout2", "src\samples\Petzold\5th\Blokout2\Blokout2.csproj", "{F52C0159-AFFE-431D-BC46-1AA6C8381400}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -101,6 +107,18 @@ Global
 		{8AD31AA4-0997-4338-93EF-691E948F7FA8}.Debug|x64.Build.0 = Debug|x64
 		{8AD31AA4-0997-4338-93EF-691E948F7FA8}.Release|x64.ActiveCfg = Release|x64
 		{8AD31AA4-0997-4338-93EF-691E948F7FA8}.Release|x64.Build.0 = Release|x64
+		{1EABEAF9-CEE7-4415-98B9-466982727848}.Debug|x64.ActiveCfg = Debug|x64
+		{1EABEAF9-CEE7-4415-98B9-466982727848}.Debug|x64.Build.0 = Debug|x64
+		{1EABEAF9-CEE7-4415-98B9-466982727848}.Release|x64.ActiveCfg = Release|x64
+		{1EABEAF9-CEE7-4415-98B9-466982727848}.Release|x64.Build.0 = Release|x64
+		{751D8704-4AF9-46E4-AD24-B43DD011ED11}.Debug|x64.ActiveCfg = Debug|x64
+		{751D8704-4AF9-46E4-AD24-B43DD011ED11}.Debug|x64.Build.0 = Debug|x64
+		{751D8704-4AF9-46E4-AD24-B43DD011ED11}.Release|x64.ActiveCfg = Release|x64
+		{751D8704-4AF9-46E4-AD24-B43DD011ED11}.Release|x64.Build.0 = Release|x64
+		{F52C0159-AFFE-431D-BC46-1AA6C8381400}.Debug|x64.ActiveCfg = Debug|x64
+		{F52C0159-AFFE-431D-BC46-1AA6C8381400}.Debug|x64.Build.0 = Debug|x64
+		{F52C0159-AFFE-431D-BC46-1AA6C8381400}.Release|x64.ActiveCfg = Release|x64
+		{F52C0159-AFFE-431D-BC46-1AA6C8381400}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -118,6 +136,9 @@ Global
 		{FE627283-6848-44D9-B17D-F483625ED43F} = {13110246-EBE1-441B-B721-B0614D62B13B}
 		{A2B09DA8-98E7-42FE-95EF-DF43258AEF4C} = {13110246-EBE1-441B-B721-B0614D62B13B}
 		{8AD31AA4-0997-4338-93EF-691E948F7FA8} = {13110246-EBE1-441B-B721-B0614D62B13B}
+		{1EABEAF9-CEE7-4415-98B9-466982727848} = {13110246-EBE1-441B-B721-B0614D62B13B}
+		{751D8704-4AF9-46E4-AD24-B43DD011ED11} = {13110246-EBE1-441B-B721-B0614D62B13B}
+		{F52C0159-AFFE-431D-BC46-1AA6C8381400} = {13110246-EBE1-441B-B721-B0614D62B13B}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {3761BFC9-DBEF-4186-BB8B-BC0D84ED9AE5}