mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Core - Reworked profile render override for the editor and new previewer
This commit is contained in:
1 parent
c6a318b6e3
commit
d2b8123a30
42 files changed
+812
-399
No files matched your search
@@ -17,13 +17,12 @@ public sealed class Profile : ProfileElement
|
||||
private readonly ObservableCollection<ScriptConfiguration> _scriptConfigurations;
|
||||
private readonly ObservableCollection<ProfileScript> _scripts;
|
||||
private bool _isFreshImport;
|
||||
private ProfileElement? _lastSelectedProfileElement;
|
||||
|
||||
internal Profile(ProfileConfiguration configuration, ProfileEntity profileEntity) : base(null!)
|
||||
{
|
||||
_scripts = new ObservableCollection<ProfileScript>();
|
||||
_scriptConfigurations = new ObservableCollection<ScriptConfiguration>();
|
||||
|
||||
|
||||
Opacity = 0d;
|
||||
ShouldDisplay = true;
|
||||
Configuration = configuration;
|
||||
@@ -67,15 +66,6 @@ public sealed class Profile : ProfileElement
|
||||
set => SetAndNotify(ref _isFreshImport, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last selected profile element of this profile
|
||||
/// </summary>
|
||||
public ProfileElement? LastSelectedProfileElement
|
||||
{
|
||||
get => _lastSelectedProfileElement;
|
||||
set => SetAndNotify(ref _lastSelectedProfileElement, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile entity this profile uses for persistent storage
|
||||
/// </summary>
|
||||
@@ -105,7 +95,7 @@ public sealed class Profile : ProfileElement
|
||||
profileScript.OnProfileUpdated(deltaTime);
|
||||
|
||||
const double OPACITY_PER_SECOND = 1;
|
||||
|
||||
|
||||
if (ShouldDisplay && Opacity < 1)
|
||||
Opacity = Math.Clamp(Opacity + OPACITY_PER_SECOND * deltaTime, 0d, 1d);
|
||||
if (!ShouldDisplay && Opacity > 0)
|
||||
@@ -123,14 +113,14 @@ public sealed class Profile : ProfileElement
|
||||
|
||||
foreach (ProfileScript profileScript in Scripts)
|
||||
profileScript.OnProfileRendering(canvas, canvas.LocalClipBounds);
|
||||
|
||||
|
||||
SKPaint? opacityPaint = null;
|
||||
bool applyOpacityLayer = Configuration.FadeInAndOut && Opacity < 1;
|
||||
|
||||
|
||||
if (applyOpacityLayer)
|
||||
{
|
||||
opacityPaint = new SKPaint();
|
||||
opacityPaint.Color = new SKColor(0, 0, 0, (byte)(255d * Easings.CubicEaseInOut(Opacity)));
|
||||
opacityPaint.Color = new SKColor(0, 0, 0, (byte) (255d * Easings.CubicEaseInOut(Opacity)));
|
||||
canvas.SaveLayer(opacityPaint);
|
||||
}
|
||||
|
||||
@@ -242,20 +232,13 @@ public sealed class Profile : ProfileElement
|
||||
AddChild(new Folder(this, this, rootFolder));
|
||||
}
|
||||
|
||||
List<RenderProfileElement> renderElements = GetAllRenderElements();
|
||||
|
||||
if (ProfileEntity.LastSelectedProfileElement != Guid.Empty)
|
||||
LastSelectedProfileElement = renderElements.FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
|
||||
else
|
||||
LastSelectedProfileElement = null;
|
||||
|
||||
while (_scriptConfigurations.Any())
|
||||
RemoveScriptConfiguration(_scriptConfigurations[0]);
|
||||
foreach (ScriptConfiguration scriptConfiguration in ProfileEntity.ScriptConfigurations.Select(e => new ScriptConfiguration(e)))
|
||||
AddScriptConfiguration(scriptConfiguration);
|
||||
|
||||
// Load node scripts last since they may rely on the profile structure being in place
|
||||
foreach (RenderProfileElement renderProfileElement in renderElements)
|
||||
foreach (RenderProfileElement renderProfileElement in GetAllRenderElements())
|
||||
renderProfileElement.LoadNodeScript();
|
||||
}
|
||||
|
||||
@@ -312,7 +295,6 @@ public sealed class Profile : ProfileElement
|
||||
ProfileEntity.Id = EntityId;
|
||||
ProfileEntity.Name = Configuration.Name;
|
||||
ProfileEntity.IsFreshImport = IsFreshImport;
|
||||
ProfileEntity.LastSelectedProfileElement = LastSelectedProfileElement?.EntityId ?? Guid.Empty;
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Save();
|
||||
|
||||
@@ -147,16 +147,7 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
|
||||
get => _activationConditionMet;
|
||||
private set => SetAndNotify(ref _activationConditionMet, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this profile configuration is being edited
|
||||
/// </summary>
|
||||
public bool IsBeingEdited
|
||||
{
|
||||
get => _isBeingEdited;
|
||||
set => SetAndNotify(ref _isBeingEdited, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile of this profile configuration
|
||||
/// </summary>
|
||||
@@ -243,8 +234,6 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ProfileConfiguration");
|
||||
if (IsBeingEdited)
|
||||
return true;
|
||||
if (Category.IsSuspended || IsSuspended || IsMissingModule)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -26,19 +26,26 @@ public interface IProfileService : IArtemisService
|
||||
ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether hotkeys are enabled.
|
||||
/// Gets or sets the focused profile configuration which is rendered exclusively.
|
||||
/// </summary>
|
||||
bool HotkeysEnabled { get; set; }
|
||||
ProfileConfiguration? FocusProfile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether rendering should only be done for profiles being edited.
|
||||
/// Gets or sets the profile element which is rendered exclusively.
|
||||
/// </summary>
|
||||
bool RenderForEditor { get; set; }
|
||||
ProfileElement? FocusProfileElement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the profile element to focus on while rendering for the editor.
|
||||
/// Gets or sets a value indicating whether the currently focused profile should receive updates.
|
||||
/// </summary>
|
||||
ProfileElement? EditorFocus { get; set; }
|
||||
bool UpdateFocusProfile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the provided profile configuration.
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration to clone.</param>
|
||||
/// <returns>The resulting clone.</returns>
|
||||
ProfileConfiguration CloneProfileConfiguration(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Activates the profile of the given <see cref="ProfileConfiguration" /> with the currently active surface.
|
||||
|
||||
@@ -15,14 +15,14 @@ namespace Artemis.Core.Services;
|
||||
internal class ProfileService : IProfileService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
|
||||
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly List<ProfileCategory> _profileCategories;
|
||||
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly List<Exception> _renderExceptions = new();
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
private DateTime _lastRenderExceptionLog;
|
||||
private DateTime _lastUpdateExceptionLog;
|
||||
@@ -45,7 +45,6 @@ internal class ProfileService : IProfileService
|
||||
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
|
||||
HotkeysEnabled = true;
|
||||
inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
||||
|
||||
if (!_profileCategories.Any())
|
||||
@@ -53,140 +52,21 @@ internal class ProfileService : IProfileService
|
||||
UpdateModules();
|
||||
}
|
||||
|
||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
if (!HotkeysEnabled)
|
||||
return;
|
||||
|
||||
lock (_profileCategories)
|
||||
{
|
||||
_pendingKeyboardEvents.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates all missing LEDs on all currently active profiles
|
||||
/// </summary>
|
||||
private void ActiveProfilesPopulateLeds()
|
||||
{
|
||||
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
||||
{
|
||||
if (profileConfiguration.Profile == null) continue;
|
||||
profileConfiguration.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
|
||||
if (!profileConfiguration.Profile.IsFreshImport) continue;
|
||||
_logger.Debug("Profile is a fresh import, adapting to surface - {profile}", profileConfiguration.Profile);
|
||||
AdaptProfile(profileConfiguration.Profile);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateModules()
|
||||
{
|
||||
lock (_profileRepository)
|
||||
{
|
||||
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>();
|
||||
foreach (ProfileCategory profileCategory in _profileCategories)
|
||||
{
|
||||
foreach (ProfileConfiguration profileConfiguration in profileCategory.ProfileConfigurations)
|
||||
profileConfiguration.LoadModules(modules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ActiveProfilesPopulateLeds();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureToggled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature is Module)
|
||||
UpdateModules();
|
||||
}
|
||||
|
||||
private void ProcessPendingKeyEvents(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.None)
|
||||
return;
|
||||
|
||||
bool before = profileConfiguration.IsSuspended;
|
||||
foreach (ArtemisKeyboardKeyEventArgs e in _pendingKeyboardEvents)
|
||||
{
|
||||
if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.Toggle)
|
||||
{
|
||||
if (profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = !profileConfiguration.IsSuspended;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (profileConfiguration.IsSuspended && profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = false;
|
||||
else if (!profileConfiguration.IsSuspended && profileConfiguration.DisableHotkey != null && profileConfiguration.DisableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If suspension was changed, save the category
|
||||
if (before != profileConfiguration.IsSuspended)
|
||||
SaveProfileCategory(profileConfiguration.Category);
|
||||
}
|
||||
|
||||
private void CreateDefaultProfileCategories()
|
||||
{
|
||||
foreach (DefaultCategoryName defaultCategoryName in Enum.GetValues<DefaultCategoryName>())
|
||||
CreateProfileCategory(defaultCategoryName.ToString());
|
||||
}
|
||||
|
||||
private void LogProfileUpdateExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastUpdateExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastUpdateExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_updateExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||
{
|
||||
_logger.Warning(exceptions.First(),
|
||||
"Exception was thrown {count} times during profile update in the last 10 seconds",
|
||||
exceptions.Count());
|
||||
}
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_updateExceptions.Clear();
|
||||
}
|
||||
|
||||
private void LogProfileRenderExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastRenderExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastRenderExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_renderExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _renderExceptions.GroupBy(e => e.StackTrace))
|
||||
{
|
||||
_logger.Warning(exceptions.First(),
|
||||
"Exception was thrown {count} times during profile render in the last 10 seconds",
|
||||
exceptions.Count());
|
||||
}
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_renderExceptions.Clear();
|
||||
}
|
||||
|
||||
public bool HotkeysEnabled { get; set; }
|
||||
public bool RenderForEditor { get; set; }
|
||||
public ProfileElement? EditorFocus { get; set; }
|
||||
public ProfileConfiguration? FocusProfile { get; set; }
|
||||
public ProfileElement? FocusProfileElement { get; set; }
|
||||
public bool UpdateFocusProfile { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateProfiles(double deltaTime)
|
||||
{
|
||||
// If there is a focus profile update only that, and only if UpdateFocusProfile is true
|
||||
if (FocusProfile != null)
|
||||
{
|
||||
if (UpdateFocusProfile)
|
||||
FocusProfile.Profile?.Update(deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_profileCategories)
|
||||
{
|
||||
// Iterate the children in reverse because the first category must be rendered last to end up on top
|
||||
@@ -200,16 +80,11 @@ internal class ProfileService : IProfileService
|
||||
// Process hotkeys that where pressed since this profile last updated
|
||||
ProcessPendingKeyEvents(profileConfiguration);
|
||||
|
||||
// Profiles being edited are updated at their own leisure
|
||||
if (profileConfiguration.IsBeingEdited && RenderForEditor)
|
||||
continue;
|
||||
|
||||
bool shouldBeActive = profileConfiguration.ShouldBeActive(false);
|
||||
if (shouldBeActive)
|
||||
{
|
||||
profileConfiguration.Update();
|
||||
if (!profileConfiguration.IsBeingEdited)
|
||||
shouldBeActive = profileConfiguration.ActivationConditionMet;
|
||||
shouldBeActive = profileConfiguration.ActivationConditionMet;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -243,20 +118,18 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RenderProfiles(SKCanvas canvas)
|
||||
{
|
||||
// If there is a focus profile, render only that
|
||||
if (FocusProfile != null)
|
||||
{
|
||||
FocusProfile.Profile?.Render(canvas, SKPointI.Empty, FocusProfileElement);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_profileCategories)
|
||||
{
|
||||
ProfileConfiguration? editedProfileConfiguration = _profileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(p => p.IsBeingEdited);
|
||||
if (editedProfileConfiguration != null)
|
||||
{
|
||||
editedProfileConfiguration.Profile?.Render(canvas, SKPointI.Empty, RenderForEditor ? EditorFocus : null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RenderForEditor)
|
||||
return;
|
||||
|
||||
// Iterate the children in reverse because the first category must be rendered last to end up on top
|
||||
for (int i = _profileCategories.Count - 1; i > -1; i--)
|
||||
{
|
||||
@@ -282,6 +155,7 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<ProfileCategory> ProfileCategories
|
||||
{
|
||||
get
|
||||
@@ -293,6 +167,7 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations
|
||||
{
|
||||
get
|
||||
@@ -304,6 +179,7 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.Icon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
@@ -314,6 +190,7 @@ internal class ProfileService : IProfileService
|
||||
profileConfiguration.Icon.SetIconByStream(profileConfiguration.Entity.IconOriginalFileName, stream);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.Icon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
@@ -327,6 +204,13 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileConfiguration CloneProfileConfiguration(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
return new ProfileConfiguration(profileConfiguration.Category, profileConfiguration.Entity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Profile ActivateProfile(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.Profile != null)
|
||||
@@ -364,9 +248,10 @@ internal class ProfileService : IProfileService
|
||||
return profile;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeactivateProfile(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.IsBeingEdited)
|
||||
if (FocusProfile == profileConfiguration)
|
||||
throw new ArtemisCoreException("Cannot disable a profile that is being edited, that's rude");
|
||||
if (profileConfiguration.Profile == null)
|
||||
return;
|
||||
@@ -378,9 +263,10 @@ internal class ProfileService : IProfileService
|
||||
OnProfileDeactivated(new ProfileConfigurationEventArgs(profileConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RequestDeactivation(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.IsBeingEdited)
|
||||
if (FocusProfile == profileConfiguration)
|
||||
throw new ArtemisCoreException("Cannot disable a profile that is being edited, that's rude");
|
||||
if (profileConfiguration.Profile == null)
|
||||
return;
|
||||
@@ -388,6 +274,7 @@ internal class ProfileService : IProfileService
|
||||
profileConfiguration.Profile.ShouldDisplay = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteProfile(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
DeactivateProfile(profileConfiguration);
|
||||
@@ -401,6 +288,7 @@ internal class ProfileService : IProfileService
|
||||
SaveProfileCategory(profileConfiguration.Category);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileCategory CreateProfileCategory(string name)
|
||||
{
|
||||
ProfileCategory profileCategory;
|
||||
@@ -415,6 +303,7 @@ internal class ProfileService : IProfileService
|
||||
return profileCategory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteProfileCategory(ProfileCategory profileCategory)
|
||||
{
|
||||
List<ProfileConfiguration> profileConfigurations = profileCategory.ProfileConfigurations.ToList();
|
||||
@@ -430,6 +319,7 @@ internal class ProfileService : IProfileService
|
||||
OnProfileCategoryRemoved(new ProfileCategoryEventArgs(profileCategory));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon)
|
||||
{
|
||||
ProfileConfiguration configuration = new(category, name, icon);
|
||||
@@ -441,6 +331,7 @@ internal class ProfileService : IProfileService
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
profileConfiguration.Category.RemoveProfileConfiguration(profileConfiguration);
|
||||
@@ -454,6 +345,7 @@ internal class ProfileService : IProfileService
|
||||
profileConfiguration.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveProfileCategory(ProfileCategory profileCategory)
|
||||
{
|
||||
profileCategory.Save();
|
||||
@@ -465,6 +357,7 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveProfile(Profile profile, bool includeChildren)
|
||||
{
|
||||
_logger.Debug("Updating profile - Saving {Profile}", profile);
|
||||
@@ -480,6 +373,7 @@ internal class ProfileService : IProfileService
|
||||
_profileRepository.Save(profile.ProfileEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileConfigurationExportModel ExportProfile(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
// The profile may not be active and in that case lets activate it real quick
|
||||
@@ -493,6 +387,7 @@ internal class ProfileService : IProfileService
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileConfiguration ImportProfile(ProfileCategory category, ProfileConfigurationExportModel exportModel,
|
||||
bool makeUnique, bool markAsFreshImport, string? nameAffix)
|
||||
{
|
||||
@@ -565,6 +460,131 @@ internal class ProfileService : IProfileService
|
||||
_profileRepository.Save(profile.ProfileEntity);
|
||||
}
|
||||
|
||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
lock (_profileCategories)
|
||||
{
|
||||
_pendingKeyboardEvents.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates all missing LEDs on all currently active profiles
|
||||
/// </summary>
|
||||
private void ActiveProfilesPopulateLeds()
|
||||
{
|
||||
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
||||
{
|
||||
if (profileConfiguration.Profile == null) continue;
|
||||
profileConfiguration.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
|
||||
if (!profileConfiguration.Profile.IsFreshImport) continue;
|
||||
_logger.Debug("Profile is a fresh import, adapting to surface - {profile}", profileConfiguration.Profile);
|
||||
AdaptProfile(profileConfiguration.Profile);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateModules()
|
||||
{
|
||||
lock (_profileRepository)
|
||||
{
|
||||
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>();
|
||||
foreach (ProfileCategory profileCategory in _profileCategories)
|
||||
{
|
||||
foreach (ProfileConfiguration profileConfiguration in profileCategory.ProfileConfigurations)
|
||||
profileConfiguration.LoadModules(modules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ActiveProfilesPopulateLeds();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureToggled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature is Module)
|
||||
UpdateModules();
|
||||
}
|
||||
|
||||
private void ProcessPendingKeyEvents(ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.None)
|
||||
return;
|
||||
|
||||
bool before = profileConfiguration.IsSuspended;
|
||||
foreach (ArtemisKeyboardKeyEventArgs e in _pendingKeyboardEvents)
|
||||
{
|
||||
if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.Toggle)
|
||||
{
|
||||
if (profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = !profileConfiguration.IsSuspended;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (profileConfiguration.IsSuspended && profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = false;
|
||||
else if (!profileConfiguration.IsSuspended && profileConfiguration.DisableHotkey != null && profileConfiguration.DisableHotkey.MatchesEventArgs(e))
|
||||
profileConfiguration.IsSuspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If suspension was changed, save the category
|
||||
if (before != profileConfiguration.IsSuspended)
|
||||
SaveProfileCategory(profileConfiguration.Category);
|
||||
}
|
||||
|
||||
private void CreateDefaultProfileCategories()
|
||||
{
|
||||
foreach (DefaultCategoryName defaultCategoryName in Enum.GetValues<DefaultCategoryName>())
|
||||
CreateProfileCategory(defaultCategoryName.ToString());
|
||||
}
|
||||
|
||||
private void LogProfileUpdateExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastUpdateExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastUpdateExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_updateExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||
{
|
||||
_logger.Warning(exceptions.First(),
|
||||
"Exception was thrown {count} times during profile update in the last 10 seconds",
|
||||
exceptions.Count());
|
||||
}
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_updateExceptions.Clear();
|
||||
}
|
||||
|
||||
private void LogProfileRenderExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastRenderExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastRenderExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_renderExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _renderExceptions.GroupBy(e => e.StackTrace))
|
||||
{
|
||||
_logger.Warning(exceptions.First(),
|
||||
"Exception was thrown {count} times during profile render in the last 10 seconds",
|
||||
exceptions.Count());
|
||||
}
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_renderExceptions.Clear();
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileActivated;
|
||||
|
||||
Reference in new issue
Block a user