Skip to content

Commit

Permalink
added hold and release commands
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaksuhn committed Nov 17, 2023
1 parent 0afd98e commit 588ad40
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 9 deletions.
13 changes: 7 additions & 6 deletions SomethingNeedDoing/Grammar/Commands/CallbackCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//internal class CallbackCommand : MacroCommand
//{
// private static readonly Regex Regex = new(@"^/callback\s+(?<name>.*?)\s*$", RegexOptions.Compiled);
// private AtkUnitBase addon;
// private AtkUnitBase* addon;
// private bool updateState;
// private List<object> valueArgs = new();

Expand All @@ -37,7 +37,7 @@

// public static CallbackCommand Parse(List<string> args)
// {

// var valueArgs = new List<object>();

// var current = "";
Expand Down Expand Up @@ -81,14 +81,15 @@
// Svc.Log.Error("Error: Unclosed quotes.");
// }

// return new CallbackCommand(text);
// return new CallbackCommand(args);
// }

// /// <inheritdoc/>
// public async override Task Execute(ActiveMacro macro, CancellationToken token)
// {
// Callback.Fire(IsAddonReady,)

// unsafe
// {
// Callback.Fire(addon, updateState, valueArgs.ToArray());
// }
// await this.PerformWait(token);
// }
//}
81 changes: 81 additions & 0 deletions SomethingNeedDoing/Grammar/Commands/HoldCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

using Dalamud.Game.ClientState.Keys;
using Dalamud.Logging;
using SomethingNeedDoing.Exceptions;
using SomethingNeedDoing.Grammar.Modifiers;
using SomethingNeedDoing.Misc;

namespace SomethingNeedDoing.Grammar.Commands;

/// <summary>
/// The /send command.
/// </summary>
internal class HoldCommand : MacroCommand
{
private static readonly Regex Regex = new(@"^/hold\s+(?<name>.*?)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

private readonly VirtualKey[] vkCodes;

/// <summary>
/// Initializes a new instance of the <see cref="HoldCommand"/> class.
/// </summary>
/// <param name="text">Original text.</param>
/// <param name="vkCodes">VirtualKey codes.</param>
/// <param name="wait">Wait value.</param>
private HoldCommand(string text, VirtualKey[] vkCodes, WaitModifier wait)
: base(text, wait)
{
this.vkCodes = vkCodes;
}

/// <summary>
/// Parse the text as a command.
/// </summary>
/// <param name="text">Text to parse.</param>
/// <returns>A parsed command.</returns>
public static HoldCommand Parse(string text)
{
_ = WaitModifier.TryParse(ref text, out var waitModifier);

var match = Regex.Match(text);
if (!match.Success)
throw new MacroSyntaxError(text);

var nameValue = ExtractAndUnquote(match, "name");
var vkCodes = nameValue.Split("+")
.Select(name =>
{
if (!Enum.TryParse<VirtualKey>(name, true, out var vkCode))
throw new MacroCommandError("Invalid virtual key");
return vkCode;
})
.ToArray();

return new HoldCommand(text, vkCodes, waitModifier);
}

/// <inheritdoc/>
public async override Task Execute(ActiveMacro macro, CancellationToken token)
{
Service.Log.Debug($"Executing: {this.Text}");

if (this.vkCodes.Length == 1)
{
Keyboard.Hold(this.vkCodes[0]);
}
else
{
var key = this.vkCodes.Last();
var mods = this.vkCodes.SkipLast(1);
Keyboard.Hold(key, mods);
}

await this.PerformWait(token);
}
}
81 changes: 81 additions & 0 deletions SomethingNeedDoing/Grammar/Commands/ReleaseCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

using Dalamud.Game.ClientState.Keys;
using Dalamud.Logging;
using SomethingNeedDoing.Exceptions;
using SomethingNeedDoing.Grammar.Modifiers;
using SomethingNeedDoing.Misc;

namespace SomethingNeedDoing.Grammar.Commands;

/// <summary>
/// The /send command.
/// </summary>
internal class ReleaseCommand : MacroCommand
{
private static readonly Regex Regex = new(@"^/release\s+(?<name>.*?)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

private readonly VirtualKey[] vkCodes;

/// <summary>
/// Initializes a new instance of the <see cref="ReleaseCommand"/> class.
/// </summary>
/// <param name="text">Original text.</param>
/// <param name="vkCodes">VirtualKey codes.</param>
/// <param name="wait">Wait value.</param>
private ReleaseCommand(string text, VirtualKey[] vkCodes, WaitModifier wait)
: base(text, wait)
{
this.vkCodes = vkCodes;
}

/// <summary>
/// Parse the text as a command.
/// </summary>
/// <param name="text">Text to parse.</param>
/// <returns>A parsed command.</returns>
public static ReleaseCommand Parse(string text)
{
_ = WaitModifier.TryParse(ref text, out var waitModifier);

var match = Regex.Match(text);
if (!match.Success)
throw new MacroSyntaxError(text);

var nameValue = ExtractAndUnquote(match, "name");
var vkCodes = nameValue.Split("+")
.Select(name =>
{
if (!Enum.TryParse<VirtualKey>(name, true, out var vkCode))
throw new MacroCommandError("Invalid virtual key");
return vkCode;
})
.ToArray();

return new ReleaseCommand(text, vkCodes, waitModifier);
}

/// <inheritdoc/>
public async override Task Execute(ActiveMacro macro, CancellationToken token)
{
Service.Log.Debug($"Executing: {this.Text}");

if (this.vkCodes.Length == 1)
{
Keyboard.Release(this.vkCodes[0]);
}
else
{
var key = this.vkCodes.Last();
var mods = this.vkCodes.SkipLast(1);
Keyboard.Release(key, mods);
}

await this.PerformWait(token);
}
}
2 changes: 2 additions & 0 deletions SomethingNeedDoing/Grammar/MacroParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public static MacroCommand ParseLine(string line)
"/requirestats" => RequireStatsCommand.Parse(line),
"/runmacro" => RunMacroCommand.Parse(line),
"/send" => SendCommand.Parse(line),
"/hold" => HoldCommand.Parse(line),
"/release" => ReleaseCommand.Parse(line),
"/target" when Service.Configuration.UseSNDTargeting => TargetCommand.Parse(line),
"/waitaddon" => WaitAddonCommand.Parse(line),
"/wait" => WaitCommand.Parse(line),
Expand Down
59 changes: 56 additions & 3 deletions SomethingNeedDoing/Interface/HelpWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ private readonly (string Name, string? Alias, string Description, string[] Modif
"/send NUMPAD0",
"/send CONTROL+MENU+SHIFT+NUMPAD0",
}),
(
"hold", null,
"Send an arbitrary keystroke, to be held down, with optional modifiers. Keys are pressed in the same order as the command.",
new[] { "wait" },
new[]
{
"/send MULTIPLY",
"/send NUMPAD0",
"/send CONTROL+MENU+SHIFT+NUMPAD0",
}),
(
"release", null,
"Send an arbitrary keystroke, to be released, with optional modifiers. Keys are pressed in the same order as the command.",
new[] { "wait" },
new[]
{
"/send MULTIPLY",
"/send NUMPAD0",
"/send CONTROL+MENU+SHIFT+NUMPAD0",
}),
(
"target", null,
"Target anyone and anything that can be selected.",
Expand Down Expand Up @@ -296,6 +316,12 @@ static void DisplayChangelog(string date, string changes, bool separator = true)

ImGui.PushFont(UiBuilder.MonoFont);

DisplayChangelog(
"2023-11-17",
"- Added /hold\n" +
"- Added /release.\n" +
"- Updated help documentation for lua commands.\n");

DisplayChangelog(
"2023-11-15",
"- Added GetClassJobId()\n");
Expand Down Expand Up @@ -850,11 +876,38 @@ bool HasStatus(string name)
// id: status effect id(s).
bool HasStatusId(uint id, ...)
IsAddonVisible(string addonName)
IsAddonReady(string addonName)
bool IsAddonVisible(string addonName)
bool IsAddonReady(string addonName)
// Can fetch nested nodes
GetNodeText(string addonName, int nodeNumber, ...)
string GetNodeText(string addonName, int nodeNumber, ...)
string GetSelectStringText(int index)
string GetSelectIconStringText(int index)
bool GetCharacterCondition(int flagID, bool hasCondition = true)
bool IsInZone(int zoneID)
int GetZoneID()
string GetCharacterName(bool includeWorld = false)
int GetItemCount(int itemID, bool includeHQ = true)
bool DeliverooIsTurnInRunning()
uint GetProgressIncrease(uint actionID)
uint GetQualityIncrease(uint actionID)
void LeaveDuty()
bool IsLocalPlayerNull()
bool IsPlayerDead()
bool IsPlayerCasting()
uint GetGil()
uint GetClassJobId()
".Trim();

ImGui.TextWrapped(text);
Expand Down
44 changes: 44 additions & 0 deletions SomethingNeedDoing/Misc/Keyboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,50 @@ public static void Send(VirtualKey key, IEnumerable<VirtualKey>? mods)
}
}

public static void Hold(VirtualKey key) => Hold(key, null);

public static void Hold(VirtualKey key, IEnumerable<VirtualKey>? mods)
{
const int WM_KEYDOWN = 0x100;

if (key != 0)
{
//var hWnd = handle ??= Process.GetCurrentProcess().MainWindowHandle;
if (WindowFunctions.TryFindGameWindow(out var hWnd))
{
if (mods != null)
{
foreach (var mod in mods)
_ = SendMessage(hWnd, WM_KEYDOWN, (IntPtr)mod, IntPtr.Zero);
}

_ = SendMessage(hWnd, WM_KEYDOWN, (IntPtr)key, IntPtr.Zero);
}
}
}

public static void Release (VirtualKey key) => Release(key, null);

public static void Release(VirtualKey key, IEnumerable<VirtualKey>? mods)
{
const int WM_KEYUP = 0x101;

if (key != 0)
{
//var hWnd = handle ??= Process.GetCurrentProcess().MainWindowHandle;
if (WindowFunctions.TryFindGameWindow(out var hWnd))
{
_ = SendMessage(hWnd, WM_KEYUP, (IntPtr)key, IntPtr.Zero);

if (mods != null)
{
foreach (var mod in mods)
_ = SendMessage(hWnd, WM_KEYUP, (IntPtr)mod, IntPtr.Zero);
}
}
}
}

[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
}

0 comments on commit 588ad40

Please sign in to comment.