Skip to content

Commit 85ea173

Browse files
committed
Port RandRect, Bezier, and Blokout2 samples from WInterop
1 parent 1bd452e commit 85ea173

File tree

12 files changed

+504
-9
lines changed

12 files changed

+504
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
9+
</ItemGroup>
10+
</Project>
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Drawing;
5+
using Windows;
6+
using Windows.Win32.Foundation;
7+
8+
namespace Bezier;
9+
10+
/// <summary>
11+
/// Sample from Programming Windows, 5th Edition.
12+
/// Original (c) Charles Petzold, 1998
13+
/// Figure 5-16, Pages 156-159.
14+
/// </summary>
15+
internal static class Program
16+
{
17+
[STAThread]
18+
private static void Main() => Application.Run(new Bezier("Bezier Splines"));
19+
}
20+
21+
internal class Bezier : MainWindow
22+
{
23+
private readonly Point[] _apt = new Point[4];
24+
25+
public Bezier(string title) : base(title)
26+
{
27+
}
28+
29+
protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
30+
{
31+
switch (message)
32+
{
33+
case MessageType.Size:
34+
int cxClient = lParam.LOWORD;
35+
int cyClient = lParam.HIWORD;
36+
37+
_apt[0].X = cxClient / 4;
38+
_apt[0].Y = cyClient / 2;
39+
_apt[1].X = cxClient / 2;
40+
_apt[1].Y = cyClient / 4;
41+
_apt[2].X = cxClient / 2;
42+
_apt[2].Y = 3 * cyClient / 4;
43+
_apt[3].X = 3 * cxClient / 4;
44+
_apt[3].Y = cyClient / 2;
45+
46+
return (LRESULT)0;
47+
48+
case MessageType.LeftButtonDown:
49+
case MessageType.RightButtonDown:
50+
case MessageType.MouseMove:
51+
MouseKey mk = (MouseKey)wParam.LOWORD;
52+
if ((mk & (MouseKey.LeftButton | MouseKey.RightButton)) != 0)
53+
{
54+
using DeviceContext dc = window.GetDeviceContext();
55+
dc.SelectObject(StockPen.White);
56+
DrawBezier(dc, _apt);
57+
58+
if ((mk & MouseKey.LeftButton) != 0)
59+
{
60+
_apt[1].X = lParam.LOWORD;
61+
_apt[1].Y = lParam.HIWORD;
62+
}
63+
64+
if ((mk & MouseKey.RightButton) != 0)
65+
{
66+
_apt[2].X = lParam.LOWORD;
67+
_apt[2].Y = lParam.HIWORD;
68+
}
69+
70+
dc.SelectObject(StockPen.Black);
71+
DrawBezier(dc, _apt);
72+
}
73+
74+
return (LRESULT)0;
75+
76+
case MessageType.Paint:
77+
window.Invalidate(true);
78+
using (DeviceContext dc = window.BeginPaint())
79+
{
80+
DrawBezier(dc, _apt);
81+
}
82+
83+
return (LRESULT)0;
84+
}
85+
86+
static void DrawBezier(DeviceContext dc, Point[] apt)
87+
{
88+
dc.PolyBezier(apt);
89+
dc.MoveTo(apt[0]);
90+
dc.LineTo(apt[1]);
91+
dc.MoveTo(apt[2]);
92+
dc.LineTo(apt[3]);
93+
}
94+
95+
return base.WindowProcedure(window, message, wParam, lParam);
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
9+
</ItemGroup>
10+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Drawing;
5+
using Windows;
6+
using Windows.Win32;
7+
using Windows.Win32.Foundation;
8+
9+
namespace Blokout2;
10+
11+
/// <summary>
12+
/// Sample from Programming Windows, 5th Edition.
13+
/// Original (c) Charles Petzold, 1998
14+
/// Figure 7-11, Pages 314-317.
15+
/// </summary>
16+
internal static class Program
17+
{
18+
[STAThread]
19+
private static void Main() => Application.Run(new Blockout2("Mouse Button & Capture Demo"));
20+
}
21+
22+
internal class Blockout2 : MainWindow
23+
{
24+
private bool _fBlocking, _fValidBox;
25+
private Point _ptBeg, _ptEnd, _ptBoxBeg, _ptBoxEnd;
26+
27+
public Blockout2(string title) : base(title)
28+
{
29+
}
30+
31+
protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
32+
{
33+
switch (message)
34+
{
35+
case MessageType.LeftButtonDown:
36+
_ptBeg.X = _ptEnd.X = lParam.LOWORD;
37+
_ptBeg.Y = _ptEnd.Y = lParam.HIWORD;
38+
DrawBoxOutline(window, _ptBeg, _ptEnd);
39+
Interop.SetCapture(window);
40+
CursorId.Cross.SetCursor();
41+
_fBlocking = true;
42+
return (LRESULT)0;
43+
case MessageType.MouseMove:
44+
if (_fBlocking)
45+
{
46+
CursorId.Cross.SetCursor();
47+
DrawBoxOutline(window, _ptBeg, _ptEnd);
48+
_ptEnd.X = lParam.LOWORD;
49+
_ptEnd.Y = lParam.HIWORD;
50+
DrawBoxOutline(window, _ptBeg, _ptEnd);
51+
}
52+
53+
return (LRESULT)0;
54+
case MessageType.LeftButtonUp:
55+
if (_fBlocking)
56+
{
57+
DrawBoxOutline(window, _ptBeg, _ptEnd);
58+
_ptBoxBeg = _ptBeg;
59+
_ptBoxEnd.X = lParam.LOWORD;
60+
_ptBoxEnd.Y = lParam.HIWORD;
61+
Interop.ReleaseCapture();
62+
CursorId.Arrow.SetCursor();
63+
_fBlocking = false;
64+
_fValidBox = true;
65+
window.Invalidate(true);
66+
}
67+
68+
return (LRESULT)0;
69+
case MessageType.Paint:
70+
using (DeviceContext dc = window.BeginPaint())
71+
{
72+
if (_fValidBox)
73+
{
74+
dc.SelectObject(StockBrush.Black);
75+
dc.Rectangle(_ptBoxBeg.X, _ptBoxBeg.Y, _ptBoxEnd.X, _ptBoxEnd.Y);
76+
}
77+
if (_fBlocking)
78+
{
79+
dc.SetRasterOperation(PenMixMode.Not);
80+
dc.SelectObject(StockBrush.Null);
81+
dc.Rectangle(_ptBeg.X, _ptBeg.Y, _ptEnd.X, _ptEnd.Y);
82+
}
83+
}
84+
85+
return (LRESULT)0;
86+
}
87+
88+
static void DrawBoxOutline(HWND window, Point ptBeg, Point ptEnd)
89+
{
90+
using DeviceContext dc = window.GetDeviceContext();
91+
dc.SetRasterOperation(PenMixMode.Not);
92+
dc.SelectObject(StockBrush.Null);
93+
dc.Rectangle(Rectangle.FromLTRB(ptBeg.X, ptBeg.Y, ptEnd.X, ptEnd.Y));
94+
}
95+
96+
return base.WindowProcedure(window, message, wParam, lParam);
97+
}
98+
}
+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Drawing;
5+
using System.Runtime.InteropServices;
6+
using Windows;
7+
using Windows.Win32;
8+
using Windows.Win32.Foundation;
9+
using Windows.Win32.Graphics.Gdi;
10+
using Windows.Win32.UI.WindowsAndMessaging;
11+
12+
namespace RandRect;
13+
14+
/// <summary>
15+
/// Sample from Programming Windows, 5th Edition.
16+
/// Original (c) Charles Petzold, 1998
17+
/// Figure 5-26, Pages 200-202.
18+
/// </summary>
19+
internal unsafe static class Program
20+
{
21+
[STAThread]
22+
private static void Main()
23+
{
24+
const string szAppName = "RandRect";
25+
26+
WindowProcedure wndProc = WindowProcedure;
27+
HMODULE module;
28+
Interop.GetModuleHandleEx(0, (PCWSTR)null, &module);
29+
30+
HWND hwnd;
31+
32+
fixed (char* appName = szAppName)
33+
fixed (char* title = "Random Rectangles")
34+
{
35+
WNDCLASSEXW wndClass = new()
36+
{
37+
cbSize = (uint)sizeof(WNDCLASSEXW),
38+
style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW,
39+
lpfnWndProc = (WNDPROC)Marshal.GetFunctionPointerForDelegate(wndProc),
40+
hInstance = module,
41+
hIcon = Interop.LoadIcon(default, Interop.IDI_APPLICATION),
42+
hCursor = Interop.LoadCursor(default, Interop.IDC_ARROW),
43+
hbrBackground = (HBRUSH)Interop.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH),
44+
lpszClassName = appName
45+
};
46+
47+
ATOM atom = Interop.RegisterClassEx(&wndClass);
48+
49+
hwnd = Interop.CreateWindowEx(
50+
WINDOW_EX_STYLE.WS_EX_OVERLAPPEDWINDOW,
51+
appName,
52+
title,
53+
WINDOW_STYLE.WS_OVERLAPPEDWINDOW,
54+
Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT, Interop.CW_USEDEFAULT,
55+
HWND.Null,
56+
HMENU.Null,
57+
module,
58+
null);
59+
60+
61+
}
62+
63+
Interop.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_SHOWDEFAULT);
64+
Interop.UpdateWindow(hwnd);
65+
66+
while (true)
67+
{
68+
if (Interop.PeekMessage(out MSG message, HWND.Null, 0, uint.MaxValue, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
69+
{
70+
if (message.message == Interop.WM_QUIT)
71+
{
72+
break;
73+
}
74+
75+
Interop.TranslateMessage(message);
76+
Interop.DispatchMessage(message);
77+
}
78+
79+
// We're crazy fast over 25 years past the source sample,
80+
// sleeping to make this a bit more interesting.
81+
Thread.Sleep(100);
82+
DrawRectangle(hwnd);
83+
}
84+
}
85+
86+
private static int s_cxClient, s_cyClient;
87+
private static readonly Random s_rand = new();
88+
89+
private static LRESULT WindowProcedure(HWND window, uint message, WPARAM wParam, LPARAM lParam)
90+
{
91+
switch ((MessageType)message)
92+
{
93+
case MessageType.Size:
94+
s_cxClient = lParam.LOWORD;
95+
s_cyClient = lParam.HIWORD;
96+
return (LRESULT)0;
97+
case MessageType.Destroy:
98+
Interop.PostQuitMessage(0);
99+
return (LRESULT)0;
100+
}
101+
102+
return Interop.DefWindowProc(window, message, wParam, lParam);
103+
}
104+
105+
private static void DrawRectangle(HWND window)
106+
{
107+
if (s_cxClient == 0 || s_cyClient == 0)
108+
return;
109+
110+
Rectangle rect = Rectangle.FromLTRB(
111+
s_rand.Next() % s_cxClient,
112+
s_rand.Next() % s_cyClient,
113+
s_rand.Next() % s_cxClient,
114+
s_rand.Next() % s_cyClient);
115+
116+
using HBRUSH brush = HBRUSH.CreateSolid(
117+
Color.FromArgb((byte)(s_rand.Next() % 256), (byte)(s_rand.Next() % 256), (byte)(s_rand.Next() % 256)));
118+
using DeviceContext dc = window.GetDeviceContext();
119+
dc.FillRectangle(rect, brush);
120+
}
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\..\..\thirtytwo\thirtytwo.csproj" />
9+
</ItemGroup>
10+
</Project>

src/thirtytwo/DeviceContextExtensions.cs

+28
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ public static bool Ellipse<T>(this T context, int left, int top, int right, int
306306
return success;
307307
}
308308

309+
public static unsafe bool PolyBezier<T>(this T context, params Point[] points) where T : IHandle<HDC> =>
310+
PolyBezier(context, points.AsSpan());
311+
312+
public static unsafe bool PolyBezier<T>(this T context, ReadOnlySpan<Point> points) where T : IHandle<HDC>
313+
{
314+
fixed (Point* p = points)
315+
{
316+
bool success = Interop.PolyBezier(context.Handle, p, (uint)points.Length);
317+
GC.KeepAlive(context.Wrapper);
318+
return success;
319+
}
320+
}
321+
309322
public static bool FillRectangle<T>(this T context, Rectangle rectangle, HBRUSH hbrush)
310323
where T : IHandle<HDC>
311324
{
@@ -314,4 +327,19 @@ public static bool FillRectangle<T>(this T context, Rectangle rectangle, HBRUSH
314327
GC.KeepAlive(context.Wrapper);
315328
return success;
316329
}
330+
331+
public static PenMixMode SetRasterOperation<T>(this T context, PenMixMode foregroundMixMode)
332+
where T : IHandle<HDC>
333+
{
334+
PenMixMode result = (PenMixMode)Interop.SetROP2(context.Handle, (R2_MODE)foregroundMixMode);
335+
GC.KeepAlive(context.Wrapper);
336+
return result;
337+
}
338+
339+
public static PenMixMode GetRasterOperation<T>(this T context) where T : IHandle<HDC>
340+
{
341+
PenMixMode result = (PenMixMode)Interop.GetROP2(context.Handle);
342+
GC.KeepAlive(context.Wrapper);
343+
return result;
344+
}
317345
}

0 commit comments

Comments
 (0)