Implemented Tolk library for screen reader in windows

master
Mohammad Shoaib Khan 2022-10-24 12:12:53 +05:30
parent 7a4ca89836
commit f2ba3e8793
6 changed files with 114 additions and 94 deletions

2
.gitignore vendored
View File

@ -3,6 +3,8 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
*.dll
.vscode/*
.git-old/
bin/

View File

@ -21,7 +21,7 @@ namespace stardew_access
MainClass.Config.ReadTile = !MainClass.Config.ReadTile;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off"));
MainClass.InfoLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off"));
});
helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) =>
@ -29,7 +29,7 @@ namespace stardew_access
MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off"));
MainClass.InfoLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off"));
});
helper.ConsoleCommands.Add("watered", "Toggle speaking watered or unwatered for crops.", (string commmand, string[] args) =>
@ -37,7 +37,7 @@ namespace stardew_access
MainClass.Config.WateredToggle = !MainClass.Config.WateredToggle;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off"));
MainClass.InfoLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off"));
});
#endregion
@ -47,14 +47,14 @@ namespace stardew_access
MainClass.Config.Radar = !MainClass.Config.Radar;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off"));
MainClass.InfoLog("Radar " + (MainClass.Config.Radar ? "on" : "off"));
});
helper.ConsoleCommands.Add("rdebug", "Toggle debugging in radar feature.", (string commmand, string[] args) =>
{
MainClass.radarDebug = !MainClass.radarDebug;
MainClass.DebugLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off"));
MainClass.InfoLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off"));
});
helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) =>
@ -62,14 +62,14 @@ namespace stardew_access
MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off"));
MainClass.InfoLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off"));
});
helper.ConsoleCommands.Add("rfocus", "Toggle focus mode in radar feature.", (string commmand, string[] args) =>
{
bool focus = MainClass.RadarFeature.ToggleFocus();
MainClass.DebugLog("Focus mode is " + (focus ? "on" : "off"));
MainClass.InfoLog("Focus mode is " + (focus ? "on" : "off"));
});
helper.ConsoleCommands.Add("rdelay", "Set the delay of radar feature in milliseconds.", (string commmand, string[] args) =>
@ -88,19 +88,19 @@ namespace stardew_access
{
MainClass.RadarFeature.delay = delay;
if (delay >= 1000)
MainClass.DebugLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds.");
MainClass.InfoLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds.");
else
MainClass.DebugLog($"Delay should be atleast 1 second or 1000 millisecond long.");
MainClass.InfoLog($"Delay should be atleast 1 second or 1000 millisecond long.");
}
else
{
MainClass.DebugLog("Invalid delay amount, it can only be in numeric form.");
MainClass.InfoLog("Invalid delay amount, it can only be in numeric form.");
}
}
else
{
MainClass.DebugLog("Enter the delay amount (in milliseconds)!");
MainClass.InfoLog("Enter the delay amount (in milliseconds)!");
}
});
@ -121,19 +121,19 @@ namespace stardew_access
{
MainClass.RadarFeature.range = range;
if (range >= 2 && range <= 10)
MainClass.DebugLog($"Range set to {MainClass.RadarFeature.range}.");
MainClass.InfoLog($"Range set to {MainClass.RadarFeature.range}.");
else
MainClass.DebugLog($"Range should be atleast 2 and maximum 10.");
MainClass.InfoLog($"Range should be atleast 2 and maximum 10.");
}
else
{
MainClass.DebugLog("Invalid range amount, it can only be in numeric form.");
MainClass.InfoLog("Invalid range amount, it can only be in numeric form.");
}
}
else
{
MainClass.DebugLog("Enter the range amount!");
MainClass.InfoLog("Enter the range amount!");
}
});
@ -152,16 +152,16 @@ namespace stardew_access
if (!MainClass.RadarFeature.exclusions.Contains(keyToAdd))
{
MainClass.RadarFeature.exclusions.Add(keyToAdd);
MainClass.DebugLog($"Added {keyToAdd} key to exclusions list.");
MainClass.InfoLog($"Added {keyToAdd} key to exclusions list.");
}
else
{
MainClass.DebugLog($"{keyToAdd} key already present in the list.");
MainClass.InfoLog($"{keyToAdd} key already present in the list.");
}
}
else
{
MainClass.DebugLog("Unable to add the key to exclusions list.");
MainClass.InfoLog("Unable to add the key to exclusions list.");
}
});
@ -177,16 +177,16 @@ namespace stardew_access
if (MainClass.RadarFeature.exclusions.Contains(keyToAdd))
{
MainClass.RadarFeature.exclusions.Remove(keyToAdd);
MainClass.DebugLog($"Removed {keyToAdd} key from exclusions list.");
MainClass.InfoLog($"Removed {keyToAdd} key from exclusions list.");
}
else
{
MainClass.DebugLog($"Cannot find {keyToAdd} key in exclusions list.");
MainClass.InfoLog($"Cannot find {keyToAdd} key in exclusions list.");
}
}
else
{
MainClass.DebugLog("Unable to remove the key from exclusions list.");
MainClass.InfoLog("Unable to remove the key from exclusions list.");
}
});
@ -199,23 +199,23 @@ namespace stardew_access
{
toPrint = $"{toPrint}\t{i + 1}: {MainClass.RadarFeature.exclusions[i]}";
}
MainClass.DebugLog(toPrint);
MainClass.InfoLog(toPrint);
}
else
{
MainClass.DebugLog("No exclusions found.");
MainClass.InfoLog("No exclusions found.");
}
});
helper.ConsoleCommands.Add("reclear", "Clear the focus exclusions in the radar featrure.", (string commmand, string[] args) =>
{
MainClass.RadarFeature.exclusions.Clear();
MainClass.DebugLog($"Cleared the focus list in the exclusions feature.");
MainClass.InfoLog($"Cleared the focus list in the exclusions feature.");
});
helper.ConsoleCommands.Add("recount", "Number of exclusions in the radar feature.", (string commmand, string[] args) =>
{
MainClass.DebugLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature.");
MainClass.InfoLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature.");
});
#endregion
@ -232,16 +232,16 @@ namespace stardew_access
if (!MainClass.RadarFeature.focus.Contains(keyToAdd))
{
MainClass.RadarFeature.focus.Add(keyToAdd);
MainClass.DebugLog($"Added {keyToAdd} key to focus list.");
MainClass.InfoLog($"Added {keyToAdd} key to focus list.");
}
else
{
MainClass.DebugLog($"{keyToAdd} key already present in the list.");
MainClass.InfoLog($"{keyToAdd} key already present in the list.");
}
}
else
{
MainClass.DebugLog("Unable to add the key to focus list.");
MainClass.InfoLog("Unable to add the key to focus list.");
}
});
@ -257,16 +257,16 @@ namespace stardew_access
if (MainClass.RadarFeature.focus.Contains(keyToAdd))
{
MainClass.RadarFeature.focus.Remove(keyToAdd);
MainClass.DebugLog($"Removed {keyToAdd} key from focus list.");
MainClass.InfoLog($"Removed {keyToAdd} key from focus list.");
}
else
{
MainClass.DebugLog($"Cannot find {keyToAdd} key in focus list.");
MainClass.InfoLog($"Cannot find {keyToAdd} key in focus list.");
}
}
else
{
MainClass.DebugLog("Unable to remove the key from focus list.");
MainClass.InfoLog("Unable to remove the key from focus list.");
}
});
@ -279,23 +279,23 @@ namespace stardew_access
{
toPrint = $"{toPrint}\t{i + 1}): {MainClass.RadarFeature.focus[i]}";
}
MainClass.DebugLog(toPrint);
MainClass.InfoLog(toPrint);
}
else
{
MainClass.DebugLog("No objects found in the focus list.");
MainClass.InfoLog("No objects found in the focus list.");
}
});
helper.ConsoleCommands.Add("rfclear", "Clear the focus list in the radar featrure.", (string commmand, string[] args) =>
{
MainClass.RadarFeature.focus.Clear();
MainClass.DebugLog($"Cleared the focus list in the radar feature.");
MainClass.InfoLog($"Cleared the focus list in the radar feature.");
});
helper.ConsoleCommands.Add("rfcount", "Number of list in the radar feature.", (string commmand, string[] args) =>
{
MainClass.DebugLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature.");
MainClass.InfoLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature.");
});
#endregion
@ -306,14 +306,14 @@ namespace stardew_access
{
if (Game1.currentLocation is not Farm)
{
MainClass.DebugLog("Can only use this command in the farm");
MainClass.InfoLog("Can only use this command in the farm");
return;
}
string? indexInString = args.ElementAtOrDefault(0);
if (indexInString == null)
{
MainClass.DebugLog("Enter the index too!");
MainClass.InfoLog("Enter the index too!");
return;
}
@ -322,12 +322,12 @@ namespace stardew_access
if (!isParsable || !(index >= 0 && index <= 9))
{
MainClass.DebugLog("Index can only be a number and from 0 to 9 only");
MainClass.InfoLog("Index can only be a number and from 0 to 9 only");
return;
}
BuildingNAnimalMenuPatches.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY());
MainClass.DebugLog($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index.");
MainClass.InfoLog($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index.");
});
helper.ConsoleCommands.Add("marklist", "List all marked positions.", (string commmand, string[] args) =>
@ -342,16 +342,16 @@ namespace stardew_access
}
if (toPrint == "")
MainClass.DebugLog("No positions marked!");
MainClass.InfoLog("No positions marked!");
else
MainClass.DebugLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
MainClass.InfoLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
});
helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) =>
{
if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm)
{
MainClass.DebugLog($"Cannot list buildings.");
MainClass.InfoLog($"Cannot list buildings.");
return;
}
@ -372,11 +372,11 @@ namespace stardew_access
if (toPrint == "")
{
MainClass.DebugLog("No appropriate buildings to list");
MainClass.InfoLog("No appropriate buildings to list");
}
else
{
MainClass.DebugLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
MainClass.InfoLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
}
});
@ -384,14 +384,14 @@ namespace stardew_access
{
if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm)
{
MainClass.DebugLog($"Cannot select building.");
MainClass.InfoLog($"Cannot select building.");
return;
}
string? indexInString = args.ElementAtOrDefault(0);
if (indexInString == null)
{
MainClass.DebugLog("Enter the index of the building too! Use buildlist");
MainClass.InfoLog("Enter the index of the building too! Use buildlist");
return;
}
@ -400,7 +400,7 @@ namespace stardew_access
if (!isParsable)
{
MainClass.DebugLog("Index can only be a number.");
MainClass.InfoLog("Index can only be a number.");
return;
}
@ -414,13 +414,13 @@ namespace stardew_access
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.DebugLog($"No building found with index {index}. Use buildlist.");
MainClass.InfoLog($"No building found with index {index}. Use buildlist.");
return;
}
if (positionIndexInString == null)
{
MainClass.DebugLog("Enter the index of marked place too! Use marklist.");
MainClass.InfoLog("Enter the index of marked place too! Use marklist.");
return;
}
@ -428,7 +428,7 @@ namespace stardew_access
if (!isParsable)
{
MainClass.DebugLog("Index can only be a number.");
MainClass.InfoLog("Index can only be a number.");
return;
}
}
@ -437,7 +437,7 @@ namespace stardew_access
{
if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero)
{
MainClass.DebugLog($"No marked position found at {index} index.");
MainClass.InfoLog($"No marked position found at {index} index.");
return;
}
}
@ -445,7 +445,7 @@ namespace stardew_access
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.DebugLog($"No building found with index {index}. Use buildlist.");
MainClass.InfoLog($"No building found with index {index}. Use buildlist.");
return;
}
}
@ -471,7 +471,7 @@ namespace stardew_access
if (response != null)
{
MainClass.DebugLog(response);
MainClass.InfoLog(response);
}
});
#endregion
@ -481,21 +481,21 @@ namespace stardew_access
{
MainClass.ScreenReader.InitializeScreenReader();
MainClass.DebugLog("Screen Reader refreshed!");
MainClass.InfoLog("Screen Reader refreshed!");
});
helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) =>
{
MainClass.Config = helper.ReadConfig<ModConfig>();
MainClass.DebugLog("Mod Config refreshed!");
MainClass.InfoLog("Mod Config refreshed!");
});
helper.ConsoleCommands.Add("refst", "Refresh static tiles", (string commmand, string[] args) =>
{
MainClass.STiles = new Features.StaticTiles();
MainClass.DebugLog("Static tiles refreshed!");
MainClass.InfoLog("Static tiles refreshed!");
});
helper.ConsoleCommands.Add("hnspercent", "Toggle between speaking in percentage or full health and stamina.", (string commmand, string[] args) =>
@ -503,7 +503,7 @@ namespace stardew_access
MainClass.Config.HealthNStaminaInPercentage = !MainClass.Config.HealthNStaminaInPercentage;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off"));
MainClass.InfoLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off"));
});
helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) =>
@ -511,7 +511,7 @@ namespace stardew_access
MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off"));
MainClass.InfoLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off"));
});
helper.ConsoleCommands.Add("warning", "Toggle warnings feature.", (string commmand, string[] args) =>
@ -519,7 +519,7 @@ namespace stardew_access
MainClass.Config.Warning = !MainClass.Config.Warning;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off"));
MainClass.InfoLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off"));
});
helper.ConsoleCommands.Add("tts", "Toggles the screen reader/tts", (string commmand, string[] args) =>
@ -527,7 +527,7 @@ namespace stardew_access
MainClass.Config.TTS = !MainClass.Config.TTS;
helper.WriteConfig(MainClass.Config);
MainClass.DebugLog("TTS is " + (MainClass.Config.TTS ? "on" : "off"));
MainClass.InfoLog("TTS is " + (MainClass.Config.TTS ? "on" : "off"));
});
#endregion
}

View File

@ -114,6 +114,7 @@ namespace stardew_access
Game1.options.setGamepadMode("force_on");
ScreenReader = new ScreenReaderController().Initialize();
ScreenReader.Say("Initializing Stardew Access", true);
CustomSoundEffects.Initialize();
@ -346,6 +347,14 @@ namespace stardew_access
monitor.Log(message, LogLevel.Error);
}
public static void InfoLog(string message)
{
if (monitor == null)
return;
monitor.Log(message, LogLevel.Info);
}
public static void DebugLog(string message)
{
if (monitor == null)

View File

@ -41,10 +41,16 @@ namespace stardew_access.ScreenReader
public void InitializeScreenReader()
{
MainClass.InfoLog("Initializing speech dispatcher...");
int res = Initialize();
if (res == 1)
{
initialized = true;
MainClass.InfoLog("Successfully initialized.");
}
else
{
MainClass.ErrorLog("Unable to initialize.");
}
}
@ -71,7 +77,16 @@ namespace stardew_access.ScreenReader
if (text.Contains('^')) text = text.Replace('^', '\n');
GoString str = new GoString(text, text.Length);
Speak(str, interrupt);
int re = Speak(str, interrupt);
if (re == 1)
{
MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}");
}
else
{
MainClass.ErrorLog($"Failed to output text: {text}");
}
}
public void SayWithChecker(string text, bool interrupt)

View File

@ -1,10 +1,10 @@
using AccessibleOutput;
using DavyKager;
namespace stardew_access.ScreenReader
{
public class ScreenReaderWindows : IScreenReader
{
public IAccessibleOutput? screenReader = null;
private bool isLoaded = false;
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
public string PrevTextTile
@ -15,43 +15,30 @@ namespace stardew_access.ScreenReader
public void InitializeScreenReader()
{
MainClass.InfoLog("Initializing Tolk...");
Tolk.Load();
NvdaOutput? nvdaOutput = null;
JawsOutput? jawsOutput = null;
SapiOutput? sapiOutput = null;
// Initialize NVDA
try
MainClass.InfoLog("Querying for the active screen reader driver...");
string name = Tolk.DetectScreenReader();
if (name != null)
{
nvdaOutput = new NvdaOutput();
MainClass.InfoLog($"The active screen reader driver is: {name}");
isLoaded = true;
}
catch (Exception) { }
// Initialize JAWS
try
else
{
jawsOutput = new JawsOutput();
MainClass.ErrorLog("None of the supported screen readers is running");
isLoaded = false;
}
catch (Exception) { }
// Initialize SAPI
try
{
sapiOutput = new SapiOutput();
}
catch (Exception) { }
if (nvdaOutput != null && nvdaOutput.IsAvailable())
screenReader = nvdaOutput;
else if (jawsOutput != null && jawsOutput.IsAvailable())
screenReader = jawsOutput;
else if (sapiOutput != null && sapiOutput.IsAvailable())
screenReader = sapiOutput;
}
public void CloseScreenReader()
{
if (isLoaded)
{
Tolk.Unload();
isLoaded = false;
}
}
public void Say(string text, bool interrupt)
@ -59,7 +46,7 @@ namespace stardew_access.ScreenReader
if (text == null)
return;
if (screenReader == null)
if (!isLoaded)
return;
if (!MainClass.Config.TTS)
@ -67,7 +54,14 @@ namespace stardew_access.ScreenReader
if (text.Contains('^')) text = text.Replace('^', '\n');
screenReader.Speak(text, interrupt);
if (Tolk.Output("Hello, World!"))
{
MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}");
}
else
{
MainClass.ErrorLog($"Failed to output text: {text}");
}
}
public void SayWithChecker(string text, bool interrupt)

View File

@ -12,9 +12,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AccessibleOutput" Version="1.0.0" />
<PackageReference Include="Lib.Harmony" Version="2.2.0" />
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="4.0.0" />
<Reference Include="./TolkDotNet.dll" />
</ItemGroup>
</Project>