Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Sharpie.Tests/BaseCursesBackendTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void TestInitialize()
public void DecodeRawMouseButtonState_ParsesUsingMouseParser()
{
_backendMock.Setup(s => s.CursesMouseEventParser)
.Returns(CursesMouseEventParser.Get(2));
.Returns(CursesMouseEventParser.Get(CursesAbiVersion.NCurses6));

_backendMock.Setup(s => s.DecodeRawMouseButtonState(It.IsAny<uint>()))
.CallBase();
Expand All @@ -104,7 +104,7 @@ public void DecodeRawMouseButtonState_ParsesUsingMouseParser()
public void DecodeRawMouseButtonState_ReturnsDefault_IfNotParsed()
{
_backendMock.Setup(s => s.CursesMouseEventParser)
.Returns(CursesMouseEventParser.Get(2));
.Returns(CursesMouseEventParser.Get(CursesAbiVersion.NCurses6));

_backendMock.Setup(s => s.DecodeRawMouseButtonState(It.IsAny<uint>()))
.CallBase();
Expand Down Expand Up @@ -1139,7 +1139,7 @@ public void color_content_IsRelayedToLibrary(int ret)
[TestMethod, DataRow(0), DataRow(-1)]
public void mousemask_IsRelayedToLibrary(int ret)
{
var p = CursesMouseEventParser.Get(2);
var p = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
_backendMock.Setup(s => s.CursesMouseEventParser)
.Returns(p);

Expand All @@ -1165,7 +1165,7 @@ public void mousemask_IsRelayedToLibrary(int ret)
public void mousemask_WhenUnix_CallsCursesButNotConsole_IfCursesFails()
{
_backendMock.Setup(s => s.CursesMouseEventParser)
.Returns(CursesMouseEventParser.Get(1));
.Returns(CursesMouseEventParser.Get(CursesAbiVersion.NCurses5));

_nativeSymbolResolverMock.MockResolve<BaseCursesFunctionMap.mousemask>()
.Setup(s => s(It.IsAny<uint>(), out It.Ref<uint>.IsAny))
Expand All @@ -1183,8 +1183,8 @@ public void mousemask_WhenUnix_CallsCursesButNotConsole_IfCursesFails()
_dotNetSystemAdapterMock.Verify(v => v.OutAndFlush(It.IsAny<string>()), Times.Never);
}

[TestMethod, SuppressMessage("ReSharper", "IdentifierTypo"), DataRow(1), DataRow(2)]
public void mousemask_WhenUnix_OutsToConsole_WhenReportingPosition(int abi)
[TestMethod, SuppressMessage("ReSharper", "IdentifierTypo"), DataRow(CursesAbiVersion.NCurses5), DataRow(CursesAbiVersion.NCurses6)]
public void mousemask_WhenUnix_OutsToConsole_WhenReportingPosition(CursesAbiVersion abi)
{
_nativeSymbolResolverMock.MockResolve<BaseCursesFunctionMap.mousemask, int>(
s => s(It.IsAny<uint>(), out It.Ref<uint>.IsAny), 0);
Expand All @@ -1199,8 +1199,8 @@ public void mousemask_WhenUnix_OutsToConsole_WhenReportingPosition(int abi)
_dotNetSystemAdapterMock.Verify(v => v.OutAndFlush("\x1b[?1003h"), Times.Once);
}

[TestMethod, SuppressMessage("ReSharper", "IdentifierTypo"), DataRow(1), DataRow(2)]
public void mousemask_WhenUnix_OutsToConsole_WhenAll(int abi)
[TestMethod, SuppressMessage("ReSharper", "IdentifierTypo"), DataRow(CursesAbiVersion.NCurses5), DataRow(CursesAbiVersion.NCurses6)]
public void mousemask_WhenUnix_OutsToConsole_WhenAll(CursesAbiVersion abi)
{
_nativeSymbolResolverMock.MockResolve<BaseCursesFunctionMap.mousemask, int>(
s => s(It.IsAny<uint>(), out It.Ref<uint>.IsAny), 0);
Expand All @@ -1222,7 +1222,7 @@ public void mousemask_WhenUnix_OutsToConsole_WhenNothing()
s => s(It.IsAny<uint>(), out It.Ref<uint>.IsAny), 0);

_backendMock.Setup(s => s.CursesMouseEventParser)
.Returns(CursesMouseEventParser.Get(1));
.Returns(CursesMouseEventParser.Get(CursesAbiVersion.NCurses5));

_backend.mousemask(0, out var _)
.ShouldBe(0);
Expand Down
80 changes: 72 additions & 8 deletions Sharpie.Tests/CursesMouseEventParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ public class CursesMouseEventParserTests
{
[TestMethod]
public void Get_ReturnsObject_ForAbi1()
{
var r = CursesMouseEventParser.Get(CursesAbiVersion.NCurses5);
r.ShouldNotBeNull();

r.ReportPosition.ShouldBe(8u << 24);
r.All.ShouldBe((8u << 24) - 1);
}

[TestMethod]
public void Get_ReturnsObject_ForAbi1_OldApi()
{
var r = CursesMouseEventParser.Get(1);
r.ShouldNotBeNull();
Expand All @@ -45,6 +55,16 @@ public void Get_ReturnsObject_ForAbi1()

[TestMethod]
public void Get_ReturnsObject_ForAbi2()
{
var r = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
r.ShouldNotBeNull();

r.ReportPosition.ShouldBe(8u << 25);
r.All.ShouldBe((8u << 25) - 1);
}

[TestMethod]
public void Get_ReturnsObject_ForAbi2_OldApi()
{
var r = CursesMouseEventParser.Get(2);
r.ShouldNotBeNull();
Expand All @@ -53,11 +73,31 @@ public void Get_ReturnsObject_ForAbi2()
r.All.ShouldBe((8u << 25) - 1);
}

[TestMethod]
public void Get_ReturnsObject_ForPdCursesAbi()
{
var r = CursesMouseEventParser.Get(CursesAbiVersion.PdCurses);
r.ShouldNotBeNull();

r.ReportPosition.ShouldBe(1u << 29);
r.All.ShouldBe((1u << 29) - 1);
}

[TestMethod]
public void Get_ReturnsObject_ForPdCursesAbi_OldApi()
{
var r = CursesMouseEventParser.Get(3);
r.ShouldNotBeNull();

r.ReportPosition.ShouldBe(1u << 29);
r.All.ShouldBe((1u << 29) - 1);
}

[TestMethod]
public void Get_ReturnsAbi1_ForUnknownAbi()
{
CursesMouseEventParser.Get(-1)
.ShouldBe(CursesMouseEventParser.Get(1));
CursesMouseEventParser.Get(CursesAbiVersion.Unknown)
.ShouldBe(CursesMouseEventParser.Get(CursesAbiVersion.Unknown));
}

[TestMethod, DataRow(1u << ((1 - 1) * 6), MouseButton.Button1, MouseButtonState.Released),
Expand All @@ -82,7 +122,7 @@ public void Get_ReturnsAbi1_ForUnknownAbi()
DataRow(16u << ((4 - 1) * 6), MouseButton.Button4, MouseButtonState.TripleClicked)]
public void Parse_ParsesTheButtonAndState_ForAbi1(uint raw, MouseButton expButton, MouseButtonState expState)
{
var parser = CursesMouseEventParser.Get(1);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses5);
var p = parser.Parse(raw);

p.ShouldBe((expButton, expState, ModifierKey.None));
Expand Down Expand Up @@ -115,7 +155,7 @@ public void Parse_ParsesTheButtonAndState_ForAbi1(uint raw, MouseButton expButto
DataRow(16u << ((5 - 1) * 5), MouseButton.Button5, MouseButtonState.TripleClicked)]
public void Parse_ParsesTheButtonAndState_ForAbi2(uint raw, MouseButton expButton, MouseButtonState expState)
{
var parser = CursesMouseEventParser.Get(2);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
var p = parser.Parse(raw);

p.ShouldBe((expButton, expState, ModifierKey.None));
Expand All @@ -130,7 +170,7 @@ public void Parse_ParsesTheButtonAndState_ForAbi2(uint raw, MouseButton expButto
DataRow((4u << 24) | (1u << 24) | (2u << 24), ModifierKey.Alt | ModifierKey.Shift | ModifierKey.Ctrl)]
public void Parse_ParsesTheModifiers_ForAbi1(uint raw, ModifierKey expMod)
{
var parser = CursesMouseEventParser.Get(1);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses5);
var p = parser.Parse(raw | 1u);

p.ShouldBe((MouseButton.Button1, MouseButtonState.Released, expMod));
Expand All @@ -145,7 +185,22 @@ public void Parse_ParsesTheModifiers_ForAbi1(uint raw, ModifierKey expMod)
DataRow((4u << 25) | (1u << 25) | (2u << 25), ModifierKey.Alt | ModifierKey.Shift | ModifierKey.Ctrl)]
public void Parse_ParsesTheModifiers_ForAbi2(uint raw, ModifierKey expMod)
{
var parser = CursesMouseEventParser.Get(2);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
var p = parser.Parse(raw | 1u);

p.ShouldBe((MouseButton.Button1, MouseButtonState.Released, expMod));
}

[TestMethod, DataRow(0u, ModifierKey.None), DataRow(2u << 26, ModifierKey.Ctrl),
DataRow(1u << 26, ModifierKey.Shift), DataRow(4u << 26, ModifierKey.Alt),
DataRow((4u << 26) | (2u << 26), ModifierKey.Alt | ModifierKey.Ctrl),
DataRow((1u << 26) | (2u << 26), ModifierKey.Shift | ModifierKey.Ctrl),
DataRow((4u << 26) | (1u << 26), ModifierKey.Alt | ModifierKey.Shift),
DataRow((2u << 26) | (1u << 26), ModifierKey.Ctrl | ModifierKey.Shift),
DataRow((4u << 26) | (1u << 26) | (2u << 26), ModifierKey.Alt | ModifierKey.Shift | ModifierKey.Ctrl)]
public void Parse_ParsesTheModifiers_ForPdCursesAbi(uint raw, ModifierKey expMod)
{
var parser = CursesMouseEventParser.Get(CursesAbiVersion.PdCurses);
var p = parser.Parse(raw | 1u);

p.ShouldBe((MouseButton.Button1, MouseButtonState.Released, expMod));
Expand All @@ -154,7 +209,7 @@ public void Parse_ParsesTheModifiers_ForAbi2(uint raw, ModifierKey expMod)
[TestMethod, DataRow(0u), DataRow(8u << 24), DataRow(1u << 24)]
public void Parse_ReturnsNullIfNoButtonPresent_ForAbi1(uint raw)
{
var parser = CursesMouseEventParser.Get(1);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses5);
var p = parser.Parse(raw);

p.ShouldBeNull();
Expand All @@ -163,7 +218,16 @@ public void Parse_ReturnsNullIfNoButtonPresent_ForAbi1(uint raw)
[TestMethod, DataRow(0u), DataRow(8u << 25), DataRow(1u << 25)]
public void Parse_ReturnsNullIfNoButtonPresent_ForAbi2(uint raw)
{
var parser = CursesMouseEventParser.Get(2);
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
var p = parser.Parse(raw);

p.ShouldBeNull();
}

[TestMethod, DataRow(0u), DataRow(8u << 26), DataRow(1u << 26)]
public void Parse_ReturnsNullIfNoButtonPresent_ForPdCursesAbi(uint raw)
{
var parser = CursesMouseEventParser.Get(CursesAbiVersion.NCurses6);
var p = parser.Parse(raw);

p.ShouldBeNull();
Expand Down
4 changes: 2 additions & 2 deletions Sharpie.Tests/NCursesBackendTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,8 @@ public void CursesMouseEventParser_ReturnsMouseParserBasedOnAbi(string ver, int
.Returns(h);

_backend.CursesMouseEventParser.ShouldBe(m == 2
? CursesMouseEventParser.Get(2)
: CursesMouseEventParser.Get(1));
? CursesMouseEventParser.Get(CursesAbiVersion.NCurses6)
: CursesMouseEventParser.Get(CursesAbiVersion.NCurses5));
}

[TestMethod]
Expand Down
4 changes: 2 additions & 2 deletions Sharpie.Tests/PdCursesBackendTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,9 @@ public void getcchar_IsRelayedToLibrary(int ret)
}

[TestMethod]
public void CursesMouseEventParser_ReturnsMouseParserAbi2()
public void CursesMouseEventParser_ReturnsMouseParserPdCursesAbi()
{
_backend.CursesMouseEventParser.ShouldBe(CursesMouseEventParser.Get(2));
_backend.CursesMouseEventParser.ShouldBe(CursesMouseEventParser.Get(CursesAbiVersion.PdCurses));
}

[TestMethod, DataRow(VideoAttribute.None, 0), DataRow(VideoAttribute.StandOut, 0x00A00000),
Expand Down
4 changes: 2 additions & 2 deletions Sharpie.Tests/PdCursesMod32BackendTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ public void getcchar_IsRelayedToLibrary(int ret)
}

[TestMethod]
public void CursesMouseEventParser_ReturnsMouseParserAbi2()
public void CursesMouseEventParser_ReturnsMouseParserPdCursesAbi()
{
_backend.CursesMouseEventParser.ShouldBe(CursesMouseEventParser.Get(2));
_backend.CursesMouseEventParser.ShouldBe(CursesMouseEventParser.Get(CursesAbiVersion.PdCurses));
}

[TestMethod, DataRow(VideoAttribute.None, 0), DataRow(VideoAttribute.StandOut, 0x00A00000),
Expand Down
55 changes: 55 additions & 0 deletions Sharpie/Backend/CursesAbiVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright (c) 2022-2023, Alexandru Ciobanu
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

namespace Sharpie.Backend;

/// <summary>
/// Specifies the supported ABI versions for the Curses library.
/// Used for mouse event parsing.
/// </summary>
public enum CursesAbiVersion
{
/// <summary>
/// Unknown ABI version. Defaults to NCurses5.
/// </summary>
Unknown,
/// <summary>
/// NCurses 5 ABI version.
/// </summary>
NCurses5,
/// <summary>
/// NCurses 6 ABI version.
/// </summary>
NCurses6,
/// <summary>
/// PDCurses (and PDCursesMod) ABI version.
/// </summary>
PdCurses
}
42 changes: 38 additions & 4 deletions Sharpie/Backend/CursesMouseEventParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ namespace Sharpie.Backend;
/// </summary>
internal abstract class CursesMouseEventParser
{
private static readonly CursesMouseEventParser V1 = new CursesMouseV1EventParser();
private static readonly CursesMouseEventParser V2 = new CursesMouseV2EventParser();
private static readonly CursesMouseEventParser NCurses5 = new CursesMouseV1EventParser();
private static readonly CursesMouseEventParser NCurses6 = new CursesMouseV2EventParser();
private static readonly CursesMouseEventParser PdCurses = new PdCursesModMouseEventParser();

[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
protected CursesMouseEventParser()
Expand Down Expand Up @@ -187,11 +188,26 @@ bool MapButton(uint flag, MouseButton b, MouseButtonState s)
/// </summary>
/// <param name="abiVersion">The ABI version.</param>
/// <returns>The mouse event parser.</returns>
[Obsolete("Use the overload that takes CursesAbiVersion instead.")]
public static CursesMouseEventParser Get(int abiVersion) =>
abiVersion switch
{
2 => V2,
var _ => V1
3 => PdCurses,
2 => NCurses6,
var _ => NCurses5
};

/// <summary>
/// Gets the mouse event parser based on the provided ABI version.
/// </summary>
/// <param name="abiVersion">The ABI version.</param>
/// <returns>The mouse event parser.</returns>
public static CursesMouseEventParser Get(CursesAbiVersion abiVersion) =>
abiVersion switch
{
CursesAbiVersion.PdCurses => PdCurses,
CursesAbiVersion.NCurses6 => NCurses6,
var _ => NCurses5
};

private sealed class CursesMouseV1EventParser: CursesMouseEventParser
Expand All @@ -214,6 +230,24 @@ protected override uint CalculateButtonState(Action action, Button button) =>
protected override uint CalculateModifierState(Modifier modifier) => (uint) modifier << 25;
}

private sealed class PdCursesModMouseEventParser: CursesMouseEventParser
{
protected override uint CalculateButtonState(Action action, Button button) =>
(uint) action << (((int) button - 1) * 5);

protected override uint CalculateModifierState(Modifier modifier)
{
return modifier switch
{
Modifier.Shift => 1 << 26,
Modifier.Ctrl => 1 << 27,
Modifier.Alt => 1 << 28,
Modifier.ReportPosition => 1 << 29,
_ => 0
};
}
}

protected enum Action
{
Released = 1,
Expand Down
6 changes: 3 additions & 3 deletions Sharpie/Backend/NCursesBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected internal override CursesMouseEventParser CursesMouseEventParser
if (_cursesMouseEventParser == null)
{
var ver = curses_version();
var abi = -1;
var abi = CursesAbiVersion.NCurses5;
if (ver != null)
{
var versionParser = new Regex(@".*(\d+)\.(\d+)\.(\d+)");
Expand All @@ -71,8 +71,8 @@ protected internal override CursesMouseEventParser CursesMouseEventParser

abi = major switch
{
>= 6 => 2,
5 => 1,
>= 6 => CursesAbiVersion.NCurses6,
5 => CursesAbiVersion.NCurses5,
var _ => abi
};
}
Expand Down
2 changes: 1 addition & 1 deletion Sharpie/Backend/PdCursesBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal class PdCursesBackend: BaseCursesBackend
internal PdCursesBackend(IDotNetSystemAdapter dotNetSystemAdapter, INativeSymbolResolver pdCursesSymbolResolver,
INativeSymbolResolver? libCSymbolResolver): base(dotNetSystemAdapter, pdCursesSymbolResolver,
libCSymbolResolver) =>
CursesMouseEventParser = CursesMouseEventParser.Get(2);
CursesMouseEventParser = CursesMouseEventParser.Get(CursesAbiVersion.PdCurses);

/// <inheritdoc cref="BaseCursesBackend.CursesMouseEventParser" />
protected internal override CursesMouseEventParser CursesMouseEventParser { get; }
Expand Down
Loading