2
0

Application.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #nullable enable
  2. using System.Diagnostics;
  3. using System.Globalization;
  4. using System.Reflection;
  5. using System.Resources;
  6. using Terminal.Gui.Resources;
  7. namespace Terminal.Gui;
  8. /// <summary>A static, singleton class representing the application. This class is the entry point for the application.</summary>
  9. /// <example>
  10. /// <code>
  11. /// Application.Init();
  12. /// var win = new Window()
  13. /// {
  14. /// Title = $"Example App ({Application.QuitKey} to quit)"
  15. /// };
  16. /// Application.Run(win);
  17. /// win.Dispose();
  18. /// Application.Shutdown();
  19. /// </code>
  20. /// </example>
  21. /// <remarks></remarks>
  22. public static partial class Application
  23. {
  24. /// <summary>Gets all cultures supported by the application without the invariant language.</summary>
  25. public static List<CultureInfo>? SupportedCultures { get; private set; }
  26. internal static List<CultureInfo> GetSupportedCultures ()
  27. {
  28. CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures);
  29. // Get the assembly
  30. var assembly = Assembly.GetExecutingAssembly ();
  31. //Find the location of the assembly
  32. string assemblyLocation = AppDomain.CurrentDomain.BaseDirectory;
  33. // Find the resource file name of the assembly
  34. var resourceFilename = $"{assembly.GetName ().Name}.resources.dll";
  35. if (cultures.Length > 1 && Directory.Exists (Path.Combine (assemblyLocation, "pt-PT")))
  36. {
  37. // Return all culture for which satellite folder found with culture code.
  38. return cultures.Where (
  39. cultureInfo =>
  40. Directory.Exists (Path.Combine (assemblyLocation, cultureInfo.Name))
  41. && File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename))
  42. )
  43. .ToList ();
  44. }
  45. // It's called from a self-contained single-file and get available cultures from the embedded resources strings.
  46. return GetAvailableCulturesFromEmbeddedResources ();
  47. }
  48. internal static List<CultureInfo> GetAvailableCulturesFromEmbeddedResources ()
  49. {
  50. ResourceManager rm = new (typeof (Strings));
  51. CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures);
  52. return cultures.Where (
  53. cultureInfo =>
  54. !cultureInfo.Equals (CultureInfo.InvariantCulture)
  55. && rm.GetResourceSet (cultureInfo, true, false) is { }
  56. )
  57. .ToList ();
  58. }
  59. // IMPORTANT: Ensure all property/fields are reset here. See Init_ResetState_Resets_Properties unit test.
  60. // Encapsulate all setting of initial state for Application; Having
  61. // this in a function like this ensures we don't make mistakes in
  62. // guaranteeing that the state of this singleton is deterministic when Init
  63. // starts running and after Shutdown returns.
  64. internal static void ResetState (bool ignoreDisposed = false)
  65. {
  66. // Shutdown is the bookend for Init. As such it needs to clean up all resources
  67. // Init created. Apps that do any threading will need to code defensively for this.
  68. // e.g. see Issue #537
  69. foreach (Toplevel? t in TopLevels)
  70. {
  71. t!.Running = false;
  72. }
  73. TopLevels.Clear ();
  74. Current = null;
  75. #if DEBUG_IDISPOSABLE
  76. // Don't dispose the Top. It's up to caller dispose it
  77. if (!ignoreDisposed && Top is { })
  78. {
  79. Debug.Assert (Top.WasDisposed);
  80. // If End wasn't called _cachedRunStateToplevel may be null
  81. if (_cachedRunStateToplevel is { })
  82. {
  83. Debug.Assert (_cachedRunStateToplevel.WasDisposed);
  84. Debug.Assert (_cachedRunStateToplevel == Top);
  85. }
  86. }
  87. #endif
  88. Top = null;
  89. _cachedRunStateToplevel = null;
  90. // MainLoop stuff
  91. MainLoop?.Dispose ();
  92. MainLoop = null;
  93. MainThreadId = -1;
  94. Iteration = null;
  95. EndAfterFirstIteration = false;
  96. // Driver stuff
  97. if (Driver is { })
  98. {
  99. Driver.SizeChanged -= Driver_SizeChanged;
  100. Driver.KeyDown -= Driver_KeyDown;
  101. Driver.KeyUp -= Driver_KeyUp;
  102. Driver.MouseEvent -= Driver_MouseEvent;
  103. Driver?.End ();
  104. Driver = null;
  105. }
  106. // Don't reset ForceDriver; it needs to be set before Init is called.
  107. //ForceDriver = string.Empty;
  108. //Force16Colors = false;
  109. _forceFakeConsole = false;
  110. // Run State stuff
  111. NotifyNewRunState = null;
  112. NotifyStopRunState = null;
  113. MouseGrabView = null;
  114. IsInitialized = false;
  115. // Mouse
  116. MouseEnteredView = null;
  117. WantContinuousButtonPressedView = null;
  118. MouseEvent = null;
  119. GrabbedMouse = null;
  120. UnGrabbingMouse = null;
  121. GrabbedMouse = null;
  122. UnGrabbedMouse = null;
  123. // Keyboard
  124. KeyDown = null;
  125. KeyUp = null;
  126. SizeChanging = null;
  127. Navigation = null;
  128. AddApplicationKeyBindings ();
  129. Colors.Reset ();
  130. // Reset synchronization context to allow the user to run async/await,
  131. // as the main loop has been ended, the synchronization context from
  132. // gui.cs does no longer process any callbacks. See #1084 for more details:
  133. // (https://github.com/gui-cs/Terminal.Gui/issues/1084).
  134. SynchronizationContext.SetSynchronizationContext (null);
  135. }
  136. #nullable enable
  137. #nullable restore
  138. #nullable enable
  139. // Only return true if the Current has changed.
  140. #nullable restore
  141. /// <summary>
  142. /// Gets a string representation of the Application as rendered by <see cref="Driver"/>.
  143. /// </summary>
  144. /// <returns>A string representation of the Application </returns>
  145. public new static string ToString ()
  146. {
  147. ConsoleDriver driver = Driver;
  148. if (driver is null)
  149. {
  150. return string.Empty;
  151. }
  152. return ToString (driver);
  153. }
  154. /// <summary>
  155. /// Gets a string representation of the Application rendered by the provided <see cref="ConsoleDriver"/>.
  156. /// </summary>
  157. /// <param name="driver">The driver to use to render the contents.</param>
  158. /// <returns>A string representation of the Application </returns>
  159. public static string ToString (ConsoleDriver driver)
  160. {
  161. var sb = new StringBuilder ();
  162. Cell [,] contents = driver.Contents;
  163. for (var r = 0; r < driver.Rows; r++)
  164. {
  165. for (var c = 0; c < driver.Cols; c++)
  166. {
  167. Rune rune = contents [r, c].Rune;
  168. if (rune.DecodeSurrogatePair (out char [] sp))
  169. {
  170. sb.Append (sp);
  171. }
  172. else
  173. {
  174. sb.Append ((char)rune.Value);
  175. }
  176. if (rune.GetColumns () > 1)
  177. {
  178. c++;
  179. }
  180. // See Issue #2616
  181. //foreach (var combMark in contents [r, c].CombiningMarks) {
  182. // sb.Append ((char)combMark.Value);
  183. //}
  184. }
  185. sb.AppendLine ();
  186. }
  187. return sb.ToString ();
  188. }
  189. }