Skip to content

Commit

Permalink
Port Beeper and LineDemo from WInterop
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyKuhne committed Feb 4, 2024
1 parent 7ece471 commit 1bd452e
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 40 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ csharp_style_prefer_not_pattern = true:suggestion
# SYSLIB1096: Convert to 'GeneratedComInterface'
dotnet_diagnostic.SYSLIB1096.severity = none

# SA1117: Parameters should be on same line or separate lines
dotnet_diagnostic.SA1117.severity = silent

[*.{cs,vb}]
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:silent
Expand Down
10 changes: 10 additions & 0 deletions src/samples/Petzold/5th/Beeper/Beeper.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>
46 changes: 46 additions & 0 deletions src/samples/Petzold/5th/Beeper/Beeper1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging;

namespace Beeper;

internal class Beeper1 : MainWindow
{
private bool _flipFlop = false;
private nuint _timerId;

public Beeper1(string title) : base(title) { }

protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case MessageType.Create:
_timerId = window.SetTimer(1000);
return (LRESULT)0;
case MessageType.Timer:
Interop.MessageBeep(MESSAGEBOX_STYLE.MB_OK);
_flipFlop = !_flipFlop;
window.Invalidate();
return (LRESULT)0;
case MessageType.Paint:
using (DeviceContext dc = window.BeginPaint())
{
using HBRUSH brush = HBRUSH.CreateSolid(_flipFlop ? Color.Red : Color.Blue);
dc.FillRectangle(window.GetClientRectangle(), brush);
}
return (LRESULT)0;
case MessageType.Destroy:
window.KillTimer(_timerId);
break;
}

return base.WindowProcedure(window, message, wParam, lParam);
}
}
45 changes: 45 additions & 0 deletions src/samples/Petzold/5th/Beeper/Beeper2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging;

namespace Beeper;

internal class Beeper2 : MainWindow
{
private bool _flipFlop = false;
private nuint _timerId;
private TimerProcedure? _timerCallback;

public Beeper2(string title) : base(title) { }

protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case MessageType.Create:
_timerCallback = TimerProcedure;
_timerId = window.SetTimer(1000, callback: _timerCallback);
return (LRESULT)0;
case MessageType.Destroy:
window.KillTimer(_timerId);
break;
}

return base.WindowProcedure(window, message, wParam, lParam);
}

private void TimerProcedure(HWND window, MessageType message, nuint timerId, uint time)
{
Interop.MessageBeep(MESSAGEBOX_STYLE.MB_OK);
_flipFlop = !_flipFlop;
using DeviceContext dc = window.GetDeviceContext();
using HBRUSH brush = HBRUSH.CreateSolid(_flipFlop ? Color.Red : Color.Blue);
dc.FillRectangle(window.GetClientRectangle(), brush);
}
}
23 changes: 23 additions & 0 deletions src/samples/Petzold/5th/Beeper/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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 Windows;

namespace Beeper;

/// <summary>
/// Samples from Programming Windows, 5th Edition.
/// Original (c) Charles Petzold, 1998
/// </summary>
internal static class Program
{
[STAThread]
private static void Main()
{
// Figure 8-1, Pages 331-333.
Application.Run(new Beeper1("Timer on Message Loop"));

// Figure 8-2, Pages 335-337.
Application.Run(new Beeper2("Timer Procedure"));
}
}
10 changes: 10 additions & 0 deletions src/samples/Petzold/5th/LineDemo/LineDemo.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>
54 changes: 54 additions & 0 deletions src/samples/Petzold/5th/LineDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 LineDemo;

/// <summary>
/// Sample from Programming Windows, 5th Edition.
/// Original (c) Charles Petzold, 1998
/// Figure 5-14, Pages 153-155.
/// </summary>
internal static class Program
{
[STAThread]
private static void Main() => Application.Run(new LineDemo("LineDemo"));

private class LineDemo : MainWindow
{
private static int s_cxClient, s_cyClient;

public LineDemo(string title) : base(title) { }

protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case MessageType.Size:
s_cxClient = lParam.LOWORD;
s_cyClient = lParam.HIWORD;
return (LRESULT)0;
case MessageType.Paint:
using (DeviceContext dc = window.BeginPaint())
{
dc.Rectangle(s_cxClient / 8, s_cyClient / 8, 7 * s_cxClient / 8, 7 * s_cyClient / 8);
dc.MoveTo(0, 0);
dc.LineTo(s_cxClient, s_cyClient);
dc.MoveTo(0, s_cyClient);
dc.LineTo(s_cxClient, 0);
dc.Ellipse(s_cxClient / 8, s_cyClient / 8, 7 * s_cxClient / 8, 7 * s_cyClient / 8);
dc.RoundRectangle(
Rectangle.FromLTRB(s_cxClient / 4, s_cyClient / 4, 3 * s_cxClient / 4, 3 * s_cyClient / 4),
new Size(s_cxClient / 4, s_cyClient / 4));
}

return (LRESULT)0;
}

return base.WindowProcedure(window, message, wParam, lParam);
}
}
}
74 changes: 57 additions & 17 deletions src/thirtytwo/DeviceContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ namespace Windows;
public static unsafe partial class DeviceContextExtensions
{
/// <inheritdoc cref="Interop.GetGraphicsMode(HDC)"/>
public static GRAPHICS_MODE GetGraphicsMode<T>(this T context)
where T : IHandle<HDC>
public static GRAPHICS_MODE GetGraphicsMode<T>(this T context) where T : IHandle<HDC>
{
GRAPHICS_MODE mode = (GRAPHICS_MODE)Interop.GetGraphicsMode(context.Handle);
GC.KeepAlive(context.Wrapper);
Expand Down Expand Up @@ -97,8 +96,8 @@ public static PolyFillMode SetPolyFillMode<T>(this T context, PolyFillMode mode)
return result;
}

public static bool Polygon<T>(this T context, params Point[] points)
where T : IHandle<HDC> => Polygon(context, points.AsSpan());
public static bool Polygon<T>(this T context, params Point[] points) where T : IHandle<HDC> =>
Polygon(context, points.AsSpan());

public static bool Polygon<T>(this T context, ReadOnlySpan<Point> points)
where T : IHandle<HDC>
Expand Down Expand Up @@ -147,7 +146,14 @@ private static (int Height, uint LengthDrawn, Rectangle Bounds) DrawTextHelper<T
// The string won't be changed, we can just pin
fixed (char* c = text)
{
int result = Interop.DrawTextEx(context.Handle, (PWSTR)c, text.Length, bounds, (DRAW_TEXT_FORMAT)format, dtp);
int result = Interop.DrawTextEx(
context.Handle,
(PWSTR)c,
text.Length,
bounds,
(DRAW_TEXT_FORMAT)format,
dtp);

if (result == 0)
{
Error.ThrowIfLastErrorNot(WIN32_ERROR.ERROR_SUCCESS);
Expand Down Expand Up @@ -182,7 +188,14 @@ public static void DrawIcon<TDeviceContext, TIcon>(
where TDeviceContext : IHandle<HDC>
where TIcon : IHandle<HICON>
{
if (!Interop.DrawIconEx(context.Handle, location.X, location.Y, icon.Handle, size.Width, size.Height, 0, default, flags))
if (!Interop.DrawIconEx(
context.Handle,
location.X, location.Y,
icon.Handle,
size.Width, size.Height,
0,
HBRUSH.Null,
flags))
{
Error.ThrowLastError();
}
Expand All @@ -204,8 +217,7 @@ public static DeviceContext CreateCompatibleDeviceContext<TDeviceContext>(this T
return DeviceContext.Create(hdc, ownsHandle: true);
}

public static Bitmap CreateCompatibleBitmap<T>(this T context, Size size)
where T : IHandle<HDC>
public static Bitmap CreateCompatibleBitmap<T>(this T context, Size size) where T : IHandle<HDC>
{
HBITMAP hbitmap = Interop.CreateCompatibleBitmap(context.Handle, size.Width, size.Height);
if (hbitmap.IsNull)
Expand Down Expand Up @@ -242,26 +254,54 @@ public static RegionType SelectClippingRegion<T>(this T context, HRGN region)
return type;
}

public static bool MoveTo<T>(this T context, Point point)
where T : IHandle<HDC>
=> context.MoveTo(point.X, point.Y);
public static bool MoveTo<T>(this T context, Point point) where T : IHandle<HDC> =>
context.MoveTo(point.X, point.Y);

public static bool MoveTo<T>(this T context, int x, int y)
where T : IHandle<HDC>
public static bool MoveTo<T>(this T context, int x, int y) where T : IHandle<HDC>
{
bool result = Interop.MoveToEx(context.Handle, x, y, null);
GC.KeepAlive(context.Wrapper);
return result;
}

public static bool LineTo<T>(this T context, Point point)
public static bool LineTo<T>(this T context, Point point) where T : IHandle<HDC> =>
context.LineTo(point.X, point.Y);

public static bool LineTo<T>(this T context, int x, int y) where T : IHandle<HDC>
{
bool success = Interop.LineTo(context.Handle, x, y);
GC.KeepAlive(context.Wrapper);
return success;
}

public static bool Rectangle<T>(this T context, Rectangle rectangle) where T : IHandle<HDC> =>
context.Rectangle(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);

public static bool Rectangle<T>(this T context, int left, int top, int right, int bottom)
where T : IHandle<HDC>
=> context.LineTo(point.X, point.Y);
{
bool success = Interop.Rectangle(context.Handle, left, top, right, bottom);
GC.KeepAlive(context.Wrapper);
return success;
}

public static bool RoundRectangle<T>(this T context, Rectangle rectangle, Size corner) where T : IHandle<HDC> =>
context.RoundRectangle(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom, corner.Width, corner.Height);

public static bool LineTo<T>(this T context, int x, int y)
public static bool RoundRectangle<T>(this T context, int left, int top, int right, int bottom, int width, int height)
where T : IHandle<HDC>
{
bool success = Interop.LineTo(context.Handle, x, y);
bool success = Interop.RoundRect(context.Handle, left, top, right, bottom, width, height);
GC.KeepAlive(context.Wrapper);
return success;
}

public static bool Ellipse<T>(this T context, Rectangle rectangle) where T : IHandle<HDC> =>
context.Ellipse(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);

public static bool Ellipse<T>(this T context, int left, int top, int right, int bottom) where T : IHandle<HDC>
{
bool success = Interop.Ellipse(context.Handle, left, top, right, bottom);
GC.KeepAlive(context.Wrapper);
return success;
}
Expand Down
10 changes: 9 additions & 1 deletion src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,12 @@ VIRTUAL_KEY
WIN32_ERROR
WINDOWPOS
WM_*
XFORMCOORDS
XFORMCOORDS
FrameRect
Rectangle
Ellipse
RoundRect
MessageBeep
CreateSolidBrush
SetCoalescableTimer
KillTimer
16 changes: 15 additions & 1 deletion src/thirtytwo/Win32/Graphics/Gdi/HBRUSH.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// 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.CompilerServices;

namespace Windows.Win32.Graphics.Gdi;

public unsafe partial struct HBRUSH
public unsafe partial struct HBRUSH : IDisposable
{
public static HBRUSH Invalid => new(-1);

public static HBRUSH CreateSolid(Color color) => Interop.CreateSolidBrush((COLORREF)color);

public static implicit operator HBRUSH(SystemColor color) => Interop.GetSysColorBrush((SYS_COLOR_INDEX)color);
public static implicit operator HBRUSH(SYS_COLOR_INDEX color) => Interop.GetSysColorBrush(color);
public static implicit operator HBRUSH(StockBrush brush) => (HBRUSH)Interop.GetStockObject((GET_STOCK_OBJECT_FLAGS)brush);
Expand All @@ -16,4 +21,13 @@ public static explicit operator HBRUSH(HGDIOBJ handle)
Debug.Assert(handle.IsNull || (OBJ_TYPE)Interop.GetObjectType(handle) == OBJ_TYPE.OBJ_BRUSH);
return new(handle.Value);
}

public void Dispose()
{
if (Value != 0 && Value != -1)
{
Interop.DeleteObject(this);
Unsafe.AsRef(in this) = default;
}
}
}
Loading

0 comments on commit 1bd452e

Please sign in to comment.