diff --git a/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs b/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs index 02fa444..e5cd2c0 100644 --- a/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs +++ b/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace DesktopMagic.BuiltInPlugins; -public class WeatherPlugin : Plugin +public class WeatherPlugin : AsyncPlugin { [Setting("city-name", "City Name")] private readonly TextBox cityInput = new TextBox("Berlin"); @@ -74,12 +74,12 @@ public class WeatherPlugin : Plugin }; } - public override Bitmap Main() + public override async Task MainAsync() { // Periodic background update if (!isLoading && cachedLat != null && cachedLon != null) { - _ = UpdateWeatherOnly(cachedLat, cachedLon); + await UpdateWeatherOnly(cachedLat, cachedLon); } Bitmap bmp = new Bitmap(1200, 800); diff --git a/src/DesktopMagic/GlobalSuppressions.cs b/src/DesktopMagic/GlobalSuppressions.cs index 18fd782..a76efbd 100644 --- a/src/DesktopMagic/GlobalSuppressions.cs +++ b/src/DesktopMagic/GlobalSuppressions.cs @@ -12,3 +12,4 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Code Smell", "S3885:\"Assembly.Load\" should be used", Justification = "Assembly.Load does not load dependencies", Scope = "member", Target = "~M:DesktopMagic.PluginWindow.ExecuteSource")] [assembly: SuppressMessage("Minor Code Smell", "S2325:Methods and properties that don't access instance data should be static", Justification = "", Scope = "member", Target = "~M:DesktopMagic.MainWindow.OpenPluginsFolderButton_Click(System.Object,System.Windows.RoutedEventArgs)")] [assembly: SuppressMessage("Minor Code Smell", "S2325:Methods and properties that don't access instance data should be static", Justification = "", Scope = "member", Target = "~M:DesktopMagic.MainWindow.ScrollViewer_PreviewMouseWheel(System.Object,System.Windows.Input.MouseWheelEventArgs)")] +[assembly: SuppressMessage("Major Code Smell", "S6966:Awaitable method should be used", Justification = "Breaks stuff", Scope = "member", Target = "~M:DesktopMagic.PluginWindow.UpdateTimer_Elapsed(System.Object,System.Timers.ElapsedEventArgs)")] diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs index 300fc85..2929afe 100644 --- a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs @@ -401,13 +401,22 @@ public partial class PluginWindow : Window, IPluginWindow } } - private void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs? e) + private async void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs? e) { try { if (IsRunning && pluginClassInstance is not null) { - Bitmap? result = pluginClassInstance.Main(); + Bitmap? result; + + if (pluginClassInstance is AsyncPlugin asyncPlugin) + { + result = await asyncPlugin.MainAsync(); + } + else + { + result = pluginClassInstance.Main(); + } if (pluginClassInstance.UpdateInterval > 0) { diff --git a/src/DesktopMagicPluginAPI/AsyncPlugin.cs b/src/DesktopMagicPluginAPI/AsyncPlugin.cs new file mode 100644 index 0000000..f7897b9 --- /dev/null +++ b/src/DesktopMagicPluginAPI/AsyncPlugin.cs @@ -0,0 +1,34 @@ +using System; +using System.Drawing; +using System.Threading.Tasks; + +namespace DesktopMagic.Api; + +/// +/// Provides an abstract base class for plugins that execute asynchronously. Derive from this class to implement plugins +/// whose main logic runs in a non-blocking manner using asynchronous operations. +/// +/// AsyncPlugin is intended for scenarios where plugin execution should not block the calling thread. +/// Override the MainAsync method to implement asynchronous plugin behavior. The synchronous Main method is sealed and +/// obsolete; it cannot be used for execution and will always throw an exception. Use MainAsync for all plugin logic. +/// The MainAsync method is typically invoked when the UpdateInterval elapses, allowing periodic asynchronous +/// execution. +public abstract class AsyncPlugin : Plugin +{ + /// + /// Occurs when the elapses. + /// + /// + public abstract Task MainAsync(); + + /// + /// This method should not be called directly! Override and use for asynchronous plugin operations. + /// + /// This method only exists to fulfill base class requirements. + /// Calling it will always result in an . + /// Thrown if this method is called directly. Use MainAsync for plugin execution instead. + public sealed override Bitmap? Main() + { + throw new InvalidOperationException($"AsyncPlugin.Main() should not be called directly. Override {nameof(MainAsync)} instead."); + } +} \ No newline at end of file