diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 55f743a..adf1681 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -34,7 +34,8 @@ namespace stardew_access.Game { if (!manuallyTriggered && prevTile != gt) { - MainClass.screenReader.prevTextTile = " "; + if(MainClass.screenReader!=null) + MainClass.screenReader.PrevTextTile = " "; } bool isColliding = isCollidingAtTile(x, y); diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 34709e5..96fddaa 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -168,6 +168,11 @@ namespace stardew_access original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)), 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)) + ); } } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 879f724..dc3a036 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -6,20 +6,10 @@ using HarmonyLib; using stardew_access.Patches; using AutoHotkey.Interop; using System.Runtime.InteropServices; +using stardew_access.ScreenReader; 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 { private Harmony? harmony; @@ -33,7 +23,7 @@ namespace stardew_access AutoHotkeyEngine ahk; public static string hudMessageQueryKey = ""; public static Radar radarFeature; - public static ScreenReader screenReader; + public static ScreenReaderInterface? screenReader; private static IModHelper _modHelper; public static IModHelper ModHelper @@ -41,17 +31,6 @@ namespace stardew_access 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 *********/ @@ -70,8 +49,7 @@ namespace stardew_access if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) InitializeAutoHotKey(); - screenReader = new ScreenReader(); - screenReader.InitializeScreenReader(); + screenReader = new ScreenReaderController().Initialize(); CustomSoundEffects.Initialize(); @@ -82,26 +60,28 @@ namespace stardew_access harmony = new Harmony(ModManifest.UniqueID); HarmonyPatches.Initialize(harmony); - #endregion - - Initialize(); - string text = "Testing"; - Speak(new GoString(text,text.Length), false); - Close(); - - + #endregion helper.Events.Input.ButtonPressed += this.OnButtonPressed; 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(); } /// Returns the Screen Reader class for other mods to use. 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) 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) return; diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 8238bb4..f483152 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -338,6 +338,11 @@ namespace stardew_access.Patches } } + internal static void ExitEventPatch() + { + if(MainClass.screenReader!=null) + MainClass.screenReader.CloseScreenReader(); + } internal static void resetGlobalVars() { currentLetterText = " "; diff --git a/stardew-access/ScreenReader/ScreenReaderController.cs b/stardew-access/ScreenReader/ScreenReaderController.cs new file mode 100644 index 0000000..42e3a02 --- /dev/null +++ b/stardew-access/ScreenReader/ScreenReaderController.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/stardew-access/ScreenReader/ScreenReaderInterface.cs b/stardew-access/ScreenReader/ScreenReaderInterface.cs new file mode 100644 index 0000000..49016cd --- /dev/null +++ b/stardew-access/ScreenReader/ScreenReaderInterface.cs @@ -0,0 +1,50 @@ +namespace stardew_access.ScreenReader +{ + public interface ScreenReaderInterface{ + + public string PrevTextTile{ + get; + set; + } + + /// Initializes the screen reader. + public void InitializeScreenReader(); + + // Closes the screen reader, this is important, call this function when closing the game. + public void CloseScreenReader(); + + /// Speaks the text via the loaded screen reader (if any). + /// The text to be narrated. + /// Whether to skip the currently speaking text or not. + public void Say(string text, bool interrupt); + + /// Speaks the text via the loaded screen reader (if any). + ///
Skips the text narration if the previously narrated text was the same as the one provided.
+ /// The text to be narrated. + /// Whether to skip the currently speaking text or not. + public void SayWithChecker(string text, bool interrupt); + + /// Speaks the text via the loaded screen reader (if any). + ///
Skips the text narration if the previously narrated text was the same as the one provided. + ///

Use this when narrating hovered component in menus to avoid interference.
+ /// The text to be narrated. + /// Whether to skip the currently speaking text or not. + public void SayWithMenuChecker(string text, bool interrupt); + + /// Speaks the text via the loaded screen reader (if any). + ///
Skips the text narration if the previously narrated text was the same as the one provided. + ///

Use this when narrating chat messages to avoid interference.
+ /// The text to be narrated. + /// Whether to skip the currently speaking text or not. + public void SayWithChatChecker(string text, bool interrupt); + + /// Speaks the text via the loaded screen reader (if any). + ///
Skips the text narration if the previously narrated text was the same as the one provided. + ///

Use this when narrating texts based on tile position to avoid interference.
+ /// The text to be narrated. + /// The X location of tile. + /// The Y location of tile. + /// Whether to skip the currently speaking text or not. + public void SayWithTileQuery(string text, int x, int y, bool interrupt); + } +} \ No newline at end of file diff --git a/stardew-access/ScreenReader/ScreenReaderLinux.cs b/stardew-access/ScreenReader/ScreenReaderLinux.cs new file mode 100644 index 0000000..e31fc2d --- /dev/null +++ b/stardew-access/ScreenReader/ScreenReaderLinux.cs @@ -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); + } + } + } +} diff --git a/stardew-access/ScreenReader.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs similarity index 51% rename from stardew-access/ScreenReader.cs rename to stardew-access/ScreenReader/ScreenReaderWindows.cs index f63ce14..2577e65 100644 --- a/stardew-access/ScreenReader.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -1,19 +1,19 @@ 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 string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = ""; - /// Initializes the screen reader. + public string PrevTextTile{ + get{ return prevTextTile; } + set{ prevTextTile=value; } + } + public void InitializeScreenReader() { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return; NvdaOutput? nvdaOutput = null; JawsOutput? jawsOutput = null; @@ -46,13 +46,12 @@ namespace stardew_access screenReader = jawsOutput; else if (sapiOutput != null && sapiOutput.IsAvailable()) screenReader = sapiOutput; - else - MainClass.monitor.Log($"Unable to load any screen reader!", LogLevel.Error); } - /// Speaks the text via the loaded screen reader (if any). - /// The text to be narrated. - /// Whether to skip the currently speaking text or not. + public void CloseScreenReader(){ + + } + public void Say(string text, bool interrupt) { if (screenReader == null) @@ -61,10 +60,6 @@ namespace stardew_access screenReader.Speak(text, interrupt); } - /// Speaks the text via the loaded screen reader (if any). - ///
Skips the text narration if the previously narrated text was the same as the one provided.
- /// The text to be narrated. - /// Whether to skip the currently speaking text or not. public void SayWithChecker(string text, bool interrupt) { if (screenReader == null) @@ -77,11 +72,6 @@ namespace stardew_access } } - /// Speaks the text via the loaded screen reader (if any). - ///
Skips the text narration if the previously narrated text was the same as the one provided. - ///

Use this when narrating hovered component in menus to avoid interference.
- /// The text to be narrated. - /// Whether to skip the currently speaking text or not. public void SayWithMenuChecker(string text, bool interrupt) { if (screenReader == null) @@ -94,11 +84,6 @@ namespace stardew_access } } - /// Speaks the text via the loaded screen reader (if any). - ///
Skips the text narration if the previously narrated text was the same as the one provided. - ///

Use this when narrating chat messages to avoid interference.
- /// The text to be narrated. - /// Whether to skip the currently speaking text or not. public void SayWithChatChecker(string text, bool interrupt) { if (screenReader == null) @@ -110,14 +95,7 @@ namespace stardew_access Say(text, interrupt); } } - - /// Speaks the text via the loaded screen reader (if any). - ///
Skips the text narration if the previously narrated text was the same as the one provided. - ///

Use this when narrating texts based on tile position to avoid interference.
- /// The text to be narrated. - /// The X location of tile. - /// The Y location of tile. - /// Whether to skip the currently speaking text or not. + public void SayWithTileQuery(string text, int x, int y, bool interrupt) { if (screenReader == null)