Added screen reader support for linux
parent
fe011b239e
commit
4b7d011a7b
|
@ -34,7 +34,8 @@ namespace stardew_access.Game
|
||||||
{
|
{
|
||||||
if (!manuallyTriggered && prevTile != gt)
|
if (!manuallyTriggered && prevTile != gt)
|
||||||
{
|
{
|
||||||
MainClass.screenReader.prevTextTile = " ";
|
if(MainClass.screenReader!=null)
|
||||||
|
MainClass.screenReader.PrevTextTile = " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isColliding = isCollidingAtTile(x, y);
|
bool isColliding = isCollidingAtTile(x, y);
|
||||||
|
|
|
@ -168,6 +168,11 @@ namespace stardew_access
|
||||||
original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)),
|
original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)),
|
||||||
prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PlaySoundPatch))
|
prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PlaySoundPatch))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
harmony.Patch(
|
||||||
|
original: AccessTools.Method(typeof(InstanceGame), nameof(InstanceGame.Exit)),
|
||||||
|
prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ExitEventPatch))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,20 +6,10 @@ using HarmonyLib;
|
||||||
using stardew_access.Patches;
|
using stardew_access.Patches;
|
||||||
using AutoHotkey.Interop;
|
using AutoHotkey.Interop;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using stardew_access.ScreenReader;
|
||||||
|
|
||||||
namespace stardew_access
|
namespace stardew_access
|
||||||
{
|
{
|
||||||
public struct GoString
|
|
||||||
{
|
|
||||||
public string msg;
|
|
||||||
public long len;
|
|
||||||
public GoString(string msg, long len)
|
|
||||||
{
|
|
||||||
this.msg = msg;
|
|
||||||
this.len = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MainClass : Mod
|
public class MainClass : Mod
|
||||||
{
|
{
|
||||||
private Harmony? harmony;
|
private Harmony? harmony;
|
||||||
|
@ -33,7 +23,7 @@ namespace stardew_access
|
||||||
AutoHotkeyEngine ahk;
|
AutoHotkeyEngine ahk;
|
||||||
public static string hudMessageQueryKey = "";
|
public static string hudMessageQueryKey = "";
|
||||||
public static Radar radarFeature;
|
public static Radar radarFeature;
|
||||||
public static ScreenReader screenReader;
|
public static ScreenReaderInterface? screenReader;
|
||||||
|
|
||||||
private static IModHelper _modHelper;
|
private static IModHelper _modHelper;
|
||||||
public static IModHelper ModHelper
|
public static IModHelper ModHelper
|
||||||
|
@ -41,17 +31,6 @@ namespace stardew_access
|
||||||
get{return _modHelper;}
|
get{return _modHelper;}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("libspeechdwrapper.so")]
|
|
||||||
private static extern void Initialize();
|
|
||||||
|
|
||||||
|
|
||||||
[DllImport("libspeechdwrapper.so")]
|
|
||||||
private static extern void Speak(GoString text, bool interrupt);
|
|
||||||
|
|
||||||
|
|
||||||
[DllImport("libspeechdwrapper.so")]
|
|
||||||
private static extern void Close();
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
|
@ -70,8 +49,7 @@ namespace stardew_access
|
||||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
InitializeAutoHotKey();
|
InitializeAutoHotKey();
|
||||||
|
|
||||||
screenReader = new ScreenReader();
|
screenReader = new ScreenReaderController().Initialize();
|
||||||
screenReader.InitializeScreenReader();
|
|
||||||
|
|
||||||
CustomSoundEffects.Initialize();
|
CustomSoundEffects.Initialize();
|
||||||
|
|
||||||
|
@ -84,24 +62,26 @@ namespace stardew_access
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
Initialize();
|
|
||||||
string text = "Testing";
|
|
||||||
Speak(new GoString(text,text.Length), false);
|
|
||||||
Close();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
|
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
|
||||||
helper.Events.GameLoop.UpdateTicked += this.onUpdateTicked;
|
helper.Events.GameLoop.UpdateTicked += this.onUpdateTicked;
|
||||||
|
AppDomain.CurrentDomain.DomainUnload += OnExit;
|
||||||
|
AppDomain.CurrentDomain.ProcessExit += OnExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnExit (object? sender, EventArgs? e)
|
||||||
|
{
|
||||||
|
// Don't if this ever gets called or not but, just in case if it does.
|
||||||
|
if(screenReader!=null)
|
||||||
|
screenReader.CloseScreenReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns the Screen Reader class for other mods to use.</summary>
|
/// <summary>Returns the Screen Reader class for other mods to use.</summary>
|
||||||
public override object GetApi()
|
public override object GetApi()
|
||||||
{
|
{
|
||||||
return new ScreenReader();
|
return new ScreenReaderController().Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpdateTicked(object sender, UpdateTickedEventArgs e)
|
private void onUpdateTicked(object? sender, UpdateTickedEventArgs? e)
|
||||||
{
|
{
|
||||||
if (!Context.IsPlayerFree)
|
if (!Context.IsPlayerFree)
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +109,7 @@ namespace stardew_access
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
|
private void OnButtonPressed(object? sender, ButtonPressedEventArgs? e)
|
||||||
{
|
{
|
||||||
if (!Context.IsPlayerFree)
|
if (!Context.IsPlayerFree)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -338,6 +338,11 @@ namespace stardew_access.Patches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ExitEventPatch()
|
||||||
|
{
|
||||||
|
if(MainClass.screenReader!=null)
|
||||||
|
MainClass.screenReader.CloseScreenReader();
|
||||||
|
}
|
||||||
internal static void resetGlobalVars()
|
internal static void resetGlobalVars()
|
||||||
{
|
{
|
||||||
currentLetterText = " ";
|
currentLetterText = " ";
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace stardew_access.ScreenReader
|
||||||
|
{
|
||||||
|
public class ScreenReaderController{
|
||||||
|
public ScreenReaderInterface? Initialize(){
|
||||||
|
ScreenReaderInterface? ScreenReader = null;
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){
|
||||||
|
ScreenReaderWindows screenReaderWindows = new ScreenReaderWindows();
|
||||||
|
screenReaderWindows.InitializeScreenReader();
|
||||||
|
|
||||||
|
ScreenReader = screenReaderWindows;
|
||||||
|
} else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)){
|
||||||
|
ScreenReaderLinux screenReaderLinux = new ScreenReaderLinux();
|
||||||
|
screenReaderLinux.InitializeScreenReader();
|
||||||
|
|
||||||
|
ScreenReader = screenReaderLinux;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScreenReader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
namespace stardew_access.ScreenReader
|
||||||
|
{
|
||||||
|
public interface ScreenReaderInterface{
|
||||||
|
|
||||||
|
public string PrevTextTile{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes the screen reader.</summary>
|
||||||
|
public void InitializeScreenReader();
|
||||||
|
|
||||||
|
// <summary>Closes the screen reader, this is important, call this function when closing the game.</summary>
|
||||||
|
public void CloseScreenReader();
|
||||||
|
|
||||||
|
/// <summary>Speaks the text via the loaded screen reader (if any).</summary>
|
||||||
|
/// <param name="text">The text to be narrated.</param>
|
||||||
|
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
||||||
|
public void Say(string text, bool interrupt);
|
||||||
|
|
||||||
|
/// <summary>Speaks the text via the loaded screen reader (if any).
|
||||||
|
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.</summary>
|
||||||
|
/// <param name="text">The text to be narrated.</param>
|
||||||
|
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
||||||
|
public void SayWithChecker(string text, bool interrupt);
|
||||||
|
|
||||||
|
/// <summary>Speaks the text via the loaded screen reader (if any).
|
||||||
|
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
||||||
|
/// <br/><br/>Use this when narrating hovered component in menus to avoid interference.</summary>
|
||||||
|
/// <param name="text">The text to be narrated.</param>
|
||||||
|
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
||||||
|
public void SayWithMenuChecker(string text, bool interrupt);
|
||||||
|
|
||||||
|
/// <summary>Speaks the text via the loaded screen reader (if any).
|
||||||
|
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
||||||
|
/// <br/><br/>Use this when narrating chat messages to avoid interference.</summary>
|
||||||
|
/// <param name="text">The text to be narrated.</param>
|
||||||
|
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
||||||
|
public void SayWithChatChecker(string text, bool interrupt);
|
||||||
|
|
||||||
|
/// <summary>Speaks the text via the loaded screen reader (if any).
|
||||||
|
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
||||||
|
/// <br/><br/>Use this when narrating texts based on tile position to avoid interference.</summary>
|
||||||
|
/// <param name="text">The text to be narrated.</param>
|
||||||
|
/// <param name="x">The X location of tile.</param>
|
||||||
|
/// <param name="y">The Y location of tile.</param>
|
||||||
|
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
||||||
|
public void SayWithTileQuery(string text, int x, int y, bool interrupt);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace stardew_access.ScreenReader
|
||||||
|
{
|
||||||
|
|
||||||
|
public struct GoString
|
||||||
|
{
|
||||||
|
public string msg;
|
||||||
|
public long len;
|
||||||
|
public GoString(string msg, long len)
|
||||||
|
{
|
||||||
|
this.msg = msg;
|
||||||
|
this.len = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScreenReaderLinux : ScreenReaderInterface
|
||||||
|
{
|
||||||
|
[DllImport("libspeechdwrapper.so")]
|
||||||
|
private static extern void Initialize();
|
||||||
|
|
||||||
|
[DllImport("libspeechdwrapper.so")]
|
||||||
|
private static extern void Speak(GoString text, bool interrupt);
|
||||||
|
|
||||||
|
[DllImport("libspeechdwrapper.so")]
|
||||||
|
private static extern void Close();
|
||||||
|
|
||||||
|
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
|
||||||
|
|
||||||
|
public string PrevTextTile{
|
||||||
|
get{ return prevTextTile; }
|
||||||
|
set{ prevTextTile=value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeScreenReader()
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseScreenReader(){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Say(string text, bool interrupt)
|
||||||
|
{
|
||||||
|
GoString str = new GoString(text, text.Length);
|
||||||
|
Speak(str, interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SayWithChecker(string text, bool interrupt)
|
||||||
|
{
|
||||||
|
if (prevText != text)
|
||||||
|
{
|
||||||
|
prevText = text;
|
||||||
|
Say(text, interrupt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SayWithMenuChecker(string text, bool interrupt)
|
||||||
|
{
|
||||||
|
if (prevMenuText != text)
|
||||||
|
{
|
||||||
|
prevMenuText = text;
|
||||||
|
Say(text, interrupt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SayWithChatChecker(string text, bool interrupt)
|
||||||
|
{
|
||||||
|
if (prevChatText != text)
|
||||||
|
{
|
||||||
|
prevChatText = text;
|
||||||
|
Say(text, interrupt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
|
||||||
|
{
|
||||||
|
string query = $"{text} x:{x} y:{y}";
|
||||||
|
|
||||||
|
if (prevTextTile != query)
|
||||||
|
{
|
||||||
|
prevTextTile = query;
|
||||||
|
Say(text, interrupt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
using AccessibleOutput;
|
using AccessibleOutput;
|
||||||
using StardewModdingAPI;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace stardew_access
|
namespace stardew_access.ScreenReader
|
||||||
{
|
{
|
||||||
public class ScreenReader
|
public class ScreenReaderWindows : ScreenReaderInterface
|
||||||
{
|
{
|
||||||
public IAccessibleOutput? screenReader = null;
|
public IAccessibleOutput? screenReader = null;
|
||||||
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
|
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
|
||||||
|
|
||||||
/// <summary>Initializes the screen reader.</summary>
|
public string PrevTextTile{
|
||||||
|
get{ return prevTextTile; }
|
||||||
|
set{ prevTextTile=value; }
|
||||||
|
}
|
||||||
|
|
||||||
public void InitializeScreenReader()
|
public void InitializeScreenReader()
|
||||||
{
|
{
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
return;
|
|
||||||
|
|
||||||
NvdaOutput? nvdaOutput = null;
|
NvdaOutput? nvdaOutput = null;
|
||||||
JawsOutput? jawsOutput = null;
|
JawsOutput? jawsOutput = null;
|
||||||
|
@ -46,13 +46,12 @@ namespace stardew_access
|
||||||
screenReader = jawsOutput;
|
screenReader = jawsOutput;
|
||||||
else if (sapiOutput != null && sapiOutput.IsAvailable())
|
else if (sapiOutput != null && sapiOutput.IsAvailable())
|
||||||
screenReader = sapiOutput;
|
screenReader = sapiOutput;
|
||||||
else
|
|
||||||
MainClass.monitor.Log($"Unable to load any screen reader!", LogLevel.Error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).</summary>
|
public void CloseScreenReader(){
|
||||||
/// <param name="text">The text to be narrated.</param>
|
|
||||||
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
}
|
||||||
|
|
||||||
public void Say(string text, bool interrupt)
|
public void Say(string text, bool interrupt)
|
||||||
{
|
{
|
||||||
if (screenReader == null)
|
if (screenReader == null)
|
||||||
|
@ -61,10 +60,6 @@ namespace stardew_access
|
||||||
screenReader.Speak(text, interrupt);
|
screenReader.Speak(text, interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).
|
|
||||||
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.</summary>
|
|
||||||
/// <param name="text">The text to be narrated.</param>
|
|
||||||
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
|
||||||
public void SayWithChecker(string text, bool interrupt)
|
public void SayWithChecker(string text, bool interrupt)
|
||||||
{
|
{
|
||||||
if (screenReader == null)
|
if (screenReader == null)
|
||||||
|
@ -77,11 +72,6 @@ namespace stardew_access
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).
|
|
||||||
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
|
||||||
/// <br/><br/>Use this when narrating hovered component in menus to avoid interference.</summary>
|
|
||||||
/// <param name="text">The text to be narrated.</param>
|
|
||||||
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
|
||||||
public void SayWithMenuChecker(string text, bool interrupt)
|
public void SayWithMenuChecker(string text, bool interrupt)
|
||||||
{
|
{
|
||||||
if (screenReader == null)
|
if (screenReader == null)
|
||||||
|
@ -94,11 +84,6 @@ namespace stardew_access
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).
|
|
||||||
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
|
||||||
/// <br/><br/>Use this when narrating chat messages to avoid interference.</summary>
|
|
||||||
/// <param name="text">The text to be narrated.</param>
|
|
||||||
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
|
||||||
public void SayWithChatChecker(string text, bool interrupt)
|
public void SayWithChatChecker(string text, bool interrupt)
|
||||||
{
|
{
|
||||||
if (screenReader == null)
|
if (screenReader == null)
|
||||||
|
@ -111,13 +96,6 @@ namespace stardew_access
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).
|
|
||||||
/// <br/>Skips the text narration if the previously narrated text was the same as the one provided.
|
|
||||||
/// <br/><br/>Use this when narrating texts based on tile position to avoid interference.</summary>
|
|
||||||
/// <param name="text">The text to be narrated.</param>
|
|
||||||
/// <param name="x">The X location of tile.</param>
|
|
||||||
/// <param name="y">The Y location of tile.</param>
|
|
||||||
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
|
|
||||||
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
|
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
|
||||||
{
|
{
|
||||||
if (screenReader == null)
|
if (screenReader == null)
|
Loading…
Reference in New Issue