Skip to content

Commit

Permalink
Port RandRect, Bezier, and Blokout2 samples from WInterop
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyKuhne committed Feb 4, 2024
1 parent 1bd452e commit 85ea173
Show file tree
Hide file tree
Showing 12 changed files with 504 additions and 9 deletions.
10 changes: 10 additions & 0 deletions src/samples/Petzold/5th/Bezier/Bezier.csproj
Original file line number Diff line number Diff line change
@@ -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>
97 changes: 97 additions & 0 deletions src/samples/Petzold/5th/Bezier/Program.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
10 changes: 10 additions & 0 deletions src/samples/Petzold/5th/Blokout2/Blokout2.csproj
Original file line number Diff line number Diff line change
@@ -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>
98 changes: 98 additions & 0 deletions src/samples/Petzold/5th/Blokout2/Program.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
121 changes: 121 additions & 0 deletions src/samples/Petzold/5th/RandRect/Program.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
10 changes: 10 additions & 0 deletions src/samples/Petzold/5th/RandRect/RandRect.csproj
Original file line number Diff line number Diff line change
@@ -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>
28 changes: 28 additions & 0 deletions src/thirtytwo/DeviceContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
{
Expand All @@ -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;
}
}
Loading

0 comments on commit 85ea173

Please sign in to comment.