add pages for new UI, fix issues

This commit is contained in:
Vincent Maas committed 2024-08-02 13:47:03 +02:00
1 parent 9a9424f1af
commit 4ed670c01b
159 files changed
+18672 -9709

No files matched your search

+5 -3
View File
@@ -1,10 +1,11 @@
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Services;
namespace ColorControl.UI;
public static class Blazor
{
public static void Start()
public static void Start(Config? config = null)
{
var builder = WebApplication.CreateBuilder();
@@ -12,8 +13,9 @@ public static class Blazor
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddSingleton<AppState>();
builder.Services.AddSingleton<OptionsService>();
builder.Services.AddSingleton<AppState>(new AppState { SelectedTheme = (config?.UseDarkMode ?? true) ? "dark" : "light" });
builder.Services.AddTransient<RpcUiClientService>();
builder.WebHost.ConfigureKestrel(o => o.ListenAnyIP(config?.UiPort > 0 ? config.UiPort : 5000));
var app = builder.Build();
+44 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
@@ -7,8 +7,51 @@
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.2.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="Components\Pages\Generic\FieldDefs.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Generic\ConfirmModal.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Game\GameSummary.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Generic\TriggerInput.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Info.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\LG\LgExecuteActionModal.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Samsung\SamsungSummary.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Nvidia\NvidiaSettings.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Nvidia\NvidiaPreset.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Samsung\SamsungDevicePage.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Generic\StepsInput.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Components\Pages\Samsung\SamsungServiceMenu.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
</Project>
+18 -3
View File
@@ -1,4 +1,8 @@
@inject AppState AppState
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services
@inject AppState AppState
@inject RpcUiClientService ClientService;
@inject NavigationManager _navigationManager;
@implements IDisposable
@attribute [StreamRendering]
@@ -9,7 +13,8 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="ColorControl.UI.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
@@ -23,8 +28,18 @@
</html>
@code {
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
var config = await ClientService.CallAsync<Config>("OptionsService", "GetConfig");
var newTheme = config.UseDarkMode ? "dark" : "light";
if (newTheme != AppState.SelectedTheme)
{
AppState.SelectedTheme = config.UseDarkMode ? "dark" : "light";
_navigationManager.Refresh();
}
AppState.OnChange += StateHasChanged;
}
+44 -14
View File
@@ -1,4 +1,13 @@
<div class="top-row ps-3 navbar navbar-dark">
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">ColorControl Web App</a>
</div>
@@ -9,29 +18,50 @@
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<NavLink class="nav-link px-3" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
@if (modules != null)
{
@foreach (var module in modules.Where(m => m.IsActive))
{
<div class="nav-item px-3">
<NavLink class="nav-link px-3" href="@module.Link">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> @module.DisplayName
</NavLink>
</div>
}
}
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="options">
<NavLink class="nav-link px-3" href="options">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Options
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link px-3" href="logging">
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Logging
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link px-3" href="info">
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Info
</NavLink>
</div>
</nav>
</div>
@code {
private Config? config;
private List<ModuleDto>? modules;
protected override async Task OnInitializedAsync()
{
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
modules = config?.Modules?.Select(m => new ModuleDto { DisplayName = m.DisplayName, IsActive = m.IsActive, Link = m.DisplayName.Split(' ')[0] }).ToList();
}
}
@@ -46,6 +46,10 @@
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.bi-list-logging-nav-menu {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-card-text" viewBox="0 0 16 16"><path d="M14.5 3a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2z"/><path d="M3 5.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5M3 8a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 8m0 2.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5"/></svg>');
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
@@ -1,11 +1,13 @@
@inherits LayoutComponentBase
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services
@inherits LayoutComponentBase
@inject AppState AppState
@inject NavigationManager _navigationManager;
@rendermode InteractiveServer
<div class="top-row px-4 pt-2">
<button type="button" class="btn btn-primary me-2" @onclick="ChangeTheme">@ThemeTransitionName</button>
<div class="top-row px-4 pt-3 text-end">
@* <button type="button" class="btn btn-primary me-2" @onclick="ChangeTheme">@ThemeTransitionName</button> *@
<a href="https://github.com/Maassoft/ColorControl" target="_blank">GitHub</a>
</div>
@@ -0,0 +1,391 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.AMD
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
@if (!Preset.IsDisplayPreset)
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" type="text" @bind="Preset.name" required />
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
<label class="form-check-label" for="showInQuickAccess">
Show in Quick Access
</label>
</div>
<ShortcutInput Label="Shortcut" @bind-Shortcut="Preset.shortcut" />
}
<div class="accordion accordion-flush" id="presetAccordion">
@if (!Preset.IsDisplayPreset)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem; visibility: hidden" type="checkbox">
}
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
Display
</button>
</span>
</h2>
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.primaryDisplay" id="primaryDisplay">
<label class="form-check-label" for="primaryDisplay">
Primary display
</label>
</div>
@if (displayPresets != null)
{
<div class="mb-2">
<label class="form-label" for="mode">Display</label>
<select class="form-select" id="mode" @bind="Preset.DisplayId" disabled="@(Preset.primaryDisplay)">
@foreach (var display in displayPresets)
{
<option value="@display.DisplayId">@display.displayName</option>
}
</select>
</div>
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.DisplayConfig.IsPrimary" id="setAsPrimaryDisplay" disabled="@(Preset.primaryDisplay)">
<label class="form-check-label" for="setAsPrimaryDisplay">
Set display as primary display
</label>
</div>
}
</div>
</div>
</div>
}
@if (resolutions != null)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.DisplayConfig.ApplyResolution" id="applyResolution">
}
<button class="accordion-button py-2 @(Preset.DisplayConfig.ApplyResolution ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#resolutionSettings">
Resolution
</button>
</span>
</h2>
<div id="resolutionSettings" class="accordion-collapse collapse @(Preset.IsDisplayPreset ? "show" : "")" data-bs-parent="#presetAccordion">
<div class="accordion-body">
@if (resolutions != null)
{
<div class="mb-2">
<label class="form-label" for="activeResolution">Active resolution</label>
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution">
@foreach (var resolution in resolutions)
{
<option value="@resolution.GetActiveString()">@resolution.GetActiveString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="virtualResolution">Virtual resolution</label>
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution">
@foreach (var resolution in resolutions)
{
<option value="@resolution.GetVirtualString()">@resolution.GetVirtualString()</option>
}
</select>
</div>
}
<div class="mb-2">
<label class="form-label" for="scaling">Scaling/Aspect ratio</label>
<select class="form-select" id="scaling" @bind="Preset.DisplayConfig.Scaling">
@foreach (var scaling in Enum.GetValues<Shared.Native.CCD.DisplayConfigScaling>())
{
<option value="@scaling.ToString()">@scaling.ToString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="rotation">Rotation</label>
<select class="form-select" id="rotation" @bind="Preset.DisplayConfig.Rotation">
@foreach (var rotation in Enum.GetValues<Shared.Native.CCD.DisplayConfigRotation>())
{
<option value="@rotation.ToString()">@rotation.ToString()</option>
}
</select>
</div>
</div>
</div>
</div>
}
@if (refreshRates != null)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.DisplayConfig.ApplyRefreshRate" id="applyRefreshRate">
}
<button class="accordion-button collapsed py-2 @(Preset.DisplayConfig.ApplyRefreshRate ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#refreshRateSettings">
Refresh Rate
</button>
</span>
</h2>
<div id="refreshRateSettings" class="accordion-collapse collapse show" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="refreshRate">Refresh Rate</label>
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate">
@foreach (var refreshRate in refreshRates)
{
<option value="@refreshRate.ToString()">@refreshRate.ToString() Hz</option>
}
</select>
</div>
</div>
</div>
</div>
}
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyColorData" id="applyColorData">
}
<button class="accordion-button collapsed py-2 @(Preset.applyColorData ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#colorSettings">
Color settings
</button>
</span>
</h2>
<div id="colorSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="bitDepth">Bit depth</label>
<select class="form-select" id="bitDepth" @bind="Preset.colorDepth">
@foreach (var bitDepth in Enum.GetValues<ADLColorDepth>())
{
<option value="@bitDepth.ToString()">@bitDepth.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="colorFormat">Format</label>
<select class="form-select" id="colorFormat" @bind="Preset.pixelFormat">
@foreach (var format in Enum.GetValues<ADLPixelFormat>())
{
<option value="@format.ToString()">@format.ToString()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyDithering" id="applyDithering">
}
<button class="accordion-button collapsed py-2 @(Preset.applyDithering ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#ditherSettings">
Dithering
</button>
</span>
</h2>
<div id="ditherSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="state">State</label>
<select class="form-select" id="state" @bind="Preset.ditherState">
@foreach (var state in Enum.GetValues<ADLDitherState>())
{
<option value="@state.ToString()">@state.GetDescription()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyHDR" id="applyHDR">
}
<button class="accordion-button collapsed py-2 @(Preset.applyHDR ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#hdrSettings">
HDR
</button>
</span>
</h2>
<div id="hdrSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.HDREnabled" id="hdrEnabled">
<label class="form-check-label" for="hdrEnabled">
HDR enabled
</label>
</div>
@if (!Preset.IsDisplayPreset) {
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.toggleHDR" id="toggleHdr">
<label class="form-check-label" for="toggleHdr">
Toggle HDR
</label>
</div>
}
<div class="mb-2">
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
</div>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">@(Preset?.IsDisplayPreset == true ? "Apply" : "Save")</button>
</div>
</div>
</div>
</div>
@code {
public AmdPreset? Preset { get; set; }
[Parameter]
public AmdPreset? PresetParam { get; set; }
[Parameter]
public Func<AmdPreset, Task>? AfterApply { get; set; }
private string? selectedActiveResolution;
private string? selectedVirtualResolution;
private string? selectedRefreshRate;
private List<VirtualResolution>? resolutions;
private List<Rational>? refreshRates;
private List<AmdPreset>? displayPresets;
private string WasValidated = "";
private bool IsVisible;
protected override async Task OnParametersSetAsync()
{
Preset = PresetParam == null ? null : PresetParam.CloneWithId();
if (Preset != null && PresetParam != null)
{
resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("AmdService", "GetAvailableResolutionsV2", Preset);
refreshRates = await _rpcClientService.CallAsync<List<Rational>>("AmdService", "GetAvailableRefreshRatesV2", Preset);
}
selectedActiveResolution = Preset?.DisplayConfig.Resolution?.GetActiveString();
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
if (Preset?.IsDisplayPreset != true)
{
displayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
}
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
if (newResolution != null)
{
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
{
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
var parts = selectedVirtualResolution.Split('x');
newResolution.VirtualWidth = uint.Parse(parts[0]);
newResolution.VirtualHeight = uint.Parse(parts[1]);
}
Preset.DisplayConfig.Resolution = newResolution;
}
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
if (newRefreshRate != null)
{
Preset.DisplayConfig.RefreshRate = newRefreshRate;
}
if (Preset.IsDisplayPreset)
{
//Preset.UpdateAutoApplySettings(PresetParam);
await ApplyDisplayPreset();
return;
}
var result = await _rpcClientService.CallAsync<bool>("AmdService", "UpdatePreset", Preset);
if (AfterApply != null)
{
await AfterApply.Invoke(Preset);
}
}
private async Task ApplyDisplayPreset()
{
var result = await _rpcClientService.CallAsync<bool>("AmdService", "ApplyPreset", Preset);
}
private void ModalOnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
private bool IsValid()
{
return Preset?.IsDisplayPreset == true || !string.IsNullOrEmpty(Preset?.name);
}
}
@@ -0,0 +1,64 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.AMD
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">AMD settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => Config = null"></button>
</div>
<div class="modal-body">
<form class="row g-1">
@if (Config == null)
{
<div>Loading...</div>
}
else
{
<ShortcutInput Label="Quick Access Shortcut" @bind-Shortcut="Config.QuickAccessShortcut" />
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => Config = null">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private AmdServiceConfig? Config { get; set; }
private async Task ApplyClick(MouseEventArgs e)
{
if (Config == null)
{
return;
}
var result = await _rpcClientService.CallAsync<bool>("AmdService", "UpdateConfig", Config);
if (result)
{
Config = null;
}
}
private async Task OnFocus(FocusEventArgs e)
{
if (Config != null)
{
return;
}
Config = await _rpcClientService.CallAsync<AmdServiceConfig>("AmdService", "GetConfig");
}
}
@@ -0,0 +1,208 @@
@page "/AMD"
@using ColorControl.Shared.Common
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.AMD
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject ProtectedLocalStorage _localStorage
<PageTitle>AMD controller</PageTitle>
<AmdSettings />
<div class="d-flex align-items-center">
<h1>AMD controller</h1>
<div class="d-flex ms-auto">
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
Settings
</button>
</div>
</div>
<h2>Displays</h2>
<AmdPresetPage PresetParam="CurrentPreset" AfterApply="AfterPresetApplied" />
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
@if (DisplayPresets == null)
{
<div>Loading...</div>
}
else
{
<div class="row mb-3">
<div class="col">
@if (!DisplayPresets.Any())
{
<span>No displays found</span>
}
@foreach (var preset in DisplayPresets)
{
<div class="card">
<div class="card-header">
@preset.displayName
@if (preset.DisplayConfig.IsPrimary == true)
{
<small class="text-muted ms-2">Primary</small>
}
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => DisplayPresetOnClick(preset)">
Settings
</button>
</div>
<div class="card-body">
<small class="text-body">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</small>
<small class="text-muted">@preset.colorDepth, @preset.pixelFormat</small>
</div>
</div>
}
</div>
</div>
}
@if (Presets == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Presets</h2>
<div class="d-flex ms-auto">
<select class="form-select me-2" id="presetOrder" @bind="PresetOrder" @bind:after="PresetOrderChanged">
@foreach (var order in Enum.GetValues<PresetOrder>())
{
<option value="@order">Order by: @order.GetDescription()</option>
}
</select>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => AddPresetOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
<div class="col">
<div class="list-group">
@foreach (var preset in Presets)
{
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
<div class="d-flex align-items-start justify-content-between">
<div>
<h5 class="mb-1">
@preset.name <small class="text-muted">@(preset.primaryDisplay ? "Primary display" : preset.displayName)</small>
@if (preset.ShowInQuickAccess)
{
<span class="ms-2 badge text-bg-warning" title="Show in Quick Access">QA</span>
}
</h5>
</div>
<span>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
</span>
</div>
<p class="mb-1">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz @(preset.applyColorData ? preset.colorDepth : "")</p>
<small class="text-muted">@preset.shortcut</small>
</a>
}
</div>
</div>
</div>
}
@code {
private List<AmdPreset>? Presets;
private List<AmdPreset>? DisplayPresets;
private List<VirtualResolution>? Resolutions;
private AmdPreset? CurrentPreset;
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await RefreshPresets();
DisplayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
Resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("AmdService", "GetAvailableResolutionsV2", DisplayPresets?.FirstOrDefault());
}
protected override async Task OnAfterRenderAsync(bool firstTime)
{
if (firstTime)
{
PresetOrder = await _localStorage.TryGet<PresetOrder>("AmdPresetOrder");
Presets = Presets?.OrderPresetsBy(PresetOrder);
StateHasChanged();
}
}
private async Task PresetOnClick(AmdPreset preset)
{
await _rpcClientService.CallAsync<bool>("AmdService", "ApplyPresetWithId", preset.id);
}
private async Task PresetOnEditClick(AmdPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnCloneClick(AmdPreset preset)
{
CurrentPreset = new AmdPreset(preset);
CurrentPreset.SetCloneValues();
}
private async Task PresetOnDeleteClick(AmdPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnDeleteConfirmed()
{
if (CurrentPreset == null)
{
return;
}
await _rpcClientService.CallAsync<bool>("AmdService", "DeletePreset", CurrentPreset.id);
await RefreshPresets();
StateHasChanged();
}
private async Task DisplayPresetOnClick(AmdPreset preset)
{
CurrentPreset = preset;
}
private async Task AddPresetOnClick()
{
CurrentPreset = new AmdPreset();
}
private async Task AfterPresetApplied(AmdPreset preset)
{
await RefreshPresets();
StateHasChanged();
}
private async Task PresetOrderChanged()
{
await _localStorage.SetAsync("AmdPresetOrder", PresetOrder);
await RefreshPresets();
}
private async Task RefreshPresets()
{
Presets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetPresets");
Presets = Presets?.OrderPresetsBy(PresetOrder);
}
}
@@ -1,19 +0,0 @@
@page "/counter"
@rendermode InteractiveServer
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@@ -0,0 +1,235 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.Game
@using ColorControl.Shared.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
<label class="form-check-label" for="showInQuickAccess">
Show in Quick Access
</label>
</div>
<div class="mb-2">
<label class="form-label" for="path">File/URI</label>
<div class="input-group mb-2">
<input class="form-control" id="path" type="text" @bind="Preset.Path" required />
<button type="button" class="btn btn-primary" @onclick="SelectFileOnClick" disabled="@SelectingFile">
<span class="@(SelectingFile ? "spinner-border spinner-border-sm" : "")" aria-hidden="true"></span>
<span role="status">Select...</span>
</button>
</div>
<div id="selectFileHelp" class="form-text">
Selecting a file opens a file dialog outside of the browser on the system where the ColorControl application is running.
</div>
</div>
<div class="mb-2">
<label class="form-label" for="parameters">Parameters</label>
<input class="form-control" id="parameters" type="text" @bind="Preset.Parameters" />
</div>
<div class="accordion accordion-flush" id="presetAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
Advanced settings
</button>
</h2>
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.RunAsAdministrator" id="runAsAdministrator">
<label class="form-check-label" for="runAsAdministrator">
Run as administrator
</label>
</div>
<FlagsInput Label="Process affinity" Labels="CpuAffinityLabels" @bind-Value="Preset.ProcessAffinityMask"></FlagsInput>
<div class="mb-2">
<label class="form-label" for="priority">Process priority</label>
<select class="form-select" id="priority" @bind="Preset.GameProcessPriorityClass">
@foreach (var priority in Enum.GetValues<GamePriorityClass>())
{
<option value="@priority.ToString()">@priority.GetDescription()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#autoApplySettings">
Auto-apply settings
</button>
</h2>
<div id="autoApplySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.AutoSettings.AllowAutoApply" id="autoApply">
<label class="form-check-label" for="autoApply">
Automatically execute preset when process starts
</label>
</div>
<div class="mb-2">
<label class="form-label" for="autoApplyAction">Auto-apply action</label>
<select class="form-select" id="priority" @bind="Preset.AutoSettings.ProcessAutoAction">
@foreach (var action in Enum.GetValues<ProcessAutoAction>())
{
<option value="@action.ToString()">@action.GetDescription()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
Steps
</button>
</h2>
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<StepsInput Label="Pre-launch" @bind-Steps="Preset.PreLaunchSteps" TPreset="GamePreset"></StepsInput>
<StepsInput Label="Post-launch" @bind-Steps="Preset.PostLaunchSteps" TPreset="GamePreset"></StepsInput>
<StepsInput Label="Finalize" @bind-Steps="Preset.FinalizeSteps" TPreset="GamePreset"></StepsInput>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private GamePreset? Preset;
[Parameter]
public GamePreset? PresetParam { get; set; }
[Parameter]
public Func<GamePreset, Task>? AfterApply { get; set; }
public List<string> CpuAffinityLabels = Enumerable.Range(0, Environment.ProcessorCount).Select(i => $"CPU #{i}").ToList();
public bool SelectingFile { get; set; }
private string WasValidated = "";
private bool IsVisible;
protected override async Task OnParametersSetAsync()
{
if (PresetParam == null)
{
return;
}
Preset = PresetParam.CloneWithId();
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdatePreset", Preset);
if (AfterApply != null)
{
await AfterApply.Invoke(Preset);
}
IsVisible = false;
}
private bool IsValid()
{
return !string.IsNullOrEmpty(Preset?.name) && !string.IsNullOrEmpty(Preset?.Path);
}
private async Task SelectFileOnClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
SelectingFile = true;
try
{
var options = new RpcUiClientOptions
{
Timeout = 60000
};
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
if (result != null)
{
Preset.Path = result;
if (string.IsNullOrEmpty(Preset.name))
{
Preset.name = Path.GetFileNameWithoutExtension(result);
}
}
}
finally
{
SelectingFile = false;
}
}
private void ModalOnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
}
@@ -0,0 +1,61 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.Game
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<div class="modal" id="settingsModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">NVIDIA settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form class="row g-1">
@if (Config == null)
{
<div>Loading...</div>
}
else
{
<ShortcutInput Label="Quick Access Shortcut" @bind-Shortcut="Config.QuickAccessShortcut" />
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Config.ApplyExternallyLaunched" id="applyExternallyLaunched">
<label class="form-check-label" for="applyExternallyLaunched">
Apply presets when launching games externally
</label>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private GameServiceConfig? Config { get; set; }
protected override async Task OnInitializedAsync()
{
Config = await _rpcClientService.CallAsync<GameServiceConfig>("GameService", "GetConfig");
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Config == null)
{
return;
}
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdateConfig", Config);
}
}
@@ -0,0 +1,160 @@
@page "/Game"
@using ColorControl.Shared.Contracts;
@using ColorControl.Shared.Contracts.Game;
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.Shared.Common;
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject ProtectedLocalStorage _localStorage
<PageTitle>Game launcher</PageTitle>
<GameSettings />
<div class="d-flex align-items-center">
<h1>Game launcher</h1>
<div class="d-flex ms-auto">
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
Settings
</button>
</div>
</div>
<div class="mb-2 d-flex align-items-center">
<h2>Presets</h2>
<div class="d-flex ms-auto">
<select class="form-select me-2" id="presetOrder" @bind="PresetOrder" @bind:after="PresetOrderChanged">
@foreach (var order in Enum.GetValues<PresetOrder>())
{
<option value="@order">Order by: @order.GetDescription()</option>
}
</select>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => AddPresetOnClick()">
Add
</button>
</div>
</div>
<GamePresetPage PresetParam="CurrentPreset" AfterApply="AfterPresetApplied" />
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
@if (Presets == null)
{
<div>Loading...</div>
}
else
{
<div class="row mb-3">
<div class="col">
<div class="list-group">
@foreach (var preset in Presets)
{
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)">
<div class="d-flex align-items-start justify-content-between">
<div>
<h5 class="mb-1">
@preset.name
@if (preset.ShowInQuickAccess)
{
<span class="ms-2 badge text-bg-warning" title="Show in Quick Access">QA</span>
}
</h5>
</div>
<span>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
</span>
</div>
</a>
}
</div>
</div>
</div>
}
@code {
private List<GamePreset>? Presets;
private GamePreset? CurrentPreset;
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await RefreshPresets();
}
protected override async Task OnAfterRenderAsync(bool firstTime)
{
if (firstTime)
{
PresetOrder = await _localStorage.TryGet<PresetOrder>("GamePresetOrder");
Presets = Presets?.OrderPresetsBy(PresetOrder);
StateHasChanged();
}
}
private async Task PresetOnClick(GamePreset preset)
{
await _rpcClientService.CallAsync<bool>("GameService", "ApplyPresetWithId", preset.id);
}
private async Task PresetOnEditClick(GamePreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnCloneClick(GamePreset preset)
{
CurrentPreset = new GamePreset(preset);
CurrentPreset.SetCloneValues();
}
private async Task PresetOnDeleteClick(GamePreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnDeleteConfirmed()
{
if (CurrentPreset == null)
{
return;
}
await _rpcClientService.CallAsync<bool>("GameService", "DeletePreset", CurrentPreset.id);
await RefreshPresets();
StateHasChanged();
}
private async Task AddPresetOnClick()
{
CurrentPreset = new GamePreset();
}
private async Task AfterPresetApplied(GamePreset preset)
{
await RefreshPresets();
StateHasChanged();
}
private async Task PresetOrderChanged()
{
await _localStorage.SetAsync("GamePresetOrder", PresetOrder);
await RefreshPresets();
}
private async Task RefreshPresets()
{
Presets = await _rpcClientService.CallAsync<List<GamePreset>>("GameService", "GetPresets");
Presets = Presets?.OrderPresetsBy(PresetOrder);
}
}
@@ -0,0 +1,36 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
<FieldsModal ModalId="confirmModal" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
@code {
[Parameter]
public string? Title { get; set; }
[Parameter]
public string? Message { get; set; }
[Parameter]
public Func<Task>? OnConfirm { get; set; }
public List<FieldDefinition>? Fields { get; set; }
protected override async Task OnParametersSetAsync()
{
if (Message != null)
{
Fields = [new FieldDefinition { FieldType = FieldType.Label, Label = Message }];
}
}
private async Task ConfirmClick()
{
if (OnConfirm != null)
{
await OnConfirm();
}
}
}
@@ -0,0 +1,125 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject NavigationManager _navigationManager;
@if (Fields == null)
{
return;
}
@foreach (var field in Fields)
{
if (field.FieldType == FieldType.Text)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-2">
<label class="form-label" for="@fieldId">@field.Label</label>
<input class="form-control" type="text" @bind="field.StringValue" id="@fieldId">
@if (field.SubLabel != null)
{
<div id="@(fieldId)_help" class="form-text">
@field.SubLabel
</div>
}
</div>
}
if (field.FieldType == FieldType.Label)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-2">
<label class="form-label" for="@fieldId">@field.Label</label>
@if (field.SubLabel != null)
{
<div id="@(fieldId)_help" class="form-text">
@field.SubLabel
</div>
}
</div>
}
else if (field.FieldType == FieldType.DropDown)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-2">
<label class="form-label" for="@fieldId">@field.Label</label>
<select class="form-select" id="@fieldId" @bind="field.StringValue">
@if (field.ValueType != null)
{
@foreach (var item in Enum.GetValues(field.ValueType))
{
<option value="@item.ToString()">@((item as IConvertible).GetDescription())</option>
}
}
else if (field.Values != null)
{
@foreach (var item in field.Values)
{
<option value="@item.ToString()">@item.ToString()</option>
}
}
</select>
@if (field.SubLabel != null)
{
<div id="@(fieldId)_help" class="form-text">
@field.SubLabel
</div>
}
</div>
}
else if (field.FieldType == FieldType.Shortcut)
{
<ShortcutInput Label="@field.Label" @bind-Shortcut="field.StringValue"></ShortcutInput>
}
else if (field.FieldType == FieldType.CheckBox)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="field.BoolValue" id="@fieldId">
<label class="form-check-label" for="@fieldId">
@field.Label
</label>
</div>
}
else if (field.FieldType == FieldType.Numeric && (field.MaxValue - field.MinValue) <= 50)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-2">
<label class="form-label" for="@fieldId">@field.Label</label>
<input class="form-control" type="number" @bind="field.IntValue" min="@((int)field.MinValue)" max="@((int)field.MaxValue)" id="@fieldId">
@if (field.SubLabel != null)
{
<div id="@(fieldId)_help" class="form-text">
@field.SubLabel
</div>
}
</div>
}
else if (field.FieldType == FieldType.Numeric && (field.MaxValue - field.MinValue) > 50)
{
var fieldId = $"fld_{Random.Shared.Next()}";
<div class="mb-2">
<label class="form-range" for="@fieldId">@field.Label (Current value: @field.IntValue)</label>
<input class="form-range" type="range" min="@((int)field.MinValue)" max="@((int)field.MaxValue)" @bind="field.IntValue" id="@fieldId">
@if (field.SubLabel != null)
{
<div id="@(fieldId)_help" class="form-text">
@field.SubLabel
</div>
}
</div>
}
}
@code {
[Parameter]
public List<FieldDefinition>? Fields { get; set; }
}
@@ -0,0 +1,127 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject NavigationManager _navigationManager;
<div class="modal" id="@ModalId" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="OnClose"></button>
</div>
<div class="modal-body">
@if (IsVisible)
{
<FieldDefs Fields="Fields"></FieldDefs>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="OnClose">Cancel</button>
@if (SecondaryCaption != null)
{
<button type="button" class="btn btn-secondary" @onclick="SecondaryClick">@SecondaryCaption</button>
}
<button class="btn btn-primary" type="button" disabled="@IsNextDisabled" data-bs-dismiss="@Dismiss" @onclick="ConfirmClick">
@if (IsNextDisabled)
{
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
}
<span role="status">@ConfirmCaption</span>
</button>
</div>
</div>
</div>
</div>
@code {
[Parameter]
public string? ModalId { get; set; }
[Parameter]
public string? ConfirmCaption { get; set; } = "Yes";
[Parameter]
public string? SecondaryCaption { get; set; }
[Parameter]
public string? Dismiss { get; set; } = "modal";
[Parameter]
public string? Title { get; set; }
[Parameter]
public string? Message { get; set; }
[Parameter]
public List<FieldDefinition>? Fields { get; set; }
[Parameter]
public Func<Task>? OnConfirm { get; set; }
[Parameter]
public Func<Task>? OnSecondary { get; set; }
[Parameter]
public Func<Task>? OnShow { get; set; }
private bool IsVisible { get; set; }
private bool IsNextDisabled { get; set; }
private async Task ConfirmClick(MouseEventArgs e)
{
if (OnConfirm == null)
{
return;
}
var dismissing = Dismiss == "modal";
IsNextDisabled = Dismiss == null;
try
{
await OnConfirm();
}
finally
{
IsNextDisabled = false;
}
if (dismissing)
{
IsVisible = false;
Dismiss = "modal";
}
}
private async Task SecondaryClick(MouseEventArgs e)
{
if (OnSecondary == null)
{
return;
}
await OnSecondary();
}
private async Task ModalFocus(FocusEventArgs e)
{
if (!IsVisible)
{
IsVisible = true;
if (OnShow != null)
{
await OnShow();
//StateHasChanged();
}
}
}
private async Task OnClose(MouseEventArgs e)
{
IsVisible = false;
}
}
@@ -0,0 +1,53 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.EventDispatcher
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
<div class="row mx-0 mb-2">
<label class="form-label ps-0">@Label</label>
@if (Labels != null)
{
var index = 1;
var checkValue = 1;
foreach (var label in Labels)
{
var name = $"check{index}";
var checkIndex = index;
<div class="form-check form-check-inline col-@(Col)">
<input class="form-check-input" type="checkbox" role="switch" id="@name" checked="@((Value & checkValue) == checkValue)" @onchange="(ChangeEventArgs e) => CheckOnChange(e, checkIndex)">
<label class="form-check-label" for="@name">@label</label>
</div>
index++;
checkValue *= 2;
}
}
</div>
@code {
[Parameter]
public List<string>? Labels { get; set; }
[Parameter]
public string? Label { get; set; }
[Parameter]
public uint Value { get; set; }
[Parameter]
public EventCallback<uint> ValueChanged { get; set; }
[Parameter]
public int Col { get; set; } = 4;
private async Task CheckOnChange(ChangeEventArgs e, int index)
{
var checkValue = (uint)Math.Pow(2, index - 1);
Value += (uint)((e.Value is bool boolValue && boolValue) ? checkValue : -checkValue);
await ValueChanged.InvokeAsync(Value);
}
}
@@ -0,0 +1,63 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.EventDispatcher
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@if (Label != null)
{
<div class="mb-2">
<label class="form-label" for="name">@Label</label>
<input class="form-control" type="text" readonly value="@Shortcut"
@onchange="(ChangeEventArgs e) => ShortcutOnChange(e)"
@onkeydown="ShortcutOnKeyDown"
@onkeyup="(KeyboardEventArgs e) => ShortcutOnKeyUp(e)"
@onfocus="ShortcutOnFocus"
@onblur="ShortcutOnBlur"/>
</div>
}
@code {
[Parameter]
public string? Label { get; set; }
[Parameter]
public string? Shortcut { get; set; }
[Parameter]
public EventCallback<string?> ShortcutChanged { get; set; }
private async Task ShortcutOnChange(ChangeEventArgs e)
{
Shortcut = e.Value?.ToString();
await ShortcutChanged.InvokeAsync(Shortcut);
}
private void ShortcutOnKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Tab")
{
return;
}
var text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(e.ShiftKey, e.CtrlKey, e.AltKey, e.Key, e.Code);
Shortcut = text;
}
private async Task ShortcutOnKeyUp(KeyboardEventArgs e)
{
//KeyboardShortcutDispatcher.HandleKeyboardShortcutUp()
await ShortcutChanged.InvokeAsync(Shortcut);
}
private void ShortcutOnFocus(FocusEventArgs e)
{
KeyboardShortcutDispatcher.IsActive = false;
}
private void ShortcutOnBlur(FocusEventArgs e)
{
KeyboardShortcutDispatcher.IsActive = true;
}
}
@@ -0,0 +1,402 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.EventDispatcher
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@typeparam TPreset where TPreset : PresetBase
@inject RpcUiClientService _rpcClientService
@if (Label != null && Steps != null)
{
var index = 0;
<div class="mb-2">
<div class="d-flex align-items-center">
<label class="form-label" for="name">@Label</label>
<div class="d-flex ms-auto">
@if (NvPresets != null || SamsungPresets != null || CustomItems != null)
{
<div class="dropdown dropend mb-1">
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">Add</button>
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 200px">
@if (NvPresets != null && NvPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="nvPreset">NVIDIA preset</label>
<select class="form-select" id="nvPreset" @bind="NvPresetToAddId">
<option value="null">Select preset</option>
@foreach (var preset in NvPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (SamsungPresets != null && SamsungPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="samsungPreset">Samsung preset</label>
<select class="form-select" id="samsungPreset" @bind="SamsungPresetToAddId">
<option value="null">Select preset</option>
@foreach (var preset in SamsungPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (LgPresets != null && LgPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="lgPreset">LG preset</label>
<select class="form-select" id="lgPreset" @bind="LgPresetToAddId">
<option value="null">Select preset</option>
@foreach (var preset in LgPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (CustomItems != null)
{
foreach (var customItem in CustomItems)
{
<div class="mb-2">
<label class="form-label" for="customItem">@customItem.Name</label>
<select class="form-select" id="customItem" @bind="customItem.SelectedItem">
<option value="null">Select item</option>
@foreach (var item in customItem.Items)
{
<option value="@item">@item</option>
}
</select>
</div>
}
}
@if (ActionItems != null)
{
foreach (var customItem in ActionItems)
{
<div class="mb-2">
<label class="form-label" for="advancedCustomItem">@customItem.Name</label>
<select class="form-select" id="advancedCustomItem" @bind="customItem.SelectedActionName" @bind:after="() => AdvancedItemChanged(customItem)">
<option value="null">Select action</option>
@foreach (var keyValue in customItem.GroupedActions)
{
<optgroup label="@keyValue.Key">
@foreach (var item in keyValue.Value)
{
<option value="@item.Name">@item.Title</option>
}
</optgroup>
}
</select>
</div>
<FieldDefs Fields="ActionFields"></FieldDefs>
}
}
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddPresetOnClick()">Add items</button>
</form>
</div>
}
</div>
</div>
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
<button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-click" type="button" role="tab" aria-controls="nav-click" aria-selected="true">Visual steps</button>
<button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-raw" type="button" role="tab" aria-controls="nav-raw" aria-selected="false">Raw text</button>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-click" role="tabpanel" aria-labelledby="nav-click-tab" tabindex="0">
<div class="btn-group btn-group-sm d-inline-flex flex-wrap" role="group" aria-label="Small button group">
@foreach (var step in PresetSteps)
{
var stepIndex = index;
<button type="button" class="btn btn-outline-primary dropdown-toggle text-break text-wrap" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">@step.Raw</button>
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 200px">
<div class="mb-2">
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" @onclick="() => RemoveOnClick(stepIndex)">Remove</button>
</div>
<div class="mb-2">
<label class="form-label" for="delay">Delay (@(step.Delay)ms)</label>
<input class="form-range" id="delay" type="range" min="0" max="10000" step="50" @bind="step.Delay" @bind:after="() => AfterDelayChanged()">
</div>
</form>
index++;
}
</div>
</div>
<div class="tab-pane fade" id="nav-raw" role="tabpanel" aria-labelledby="nav-raw-tab" tabindex="0">
<textarea class="form-control" id="rawStepText" rows="3" @onchange="RawTextOnChange" @onblur="RawTextOnBlur" value="@RawText"></textarea>
</div>
</div>
</div>
}
@code {
[Parameter]
public string? Label { get; set; }
[Parameter]
public List<string>? Steps { get; set; }
[Parameter]
public EventCallback<List<string>?> StepsChanged { get; set; }
[Parameter]
public List<CustomStepItem>? CustomItems { get; set; }
[Parameter]
public List<ActionStepItem>? ActionItems { get; set; }
[Parameter]
public bool NvPresetsEnabled { get; set; } = true;
[Parameter]
public bool SamsungPresetsEnabled { get; set; } = true;
[Parameter]
public bool LgPresetsEnabled { get; set; } = true;
public class ActionStepItem
{
public string Name { get; private set; }
public List<InvokableActionDto<TPreset>> Actions { get; private set; }
public string? SelectedActionName { get; set; }
public Dictionary<string, List<InvokableActionDto<TPreset>>> GroupedActions;
public ActionStepItem(string name, List<InvokableActionDto<TPreset>> actions)
{
Name = name;
Actions = actions;
GroupedActions = actions.GroupBy(a => a.Category).ToDictionary(k => k.Key, v => v.ToList());
}
public string GetStepString(List<FieldDefinition>? fields)
{
if (string.IsNullOrEmpty(SelectedActionName))
{
return "";
}
var action = Actions.FirstOrDefault(a => a.Name == SelectedActionName);
if (action == null)
{
return "";
}
var field = fields?.FirstOrDefault();
if (field == null || field.Value == null)
{
return $"{action.Name}";
}
return $"{action.Name}({field.Value?.ToString()})";
}
}
private List<NvPreset>? NvPresets { get; set; }
private List<SamsungPreset>? SamsungPresets { get; set; }
private List<LgPreset>? LgPresets { get; set; }
private int? NvPresetToAddId { get; set; }
private int? SamsungPresetToAddId { get; set; }
private int? LgPresetToAddId { get; set; }
private List<PresetStep> PresetSteps = new();
private string? RawText;
private List<FieldDefinition>? ActionFields;
protected override async Task OnParametersSetAsync()
{
if (Steps != null)
{
PresetSteps.Clear();
PresetSteps.AddRange(Steps.Select(s => PresetStep.Parse(s)));
RawText = string.Join(", ", PresetSteps);
}
}
protected override async Task OnInitializedAsync()
{
var config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
if (config.HasModule(Module.NvModule))
{
NvPresets = (await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetPresets"))?.OrderBy(p => p.name).ToList();
}
if (config.HasModule(Module.SamsungModule))
{
SamsungPresets = (await _rpcClientService.CallAsync<List<SamsungPreset>>("SamsungService", "GetPresets"))?.OrderBy(p => p.name).ToList();
}
if (config.HasModule(Module.LgModule))
{
LgPresets = (await _rpcClientService.CallAsync<List<LgPreset>>("LgService", "GetPresets"))?.OrderBy(p => p.name).ToList();
}
}
private async Task RemoveOnClick(int index)
{
if (PresetSteps.Count > index)
{
PresetSteps.RemoveAt(index);
}
await EmitSteps();
}
private async Task AfterDelayChanged()
{
await EmitSteps();
}
private async Task AddPresetOnClick()
{
AddPreset(NvPresets, "NvPreset", NvPresetToAddId);
AddPreset(SamsungPresets, "SamsungPreset", SamsungPresetToAddId);
AddPreset(LgPresets, "LgPreset", LgPresetToAddId);
if (CustomItems != null && Steps != null)
{
foreach (var customItem in CustomItems)
{
if (string.IsNullOrWhiteSpace(customItem.SelectedItem))
{
continue;
}
PresetSteps.Add(PresetStep.Parse(customItem.SelectedItem));
//customItem.SelectedItem = null;
}
}
if (ActionItems != null && Steps != null)
{
foreach (var customItem in ActionItems)
{
if (string.IsNullOrWhiteSpace(customItem.SelectedActionName))
{
continue;
}
var stepString = customItem.GetStepString(ActionFields);
PresetSteps.Add(PresetStep.Parse(stepString));
//customItem.SelectedItem = null;
}
}
await EmitSteps();
}
private void AddPreset<T>(List<T>? presets, string type, int? id) where T : PresetBase
{
var preset = presets?.FirstOrDefault(p => p.id == id);
if (preset == null || Steps == null || preset.id == 0)
{
return;
}
PresetSteps.Add(PresetStep.Parse($"{type}({preset.name})"));
}
private async Task EmitSteps()
{
if (Steps != null)
{
Steps.Clear();
Steps.AddRange(PresetSteps.Select(ps => ps.UpdatedRaw));
RawText = string.Join(", ", PresetSteps);
await StepsChanged.InvokeAsync(Steps);
}
StateHasChanged();
}
private async Task RawTextOnBlur(FocusEventArgs e)
{
var newSteps = new List<string>();
Utils.ParseWords(newSteps, RawText);
PresetSteps.Clear();
PresetSteps.AddRange(newSteps.Select(s => PresetStep.Parse(s)));
await EmitSteps();
}
private async Task RawTextOnChange(ChangeEventArgs e)
{
RawText = e.Value?.ToString();
}
private void AdvancedItemChanged(ActionStepItem customItem)
{
var action = customItem.Actions.FirstOrDefault(a => a.Name == customItem.SelectedActionName);
if (action == null)
{
ActionFields = null;
return;
}
ActionFields = [CreateField(action)];
}
private FieldDefinition CreateField(InvokableActionDto<TPreset> action)
{
var fieldType = FieldType.Numeric;
var strValue = default(string);
var subLabel = default(string);
var label = action.Title;
if (action.EnumType != null)
{
fieldType = FieldType.DropDown;
strValue = Enum.GetNames(action.EnumType).First();
subLabel = "Note: not all values are available on all models.";
}
else if (action.MinValue == action.MaxValue)
{
fieldType = FieldType.Label;
label = $"This will execute {action.Title}";
}
var field = new FieldDefinition
{
FieldType = fieldType,
Label = label,
SubLabel = subLabel,
ValueType = action.EnumType,
MinValue = action.MinValue,
MaxValue = action.MaxValue,
};
if (fieldType == FieldType.Numeric)
{
field.IntValue = action.CurrentValue;
}
else
{
field.StringValue = strValue;
}
return field;
}
}
@@ -0,0 +1,49 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.EventDispatcher
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@if (Triggers != null)
{
foreach (var trigger in Triggers)
{
<div class="mb-2">
<label class="form-label" for="triggerType">Trigger type</label>
<select class="form-select" id="triggerType" @bind="trigger.Trigger">
@foreach (var type in Enum.GetValues<PresetTriggerType>())
{
<option value="@type.ToString()">@type.GetDescription()</option>
}
</select>
</div>
<FlagsInput Label="Conditions" Value="@((uint)trigger.Conditions)" Labels="ConditionLabels" Col="6" ValueChanged="ConditionsChanged"></FlagsInput>
<div class="mb-2">
<label class="form-label" for="includedProcesses">Included processes</label>
<input class="form-control" type="text" @bind="trigger.IncludedProcessesAsString" />
</div>
<div class="mb-2">
<label class="form-label" for="excludedProcesses">Excluded processes</label>
<input class="form-control" type="text" @bind="trigger.ExcludedProcessesAsString" />
</div>
}
}
@code {
[Parameter]
public List<PresetTrigger>? Triggers { get; set; }
private List<string> ConditionLabels = Utils.GetDescriptions<PresetConditionType>(fromValue: 1).ToList();
public void ConditionsChanged(uint value)
{
if (Triggers == null || !Triggers.Any())
{
return;
}
var trigger = Triggers.First();
trigger.Conditions = (PresetConditionType)value;
}
}
+55 -2
View File
@@ -1,7 +1,60 @@
@page "/"
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
<h1>Color Control</h1>
Welcome to your new app.
<h2>Modules</h2>
@if (Modules == null)
{
<div>Loading...</div>
}
else
{
<div class="row">
@foreach (var module in Modules.Where(m => m.IsActive))
{
<div class="col">
<div class="card h-100 mb-3">
<div class="card-header">
@module.DisplayName
</div>
<div class="card-body">
<a href="@module.Link" class="btn btn-primary stretched-link mb-2">Open module</a>
<ul>
@foreach (var info in module.Info)
{
<li>@info</li>
}
</ul>
</div>
</div>
</div>
}
</div>
}
@code {
private Config? Config;
private List<ModuleDto>? Modules;
protected override async Task OnInitializedAsync()
{
Config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
Modules = await _rpcClientService.CallAsync<List<ModuleDto>>("OptionsService", "GetModules");
if (Modules != null)
{
Modules.ForEach(m => m.Link = m.DisplayName.Split(' ')[0]);
}
}
}
@@ -0,0 +1,50 @@
@page "/info"
@using ColorControl.Shared.Common;
@using ColorControl.Shared.Services;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<PageTitle>Logging</PageTitle>
<h1>Information</h1>
@if (info == null)
{
<div>Loading...</div>
}
else
{
<h3 class="mb-2">@info.ApplicationTitle - @info.LegalCopyright</h3>
<h5>This app contains the following 3rd party plugins:</h5>
<ul class="list-group">
<li class="list-group-item">lgtv.net by gr4b4z</li>
<li class="list-group-item">Newtonsoft.Json by James Newton-King</li>
<li class="list-group-item">NLog by Jarek Kowalski, Kim Christensen, Julian Verdurmen</li>
<li class="list-group-item">NvAPIWrapper.Net by Soroush Falahati</li>
<li class="list-group-item">NWin32 by zmjack</li>
<li class="list-group-item">TaskScheduler by David Hall</li>
<li class="list-group-item">NVIDIA Profile Inspector by Orbmu2k</li>
<li class="list-group-item">NvidiaML wrapper by LibreHardwareMonitor</li>
<li class="list-group-item">Novideo sRGB by ledoge</li>
<li class="list-group-item">NLogViewer by dojo90</li>
<li class="list-group-item">WPFDarkTheme by AngryCarrot789</li>
<li class="list-group-item">Little-CMS by mm2</li>
<li class="list-group-item">MHC2 by dantmnf</li>
</ul>
}
@code {
private InfoDto? info;
protected override async Task OnInitializedAsync()
{
info = await _rpcClientService.CallAsync<InfoDto>("OptionsService", "GetInfo");
}
}
@@ -0,0 +1,215 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Services
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="deviceModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Device?.Name == null ? $"Settings for new device" : $"Settings of device {Device.Name}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form class="row g-1">
@if (Device == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" id="name" type="text" @bind="Device.Name" />
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Device.IsCustom" id="isCustom">
<label class="form-check-label" for="isCustom">
Is custom device
</label>
</div>
<div class="mb-2">
<label class="form-label" for="ipAddress">IP-address</label>
<input class="form-control" id="ipAddress" type="text" @bind="Device.IpAddress" disabled="@(!Device.IsCustom)" />
</div>
<div class="mb-2">
<label class="form-label" for="macAddress">MAC-address</label>
<input class="form-control" id="macAddress" type="text" @bind="Device.MacAddress" disabled="@(!Device.IsCustom)" />
</div>
@if (!Device.IsConnected)
{
<div class="mb-2">
<button class="btn btn-primary me-2" type="button" disabled="@IsTestingConnection" @onclick="TestConnectionClick">
@if (IsTestingConnection)
{
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
}
<span role="status">Test connection</span>
</button>
@if (IsTestConnectionSuccessful != null)
{
if (IsTestConnectionSuccessful == true)
{
<span class="text-success">Connection test was successful!</span>
}
else
{
<span class="text-danger">Connection failed!</span>
}
}
</div>
}
<div class="accordion accordion-flush" id="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
Device options
</button>
</h2>
<div id="deviceOptions" class="accordion-collapse collapse" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterStartup" @bind="Device.Options.PowerOnAfterStartup">
<label class="form-check-label" for="checkPowerOnAfterStartup">Power on after startup</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterResume" @bind="Device.Options.PowerOnAfterResume">
<label class="form-check-label" for="checkPowerOnAfterResume">Power on after resume from standby</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnShutdown" @bind="Device.Options.PowerOffOnShutdown">
<label class="form-check-label" for="checkPowerOffOnShutdown">Power off on shutdown</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnStandby" @bind="Device.Options.PowerOffOnStandby">
<label class="form-check-label" for="checkPowerOffOnStandby">Power off on standby</label>
</div>
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnScreensaverStart" @bind="Device.Options.PowerOffOnScreenSaver">
<label class="form-check-label" for="checkPowerOffOnScreensaverStart">Power off when screensaver activates</label>
<button class="btn btn-sm btn-primary ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#screenSaverSettings" aria-expanded="false" aria-controls="screenSaverSettings">
Settings
</button>
</div>
<div class="collapse mb-3" id="screenSaverSettings">
<div class="card card-body">
<div class="mb-2">
<label class="form-label" for="minScreenSaverDuration">Minimal screen saver duration (seconds)</label>
<input class="form-control" id="minScreenSaverDuration" type="number" @bind="Device.Options.ScreenSaverMinimalDuration" />
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkTurnScreenOffInsteadOfPowerOff" @bind="Device.Options.TurnScreenOffOnScreenSaver">
<label class="form-check-label" for="checkTurnScreenOffInsteadOfPowerOff">Turn screen off instead of power off</label>
<div class="form-text">NOTE: turning the screen off might not work correctly on all models</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkTurnScreenOnInsteadOfPowerOn" @bind="Device.Options.TurnScreenOnAfterScreenSaver">
<label class="form-check-label" for="checkTurnScreenOnInsteadOfPowerOn">Turn screen on instead of power on</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPerformActionOnManualScreenSaver" @bind="Device.Options.HandleManualScreenSaver">
<label class="form-check-label" for="checkPerformActionOnManualScreenSaver">Perform action even on manually executed screen saver</label>
</div>
</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnOnScreensaverEnd" @bind="Device.Options.PowerOnAfterScreenSaver">
<label class="form-check-label" for="checkPowerOnOnScreensaverEnd">Power on when screensaver deactivates</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterManualPowerOff" @bind="Device.Options.PowerOnAfterManualPowerOff">
<label class="form-check-label" for="checkPowerOnAfterManualPowerOff">Power on even after manual power off</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkAllowTriggers" @bind="Device.Options.TriggersEnabled">
<label class="form-check-label" for="checkAllowTriggers">Allow triggers to be fired for this device</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkWindowsPowerOn" @bind="Device.Options.PowerOnByWindows">
<label class="form-check-label" for="checkWindowsPowerOn">Use Windows power settings to power on</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkWindowsPowerOff" @bind="Device.Options.PowerOffByWindows">
<label class="form-check-label" for="checkWindowsPowerOff">Use Windows power settings to power off</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkUseSecureConnection" @bind="Device.Options.UseSecureConnection">
<label class="form-check-label" for="checkUseSecureConnection">Use secure connection</label>
</div>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private LgDeviceDto? _device;
[Parameter]
public LgDeviceDto? Device { get; set; }
[Parameter]
public Func<LgDeviceDto, Task>? AfterApply { get; set; }
private bool IsTestingConnection;
private bool? IsTestConnectionSuccessful;
protected override async Task OnParametersSetAsync()
{
if (_device != Device)
{
_device = Device;
}
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Device == null)
{
return;
}
var result = await _rpcClientService.CallAsync<bool>("LgService", "UpdateDevice", Device);
if (AfterApply != null)
{
await AfterApply.Invoke(Device);
}
}
private async Task TestConnectionClick(MouseEventArgs e)
{
IsTestingConnection = true;
try
{
IsTestConnectionSuccessful = await _rpcClientService.CallWithOptionsAsync<bool>("LgService", "TestDevice", new RpcUiClientOptions { Timeout = 120000 }, Device);
}
finally
{
IsTestingConnection = false;
}
}
}
@@ -0,0 +1,117 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<FieldsModal ModalId="executeActionModal" Fields="Fields" Title="@Title" ConfirmCaption="Apply" OnConfirm="ConfirmClick"></FieldsModal>
@code {
[Parameter]
public InvokableActionDto<LgPreset>? Action { get; set; }
[Parameter]
public List<LgDeviceDto>? Devices { get; set; }
[Parameter]
public Func<Task>? OnConfirm { get; set; }
public string? Title { get; set; }
public List<FieldDefinition>? Fields { get; set; }
private FieldDefinition? Field;
private FieldDefinition? DeviceField;
protected override async Task OnParametersSetAsync()
{
if (Action == null)
{
return;
}
Title = $"Execute action {Action.Title}";
Field = CreateField(Action);
if (Devices?.Any() == true)
{
DeviceField = FieldDefinition.CreateDropDownField("Device", Devices.Select(d => d.ToString()).ToList());
DeviceField.Value = Devices.FirstOrDefault(d => d.IsSelected)?.ToString();
Fields = [DeviceField, Field];
}
else
{
Fields = [Field];
}
}
private FieldDefinition CreateField(InvokableActionDto<LgPreset> action)
{
var fieldType = FieldType.Numeric;
var strValue = default(string);
var subLabel = default(string);
var label = action.Title;
if (action.EnumType != null)
{
fieldType = FieldType.DropDown;
strValue = Enum.GetNames(action.EnumType).First();
subLabel = "Note: not all values are available on all models.";
}
else if (action.MinValue == action.MaxValue)
{
fieldType = FieldType.Label;
label = $"This will execute {action.Title}. Press Apply to continue.";
}
var field = new FieldDefinition
{
FieldType = fieldType,
Label = label,
SubLabel = subLabel,
ValueType = action.EnumType,
MinValue = action.MinValue,
MaxValue = action.MaxValue,
};
if (fieldType == FieldType.Numeric)
{
field.IntValue = action.CurrentValue;
}
else
{
field.StringValue = strValue;
}
return field;
}
private async Task ConfirmClick()
{
if (Action == null || Field == null)
{
return;
}
if (Devices != null && DeviceField != null)
{
Action.SelectedDevice = Devices.FirstOrDefault(d => d.ToString() == DeviceField.Value?.ToString());
}
var parameters = new List<string> { Field.StringValue };
await _rpcClientService.CallAsync<bool>("LgService", "ExecuteInvokableAction", Action, parameters);
if (OnConfirm != null)
{
await OnConfirm();
}
}
}
@@ -0,0 +1,184 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@using static ColorControl.UI.Components.Pages.Generic.StepsInput<ColorControl.Shared.Contracts.LG.LgPreset>
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
</div>
@if (Devices != null)
{
<div class="mb-2">
<label class="form-label" for="device">Device</label>
<select class="form-select" id="device" @bind="Preset.DeviceMacAddress">
<option value="">Globally selected device</option>
@foreach (var device in Devices)
{
<option value="@device.MacAddress">@device.Name</option>
}
</select>
</div>
}
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
<label class="form-check-label" for="showInQuickAccess">
Show in Quick Access
</label>
</div>
<ShortcutInput Label="Shortcut" @bind-Shortcut="Preset.shortcut" />
<div class="accordion accordion-flush" id="presetAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
Advanced settings
</button>
</h2>
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<TriggerInput Triggers="Preset.Triggers"></TriggerInput>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
Steps
</button>
</h2>
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<StepsInput Label="Steps" @bind-Steps="Preset.steps" CustomItems="CustomItems" ActionItems="CustomActions" LgPresetsEnabled="false" TPreset="LgPreset"></StepsInput>
<div class="mb-2">
<label class="form-label" for="description">Description</label>
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
</div>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="@(!IsValid() ? "" : "modal")" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private LgPreset? Preset;
[Parameter]
public LgPreset? PresetParam { get; set; }
[Parameter]
public List<LgDeviceDto>? Devices { get; set; }
[Parameter]
public Func<LgPreset, Task>? AfterApply { get; set; }
private List<CustomStepItem>? CustomItems;
private List<ActionStepItem>? CustomActions;
private string WasValidated = "";
private bool IsVisible;
protected override async Task OnParametersSetAsync()
{
if (PresetParam == null)
{
return;
}
Preset = PresetParam.CloneWithId();
}
protected override async Task OnInitializedAsync()
{
CustomItems = [new CustomStepItem("RC buttons", Enum.GetValues<ButtonType>().Select(b =>
{
var result = b.ToString();
if (result[0] == '_')
{
result = result.Substring(1);
}
return result;
}).ToList())];
var actions = await _rpcClientService.CallAsync<List<InvokableActionDto<LgPreset>>>("LgService", "GetInvokableActions");
if (actions != null)
{
CustomActions = [new ActionStepItem("Actions", actions.Where(a => !a.Advanced).ToList())];
}
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var result = await _rpcClientService.CallAsync<bool>("LgService", "UpdatePreset", Preset);
if (AfterApply != null)
{
await AfterApply.Invoke(Preset);
}
}
private void ModalOnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
private bool IsValid()
{
return !string.IsNullOrEmpty(Preset?.name);
}
}
@@ -0,0 +1,34 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<FieldsModal Title="Lg settings" ConfirmCaption="Save" ModalId="settingsModal" Fields="Fields" OnConfirm="ApplyClick" OnShow="ModalOnShow"></FieldsModal>
@code {
private LgServiceConfigDto? Config { get; set; }
private List<FieldDefinition>? Fields { get; set; }
protected async Task ModalOnShow()
{
Config = await _rpcClientService.CallAsync<LgServiceConfigDto>("LgService", "GetConfig");
Fields = LgFields.GetSettingsFields(Config);
StateHasChanged();
}
private async Task ApplyClick()
{
if (Config == null)
{
return;
}
FieldDefinition.UpdateObject(Fields, Config);
var result = await _rpcClientService.CallAsync<bool>("LgService", "UpdateConfig", Config);
}
}
@@ -0,0 +1,353 @@
@page "/LG"
@using ColorControl.Shared.Common
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.LG
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject ProtectedLocalStorage _localStorage
<PageTitle>LG controller</PageTitle>
<LgSettings />
<LgExecuteActionModal Action="@CurrentInvokableAction" Devices="Devices" />
<div class="d-flex align-items-center">
<h1>LG controller</h1>
<div class="d-flex ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
Buttons
</button>
<ul class="dropdown-menu">
@foreach (var button in Enum.GetValues<ButtonType>())
{
<li>
<a class="dropdown-item" @onclick="() => ButtonOnClick(button)">@button.GetDescription()</a>
</li>
}
</ul>
</div>
@if (ActionGroups != null)
{
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
Expert
</button>
<ul class="dropdown-menu">
@foreach (var group in ActionGroups)
{
<li class="dropstart">
<a class="dropdown-item dropdown-toggle" data-bs-toggle="dropdown" href="#">@group.Name</a>
<ul class="dropdown-menu dropdown-submenu">
@foreach (var action in group.Actions)
{
<li><a class="dropdown-item" @onclick="() => ActionOnclick(action)" data-bs-toggle="modal" data-bs-target="#executeActionModal">@action.Title</a></li>
}
</ul>
</li>
}
</ul>
</div>
}
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
Settings
</button>
</div>
</div>
<LgDevicePage Device="CurrentDevice" />
<LgPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
@if (Devices == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Devices</h2>
<div class="d-flex ms-auto">
<button type="button" class="btn btn-sm btn-primary me-2" disabled="@IsRefreshDisabled" @onclick="RefreshDevicesOnClick">
@if (IsRefreshDisabled)
{
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
}
<span role="status">Refresh</span>
</button>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
@if (!Devices.Any())
{
<span>No devices found</span>
}
@foreach (var device in Devices)
{
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-header d-flex align-items-baseline">
<span>
@device.Name
@if (device.IsConnected)
{
<span class="ms-2 badge text-bg-success" title="Device is connected">Connected</span>
}
else
{
<span class="ms-2 badge text-bg-danger" title="Device is disconnected">Disconnected</span>
}
@if (device.IsSelected)
{
<span class="ms-2 badge text-bg-primary" title="Global device">Primary</span>
}
</span>
<div class="d-flex ms-auto">
@if (!device.IsSelected)
{
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
}
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
Settings
</button>
</div>
</div>
<div class="card-body">
<small class="text-muted">@device.IpAddress (@(device.MacAddress ?? "No MAC-address entered"))</small>
</div>
</div>
</div>
}
</div>
}
@if (Presets == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Presets</h2>
<div class="d-flex ms-auto">
<select class="form-select me-2" id="presetOrder" @bind="PresetOrder" @bind:after="PresetOrderChanged">
@foreach (var order in Enum.GetValues<PresetOrder>())
{
<option value="@order">Order by: @order.GetDescription()</option>
}
</select>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => AddPresetOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
<div class="col">
<div class="list-group">
@foreach (var preset in Presets)
{
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
<div class="d-flex align-items-start justify-content-between">
<div>
<h5 class="mb-1">@preset.name <small class="text-muted">@GetDeviceName(preset)</small></h5>
@if (preset.Triggers.Any(t => t.Trigger != PresetTriggerType.None))
{
<div><small class="text-muted">Trigger: @preset.Triggers[0].ToString()</small></div>
}
<div><small class="text-muted">@preset.shortcut</small></div>
</div>
<span>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
</span>
</div>
</a>
}
</div>
</div>
</div>
}
@code {
private class ActionGroup
{
public string Name { get; }
public List<InvokableActionDto<LgPreset>> Actions { get; }
public ActionGroup(string name, List<InvokableActionDto<LgPreset>> actions)
{
Name = (name ?? "Misc");
Actions = actions;
}
}
private List<LgPreset>? Presets;
private List<LgDeviceDto>? Devices;
private List<InvokableActionDto<LgPreset>>? InvokableActions;
private List<ActionGroup>? ActionGroups;
private LgServiceConfigDto? Config;
private LgPreset? CurrentPreset;
private LgDeviceDto? CurrentDevice;
private bool IsRefreshDisabled;
private InvokableActionDto<LgPreset>? CurrentInvokableAction;
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
Config = await _rpcClientService.CallAsync<LgServiceConfigDto>("LgService", "GetConfig");
await RefreshPresets();
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<LgPreset>>>("LgService", "GetInvokableActions");
ActionGroups = InvokableActions.GroupBy(a => a.Category).Select(g => new ActionGroup(g.Key, g.ToList())).ToList();
}
protected override async Task OnAfterRenderAsync(bool firstTime)
{
if (firstTime)
{
PresetOrder = await _localStorage.TryGet<PresetOrder>("LgPresetOrder");
Presets = Presets?.OrderPresetsBy(PresetOrder);
StateHasChanged();
}
}
private async Task PresetOnClick(LgPreset preset)
{
await _rpcClientService.CallAsync<bool>("LgService", "ApplyPresetWithId", preset.id);
}
private async Task PresetOnEditClick(LgPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnCloneClick(LgPreset preset)
{
CurrentPreset = new LgPreset(preset);
CurrentPreset.SetCloneValues();
}
private async Task PresetOnDeleteClick(LgPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnDeleteConfirmed()
{
if (CurrentPreset == null)
{
return;
}
await _rpcClientService.CallAsync<bool>("LgService", "DeletePreset", CurrentPreset.id);
await RefreshPresets();
StateHasChanged();
}
private async Task DeviceOnClick(LgDeviceDto device)
{
CurrentDevice = device;
}
private async Task AddDeviceOnClick()
{
CurrentDevice = new LgDeviceDto { IsCustom = true };
}
private async Task AddPresetOnClick()
{
CurrentPreset = new LgPreset();
}
private async Task AfterPresetApplied(LgPreset preset)
{
await RefreshPresets();
StateHasChanged();
}
private async Task RefreshDevicesOnClick(MouseEventArgs e)
{
IsRefreshDisabled = true;
try
{
try
{
await _rpcClientService.CallAsync<bool>("LgService", "RefreshDevices");
}
catch
{
}
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
}
finally
{
IsRefreshDisabled = false;
}
StateHasChanged();
}
private async Task ActionOnclick(InvokableActionDto<LgPreset> action)
{
CurrentInvokableAction = action;
}
private string GetDeviceName(LgPreset preset)
{
if (string.IsNullOrEmpty(preset.DeviceMacAddress))
{
return "Global";
}
return Devices?.FirstOrDefault(d => d.MacAddress == preset.DeviceMacAddress)?.Name ?? preset.DeviceMacAddress;
}
private async Task SetAsPrimaryOnClick(LgDeviceDto device)
{
var result = await _rpcClientService.CallAsync<bool>("LgService", "SetSelectedDevice", device);
if (result)
{
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
}
}
private async Task PresetOrderChanged()
{
await _localStorage.SetAsync("LgPresetOrder", PresetOrder);
await RefreshPresets();
}
private async Task RefreshPresets()
{
Presets = await _rpcClientService.CallAsync<List<LgPreset>>("LgService", "GetPresets");
Presets = Presets?.OrderPresetsBy(PresetOrder);
}
private async Task ButtonOnClick(ButtonType button)
{
var preset = new LgPreset(button.ToString(), null, [button.ToString()]);
await _rpcClientService.CallAsync<bool>("LgService", "ApplyPreset", preset);
}
}
@@ -0,0 +1,205 @@
@page "/logging"
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<PageTitle>Logging</PageTitle>
<h1>Logging</h1>
@if (config == null)
{
<div>Loading...</div>
}
else
{
<div class="row mb-3">
<div class="col-lg-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Settings</h5>
</div>
<form class="ms-3 me-3">
<div class="mb-3 col-md-3">
<label class="form-label" for="logLevel">
Log Level
</label>
<select class="form-select" @bind="logLevel" id="logLevel" @oninput="LogLevelInput">
@foreach (var level in logLevels)
{
<option value="@level.Name">@level.Name</option>
}
</select>
</div>
</form>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-3">
<label class="form-label" for="logSource">
Show log from
</label>
<select class="form-select" @bind="logSource" id="logSource" @bind:after="GetLog">
<option value="0">Main application</option>
<option value="1">Service</option>
</select>
</div>
<div class="col-3">
<label class="form-label" for="logOrder">
Order
</label>
<select class="form-select" @bind="logOrder" id="logOrder" @bind:after="GetLog">
<option value="0">Newest first</option>
<option value="1">Oldest first</option>
</select>
</div>
<div class="col-3">
<label class="form-label" for="maxLines">
Maximum number of lines to show
</label>
<select class="form-select" @bind="maxLines" id="maxLines" @bind:after="GetLog">
<option value="1000">1000</option>
<option value="10000">10000</option>
<option value="999999999">Show all</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">TimeStamp</th>
<th scope="col">
<select class="form-select fw-bold" id="logLevel" @oninput="LogLevelFilterInput">
@foreach (var level in logLevels)
{
<option class="fw-bold" value="@level.Name">@level.Name</option>
}
</select>
</th>
<th scope="col">Name</th>
<th scope="col">Message</th>
</tr>
</thead>
<tbody>
@foreach (var logLine in logLines.Where(l => l.Show(logLevelFilter)))
{
<tr class="table-@logLine.BsClass">
<td style="min-width: 220px">@logLine.TimeStamp</td>
<td style="min-width: 120px">@logLine.Level</td>
<td>@logLine.Name</td>
<td>@logLine.Message</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
@code {
private Config? config;
private string logLevel = "0";
private int logSource = 0;
private int maxLines = 1000;
private int logOrder = 0;
private List<string>? allLines;
private List<LogLine> logLines = new();
private string logLevelFilterName = NLog.LogLevel.Debug.Name;
private NLog.LogLevel logLevelFilter = NLog.LogLevel.Debug;
private List<NLog.LogLevel> logLevels = [NLog.LogLevel.Trace, NLog.LogLevel.Debug, NLog.LogLevel.Info, NLog.LogLevel.Warn, NLog.LogLevel.Error, NLog.LogLevel.Fatal];
private class LogLine
{
public string TimeStamp { get; }
public string Level { get; }
public string Name { get; }
public string Message { get; }
public NLog.LogLevel LogLevel { get; }
public LogLine(string timeStamp, string level, string name, string message)
{
TimeStamp = timeStamp;
Level = level;
Name = name;
Message = message;
LogLevel = NLog.LogLevel.FromString(level);
}
public string BsClass => Level switch
{
"TRACE" => "dark",
"DEBUG" => "secondary",
"INFO" => "info",
"WARN" => "warning",
"ERROR" => "danger",
"FATAL" => "secondary",
};
public bool Show(NLog.LogLevel level) => LogLevel >= level;
}
protected override async Task OnInitializedAsync()
{
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
logLevel = config.LogLevel;
await GetLog();
}
private async Task GetLog()
{
allLines = await _rpcClientService.CallAsync<List<string>>("LoggingService", "LoadLog", logSource, maxLines, logOrder);
ProcessLines();
}
private void ProcessLines()
{
logLines.Clear();
if (allLines == null)
{
return;
}
var lastLogLine = default(LogLine);
foreach (var line in allLines)
{
var strings = line.Split('|');
if (strings.Count() == 4)
{
lastLogLine = new LogLine(strings[0], strings[1], strings[2], strings[3]);
logLines.Add(lastLogLine);
}
}
}
private async Task LogLevelInput(ChangeEventArgs e)
{
var value = e.Value ?? NLog.LogLevel.Debug.Name;
await _rpcClientService.CallAsync<bool>("LoggingService", "SetLogLevel", value);
}
private async Task LogLevelFilterInput(ChangeEventArgs e)
{
var value = e.Value as string ?? NLog.LogLevel.Debug.Name;
logLevelFilter = NLog.LogLevel.FromString(value);
}
}
@@ -0,0 +1,181 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal modal-lg" id="ditheringModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Test dithering</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form class="row g-1">
<div class="row">
@if (displayPresets != null)
{
<div class="mb-2">
<label class="form-label" for="mode">Display</label>
<select class="form-select" id="mode" @bind="SelectedDisplayId" disabled="@(displayPresets.Count <= 1)">
@foreach (var display in displayPresets)
{
<option value="@display.DisplayId">@display.displayName</option>
}
</select>
</div>
}
</div>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
<div class="row">
<div class="mb-2">
<label class="form-label" for="state">State</label>
<select class="form-select" id="state" @bind="Preset.NvDitherState" @bind:after="ApplyNormalPreset">
@foreach (var state in Enum.GetValues<NvDitherState>())
{
<option value="@state.ToString()">@state.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="mode">Mode</label>
<select class="form-select" id="mode" @bind="Preset.NvDitherMode" @bind:after="ApplyNormalPreset">
@foreach (var mode in Enum.GetValues<NvDitherMode>())
{
<option value="@mode.ToString()">@mode.GetDescription()</option>
}
</select>
</div>
<div class="mb-3">
<label class="form-label" for="bits">Bit depth</label>
<select class="form-select" id="bits" @bind="Preset.NvDitherBits" @bind:after="ApplyNormalPreset">
@foreach (var bits in Enum.GetValues<NvDitherBits>())
{
<option value="@bits.ToString()">@bits.GetDescription()</option>
}
</select>
</div>
</div>
<div class="">
<div class="btn-group ms-2 mb-2">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false">
Gradient
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" @onclick="() => GradientColor = GrayGradient">Grayscale</a></li>
<li><a class="dropdown-item" @onclick="() => GradientColor = RedGradient">Red</a></li>
<li><a class="dropdown-item" @onclick="() => GradientColor = GreenGradient">Green</a></li>
<li><a class="dropdown-item" @onclick="() => GradientColor = BlueGradient">Blue</a></li>
</ul>
</div>
<div class="mb-2 col-24 mx-2" style="height: 200px; background: linear-gradient(to right, #000000 0%, @GradientColor 100%)">
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<span class="me-auto">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="SetDitherRegistryKeyOnClick">Set dither registry key</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="RestartDriverOnClick">Restart driver</button>
</span>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
@code {
public const string GrayGradient = "#ffffff";
public const string RedGradient = "#ff0000";
public const string GreenGradient = "#00ff00";
public const string BlueGradient = "#0000ff";
private bool _visible;
[Parameter]
public bool Visible { get; set; }
public NvPreset? Preset { get; set; }
private string? SelectedDisplayId { get; set; }
private List<NvPreset>? displayPresets;
private string GradientColor { get; set; } = GrayGradient;
protected override async Task OnParametersSetAsync()
{
if (_visible != Visible)
{
_visible = Visible;
if (Visible)
{
await GetSettingsAsync();
}
}
}
protected async Task GetSettingsAsync()
{
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
if (displayPresets?.Any() == true)
{
Preset = displayPresets.FirstOrDefault(d => d.DisplayConfig.IsPrimary == true) ?? displayPresets.First();
SelectedDisplayId = Preset.DisplayId;
}
}
private async Task DitherOnChange(EventArgs e)
{
if (Preset == null)
{
return;
}
Preset.applyDithering = true;
await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", Preset.id);
}
private async Task ApplyNormalPreset()
{
await ApplyPreset();
}
private async Task SetDitherRegistryKeyOnClick(MouseEventArgs e)
{
await ApplyPreset(true);
}
private async Task RestartDriverOnClick(MouseEventArgs e)
{
await ApplyPreset(true, true);
}
private async Task ApplyPreset(bool setRegistryKey = false, bool restartDriver = false)
{
if (Preset == null)
{
return;
}
Preset.applyDithering = true;
Preset.SetDitherRegistryKey = setRegistryKey;
Preset.RestartDriver = restartDriver;
await _rpcClientService.CallAsync<bool>("NvService", "ApplyPreset", Preset);
}
}
@@ -0,0 +1,247 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal modal-lg" id="driverSettingsModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" @onfocus="OnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset != null ? Preset.IsDisplayPreset ? $"Driver settings for display {Preset.displayName}" : $"Driver settings for {Preset.IdOrName}" : "Driver settings")</h5>
@if (Preset == null)
{
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
}
else
{
<button type="button" class="btn-close" data-bs-toggle="modal" data-bs-target="#presetModal" aria-label="Close" @onclick="() => IsVisible = false"></button>
}
</div>
<div class="modal-body">
<form class="row g-1">
@if (ProfileNames != null)
{
<div class="mb-2">
<label class="form-label" for="profileName">Profile</label>
<select class="form-select" id="profileName" @bind="CurrentProfileName" @bind:after="ProfileOnChange">
@foreach (var profileName in ProfileNames)
{
<option value="@profileName">@profileName</option>
}
</select>
</div>
}
<div class="accordion accordion-flush" id="driverSettingsAccordion">
@if (Groups != null)
{
foreach (var group in Groups)
{
var groupId = $"{group.Name.Replace(' ', '_')}Settings";
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2 @(group.HasChangedSettings ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#@groupId">
@group.Name
</button>
</h2>
<div id="@groupId" class="accordion-collapse collapse" data-bs-parent="#driverSettingsAccordion">
<div class="accordion-body">
@foreach (var setting in group.DriverSettings)
{
if (NvSettingConstants.RangeDriverSettings.Contains(setting.SettingId) && setting.Values != null)
{
<div class="mb-2">
<label class="form-label @(setting.Value != setting.DefaultValue && setting.Value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "")" for="@setting.SettingId">@setting.SettingText (@setting.Value)</label>
<input class="form-range" type="range" min="@setting.Values.Min(v => v.Value)" max="@setting.Values.Max(v => v.Value)" @bind="setting.Value" id="@setting.SettingId">
</div>
}
else if (setting.Values != null)
{
<div class="mb-2">
<label class="form-label @(setting.Value != setting.DefaultValue && setting.Value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "")" for="@setting.SettingId">@setting.SettingText</label>
<select class="form-select" id="@setting.SettingId" @bind="setting.Value">
@if (Preset != null)
{
<option value="@(NvSettingConstants.UnsetDwordValue)">Unchanged</option>
}
@foreach (var settingValue in setting.Values)
{
<option value="@settingValue.Value" class="@(setting.DefaultValue == settingValue.Value ? "text-success" : "")">@settingValue.ValueName</option>
}
</select>
</div>
}
}
</div>
</div>
</div>
}
}
</div>
</form>
</div>
<div class="modal-footer">
@if (Preset == null)
{
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Apply</button>
}
else
{
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => IsVisible = false">Close</button>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="ApplyClick">Save</button>
}
</div>
</div>
</div>
</div>
@code {
public class GroupedSettings
{
public string Name { get; private set; }
public List<NvSettingItemDto> DriverSettings { get; private set; }
public bool HasChangedSettings { get; private set; }
public GroupedSettings(string name, List<NvSettingItemDto> settings, bool hasChangedSettings)
{
Name = name;
DriverSettings = settings;
HasChangedSettings = hasChangedSettings;
}
}
private NvPreset? _preset;
[Parameter]
public NvPreset? Preset { get; set; }
[Parameter]
public Func<NvPreset, Task>? AfterApply { get; set; }
private List<GroupedSettings>? Groups { get; set; }
private List<NvSettingItemDto>? DriverSettings { get; set; }
private List<string>? ProfileNames { get; set; }
private string? CurrentProfileName { get; set; }
private bool IsVisible { get; set; }
protected override async Task OnParametersSetAsync()
{
if (_preset != Preset)
{
_preset = Preset;
CurrentProfileName = Preset?.DriverProfileName;
DriverSettings = null;
await LoadDriverSettings();
}
}
protected async Task LoadDriverSettings()
{
if (DriverSettings == null)
{
DriverSettings = await _rpcClientService.CallAsync<List<NvSettingItemDto>>("NvService", "GetDriverSettings", CurrentProfileName);
if (DriverSettings != null)
{
DriverSettings.ForEach(s => s.ValueChanged = false);
}
}
if (DriverSettings != null)
{
if (Preset != null)
{
foreach (var setting in DriverSettings)
{
setting.Value = setting.DefaultValue;
setting.ValueChanged = false;
}
foreach (var presetSetting in Preset.driverSettings)
{
var setting = DriverSettings.FirstOrDefault(s => s.SettingId == presetSetting.Key);
if (setting == null)
{
continue;
}
setting.Value = presetSetting.Value;
//setting.ValueChanged = false;
}
}
Groups = DriverSettings.OrderBy(s => s.GroupName).GroupBy(s => s.GroupName).Select(g =>
{
var settings = g.ToList();
var changedSettings = settings.Any(s => s.Value != s.DefaultValue && s.Value != NvSettingConstants.UnsetDwordValue);
var group = new GroupedSettings(g.Key, g.ToList(), changedSettings);
return group;
}).ToList();
}
StateHasChanged();
}
private async Task ApplyClick(MouseEventArgs e)
{
if (DriverSettings == null)
{
return;
}
var changedSettings = DriverSettings.Where(s => s.ValueChanged);
if (Preset != null)
{
Preset.UpdateDriverSettings(changedSettings);
return;
}
var changedSettingsDictionary = changedSettings.ToDictionary(k => k.SettingId, v => v.Value);
var result = await _rpcClientService.CallAsync<bool>("NvService", "SetDriverSettings", changedSettingsDictionary, CurrentProfileName);
}
private async Task ProfileOnChange()
{
if (CurrentProfileName?.Contains("Base") == true)
{
CurrentProfileName = null;
}
if (Preset != null)
{
Preset.DriverProfileName = CurrentProfileName;
}
DriverSettings = null;
await LoadDriverSettings();
}
private async Task OnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
IsVisible = true;
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames");
await LoadDriverSettings();
}
}
}
@@ -0,0 +1,618 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
@if (!Preset.IsDisplayPreset)
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" type="text" @bind="Preset.name" required />
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
<label class="form-check-label" for="showInQuickAccess">
Show in Quick Access
</label>
</div>
<ShortcutInput Label="Shortcut" @bind-Shortcut="Preset.shortcut" />
}
<div class="accordion accordion-flush" id="presetAccordion">
@if (!Preset.IsDisplayPreset)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem; visibility: hidden" type="checkbox">
}
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
Display
</button>
</span>
</h2>
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.primaryDisplay" id="primaryDisplay">
<label class="form-check-label" for="primaryDisplay">
Primary display
</label>
</div>
@if (displayPresets != null)
{
<div class="mb-2">
<label class="form-label" for="mode">Display</label>
<select class="form-select" id="mode" @bind="Preset.DisplayId" disabled="@(Preset.primaryDisplay)">
@foreach (var display in displayPresets)
{
<option value="@display.DisplayId">@display.displayName</option>
}
</select>
</div>
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.DisplayConfig.IsPrimary" id="setAsPrimaryDisplay" disabled="@(Preset.primaryDisplay)">
<label class="form-check-label" for="setAsPrimaryDisplay">
Set display as primary display
</label>
</div>
}
</div>
</div>
</div>
}
@if (resolutions != null)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.DisplayConfig.ApplyResolution" id="applyResolution">
}
<button class="accordion-button py-2 @(!Preset.IsDisplayPreset ? "collapsed" : "") @(Preset.DisplayConfig.ApplyResolution ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#resolutionSettings">
Resolution
</button>
</span>
</h2>
<div id="resolutionSettings" class="accordion-collapse collapse @(Preset.IsDisplayPreset ? "show" : "")" data-bs-parent="#presetAccordion">
<div class="accordion-body">
@if (resolutions != null)
{
<div class="mb-2">
<label class="form-label" for="activeResolution">Active resolution</label>
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution">
<option value="0x0">Not set</option>
@foreach (var resolution in resolutions)
{
<option value="@resolution.GetActiveString()">@resolution.GetActiveString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="virtualResolution">Virtual resolution</label>
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution">
<option value="0x0">Not set</option>
@foreach (var resolution in resolutions)
{
<option value="@resolution.GetVirtualString()">@resolution.GetVirtualString()</option>
}
</select>
</div>
}
<div class="mb-2">
<label class="form-label" for="scaling">Scaling/Aspect ratio</label>
<select class="form-select" id="scaling" @bind="Preset.DisplayConfig.Scaling">
@foreach (var scaling in Enum.GetValues<Shared.Native.CCD.DisplayConfigScaling>().Where(s => s != Shared.Native.CCD.DisplayConfigScaling.ForceUint32))
{
<option value="@scaling.ToString()">@scaling.ToString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="rotation">Rotation</label>
<select class="form-select" id="rotation" @bind="Preset.DisplayConfig.Rotation">
@foreach (var rotation in Enum.GetValues<Shared.Native.CCD.DisplayConfigRotation>().Where(s => s != Shared.Native.CCD.DisplayConfigRotation.ForceUint32))
{
<option value="@rotation.ToString()">@rotation.ToString()</option>
}
</select>
</div>
</div>
</div>
</div>
}
@if (refreshRates != null)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.DisplayConfig.ApplyRefreshRate" id="applyRefreshRate">
}
<button class="accordion-button collapsed py-2 @(Preset.DisplayConfig.ApplyRefreshRate ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#refreshRateSettings">
Refresh Rate
</button>
</span>
</h2>
<div id="refreshRateSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
@if (refreshRates != null)
{
<div class="mb-2">
<label class="form-label" for="refreshRate">Refresh Rate</label>
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate">
@foreach (var refreshRate in refreshRates)
{
<option value="@refreshRate.ToString()">@refreshRate.ToString() Hz</option>
}
</select>
</div>
}
</div>
</div>
</div>
}
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyColorData" id="applyColorData">
}
<button class="accordion-button collapsed py-2 @(Preset.applyColorData ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#colorSettings">
Color settings
</button>
</span>
</h2>
<div id="colorSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.colorData.UseDefaultSettings" id="useDefaultSettings">
<label class="form-check-label" for="useDefaultSettings">
Use default settings
</label>
</div>
<div class="mb-2">
<label class="form-label" for="bitDepth">Bit depth</label>
<select class="form-select" id="bitDepth" @bind="Preset.colorData.ColorDepth" disabled="@Preset.colorData.UseDefaultSettings">
@foreach (var bitDepth in Enum.GetValues<ColorDataDepth>())
{
<option value="@bitDepth.ToString()">@bitDepth.ToString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="colorFormat">Format</label>
<select class="form-select" id="colorFormat" @bind="Preset.colorData.ColorFormat" disabled="@Preset.colorData.UseDefaultSettings">
@foreach (var format in Enum.GetValues<ColorDataFormat>())
{
<option value="@format.ToString()">@format.ToString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="dynamicRange">Dynamic range</label>
<select class="form-select" id="dynamicRange" @bind="Preset.colorData.DynamicRange" disabled="@Preset.colorData.UseDefaultSettings">
@foreach (var range in Enum.GetValues<ColorDataDynamicRange>())
{
<option value="@range.ToString()">@range.ToString()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="colorimetry">Color space</label>
<select class="form-select" id="colorimetry" @bind="Preset.colorData.Colorimetry" disabled="@Preset.colorData.UseDefaultSettings">
@foreach (var space in Enum.GetValues<ColorDataColorimetry>())
{
<option value="@space.ToString()">@space.ToString()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.ApplyColorEnhancements" id="ApplyColorEnhancements">
}
<button class="accordion-button collapsed py-2 @(Preset.ApplyColorEnhancements ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#colorEnhancements">
Color enhancements
</button>
</span>
</h2>
<div id="colorEnhancements" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="digitalVibrance">Digital vibrance: @Preset.ColorEnhancementSettings.DigitalVibranceLevel</label>
<input class="form-range" type="range" min="0" max="100" @bind="Preset.ColorEnhancementSettings.DigitalVibranceLevel" id="digitalVibrance">
</div>
<div class="mb-2">
<label class="form-label" for="hue">Hue: @Preset.ColorEnhancementSettings.HueAngle°</label>
<input class="form-range" type="range" min="0" max="359" @bind="Preset.ColorEnhancementSettings.HueAngle" id="hue">
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyDriverSettings" id="applyDriverSettings">
}
<button class="accordion-button collapsed py-2 @(Preset.applyDriverSettings ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#driverSettings">
Driver settings
</button>
</span>
</h2>
<div id="driverSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-3">
<button class="btn btn-secondary btn-sm" type="button" data-bs-target="#driverSettingsModal" data-bs-toggle="modal" @onclick="DriverSettingsOnClick">Open driver settings</button>
<span>@Preset.driverSettings.Count settings configured</span>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.ApplyNovideoSettings" id="applyNovideo">
}
<button class="accordion-button collapsed py-2 @(Preset.ApplyNovideoSettings ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#novideoSettings">
Novideo settings
</button>
</span>
</h2>
<div id="novideoSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.NovideoSettings.ApplyClamp" id="applyClamp">
<label class="form-check-label" for="applyClamp">
Apply clamp
</label>
</div>
<div class="mb-2">
<label class="form-label" for="novideoColorSpace">Target color space</label>
<select class="form-select" id="novideoColorSpace" @bind="Preset.NovideoSettings.ColorSpace">
@foreach (var colorSpace in Enum.GetValues<NovideoColorSpace>())
{
<option value="@colorSpace.ToString()">@colorSpace.ToString()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyDithering" id="applyDithering">
}
<button class="accordion-button collapsed py-2 @(Preset.applyDithering ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#ditherSettings">
Dithering
</button>
</span>
</h2>
<div id="ditherSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="state">State</label>
<select class="form-select" id="state" @bind="Preset.NvDitherState">
@foreach (var state in Enum.GetValues<NvDitherState>())
{
<option value="@state.ToString()">@state.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="mode">Mode</label>
<select class="form-select" id="mode" @bind="Preset.NvDitherMode">
@foreach (var mode in Enum.GetValues<NvDitherMode>())
{
<option value="@mode.ToString()">@mode.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="bits">Bit depth</label>
<select class="form-select" id="bits" @bind="Preset.NvDitherBits">
@foreach (var bits in Enum.GetValues<NvDitherBits>())
{
<option value="@bits.ToString()">@bits.GetDescription()</option>
}
</select>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyHDR" id="applyHDR">
}
<button class="accordion-button collapsed py-2 @(Preset.applyHDR ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#hdrSettings">
HDR
</button>
</span>
</h2>
<div id="hdrSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.HDREnabled" id="hdrEnabled">
<label class="form-check-label" for="hdrEnabled">
HDR enabled
</label>
</div>
@if (!Preset.IsDisplayPreset) {
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.toggleHDR" id="toggleHdr">
<label class="form-check-label" for="toggleHdr">
Toggle HDR
</label>
</div>
}
<div class="mb-2">
<label class="form-label" for="mode">Output mode</label>
<select class="form-select" id="mode" @bind="Preset.HdrSettings.DisplayOutputMode">
@foreach (var mode in Enum.GetValues<NvOutputMode>())
{
<option value="@mode.ToString()">@mode.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
</div>
</div>
</div>
</div>
@if (!Preset.IsDisplayPreset || Preset.MonitorConnectionType == NvAPIWrapper.Native.GPU.MonitorConnectionType.HDMI)
{
<div class="accordion-item">
<h2 class="accordion-header">
<span class="d-flex align-items-baseline">
@if (!Preset.IsDisplayPreset)
{
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyHdmiSettings" id="applyHdmiSettings">
}
<button class="accordion-button collapsed py-2 @(Preset.applyHdmiSettings ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#hdmiSettings">
HDMI settings
</button>
</span>
</h2>
<div id="hdmiSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<div class="mb-2">
<label class="form-label" for="contentType">Content type</label>
<select class="form-select" id="contentType" @bind="Preset.HdmiInfoFrameSettings.ContentType" @oninput="HdmiSettingsOnInput">
@foreach (var type in Enum.GetValues<InfoFrameVideoContentType>())
{
<option value="@type.ToString()">@type.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="colorimetry">Colorimetry</label>
<select class="form-select" id="colorimetry" @bind="Preset.HdmiInfoFrameSettings.Colorimetry" @oninput="HdmiSettingsOnInput">
@foreach (var colorimetry in Enum.GetValues<InfoFrameVideoColorimetry>())
{
<option value="@colorimetry.ToString()">@colorimetry.GetDescription()</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="extendedColorimetry">Extended colorimetry</label>
<select class="form-select" id="extendedColorimetry" @bind="Preset.HdmiInfoFrameSettings.ExtendedColorimetry" @oninput="HdmiSettingsOnInput">
@foreach (var extendedColorimetry in Enum.GetValues<InfoFrameVideoExtendedColorimetry>())
{
<option value="@extendedColorimetry.ToString()">@extendedColorimetry.GetDescription()</option>
}
</select>
</div>
</div>
</div>
</div>
}
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">@(Preset?.IsDisplayPreset == true ? "Apply" : "Save")</button>
</div>
</div>
</div>
</div>
@code {
public NvPreset? Preset { get; set; }
[Parameter]
public NvPreset? PresetParam { get; set; }
[Parameter]
public Func<NvPreset, Task>? AfterApply { get; set; }
[Parameter]
public Func<NvPreset, Task>? SetDriverSettingsPreset { get; set; }
private string? selectedActiveResolution;
private string? selectedVirtualResolution;
private string? selectedRefreshRate;
private List<VirtualResolution>? resolutions;
private List<Rational>? refreshRates;
private List<NvPreset>? displayPresets;
private string WasValidated = "";
private bool IsVisible;
private bool IsDriverSettingsOpened;
protected override async Task OnParametersSetAsync()
{
if (IsDriverSettingsOpened && Preset != null)
{
return;
}
Preset = PresetParam == null ? null : PresetParam.CloneWithId();
if (Preset != null && PresetParam != null)
{
resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("NvService", "GetAvailableResolutionsV2", Preset);
refreshRates = await _rpcClientService.CallAsync<List<Rational>>("NvService", "GetAvailableRefreshRatesV2", Preset);
}
selectedActiveResolution = Preset?.DisplayConfig.Resolution?.GetActiveString();
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
if (Preset?.IsDisplayPreset != true)
{
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
}
}
protected override void OnAfterRender(bool firstRender)
{
// if (IsVisible && Preset?.IsDisplayPreset == false)
// {
// Preset.UpdateAutoApplySettings(keepChanges: true);
// }
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
if (newResolution != null)
{
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
{
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
var parts = selectedVirtualResolution.Split('x');
newResolution.VirtualWidth = uint.Parse(parts[0]);
newResolution.VirtualHeight = uint.Parse(parts[1]);
}
Preset.DisplayConfig.Resolution = newResolution;
}
else
{
Preset.DisplayConfig.Resolution = new VirtualResolution(0, 0);
}
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
if (newRefreshRate != null)
{
Preset.DisplayConfig.RefreshRate = newRefreshRate;
}
Preset.ApplyNovideoSettings = Preset.NovideoSettings.ApplyClamp;
if (Preset.IsDisplayPreset)
{
Preset.UpdateAutoApplySettings(PresetParam);
await ApplyDisplayPreset();
}
else
{
var result = await _rpcClientService.CallAsync<bool>("NvService", "UpdatePreset", Preset);
}
if (AfterApply != null)
{
await AfterApply.Invoke(Preset);
}
}
private async Task ApplyDisplayPreset()
{
var result = await _rpcClientService.CallAsync<bool>("NvService", "ApplyPreset", Preset);
}
private void HdmiSettingsOnInput(ChangeEventArgs e)
{
Preset!.applyHdmiSettings = true;
}
private async Task DriverSettingsOnClick(MouseEventArgs e)
{
IsDriverSettingsOpened = true;
SetDriverSettingsPreset?.Invoke(Preset!);
}
private void ModalOnFocus(FocusEventArgs e)
{
if (IsVisible && IsDriverSettingsOpened)
{
IsDriverSettingsOpened = false;
StateHasChanged();
}
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
private bool IsValid()
{
return Preset?.IsDisplayPreset == true || !string.IsNullOrEmpty(Preset?.name);
}
}
@@ -0,0 +1,76 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">NVIDIA settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => Config = null"></button>
</div>
<div class="modal-body">
<form class="row g-1">
@if (Config == null)
{
<div>Loading...</div>
}
else
{
<ShortcutInput Label="Quick Access Shortcut" @bind-Shortcut="Config.QuickAccessShortcut" />
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Config.ShowOverclocking" id="showOverclocking">
<label class="form-check-label" for="showOverclocking">
Show overclocking
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Config.ApplyNovideoOnStartup" id="applyNovideoOnStartup">
<label class="form-check-label" for="applyNovideoOnStartup">
Apply Novideo settings on startup
</label>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => Config = null">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private NvServiceConfig? Config { get; set; }
private async Task ApplyClick(MouseEventArgs e)
{
if (Config == null)
{
return;
}
var result = await _rpcClientService.CallAsync<bool>("NvService", "UpdateConfig", Config);
if (result)
{
Config = null;
}
}
private async Task OnFocus(FocusEventArgs e)
{
if (Config != null)
{
return;
}
Config = await _rpcClientService.CallAsync<NvServiceConfig>("NvService", "GetConfig");
}
}
@@ -0,0 +1,251 @@
@page "/NVIDIA"
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.Shared.Common;
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject ProtectedLocalStorage _localStorage
<PageTitle>NVIDIA controller</PageTitle>
<NvidiaSettings />
<NvidiaDithering Visible="DitheringVisible" />
<NvidiaDriverSettings Preset="CurrentDriverSettingsPreset" />
<div class="d-flex align-items-center">
<h1>NVIDIA controller</h1>
<div class="d-flex ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false">
Tools
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#ditheringModal" @onclick="() => DitheringVisible = true">Test dithering</a></li>
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#driverSettingsModal" @onclick="() => CurrentDriverSettingsPreset = null">Driver settings</a></li>
</ul>
</div>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
Settings
</button>
</div>
</div>
<h2>Displays</h2>
<NvidiaPreset PresetParam="CurrentPreset" AfterApply="AfterPresetApplied" SetDriverSettingsPreset="SetDriverSettingsPreset" />
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
@if (DisplayPresets == null)
{
<div>Loading...</div>
}
else
{
<div class="row mb-3">
<div class="col">
@if (!DisplayPresets.Any())
{
<span>No displays found</span>
}
@foreach (var preset in DisplayPresets)
{
<div class="card mb-3">
<div class="card-header">
@preset.displayName
@if (preset.DisplayConfig.IsPrimary == true)
{
<span class="ms-2 badge text-bg-primary" title="Primary display">Primary</span>
}
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => DisplayPresetOnClick(preset)">
Settings
</button>
</div>
<div class="card-body">
<small class="text-body">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</small>
<small class="text-muted">@preset.colorData</small>
</div>
</div>
}
</div>
</div>
}
@if (Presets == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Presets</h2>
<div class="d-flex ms-auto">
<select class="form-select me-2" id="presetOrder" @bind="PresetOrder" @bind:after="PresetOrderChanged">
@foreach (var order in Enum.GetValues<PresetOrder>())
{
<option value="@order">Order by: @order.GetDescription()</option>
}
</select>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => AddPresetOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
<div class="col">
<div class="list-group">
@foreach (var preset in Presets)
{
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
<div class="d-flex align-items-start justify-content-between">
<div>
<h5 class="mb-1">
@preset.name <small class="text-muted">@(preset.primaryDisplay ? "Primary display" : preset.displayName)</small>
@if (preset.ShowInQuickAccess)
{
<span class="ms-2 badge text-bg-warning" title="Show in Quick Access">QA</span>
}
</h5>
</div>
<span>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
</span>
</div>
<p class="mb-1">
@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz
@(preset.applyColorData ? " " + preset.colorData : "")
@if (preset.ApplyColorEnhancements)
{
<span>, @preset.ColorEnhancementSettings</span>
}
@if (preset.applyHDR)
{
<span>, HDR: @preset.GetHdrDescription()</span>
}
@if (preset.applyDriverSettings)
{
<span>, @preset.driverSettings.Count changed driver settings</span>
}
@if (preset.ApplyNovideoSettings)
{
<span>, @preset.NovideoSettings</span>
}
</p>
<small class="text-muted">@preset.shortcut</small>
</a>
}
</div>
</div>
</div>
}
@code {
private List<NvPreset>? Presets;
private List<NvPreset>? DisplayPresets;
private NvPreset? CurrentPreset;
private NvPreset? CurrentDriverSettingsPreset;
private bool DitheringVisible;
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await RefreshPresets();
DisplayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
}
protected override async Task OnAfterRenderAsync(bool firstTime)
{
if (firstTime)
{
PresetOrder = await _localStorage.TryGet<PresetOrder>("NvidiaPresetOrder");
Presets = Presets?.OrderPresetsBy(PresetOrder);
StateHasChanged();
}
}
private async Task PresetOnClick(NvPreset preset)
{
await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", preset.id);
}
private async Task PresetOnEditClick(NvPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnCloneClick(NvPreset preset)
{
CurrentPreset = new NvPreset(preset);
CurrentPreset.SetCloneValues();
}
private async Task PresetOnDeleteClick(NvPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnDeleteConfirmed()
{
if (CurrentPreset == null)
{
return;
}
await _rpcClientService.CallAsync<bool>("NvService", "DeletePreset", CurrentPreset.id);
await RefreshPresets();
StateHasChanged();
}
private async Task DisplayPresetOnClick(NvPreset preset)
{
CurrentPreset = preset;
}
private async Task AddPresetOnClick()
{
CurrentPreset = new NvPreset();
}
private async Task AfterPresetApplied(NvPreset preset)
{
if (preset.IsDisplayPreset)
{
DisplayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
}
else
{
await RefreshPresets();
}
StateHasChanged();
}
private async Task SetDriverSettingsPreset(NvPreset preset)
{
CurrentDriverSettingsPreset = preset;
StateHasChanged();
}
private async Task PresetOrderChanged()
{
await _localStorage.SetAsync("NvidiaPresetOrder", PresetOrder);
await RefreshPresets();
}
private async Task RefreshPresets()
{
Presets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetPresets");
Presets = Presets?.OrderPresetsBy(PresetOrder);
}
}
+300 -32
View File
@@ -1,52 +1,320 @@
@page "/options"
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject OptionsService _optionsService
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<PageTitle>Options</PageTitle>
<h1>Options</h1>
<div class="card">
<div class="card-body">
<h5 class="card-title">General</h5>
@if (config == null)
{
<div>Loading...</div>
}
else
{
<div class="row">
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">General</h5>
<form>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.AutoStart" id="startAfterLogin" @oninput="StartAfterLoginInput">
<label class="form-check-label" for="startAfterLogin">
Automatically start after login
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.CheckForUpdates" id="checkForUpdates" @oninput="CheckForUpdatesInput">
<label class="form-check-label" for="checkForUpdates">
Automatically check for updates
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.AutoInstallUpdates" id="autoInstallUpdates" @oninput="AutoInstallUpdatesInput">
<label class="form-check-label" for="autoInstallUpdates">
Automatically install updates
</label>
</div>
<div class="mb-3">
<label class="form-label" for="elevationMethod">
Elevation Method
</label>
<select class="form-select" @bind="elevationMethod" id="elevationMethod" @oninput="ElevationMethodInput" aria-describedby="elevationMethodHelp">
<option value="0">None</option>
<option value="1">Run as administrator</option>
<option value="2">Use service</option>
<option value="3">Use elevated process</option>
</select>
<div id="elevationMethodHelp" class="form-text">
Elevation is needed in some cases where ColorControl needs administrator rights.
Some operations like installing a service, changing the priority of a process or creating a temporary IP-route for improved WOL-functionality will not work without those rights.
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">UI-settings</h5>
<form>
<div class="mb-2">
<label class="form-label" for="uiType">Default UI</label>
<select class="form-select" id="uiType" @bind="config.UiType" @oninput="UiTypeOnInput">
@foreach (var type in Enum.GetValues<UiType>())
{
<option value="@type.ToString()">@type.GetDescription()</option>
}
</select>
</div>
<div class="mb-3">
<label class="form-label" for="webUiPort">
Web UI port
</label>
<input class="form-control" type="number" @bind="config.UiPort" id="webUiPort" @bind:after="UiPortBlur">
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.MinimizeOnClose" id="minimizeOnClose" @oninput="MinimizeOnCloseInput">
<label class="form-check-label" for="minimizeOnClose">
Minimize on close
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.StartMinimized" id="startMinimized" @oninput="StartMinimizedInput">
<label class="form-check-label" for="startMinimized">
Start minimized
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.MinimizeToTray" id="minimizeToTray" @oninput="MinimizeToTrayInput">
<label class="form-check-label" for="minimizeToTray">
Minimize to system tray
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.UseDarkMode" id="useDarkMode" @oninput="UseDarkModeInput">
<label class="form-check-label" for="useDarkMode">
Dark Mode
</label>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Modules</h5>
<form>
@foreach (var module in config.Modules)
{
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="@("module_" + module.DisplayName)" @bind="module.IsActive" @oninput="(ChangeEventArgs e) => ModuleOnInput(module, e)">
<label class="form-check-label" for="@("module_" + module.DisplayName)">@module.DisplayName</label>
</div>
}
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Advanced</h5>
<form>
<div class="mb-3 form-check">
<label class="form-range" for="processMonitorPollingInterval">
Polling interval of process monitor (@config.ProcessMonitorPollingInterval milliseconds)
</label>
<input class="form-range" type="range" min="50" max="10000" step="50" @bind="config.ProcessMonitorPollingInterval" id="processMonitorPollingInterval" aria-describedby="processMonitorPollingIntervalHelp" @bind:after="PollingIntervalOnInput">
<div id="processMonitorPollingIntervalHelp" class="form-text">Decreasing this delay may execute triggered presets sooner but can cause a higher CPU load</div>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.UseRawInput" id="useRawInput" aria-describedby="useRawInputHelp" @oninput="UseRawInputInput">
<label class="form-check-label" for="useRawInput">
Use Raw Input for shortcuts (hot keys)
</label>
<div id="useRawInputHelp" class="form-text">This enables shortcuts to work during applications/games that block certain keys (like WinKey or Control). NOTE: if the application in the foreground runs with higher privileges than ColorControl, Raw Input does not work and normal hot keys are used</div>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.SetMinTmlAndMaxTml" id="setMinTmlAndMaxTml" @oninput="SetMinMaxTmlInput">
<label class="form-check-label" for="setMinTmlAndMaxTml">
Set MinTML and MaxTML when applying color profiles
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.DisableErrorPopupWhenApplyingPreset" id="disableErrorPopupWhenApplyingPreset" @oninput="DisableErrorPopupInput">
<label class="form-check-label" for="disableErrorPopupWhenApplyingPreset">
Disable error popup when applying presets
</label>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Miscellaneous</h5>
<form>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.FixChromeFonts" id="fixChromeFonts" aria-describedby="fixChromeFontsHelp" @bind:after="FixChromeFontsInput">
<label class="form-check-label" for="fixChromeFonts">
ClearType: fix the bad fonts in Chrome when using a monitor with a different pixel structure
</label>
<div id="fixChromeFontsHelp" class="form-text">This will add the parameter --disable-lcd-text to Chrome which enables grayscale anti-aliasing</div>
</div>
<ShortcutInput Label="Shortcut to start the blank screensaver" @bind-Shortcut="config.ScreenSaverShortcut" @bind-Shortcut:after="UpdateScreenSaverShortcut" />
</form>
</div>
</div>
</div>
</div>
<form class="ms-3">
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.AutoStart" id="startAfterLogin">
<label class="form-check-label" for="startAfterLogin">
Automatically start after login
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.StartMinimized" id="startMinimized">
<label class="form-check-label" for="startMinimized">
Start minimized
</label>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.MinimizeToTray" id="minimizeToTray">
<label class="form-check-label" for="minimizeToTray">
Minimize to tray
</label>
</div>
<div class="mb-3">
<button class="btn btn-primary" @onclick="SaveConfig">Save</button>
</div>
</form>
</div>
}
@code {
private Config config;
private Config? config;
private string elevationMethod = "0";
protected override async Task OnInitializedAsync()
{
config = _optionsService.GetConfig();
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
elevationMethod = ((int)config.ElevationMethod).ToString();
}
private void SaveConfig()
private async Task SaveConfig()
{
_optionsService.SetConfig(config);
await _rpcClientService.CallAsync<bool>("OptionsService", "SetConfig", config);
}
private async Task UseDarkModeInput(ChangeEventArgs e)
{
var value = e.Value != null ? (bool)e.Value : false;
var result = await UpdateBoolSetting("SetDarkMode", e);
if (result)
{
AppState.SetTheme(value ? "dark" : "light");
//ThemeTransitionName = AppState.SelectedTheme == "light" ? "Dark theme" : "Light theme";
_navigationManager.Refresh();
}
}
private async Task StartAfterLoginInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetStartAfterLogin", e);
}
private async Task MinimizeOnCloseInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetMinimizeOnClose", e);
}
private async Task StartMinimizedInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetStartMinimized", e);
}
private async Task MinimizeToTrayInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetMinimizeToTray", e);
}
private async Task CheckForUpdatesInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetCheckForUpdates", e);
}
private async Task AutoInstallUpdatesInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetAutoInstallUpdates", e);
}
private async Task ModuleOnInput(Module module, ChangeEventArgs e)
{
module.IsActive = e.Value != null ? (bool)e.Value : false;
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "UpdateModule", module);
if (result)
{
_navigationManager.NavigateTo("/options", true);
}
}
private async Task PollingIntervalOnInput()
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetProcessMonitorPollingInterval", config!.ProcessMonitorPollingInterval);
}
private async Task UseRawInputInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetUseRawInput", e);
}
private async Task SetMinMaxTmlInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetMinTmlAndMaxTml", e);
}
private async Task DisableErrorPopupInput(ChangeEventArgs e)
{
await UpdateBoolSetting("SetDisableErrorPopupWhenApplyingPreset", e);
}
private async Task FixChromeFontsInput()
{
await UpdateBoolSetting2("SetFixChromeFonts", config!.FixChromeFonts);
}
private async Task UpdateScreenSaverShortcut()
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetScreenSaverShortcut", config!.ScreenSaverShortcut);
}
private async Task<bool> UpdateBoolSetting(string method, ChangeEventArgs e)
{
var value = e.Value != null ? (bool)e.Value : false;
return await UpdateBoolSetting2(method, value);
}
private async Task<bool> UpdateBoolSetting2(string method, bool value)
{
return await _rpcClientService.CallAsync<bool>("OptionsService", method, value);
}
private async Task ElevationMethodInput(ChangeEventArgs e)
{
var value = e.Value != null ? (ElevationMethod)int.Parse((string)e.Value) : ElevationMethod.None;
await _rpcClientService.CallAsync<bool>("OptionsService", "SetElevationMethod", value);
config!.ElevationMethod = value;
}
private async Task UiTypeOnInput(ChangeEventArgs e)
{
if (Enum.TryParse<UiType>(e.Value?.ToString(), out var uiType))
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetUiType", uiType);
config!.UiType = uiType;
}
}
private async Task UiPortBlur()
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetUiPort", config!.UiPort);
}
}
@@ -0,0 +1,237 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.Services
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="deviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Device?.Name == null ? $"Add new device" : $"Settings of device {Device.Name}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Device == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" id="name" type="text" @bind="Device.Name" required />
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" @bind="Device.IsCustom" id="isCustom">
<label class="form-check-label" for="isCustom">
Is custom device
</label>
</div>
<div class="mb-2">
<label class="form-label" for="ipAddress">IP-address</label>
<input class="form-control" id="ipAddress" type="text" @bind="Device.IpAddress" disabled="@(!Device.IsCustom)" required="Device.IsCustom" />
</div>
<div class="mb-2">
<label class="form-label" for="macAddress">MAC-address</label>
<input class="form-control" id="macAddress" type="text" @bind="Device.MacAddress" disabled="@(!Device.IsCustom)" />
</div>
@if (!Device.IsConnected)
{
<div class="mb-2">
<button class="btn btn-primary me-2" type="button" disabled="@IsTestingConnection" @onclick="TestConnectionClick">
@if (IsTestingConnection)
{
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
}
<span role="status">Test connection</span>
</button>
@if (IsTestConnectionSuccessful != null)
{
if (IsTestConnectionSuccessful == true)
{
<span class="text-success">Connection test was successful!</span>
}
else
{
<span class="text-danger">Connection failed!</span>
}
}
</div>
}
<div class="accordion accordion-flush" id="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
Device options
</button>
</h2>
<div id="deviceOptions" class="accordion-collapse collapse" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterStartup" @bind="Device.Options.PowerOnAfterStartup">
<label class="form-check-label" for="checkPowerOnAfterStartup">Power on after startup</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterResume" @bind="Device.Options.PowerOnAfterResume">
<label class="form-check-label" for="checkPowerOnAfterResume">Power on after resume from standby</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnShutdown" @bind="Device.Options.PowerOffOnShutdown">
<label class="form-check-label" for="checkPowerOffOnShutdown">Power off on shutdown</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnStandby" @bind="Device.Options.PowerOffOnStandby">
<label class="form-check-label" for="checkPowerOffOnStandby">Power off on standby</label>
</div>
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOffOnScreensaverStart" @bind="Device.Options.PowerOffOnScreenSaver">
<label class="form-check-label" for="checkPowerOffOnScreensaverStart">Power off when screensaver activates</label>
<button class="btn btn-sm btn-primary ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#screenSaverSettings" aria-expanded="false" aria-controls="screenSaverSettings">
Settings
</button>
</div>
<div class="collapse mb-3" id="screenSaverSettings">
<div class="card card-body">
<div class="mb-2">
<label class="form-label" for="minScreenSaverDuration">Minimal screen saver duration (seconds)</label>
<input class="form-control" id="minScreenSaverDuration" type="number" @bind="Device.Options.ScreenSaverMinimalDuration" />
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkTurnScreenOffInsteadOfPowerOff" @bind="Device.Options.TurnScreenOffOnScreenSaver">
<label class="form-check-label" for="checkTurnScreenOffInsteadOfPowerOff">Turn screen off instead of power off</label>
<div class="form-text">NOTE: turning the screen off might not work correctly on all models</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkTurnScreenOnInsteadOfPowerOn" @bind="Device.Options.TurnScreenOnAfterScreenSaver">
<label class="form-check-label" for="checkTurnScreenOnInsteadOfPowerOn">Turn screen on instead of power on</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPerformActionOnManualScreenSaver" @bind="Device.Options.HandleManualScreenSaver">
<label class="form-check-label" for="checkPerformActionOnManualScreenSaver">Perform action even on manually executed screen saver</label>
</div>
</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnOnScreensaverEnd" @bind="Device.Options.PowerOnAfterScreenSaver">
<label class="form-check-label" for="checkPowerOnOnScreensaverEnd">Power on when screensaver deactivates</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkPowerOnAfterManualPowerOff" @bind="Device.Options.PowerOnAfterManualPowerOff">
<label class="form-check-label" for="checkPowerOnAfterManualPowerOff">Power on even after manual power off</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkAllowTriggers" @bind="Device.Options.TriggersEnabled">
<label class="form-check-label" for="checkAllowTriggers">Allow triggers to be fired for this device</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkWindowsPowerOn" @bind="Device.Options.PowerOnByWindows">
<label class="form-check-label" for="checkWindowsPowerOn">Use Windows power settings to power on</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkWindowsPowerOff" @bind="Device.Options.PowerOffByWindows">
<label class="form-check-label" for="checkWindowsPowerOff">Use Windows power settings to power off</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkUseSecureConnection" @bind="Device.Options.UseSecureConnection">
<label class="form-check-label" for="checkUseSecureConnection">Use secure connection</label>
</div>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private SamsungDeviceDto? _device;
[Parameter]
public SamsungDeviceDto? Device { get; set; }
[Parameter]
public Func<SamsungDeviceDto, Task>? AfterApply { get; set; }
private bool IsTestingConnection;
private bool? IsTestConnectionSuccessful;
private string WasValidated = "";
private bool IsVisible;
protected override async Task OnParametersSetAsync()
{
if (_device != Device)
{
_device = Device;
}
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Device == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "UpdateDevice", Device);
if (AfterApply != null)
{
await AfterApply.Invoke(Device);
}
}
private async Task TestConnectionClick(MouseEventArgs e)
{
IsTestingConnection = true;
try
{
IsTestConnectionSuccessful = await _rpcClientService.CallWithOptionsAsync<bool>("SamsungService", "TestDevice", new RpcUiClientOptions { Timeout = 120000 }, Device);
}
finally
{
IsTestingConnection = false;
}
}
private void ModalOnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
private bool IsValid()
{
return !string.IsNullOrEmpty(Device?.Name) && (Device?.IsCustom != true || !string.IsNullOrEmpty(Device?.IpAddress));
}
}
@@ -0,0 +1,173 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@using static ColorControl.UI.Components.Pages.Generic.StepsInput<ColorControl.Shared.Contracts.Samsung.SamsungPreset>
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject AppState AppState
@inject NavigationManager _navigationManager;
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
</div>
<div class="modal-body">
<form class="row g-1 needs-validation @WasValidated" novalidate>
@if (Preset == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2">
<label class="form-label" for="name">Name</label>
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
</div>
@if (Devices != null)
{
<div class="mb-2">
<label class="form-label" for="device">Device</label>
<select class="form-select" id="device" @bind="Preset.DeviceMacAddress">
<option value="">Globally selected device</option>
@foreach (var device in Devices)
{
<option value="@device.MacAddress">@device.Name</option>
}
</select>
</div>
}
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
<label class="form-check-label" for="showInQuickAccess">
Show in Quick Access
</label>
</div>
<ShortcutInput Label="Shortcut" @bind-Shortcut="Preset.shortcut" />
<div class="accordion accordion-flush" id="presetAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
Advanced settings
</button>
</h2>
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<TriggerInput Triggers="Preset.Triggers"></TriggerInput>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
Steps
</button>
</h2>
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<StepsInput Label="Steps" @bind-Steps="Preset.Steps" CustomItems="CustomItems" ActionItems="CustomActions" SamsungPresetsEnabled="false" TPreset="SamsungPreset"></StepsInput>
<div class="mb-2">
<label class="form-label" for="description">Description</label>
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
</div>
</div>
</div>
</div>
</div>
}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
</div>
</div>
</div>
</div>
@code {
private SamsungPreset? Preset;
[Parameter]
public SamsungPreset? PresetParam { get; set; }
[Parameter]
public List<SamsungDeviceDto>? Devices { get; set; }
[Parameter]
public Func<SamsungPreset, Task>? AfterApply { get; set; }
private List<CustomStepItem>? CustomItems;
private List<ActionStepItem>? CustomActions;
private string WasValidated = "";
private bool IsVisible;
protected override async Task OnParametersSetAsync()
{
if (PresetParam == null)
{
return;
}
Preset = PresetParam.CloneWithId();
}
protected override async Task OnInitializedAsync()
{
CustomItems = [new CustomStepItem("RC buttons", Enum.GetValues<ButtonType>().Select(b => b.ToString()).ToList())];
var actions = await _rpcClientService.CallAsync<List<InvokableActionDto<SamsungPreset>>>("SamsungService", "GetInvokableActions");
if (actions != null)
{
CustomActions = [new ActionStepItem("Actions", actions.Where(a => !a.Advanced).ToList())];
}
}
private async Task ApplyClick(MouseEventArgs e)
{
if (Preset == null)
{
return;
}
if (!IsValid())
{
WasValidated = "was-validated";
return;
}
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "UpdatePreset", Preset);
if (AfterApply != null)
{
await AfterApply.Invoke(Preset);
}
}
private void ModalOnFocus(FocusEventArgs e)
{
if (!IsVisible)
{
WasValidated = "";
IsVisible = true;
}
}
private bool IsValid()
{
return !string.IsNullOrEmpty(Preset?.name);
}
}
@@ -0,0 +1,233 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<FieldsModal ModalId="serviceMenuModal" Title="@Title" ConfirmCaption="@ConfirmCaption" SecondaryCaption="@SecondaryCaption"
Fields="Fields" OnConfirm="NextClick" OnSecondary="SecondaryClick" Dismiss="@Dismiss" OnShow="OnShow"></FieldsModal>
@code {
[Parameter]
public List<SamsungDeviceDto>? Devices { get; set; }
private string? Title { get; set; }
private string? ConfirmCaption { get; set; }
private string? SecondaryCaption { get; set; }
private string? Dismiss { get; set; }
private List<FieldDefinition>? Fields { get; set; }
private FieldDefinition? DeviceField { get; set; }
private Func<Task>? NextAction;
private Func<Task>? SecondaryAction;
protected override async Task OnInitializedAsync()
{
// NextAction = FirstStepExit;
// Title = "Access Service Menu";
// ConfirmCaption = "Next >";
// Fields = [AddField("Service Menu Access Type", ServiceMenuType.FactoryMenu, subLabel: "Both types will not reset your settings. 'FactoryMenu' is recommended for 2024 series but will also work for older ones. Click 'Next >' to continue or 'X' to stop")];
}
protected async Task OnShow()
{
NextAction = FirstStepExit;
Title = "Access Service Menu";
ConfirmCaption = "Next >";
SecondaryCaption = null;
SecondaryAction = null;
Dismiss = null;
Fields = [AddField("Service Menu Access Type", ServiceMenuType.FactoryMenu, subLabel: "Both types will not reset your settings. 'FactoryMenu' is recommended for 2024 series but will also work for older ones. Click 'Next >' to continue or 'X' to stop")];
if (Devices?.Any() == true)
{
DeviceField = FieldDefinition.CreateDropDownField("Device", Devices.Select(d => d.ToString()).ToList());
DeviceField.Value = Devices.FirstOrDefault(d => d.IsSelected)?.ToString();
Fields.Insert(0, DeviceField);
}
StateHasChanged();
}
private async Task NextClick()
{
if (NextAction == null)
{
return;
}
await NextAction();
}
private async Task SecondaryClick()
{
if (SecondaryAction != null)
{
await SecondaryAction();
}
}
private async Task FirstStepExit()
{
if (Fields == null)
{
return;
}
var menuType = Fields.First(f => f.ValueType == typeof(ServiceMenuType)).ValueAsEnum<ServiceMenuType>();
if (menuType == ServiceMenuType.FactoryMenu)
{
await FactoryMenuFirstStepEnter();
}
else
{
await HospitalityMenuFirstStepEnter();
}
}
protected async Task FactoryMenuFirstStepEnter()
{
await ExecutePreset(SamsungFactoryMenuPresets.Preset1);
NextAction = FactoryMenuSecondStepEnter;
Title = "Access Service Menu - Step 2";
ConfirmCaption = "Next >";
SecondaryCaption = "Skip";
SecondaryAction = FactoryMenuSecondStepEnterWithoutExecute;
Fields = [AddField("Service Menu - Advanced", null, FieldType.Label, "If the service menu opened and you want to access the Advanced settings, click 'Next >' or click 'Skip' to skip")];
StateHasChanged();
}
protected async Task FactoryMenuSecondStepEnterWithoutExecute()
{
NextAction = FactoryMenuSecondStepExit;
Title = "Exit Service Menu";
ConfirmCaption = "Next >";
SecondaryCaption = null;
SecondaryAction = null;
Fields = [AddField("Exit Service Menu Type", ExitServiceMenuType.Exit, subLabel: "Choose how to exit the service menu. You can either reboot the TV or just exit the service menu (if, for example, no changes have been made)")];
StateHasChanged();
}
protected async Task FactoryMenuSecondStepEnter()
{
await ExecutePreset(SamsungFactoryMenuPresets.PresetAdvanced);
await FactoryMenuSecondStepEnterWithoutExecute();
}
protected async Task FactoryMenuSecondStepExit()
{
if (Fields == null)
{
return;
}
var menuType = Fields.First().ValueAsEnum<ExitServiceMenuType>();
if (menuType == ExitServiceMenuType.Exit)
{
await ExitServiceMenuEnter();
}
else
{
await RebootTvEnter();
}
}
protected async Task ExitServiceMenuEnter()
{
await ExecutePreset(SamsungFactoryMenuPresets.Exit);
NextAction = null;
Title = "Exited Service Menu";
Dismiss = "modal";
ConfirmCaption = "OK";
SecondaryCaption = "Factory";
SecondaryAction = ExecuteFactoryKey;
Fields = [AddField("Service Menu - Exited", null, FieldType.Label, "Exited the service menu. Click OK to close. If the TV is stuck on cycling full screen RGB images, use the 'Factory' button below.")];
StateHasChanged();
}
protected async Task RebootTvEnter()
{
await ExecutePreset(SamsungGenericPresets.Reboot);
NextAction = null;
Title = "Rebooted TV";
Dismiss = "modal";
ConfirmCaption = "OK";
Fields = [AddField("Service Menu - Rebooted TV", null, FieldType.Label, "Rebooted the TV. Power on the TV manually if the TV does not come on automatically.")];
StateHasChanged();
}
protected async Task HospitalityMenuFirstStepEnter()
{
await ExecutePreset(SamsungHospitalityMenuPresets.Preset1);
NextAction = HospitalityMenuSecondStepEnter;
Title = "Access Service Menu - Step 2";
ConfirmCaption = "Next >";
Fields = [AddField("Service Menu opened?", null, FieldType.Label, "If the menu is opened with the 'Hospitality Mode' item highlighted and the rest disabled, click 'Next >' to continue or 'X' to stop")];
StateHasChanged();
}
protected async Task HospitalityMenuSecondStepEnter()
{
await ExecutePreset(SamsungHospitalityMenuPresets.Preset2);
NextAction = HospitalityMenuThirdStepEnter;
Title = "Access Service Menu - Step 3";
ConfirmCaption = "Next >";
SecondaryCaption = "Skip";
Fields = [AddField("Service Menu - Advanced", null, FieldType.Label, "If the 'Advanced' item is highlighted and you want to access these settings, click 'Next >' or click 'Skip' to skip")];
StateHasChanged();
}
protected async Task HospitalityMenuThirdStepEnter()
{
await ExecutePreset(SamsungHospitalityMenuPresets.Preset3);
await FactoryMenuSecondStepEnterWithoutExecute();
}
protected async Task ExecuteFactoryKey()
{
await ExecutePreset(SamsungFactoryMenuPresets.KeyFactory);
}
private async Task ExecutePreset(SamsungPreset preset)
{
if (Devices != null && DeviceField?.Value != null)
{
var device = Devices.FirstOrDefault(d => d.ToString() == DeviceField.Value.ToString());
preset.DeviceMacAddress = device?.MacAddress;
}
await _rpcClientService.CallAsync<bool>("SamsungService", "ApplyPreset", preset);
//await Task.Delay(2000);
}
private FieldDefinition AddField(string label, object? value, FieldType fieldType = FieldType.DropDown, string? subLabel = null)
{
return new FieldDefinition
{
FieldType = fieldType,
Value = value,
StringValue = value?.ToString(),
ValueType = value?.GetType(),
Label = label,
SubLabel = subLabel
};
}
}
@@ -0,0 +1,34 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.Services;
@using ColorControl.Shared.Common;
@using ColorControl.UI.Components.Pages.Generic
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
<FieldsModal Title="Samsung settings" ConfirmCaption="Save" ModalId="settingsModal" Fields="Fields" OnConfirm="ApplyClick" OnShow="ModalOnShow"></FieldsModal>
@code {
private SamsungServiceConfigDto? Config { get; set; }
private List<FieldDefinition>? Fields { get; set; }
protected async Task ModalOnShow()
{
Config = await _rpcClientService.CallAsync<SamsungServiceConfigDto>("SamsungService", "GetConfig");
Fields = SamsungFields.GetSettingsFields(Config);
StateHasChanged();
}
private async Task ApplyClick()
{
if (Config == null)
{
return;
}
FieldDefinition.UpdateObject(Fields, Config);
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "UpdateConfig", Config);
}
}
@@ -0,0 +1,333 @@
@page "/Samsung"
@using ColorControl.Shared.Common
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.Shared.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@rendermode InteractiveServer
@inject RpcUiClientService _rpcClientService
@inject ProtectedLocalStorage _localStorage
<PageTitle>Samsung controller</PageTitle>
<SamsungSettings />
<SamsungServiceMenu Devices="Devices"></SamsungServiceMenu>
<div class="d-flex align-items-center">
<h1>Samsung controller</h1>
<div class="d-flex ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
Buttons
</button>
<ul class="dropdown-menu">
@foreach (var button in Enum.GetValues<ButtonType>())
{
<li>
<a class="dropdown-item" @onclick="() => ButtonOnClick(button)">@button.GetDescription()</a>
</li>
}
</ul>
</div>
@if (InvokableActions != null)
{
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false">
Expert
</button>
<ul class="dropdown-menu">
@foreach (var action in InvokableActions)
{
if (action.Name == "ServiceMenu")
{
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#serviceMenuModal">Service menu</a></li>
}
else
{
<li><a class="dropdown-item" @onclick="() => ActionOnclick(action)">@action.Name</a></li>
}
}
</ul>
</div>
}
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
Settings
</button>
</div>
</div>
<SamsungDevicePage Device="CurrentDevice" AfterApply="AfterDeviceApplied" />
<SamsungPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
@if (Devices == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Devices</h2>
<div class="d-flex ms-auto">
<button type="button" class="btn btn-sm btn-primary me-2" disabled="@IsRefreshDisabled" @onclick="RefreshDevicesOnClick">
@if (IsRefreshDisabled)
{
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
}
<span role="status">Refresh</span>
</button>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
@if (!Devices.Any())
{
<span>No devices found</span>
}
@foreach (var device in Devices)
{
<div class="col-lg-6 mb-3">
<div class="card h-100">
<div class="card-header d-flex align-items-baseline">
<span>
@device.Name
@if (device.IsConnected)
{
<span class="ms-2 badge text-bg-success" title="Device is connected">Connected</span>
}
else
{
<span class="ms-2 badge text-bg-danger" title="Device is disconnected">Disconnected</span>
}
@if (device.IsSelected)
{
<span class="ms-2 badge text-bg-primary" title="Global device">Primary</span>
}
</span>
<div class="d-flex ms-auto">
@if (!device.IsSelected)
{
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
}
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
Settings
</button>
</div>
</div>
<div class="card-body">
<small class="text-muted">@device.IpAddress (@(device.MacAddress ?? "No MAC-address entered"))</small>
</div>
</div>
</div>
}
</div>
}
@if (Presets == null)
{
<div>Loading...</div>
}
else
{
<div class="mb-2 d-flex align-items-center">
<h2>Presets</h2>
<div class="d-flex ms-auto">
<select class="form-select me-2" id="presetOrder" @bind="PresetOrder" @bind:after="PresetOrderChanged">
@foreach (var order in Enum.GetValues<PresetOrder>())
{
<option value="@order">Order by: @order.GetDescription()</option>
}
</select>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => AddPresetOnClick()">
Add
</button>
</div>
</div>
<div class="row mb-3">
<div class="col">
<div class="list-group">
@foreach (var preset in Presets)
{
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
<div class="d-flex align-items-start justify-content-between">
<div>
<h5 class="mb-1">@preset.name <small class="text-muted">@GetDeviceName(preset)</small></h5>
@if (preset.Triggers.Any(t => t.Trigger != PresetTriggerType.None))
{
<div><small class="text-muted">Trigger: @preset.Triggers[0].ToString()</small></div>
}
<div><small class="text-muted">@preset.shortcut</small></div>
</div>
<span>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
</span>
</div>
</a>
}
</div>
</div>
</div>
}
@code {
private List<SamsungPreset>? Presets;
private List<SamsungDeviceDto>? Devices;
private List<InvokableActionDto<SamsungPreset>>? InvokableActions;
private SamsungServiceConfigDto? Config;
private SamsungPreset? CurrentPreset;
private SamsungDeviceDto? CurrentDevice;
private bool IsRefreshDisabled;
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
Config = await _rpcClientService.CallAsync<SamsungServiceConfigDto>("SamsungService", "GetConfig");
await RefreshPresets();
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<SamsungPreset>>>("SamsungService", "GetInvokableActions");
}
protected override async Task OnAfterRenderAsync(bool firstTime)
{
if (firstTime)
{
PresetOrder = await _localStorage.TryGet<PresetOrder>("SamsungPresetOrder");
Presets = Presets?.OrderPresetsBy(PresetOrder);
StateHasChanged();
}
}
private async Task PresetOnClick(SamsungPreset preset)
{
await _rpcClientService.CallAsync<bool>("SamsungService", "ApplyPresetWithId", preset.id);
}
private async Task PresetOnEditClick(SamsungPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnCloneClick(SamsungPreset preset)
{
CurrentPreset = new SamsungPreset(preset);
CurrentPreset.SetCloneValues();
}
private async Task PresetOnDeleteClick(SamsungPreset preset)
{
CurrentPreset = preset;
}
private async Task PresetOnDeleteConfirmed()
{
if (CurrentPreset == null)
{
return;
}
await _rpcClientService.CallAsync<bool>("SamsungService", "DeletePreset", CurrentPreset.id);
await RefreshPresets();
StateHasChanged();
}
private async Task DeviceOnClick(SamsungDeviceDto device)
{
CurrentDevice = device;
}
private async Task AddDeviceOnClick()
{
CurrentDevice = new SamsungDeviceDto { IsCustom = true };
}
private async Task AddPresetOnClick()
{
CurrentPreset = new SamsungPreset();
}
private async Task AfterPresetApplied(SamsungPreset preset)
{
await RefreshPresets();
StateHasChanged();
}
private async Task AfterDeviceApplied(SamsungDeviceDto device)
{
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
StateHasChanged();
}
private async Task RefreshDevicesOnClick(MouseEventArgs e)
{
IsRefreshDisabled = true;
try
{
await _rpcClientService.CallAsync<bool>("SamsungService", "RefreshDevices");
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
}
finally
{
IsRefreshDisabled = false;
}
StateHasChanged();
}
private async Task ActionOnclick(InvokableActionDto<SamsungPreset> action)
{
await _rpcClientService.CallAsync<bool>("SamsungService", "ExecuteInvokableAction", action);
}
private string GetDeviceName(SamsungPreset preset)
{
if (string.IsNullOrEmpty(preset.DeviceMacAddress))
{
return "Global";
}
return Devices?.FirstOrDefault(d => d.MacAddress == preset.DeviceMacAddress)?.Name ?? preset.DeviceMacAddress;
}
private async Task SetAsPrimaryOnClick(SamsungDeviceDto device)
{
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "SetSelectedDevice", device);
if (result)
{
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
}
}
private async Task PresetOrderChanged()
{
await _localStorage.SetAsync("SamsungPresetOrder", PresetOrder);
await RefreshPresets();
}
private async Task RefreshPresets()
{
Presets = await _rpcClientService.CallAsync<List<SamsungPreset>>("SamsungService", "GetPresets");
Presets = Presets?.OrderPresetsBy(PresetOrder);
}
private async Task ButtonOnClick(ButtonType button)
{
var preset = new SamsungPreset(button.ToString(), null, [button.ToString()]);
await _rpcClientService.CallAsync<bool>("SamsungService", "ApplyPreset", preset);
}
}
@@ -1,64 +0,0 @@
@page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
@@ -0,0 +1,43 @@
using ColorControl.Shared.Contracts;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.Security.Cryptography;
namespace ColorControl.UI.Generics;
public static class PresetExtensions
{
public static List<T>? OrderPresetsBy<T>(this List<T> presets, PresetOrder order) where T : PresetBase
{
if (presets == null)
{
return null;
}
if (order == PresetOrder.ByName)
{
presets = presets.OrderBy(p => p.name).ToList();
}
else if (order == PresetOrder.ByLastUsed)
{
presets = presets.OrderByDescending(p => p.LastUsed).ToList();
}
return presets;
}
public static async ValueTask<TValue?> TryGet<TValue>(this ProtectedBrowserStorage storage, string key, TValue? def = default)
{
try
{
var result = await storage.GetAsync<TValue>(key);
var value = result.Success ? result.Value : def;
return value;
}
catch (CryptographicException)
{
await storage.DeleteAsync(key);
return def;
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
public class AppState
{
public string SelectedTheme { get; private set; } = "light";
public string SelectedTheme { get; set; } = "light";
public event Action? OnChange;
@@ -1,4 +1,5 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
+1
View File
@@ -1,4 +1,5 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
+4
View File
@@ -49,3 +49,7 @@ h1:focus {
.darker-border-checkbox.form-check-input {
border-color: #929292;
}
.dropdown-item, .list-group-item {
cursor: pointer;
}
+7 -2
View File
@@ -18,8 +18,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>9.9.0.1</ApplicationVersion>
<Version>9.9.0.1</Version>
<ApplicationVersion>10.0.0.0</ApplicationVersion>
<Version>10.0.0.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
@@ -52,6 +52,7 @@
<PathMap Condition="'$(Configuration)' == 'Release'">$(MSBuildThisFileDirectory)=.\</PathMap>
</PropertyGroup>
<ItemGroup>
<None Remove="XForms\BrowserWindow.xaml" />
<None Remove="XForms\ColorProfileWindow.xaml" />
<None Remove="XForms\LogWindow.xaml" />
</ItemGroup>
@@ -122,6 +123,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="XForms\BrowserWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="XForms\LogWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
+1 -1
View File
@@ -111,7 +111,7 @@ Options:
{
var backgroundService = Program.ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
backgroundService.PipeName = "elevatedpipe";
backgroundService.PipeName = PipeUtils.ElevatedPipe;
await backgroundService.StartAndStopWithMutex();
+103 -59
View File
@@ -46,7 +46,6 @@ namespace ColorControl.Forms
grpGeneralOptions = new System.Windows.Forms.GroupBox();
btnOptionsLog = new System.Windows.Forms.Button();
btnOptionsAdvanced = new System.Windows.Forms.Button();
chkOptionsUseDarkMode = new System.Windows.Forms.CheckBox();
chkAutoInstallUpdates = new System.Windows.Forms.CheckBox();
btnStartStopService = new System.Windows.Forms.Button();
btnElevationInfo = new System.Windows.Forms.Button();
@@ -55,26 +54,31 @@ namespace ColorControl.Forms
rbElevationAdmin = new System.Windows.Forms.RadioButton();
label8 = new System.Windows.Forms.Label();
rbElevationNone = new System.Windows.Forms.RadioButton();
chkGdiScaling = new System.Windows.Forms.CheckBox();
chkCheckForUpdates = new System.Windows.Forms.CheckBox();
chkStartAfterLogin = new System.Windows.Forms.CheckBox();
chkOptionsUseDarkMode = new System.Windows.Forms.CheckBox();
chkGdiScaling = new System.Windows.Forms.CheckBox();
chkMinimizeToSystemTray = new System.Windows.Forms.CheckBox();
chkMinimizeOnClose = new System.Windows.Forms.CheckBox();
chkStartMinimized = new System.Windows.Forms.CheckBox();
chkStartAfterLogin = new System.Windows.Forms.CheckBox();
mnuColorProfiles = new System.Windows.Forms.ContextMenuStrip(components);
miCreateHDRColorProfile = new System.Windows.Forms.ToolStripMenuItem();
miCreateSDRColorProfile = new System.Windows.Forms.ToolStripMenuItem();
lblUiType = new System.Windows.Forms.Label();
cbxUiType = new System.Windows.Forms.ComboBox();
grpUi = new System.Windows.Forms.GroupBox();
grpOptionsModules.SuspendLayout();
grpMiscellaneousOptions.SuspendLayout();
grpGeneralOptions.SuspendLayout();
mnuColorProfiles.SuspendLayout();
grpUi.SuspendLayout();
SuspendLayout();
//
// grpOptionsModules
//
grpOptionsModules.Controls.Add(label1);
grpOptionsModules.Controls.Add(chkModules);
grpOptionsModules.Location = new Point(11, 223);
grpOptionsModules.Location = new Point(14, 344);
grpOptionsModules.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
grpOptionsModules.Name = "grpOptionsModules";
grpOptionsModules.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
@@ -112,7 +116,7 @@ namespace ColorControl.Forms
grpMiscellaneousOptions.Controls.Add(edtBlankScreenSaverShortcut);
grpMiscellaneousOptions.Controls.Add(lblFixChromeFontsDescription);
grpMiscellaneousOptions.Controls.Add(chkFixChromeFonts);
grpMiscellaneousOptions.Location = new Point(11, 360);
grpMiscellaneousOptions.Location = new Point(14, 481);
grpMiscellaneousOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
grpMiscellaneousOptions.Name = "grpMiscellaneousOptions";
grpMiscellaneousOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
@@ -190,7 +194,6 @@ namespace ColorControl.Forms
//
grpGeneralOptions.Controls.Add(btnOptionsLog);
grpGeneralOptions.Controls.Add(btnOptionsAdvanced);
grpGeneralOptions.Controls.Add(chkOptionsUseDarkMode);
grpGeneralOptions.Controls.Add(chkAutoInstallUpdates);
grpGeneralOptions.Controls.Add(btnStartStopService);
grpGeneralOptions.Controls.Add(btnElevationInfo);
@@ -199,24 +202,20 @@ namespace ColorControl.Forms
grpGeneralOptions.Controls.Add(rbElevationAdmin);
grpGeneralOptions.Controls.Add(label8);
grpGeneralOptions.Controls.Add(rbElevationNone);
grpGeneralOptions.Controls.Add(chkGdiScaling);
grpGeneralOptions.Controls.Add(chkCheckForUpdates);
grpGeneralOptions.Controls.Add(chkMinimizeToSystemTray);
grpGeneralOptions.Controls.Add(chkMinimizeOnClose);
grpGeneralOptions.Controls.Add(chkStartMinimized);
grpGeneralOptions.Controls.Add(chkStartAfterLogin);
grpGeneralOptions.Location = new Point(7, 6);
grpGeneralOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
grpGeneralOptions.Name = "grpGeneralOptions";
grpGeneralOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
grpGeneralOptions.Size = new Size(662, 211);
grpGeneralOptions.Size = new Size(662, 202);
grpGeneralOptions.TabIndex = 2;
grpGeneralOptions.TabStop = false;
grpGeneralOptions.Text = "General";
//
// btnOptionsLog
//
btnOptionsLog.Location = new Point(103, 172);
btnOptionsLog.Location = new Point(405, 47);
btnOptionsLog.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnOptionsLog.Name = "btnOptionsLog";
btnOptionsLog.Size = new Size(88, 27);
@@ -227,7 +226,7 @@ namespace ColorControl.Forms
//
// btnOptionsAdvanced
//
btnOptionsAdvanced.Location = new Point(7, 172);
btnOptionsAdvanced.Location = new Point(405, 17);
btnOptionsAdvanced.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnOptionsAdvanced.Name = "btnOptionsAdvanced";
btnOptionsAdvanced.Size = new Size(88, 27);
@@ -236,22 +235,10 @@ namespace ColorControl.Forms
btnOptionsAdvanced.UseVisualStyleBackColor = true;
btnOptionsAdvanced.Click += btnOptionsAdvanced_Click;
//
// chkOptionsUseDarkMode
//
chkOptionsUseDarkMode.AutoSize = true;
chkOptionsUseDarkMode.Location = new Point(216, 47);
chkOptionsUseDarkMode.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkOptionsUseDarkMode.Name = "chkOptionsUseDarkMode";
chkOptionsUseDarkMode.Size = new Size(164, 19);
chkOptionsUseDarkMode.TabIndex = 67;
chkOptionsUseDarkMode.Text = "Dark Mode (experimental)";
chkOptionsUseDarkMode.UseVisualStyleBackColor = true;
chkOptionsUseDarkMode.CheckedChanged += chkOptionsUseDarkMode_CheckedChanged;
//
// chkAutoInstallUpdates
//
chkAutoInstallUpdates.AutoSize = true;
chkAutoInstallUpdates.Location = new Point(7, 147);
chkAutoInstallUpdates.Location = new Point(7, 72);
chkAutoInstallUpdates.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkAutoInstallUpdates.Name = "chkAutoInstallUpdates";
chkAutoInstallUpdates.Size = new Size(179, 19);
@@ -263,7 +250,7 @@ namespace ColorControl.Forms
// btnStartStopService
//
btnStartStopService.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
btnStartStopService.Location = new Point(614, 129);
btnStartStopService.Location = new Point(405, 150);
btnStartStopService.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnStartStopService.Name = "btnStartStopService";
btnStartStopService.Size = new Size(40, 27);
@@ -275,7 +262,7 @@ namespace ColorControl.Forms
// btnElevationInfo
//
btnElevationInfo.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
btnElevationInfo.Location = new Point(329, 67);
btnElevationInfo.Location = new Point(120, 88);
btnElevationInfo.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnElevationInfo.Name = "btnElevationInfo";
btnElevationInfo.Size = new Size(22, 27);
@@ -287,7 +274,7 @@ namespace ColorControl.Forms
// rbElevationService
//
rbElevationService.AutoSize = true;
rbElevationService.Location = new Point(216, 133);
rbElevationService.Location = new Point(7, 154);
rbElevationService.Name = "rbElevationService";
rbElevationService.Size = new Size(136, 19);
rbElevationService.TabIndex = 63;
@@ -300,7 +287,7 @@ namespace ColorControl.Forms
// rbElevationProcess
//
rbElevationProcess.AutoSize = true;
rbElevationProcess.Location = new Point(216, 154);
rbElevationProcess.Location = new Point(7, 175);
rbElevationProcess.Name = "rbElevationProcess";
rbElevationProcess.Size = new Size(189, 19);
rbElevationProcess.TabIndex = 62;
@@ -313,7 +300,7 @@ namespace ColorControl.Forms
// rbElevationAdmin
//
rbElevationAdmin.AutoSize = true;
rbElevationAdmin.Location = new Point(216, 112);
rbElevationAdmin.Location = new Point(7, 133);
rbElevationAdmin.Name = "rbElevationAdmin";
rbElevationAdmin.Size = new Size(97, 19);
rbElevationAdmin.TabIndex = 61;
@@ -326,7 +313,7 @@ namespace ColorControl.Forms
// label8
//
label8.AutoSize = true;
label8.Location = new Point(216, 73);
label8.Location = new Point(7, 94);
label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
label8.Name = "label8";
label8.Size = new Size(105, 15);
@@ -336,7 +323,7 @@ namespace ColorControl.Forms
// rbElevationNone
//
rbElevationNone.AutoSize = true;
rbElevationNone.Location = new Point(216, 91);
rbElevationNone.Location = new Point(7, 112);
rbElevationNone.Name = "rbElevationNone";
rbElevationNone.Size = new Size(54, 19);
rbElevationNone.TabIndex = 10;
@@ -346,22 +333,10 @@ namespace ColorControl.Forms
rbElevationNone.UseVisualStyleBackColor = true;
rbElevationNone.CheckedChanged += rbElevationNone_CheckedChanged;
//
// chkGdiScaling
//
chkGdiScaling.AutoSize = true;
chkGdiScaling.Location = new Point(216, 22);
chkGdiScaling.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkGdiScaling.Name = "chkGdiScaling";
chkGdiScaling.Size = new Size(197, 19);
chkGdiScaling.TabIndex = 7;
chkGdiScaling.Text = "Use GDI Scaling (requires restart)";
chkGdiScaling.UseVisualStyleBackColor = true;
chkGdiScaling.CheckedChanged += chkGdiScaling_CheckedChanged;
//
// chkCheckForUpdates
//
chkCheckForUpdates.AutoSize = true;
chkCheckForUpdates.Location = new Point(7, 122);
chkCheckForUpdates.Location = new Point(7, 47);
chkCheckForUpdates.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkCheckForUpdates.Name = "chkCheckForUpdates";
chkCheckForUpdates.Size = new Size(197, 19);
@@ -370,6 +345,42 @@ namespace ColorControl.Forms
chkCheckForUpdates.UseVisualStyleBackColor = true;
chkCheckForUpdates.CheckedChanged += chkCheckForUpdates_CheckedChanged;
//
// chkStartAfterLogin
//
chkStartAfterLogin.AutoSize = true;
chkStartAfterLogin.Location = new Point(7, 22);
chkStartAfterLogin.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkStartAfterLogin.Name = "chkStartAfterLogin";
chkStartAfterLogin.Size = new Size(183, 19);
chkStartAfterLogin.TabIndex = 2;
chkStartAfterLogin.Text = "Automatically start after login";
chkStartAfterLogin.UseVisualStyleBackColor = true;
chkStartAfterLogin.CheckedChanged += chkStartAfterLogin_CheckedChanged;
//
// chkOptionsUseDarkMode
//
chkOptionsUseDarkMode.AutoSize = true;
chkOptionsUseDarkMode.Location = new Point(329, 22);
chkOptionsUseDarkMode.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkOptionsUseDarkMode.Name = "chkOptionsUseDarkMode";
chkOptionsUseDarkMode.Size = new Size(224, 19);
chkOptionsUseDarkMode.TabIndex = 67;
chkOptionsUseDarkMode.Text = "Dark Mode (WinForms: experimental)";
chkOptionsUseDarkMode.UseVisualStyleBackColor = true;
chkOptionsUseDarkMode.CheckedChanged += chkOptionsUseDarkMode_CheckedChanged;
//
// chkGdiScaling
//
chkGdiScaling.AutoSize = true;
chkGdiScaling.Location = new Point(329, 47);
chkGdiScaling.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkGdiScaling.Name = "chkGdiScaling";
chkGdiScaling.Size = new Size(257, 19);
chkGdiScaling.TabIndex = 7;
chkGdiScaling.Text = "WinForms: Use GDI Scaling (requires restart)";
chkGdiScaling.UseVisualStyleBackColor = true;
chkGdiScaling.CheckedChanged += chkGdiScaling_CheckedChanged;
//
// chkMinimizeToSystemTray
//
chkMinimizeToSystemTray.AutoSize = true;
@@ -406,18 +417,6 @@ namespace ColorControl.Forms
chkStartMinimized.UseVisualStyleBackColor = true;
chkStartMinimized.CheckedChanged += chkStartMinimized_CheckedChanged;
//
// chkStartAfterLogin
//
chkStartAfterLogin.AutoSize = true;
chkStartAfterLogin.Location = new Point(7, 22);
chkStartAfterLogin.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkStartAfterLogin.Name = "chkStartAfterLogin";
chkStartAfterLogin.Size = new Size(183, 19);
chkStartAfterLogin.TabIndex = 2;
chkStartAfterLogin.Text = "Automatically start after login";
chkStartAfterLogin.UseVisualStyleBackColor = true;
chkStartAfterLogin.CheckedChanged += chkStartAfterLogin_CheckedChanged;
//
// mnuColorProfiles
//
mnuColorProfiles.ImageScalingSize = new Size(20, 20);
@@ -439,13 +438,52 @@ namespace ColorControl.Forms
miCreateSDRColorProfile.Text = "Create SDR Color Profile...";
miCreateSDRColorProfile.Click += miCreateSDRColorProfile_Click;
//
// lblUiType
//
lblUiType.AutoSize = true;
lblUiType.Location = new Point(6, 21);
lblUiType.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblUiType.Name = "lblUiType";
lblUiType.Size = new Size(62, 15);
lblUiType.TabIndex = 70;
lblUiType.Text = "Default UI:";
//
// cbxUiType
//
cbxUiType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
cbxUiType.FormattingEnabled = true;
cbxUiType.Items.AddRange(new object[] { "WinForms", "Web (default browser)", "Web (embedded browser)" });
cbxUiType.Location = new Point(105, 18);
cbxUiType.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
cbxUiType.Name = "cbxUiType";
cbxUiType.Size = new Size(208, 23);
cbxUiType.TabIndex = 69;
cbxUiType.SelectedIndexChanged += cbxUiType_SelectedIndexChanged;
//
// grpUi
//
grpUi.Controls.Add(lblUiType);
grpUi.Controls.Add(chkStartMinimized);
grpUi.Controls.Add(chkOptionsUseDarkMode);
grpUi.Controls.Add(cbxUiType);
grpUi.Controls.Add(chkMinimizeOnClose);
grpUi.Controls.Add(chkMinimizeToSystemTray);
grpUi.Controls.Add(chkGdiScaling);
grpUi.Location = new Point(7, 214);
grpUi.Name = "grpUi";
grpUi.Size = new Size(662, 124);
grpUi.TabIndex = 8;
grpUi.TabStop = false;
grpUi.Text = "UI-settings";
//
// OptionsPanel
//
Controls.Add(grpUi);
Controls.Add(grpOptionsModules);
Controls.Add(grpMiscellaneousOptions);
Controls.Add(grpGeneralOptions);
Name = "OptionsPanel";
Size = new Size(732, 523);
Size = new Size(732, 615);
grpOptionsModules.ResumeLayout(false);
grpOptionsModules.PerformLayout();
grpMiscellaneousOptions.ResumeLayout(false);
@@ -453,6 +491,8 @@ namespace ColorControl.Forms
grpGeneralOptions.ResumeLayout(false);
grpGeneralOptions.PerformLayout();
mnuColorProfiles.ResumeLayout(false);
grpUi.ResumeLayout(false);
grpUi.PerformLayout();
ResumeLayout(false);
}
@@ -491,5 +531,9 @@ namespace ColorControl.Forms
private System.Windows.Forms.ToolStripMenuItem miCreateSDRColorProfile;
#endregion
private System.Windows.Forms.Label lblUiType;
private System.Windows.Forms.ComboBox cbxUiType;
private System.Windows.Forms.GroupBox grpUi;
}
}
+22 -12
View File
@@ -16,20 +16,20 @@ public partial class OptionsPanel : UserControl
{
private readonly WinApiService _winApiService;
private readonly WinApiAdminService _winApiAdminService;
private readonly GlobalContext _globalContext;
private readonly ElevationService _elevationService;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly OptionsService _optionsService;
private bool _initialized;
private Config _config;
public OptionsPanel(WinApiService winApiService, WinApiAdminService winApiAdminService, GlobalContext globalContext, ElevationService elevationService, KeyboardShortcutDispatcher keyboardShortcutDispatcher)
public OptionsPanel(WinApiService winApiService, WinApiAdminService winApiAdminService, ElevationService elevationService, KeyboardShortcutDispatcher keyboardShortcutDispatcher, OptionsService optionsService)
{
_winApiService = winApiService;
_winApiAdminService = winApiAdminService;
_globalContext = globalContext;
_elevationService = elevationService;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_config = globalContext.Config;
_optionsService = optionsService;
_config = _optionsService.GetConfig();
InitializeComponent();
@@ -87,6 +87,9 @@ public partial class OptionsPanel : UserControl
ElevationMethod.UseElevatedProcess => rbElevationProcess.Checked = true,
_ => false
};
cbxUiType.SelectedIndex = (int)_config.UiType;
}
finally
{
@@ -121,12 +124,12 @@ public partial class OptionsPanel : UserControl
private void edtShortcut_KeyDown(object sender, KeyEventArgs e)
{
((TextBox)sender).Text = _keyboardShortcutDispatcher.FormatKeyboardShortcut(e);
((TextBox)sender).Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(e);
}
private void edtShortcut_KeyUp(object sender, KeyEventArgs e)
{
_keyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
KeyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
}
private void btnSetShortcutScreenSaver_Click(object sender, EventArgs e)
@@ -167,8 +170,7 @@ public partial class OptionsPanel : UserControl
{
if (_initialized)
{
_config.MinimizeToTray = chkMinimizeToSystemTray.Checked;
Program.GetNotifyIcon().Visible = _config.MinimizeToTray;
_optionsService.SetMinimizeToTray(chkMinimizeToSystemTray.Checked);
}
}
@@ -193,7 +195,7 @@ public partial class OptionsPanel : UserControl
return;
}
var enabled = chkStartAfterLogin.Checked;
_elevationService.RegisterScheduledTask(enabled);
_optionsService.SetStartAfterLogin(enabled);
rbElevationAdmin.Enabled = enabled;
if (!enabled && rbElevationAdmin.Checked)
@@ -281,9 +283,7 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
return;
}
_config.UseDarkMode = chkOptionsUseDarkMode.Checked;
Program.SetTheme(_config.UseDarkMode);
_optionsService.SetDarkMode(chkOptionsUseDarkMode.Checked);
}
private void btnOptionsAdvanced_Click(object sender, EventArgs e)
@@ -378,4 +378,14 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
_config.StartMinimized = chkStartMinimized.Checked;
}
}
private void cbxUiType_SelectedIndexChanged(object sender, EventArgs e)
{
if (!_initialized)
{
return;
}
_config.UiType = (UiType)cbxUiType.SelectedIndex;
}
}
+336 -347
View File
@@ -10,7 +10,6 @@ using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using NWin32;
@@ -22,361 +21,351 @@ using System.Windows.Forms;
namespace ColorControl
{
public partial class MainForm : Form
{
private static bool SystemShutdown = false;
private static bool EndSession = false;
public partial class MainForm : Form
{
private static bool SystemShutdown = false;
private static bool EndSession = false;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ServiceManager _serviceManager;
private readonly IServiceProvider _serviceProvider;
private readonly ElevationService _elevationService;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ServiceManager _serviceManager;
private readonly IServiceProvider _serviceProvider;
private readonly ElevationService _elevationService;
private NvPanel _nvPanel;
private NvPanel _nvPanel;
public NotifyIconManager _notifyIconManager;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly UpdateManager _updateManager;
private readonly WindowMessageDispatcher _windowMessageDispatcher;
private Config _config;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly UpdateManager _updateManager;
private readonly WindowMessageDispatcher _windowMessageDispatcher;
private Config _config;
private LgPanel _lgPanel;
private SamsungPanel _samsungPanel;
private LgPanel _lgPanel;
private SamsungPanel _samsungPanel;
private AmdPanel _amdPanel;
private bool _skipResize;
private AmdPanel _amdPanel;
private bool _skipResize;
private GamePanel _gamePanel;
private GamePanel _gamePanel;
public MainForm(GlobalContext globalContext, ServiceManager serviceManager, IServiceProvider serviceProvider, ElevationService elevationService,
KeyboardShortcutDispatcher keyboardShortcutDispatcher, UpdateManager updateManager, WindowMessageDispatcher windowMessageDispatcher)
{
InitializeComponent();
public MainForm(GlobalContext globalContext, ServiceManager serviceManager, IServiceProvider serviceProvider, ElevationService elevationService,
NotifyIconManager notifyIconManager, KeyboardShortcutDispatcher keyboardShortcutDispatcher, UpdateManager updateManager, WindowMessageDispatcher windowMessageDispatcher)
{
InitializeComponent();
Icon = Resources.AppIcon;
_serviceManager = serviceManager;
_serviceProvider = serviceProvider;
_elevationService = elevationService;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_updateManager = updateManager;
_windowMessageDispatcher = windowMessageDispatcher;
_config = Program.Config;
Icon = Resources.AppIcon;
Text = globalContext.ApplicationTitleAdmin;
_serviceManager = serviceManager;
_serviceProvider = serviceProvider;
_elevationService = elevationService;
_notifyIconManager = notifyIconManager;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_updateManager = updateManager;
_windowMessageDispatcher = windowMessageDispatcher;
_config = Program.Config;
LoadConfig();
MessageForms.MainForm = this;
Text = globalContext.ApplicationTitleAdmin;
InitModules();
Task.Run(AfterInitialized);
LoadConfig();
MessageForms.MainForm = this;
_notifyIconManager.Build();
InitModules();
Task.Run(AfterInitialized);
if (_config.UseDarkMode)
{
SetTheme(_config.UseDarkMode);
}
}
private void InitModules()
{
var _ = tcMain.Handle;
_nvPanel = InitPanel<NvPanel>("NVIDIA controller");
_amdPanel = InitPanel<AmdPanel>("AMD controller");
_lgPanel = InitPanel<LgPanel>("LG controller");
_samsungPanel = InitPanel<SamsungPanel>("Samsung controller");
_gamePanel = InitPanel<GamePanel>("Game launcher");
tcMain.SelectedIndex = 0;
}
private T InitPanel<T>(string displayName) where T : UserControl, IModulePanel
{
var module = _config.Modules.FirstOrDefault(m => m.IsActive && m.DisplayName == displayName);
if (module == null)
{
return default;
}
try
{
var panel = _serviceProvider.GetRequiredService<T>();
panel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
panel.Init();
var tabPage = new TabPage(module.DisplayName);
tcMain.TabPages.Insert(tcMain.TabPages.Count - 2, tabPage);
tabPage.Controls.Add(panel);
panel.Size = tabPage.ClientSize;
panel.BackColor = SystemColors.Window;
//Logger.Debug($"Panel {panel.GetType().Name}, size: {panel.Size.Width}x{panel.Size.Height}");
return panel;
}
catch (Exception ex)
{
Logger.Debug($"Error creating panel {typeof(T).Name}: {ex.ToLogString()}");
return null;
}
}
public void OpenForm()
{
Show();
WindowState = FormWindowState.Normal;
Activate();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (Program.UserExit)
{
return;
}
if (!(SystemShutdown || EndSession) && _config.MinimizeOnClose)
{
e.Cancel = true;
WindowState = FormWindowState.Minimized;
Program.UserExit = false;
return;
}
GlobalSave();
if (SystemShutdown)
{
Logger.Debug($"MainForm_FormClosing: SystemShutdown");
}
Program.Exit();
}
private void GlobalSave()
{
_serviceManager.Save();
_nvPanel?.Save();
_amdPanel?.Save();
_lgPanel?.Save();
_gamePanel?.Save();
_samsungPanel?.Save();
SaveConfig();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeConstants.WM_QUERYENDSESSION)
{
SystemShutdown = true;
EndSession = true;
_windowMessageDispatcher.DispatchEvent(WindowMessageDispatcher.Event_WindowMessageQueryEndSession, new WindowMessageEventArgs(m));
}
else if (m.Msg == Utils.WM_BRINGTOFRONT)
{
Logger.Debug("WM_BRINGTOFRONT message received, opening form");
OpenForm();
}
base.WndProc(ref m);
}
private void MainForm_Resize(object sender, EventArgs e)
{
if (_skipResize)
{
return;
}
if (WindowState == FormWindowState.Minimized)
{
if (_config.MinimizeToTray)
{
Hide();
}
}
else if (WindowState == FormWindowState.Normal && _config != null)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
private void LoadConfig()
{
_skipResize = true;
try
{
Width = _config.FormWidth;
Height = _config.FormHeight;
}
finally
{
_skipResize = false;
}
}
private void SaveConfig()
{
if (WindowState != FormWindowState.Minimized)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
Utils.WriteObject(Program.ConfigFilename, _config);
}
private void MainForm_Shown(object sender, EventArgs e)
{
InitSelectedTab();
_elevationService.CheckElevationMethod();
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
InitSelectedTab();
}
private void InitSelectedTab()
{
if (tcMain.SelectedTab == tabInfo)
{
InitInfoTab();
}
else if (tcMain.SelectedTab == tabOptions)
{
InitOptionsTab();
}
else if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private void InitOptionsTab()
{
if (tabOptions.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<OptionsPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabOptions.Controls.Add(control);
control.Size = tabInfo.ClientSize;
}
private void InitInfoTab()
{
if (tabInfo.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<InfoPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabInfo.Controls.Add(control);
control.Size = tabInfo.ClientSize;
//control.BackColor = SystemColors.Window;
}
private void MainForm_Activated(object sender, EventArgs e)
{
if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private async Task AfterInitialized()
{
if (_nvPanel != null)
{
await _nvPanel.AfterInitialized();
}
if (_amdPanel != null)
{
await _amdPanel.AfterInitialized();
}
}
private void MainForm_Deactivate(object sender, EventArgs e)
{
GlobalSave();
}
private void MainForm_ResizeBegin(object sender, EventArgs e)
{
SuspendLayout();
}
private void MainForm_ResizeEnd(object sender, EventArgs e)
{
ResumeLayout(true);
}
private async void MainForm_Click(object sender, EventArgs e)
{
_serviceManager.NvService?.TestResolution();
}
public void SetTheme(bool toDark)
{
this.UpdateTheme(toDark);
WinApi.RefreshImmersiveColorPolicyState();
DarkModeUtils.InitWpfTheme();
DarkModeUtils.SetContextMenuForeColor(_notifyIconManager.NotifyIcon.ContextMenuStrip, FormUtils.CurrentForeColor);
InitSelectedTab();
}
private void btnUpdate_Click(object sender, EventArgs e)
{
_updateManager.InstallUpdate();
Program.Restart();
}
internal void CloseForRestart()
{
Hide();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData == (Keys.Control | Keys.PageUp) || keyData == (Keys.Control | Keys.PageDown)) && KeyboardShortcutDispatcher.IsShortcutControlFocused())
{
var control = this.FindFocusedControl() as TextBox;
if (control != null)
{
var keyEvent = new KeyEventArgs(keyData);
control.Text = _keyboardShortcutDispatcher.FormatKeyboardShortcut(keyEvent);
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
if (_config.UseDarkMode)
{
SetTheme(_config.UseDarkMode);
}
}
private void InitModules()
{
var _ = tcMain.Handle;
_nvPanel = InitPanel<NvPanel>("NVIDIA controller");
_amdPanel = InitPanel<AmdPanel>("AMD controller");
_lgPanel = InitPanel<LgPanel>("LG controller");
_samsungPanel = InitPanel<SamsungPanel>("Samsung controller");
_gamePanel = InitPanel<GamePanel>("Game launcher");
tcMain.SelectedIndex = 0;
}
private T InitPanel<T>(string displayName) where T : UserControl, IModulePanel
{
var module = _config.Modules.FirstOrDefault(m => m.IsActive && m.DisplayName == displayName);
if (module == null)
{
return default;
}
try
{
var panel = _serviceProvider.GetRequiredService<T>();
panel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
panel.Init();
var tabPage = new TabPage(module.DisplayName);
tcMain.TabPages.Insert(tcMain.TabPages.Count - 2, tabPage);
tabPage.Controls.Add(panel);
panel.Size = tabPage.ClientSize;
panel.BackColor = SystemColors.Window;
//Logger.Debug($"Panel {panel.GetType().Name}, size: {panel.Size.Width}x{panel.Size.Height}");
return panel;
}
catch (Exception ex)
{
Logger.Debug($"Error creating panel {typeof(T).Name}: {ex.ToLogString()}");
return null;
}
}
public void OpenForm()
{
Show();
WindowState = FormWindowState.Normal;
Activate();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (Program.UserExit)
{
return;
}
if (!(SystemShutdown || EndSession) && _config.MinimizeOnClose)
{
e.Cancel = true;
WindowState = FormWindowState.Minimized;
Program.UserExit = false;
return;
}
GlobalSave();
if (SystemShutdown)
{
Logger.Debug($"MainForm_FormClosing: SystemShutdown");
}
Program.Exit();
}
private void GlobalSave()
{
_serviceManager.Save();
_nvPanel?.Save();
_amdPanel?.Save();
_lgPanel?.Save();
_gamePanel?.Save();
_samsungPanel?.Save();
SaveConfig();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeConstants.WM_QUERYENDSESSION)
{
SystemShutdown = true;
EndSession = true;
_windowMessageDispatcher.DispatchEvent(WindowMessageDispatcher.Event_WindowMessageQueryEndSession, new WindowMessageEventArgs(m));
}
else if (m.Msg == Utils.WM_BRINGTOFRONT)
{
Logger.Debug("WM_BRINGTOFRONT message received, opening form");
OpenForm();
}
base.WndProc(ref m);
}
private void MainForm_Resize(object sender, EventArgs e)
{
if (_skipResize)
{
return;
}
if (WindowState == FormWindowState.Minimized)
{
if (_config.MinimizeToTray)
{
Hide();
}
}
else if (WindowState == FormWindowState.Normal && _config != null)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
private void LoadConfig()
{
_skipResize = true;
try
{
Width = _config.FormWidth;
Height = _config.FormHeight;
}
finally
{
_skipResize = false;
}
}
private void SaveConfig()
{
if (WindowState != FormWindowState.Minimized)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
Utils.WriteObject(Program.ConfigFilename, _config);
}
private void MainForm_Shown(object sender, EventArgs e)
{
InitSelectedTab();
_elevationService.CheckElevationMethod();
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
InitSelectedTab();
}
private void InitSelectedTab()
{
if (tcMain.SelectedTab == tabInfo)
{
InitInfoTab();
}
else if (tcMain.SelectedTab == tabOptions)
{
InitOptionsTab();
}
else if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private void InitOptionsTab()
{
if (tabOptions.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<OptionsPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabOptions.Controls.Add(control);
control.Size = tabInfo.ClientSize;
}
private void InitInfoTab()
{
if (tabInfo.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<InfoPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabInfo.Controls.Add(control);
control.Size = tabInfo.ClientSize;
//control.BackColor = SystemColors.Window;
}
private void MainForm_Activated(object sender, EventArgs e)
{
if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private async Task AfterInitialized()
{
if (_nvPanel != null)
{
await _nvPanel.AfterInitialized();
}
if (_amdPanel != null)
{
await _amdPanel.AfterInitialized();
}
}
private void MainForm_Deactivate(object sender, EventArgs e)
{
GlobalSave();
}
private void MainForm_ResizeBegin(object sender, EventArgs e)
{
SuspendLayout();
}
private void MainForm_ResizeEnd(object sender, EventArgs e)
{
ResumeLayout(true);
}
private async void MainForm_Click(object sender, EventArgs e)
{
_serviceManager.NvService?.TestResolution();
}
public void SetTheme(bool toDark)
{
this.UpdateTheme(toDark);
InitSelectedTab();
}
private async void btnUpdate_Click(object sender, EventArgs e)
{
await _updateManager.InstallUpdate();
Program.Restart();
}
internal void CloseForRestart()
{
Hide();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData == (Keys.Control | Keys.PageUp) || keyData == (Keys.Control | Keys.PageDown)) && KeyboardShortcutDispatcher.IsShortcutControlFocused())
{
var control = this.FindFocusedControl() as TextBox;
if (control != null)
{
var keyEvent = new KeyEventArgs(keyData);
control.Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(keyEvent);
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
+2 -2
View File
@@ -126,13 +126,13 @@ internal class MainWorker
if (!_config.StartMinimized /*|| Debugger.IsAttached*/)
{
Program.OpenMainForm();
Program.OpenDefaultUi();
}
}
private void HandleUserBringToFrontEvent(object sender, WindowMessageEventArgs e)
{
Program.OpenMainForm();
Program.OpenDefaultUi();
}
private void HandlePowerBroadcastEvent(object sender, WindowMessageEventArgs e)
+426 -366
View File
@@ -29,370 +29,430 @@ using System.Windows.Forms;
namespace ColorControl
{
static class Program
{
public const string TS_TASKNAME = "ColorControl";
public static string DataDir { get; private set; }
public static string ConfigFilename { get; private set; }
public static string LogFilename { get; private set; }
public static Config Config { get; private set; }
public static GlobalContext AppContext { get; private set; }
public static bool IsRestarting { get; private set; }
public static bool UserExit = false;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static Mutex _mutex;
public static MainForm _mainForm;
private static LoggingRule _loggingRule;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread] // autogenerated
public static void Main(string[] args)
{
// STA
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
DataDir = Utils.GetDataPath();
var runAsService = args.Contains("--service") || WinApiService.IsAdministratorStatic() && Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
var runElevated = args.Contains("--elevated");
InitLogger(runAsService, runElevated);
Logger.Debug($"Using data path: {DataDir}");
//Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
if (runAsService)
{
await RunService(args);
return;
}
await RunApp(args);
}
static async Task RunApp(string[] args)
{
var currentDomain = AppDomain.CurrentDomain;
// Handler for unhandled exceptions.
currentDomain.UnhandledException += GlobalUnhandledExceptionHandler;
// Handler for exceptions in threads behind forms.
Application.ThreadException += GlobalThreadExceptionHandler;
LoadConfig();
if (Config.UseGdiScaling)
{
var result = Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
Logger.Debug($"Result of setting SetHighDpiMode: {result}");
}
var mutexId = $"Global\\{typeof(MainForm).GUID}";
var startUpParams = StartUpParams.Parse(args);
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, mutexId);
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
AppContext.ServiceProvider = ServiceProvider;
MessageForms.Title = AppContext.ApplicationTitle;
var existingProcess = Utils.GetProcessByName("ColorControl");
try
{
if (await CommandLineHandler.HandleStartupParams(startUpParams, existingProcess))
{
return;
}
}
finally
{
Utils.CloseConsole();
}
_mutex = new Mutex(true, AppContext.MutexId, out var mutexCreated);
try
{
if (!mutexCreated)
{
if (existingProcess != null && existingProcess.Threads.Count > 0)
{
var thread = existingProcess.Threads[0];
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
return;
}
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
}
else
{
_mutex.WaitOne();
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (Debugger.IsAttached)
{
//Utils.StartService();
var winApiService = ServiceProvider.GetRequiredService<WinApiService>();
if (winApiService.IsAdministrator())
{
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
backgroundService.PipeName = PipeUtils.ElevatedPipe;
Task.Run(async () => await backgroundService.StartAsync(CancellationToken.None));
}
}
var worker = ServiceProvider.GetRequiredService<MainWorker>();
await worker.DoWork();
if (Debugger.IsAttached && !IsRestarting)
{
var winApiAdminService = ServiceProvider.GetRequiredService<WinApiAdminService>();
winApiAdminService.StopService();
}
}
catch (Exception ex)
{
MessageBox.Show("Error while initializing application: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
finally
{
if (mutexCreated)
{
_mutex?.Close();
}
}
}
public static void OpenMainForm()
{
_mainForm ??= ServiceProvider.GetRequiredService<MainForm>();
_mainForm.OpenForm();
}
public static void Exit()
{
UserExit = true;
_mainForm?.Close();
if (AppContext.MainHandle != 0)
{
NativeMethods.SendMessageW(AppContext.MainHandle, NativeConstants.WM_CLOSE, 0, 0);
}
}
public static void Restart()
{
IsRestarting = true;
_mainForm?.CloseForRestart();
_mutex?.Close();
_mutex = null;
Application.Restart();
Environment.Exit(0);
}
private static bool UiServerStarted;
public static async Task OpenNewUi()
{
if (!UiServerStarted)
{
var _ = Task.Run(ColorControl.UI.Blazor.Start);
UiServerStarted = true;
await Task.Delay(500);
}
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StartProcess("http://localhost:5000");
}
private static void InitLogger(bool runAsService, bool runElevated)
{
var config = new LoggingConfiguration();
// Targets where to log to: File and Console
var logFileAppendix = runAsService ? "_svc" : runElevated ? "_elevated" : "";
LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
_loggingRule = new LoggingRule("*", LogLevel.Trace, LogLevel.Fatal, logfile);
_loggingRule.RuleName = "ColorControl";
// Rules for mapping loggers to targets
config.AddRule(_loggingRule);
// Apply config
LogManager.Configuration = config;
}
private static void LoadConfig()
{
ConfigFilename = Path.Combine(DataDir, "Settings.json");
try
{
if (File.Exists(ConfigFilename))
{
var data = File.ReadAllText(ConfigFilename);
Config = JsonConvert.DeserializeObject<Config>(data);
}
}
catch (Exception ex)
{
Logger.Error($"LoadConfig: {ex.Message}");
}
Config ??= new Config();
var minLogLevel = LogLevel.FromString(Config.LogLevel);
if (minLogLevel != LogLevel.Trace)
{
_loggingRule.SetLoggingLevels(minLogLevel, LogLevel.Fatal);
LogManager.ReconfigExistingLoggers();
}
}
public static IServiceProvider ServiceProvider { get; private set; }
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
//services.AddSingleton<NvService>();
services.RegisterSharedServices();
services.AddSingleton<MainWorker>();
services.AddSingleton<NotifyIconManager>();
services.AddSingleton<LgService>();
services.AddSingleton<GameService>();
services.AddSingleton<SamsungService>();
services.AddSingleton<AmdService>();
services.AddSingleton<PowerEventDispatcher>();
services.AddSingleton<ProcessEventDispatcher>();
services.AddSingleton<WindowMessageDispatcher>();
services.AddSingleton<KeyboardShortcutDispatcher>();
services.AddSingleton<MainForm>();
services.AddSingleton<LogWindow>();
services.AddTransient<ColorProfileWindow>();
services.AddSingleton<RestartDetector>();
services.AddSingleton<InfoPanel>();
services.AddSingleton<OptionsPanel>();
services.AddSingleton<ElevationService>();
services.AddSingleton<UpdateManager>();
services.AddSingleton<AmdPanel>();
services.AddSingleton<NvPanel>();
services.AddSingleton<LgPanel>();
services.AddSingleton<SamsungPanel>();
services.AddSingleton<GamePanel>();
});
}
private static async Task RunService(string[] args)
{
Logger.Debug("RUNNING SERVICE");
AppContext = new GlobalContext(null, new StartUpParams(), DataDir, _loggingRule, null);
using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "ColorControl Service";
})
.ConfigureServices(services =>
{
services.RegisterSharedServices();
services.AddSingleton<PowerEventDispatcher>();
services.AddHostedService<ColorControlBackgroundService>();
})
.Build();
ServiceProvider = host.Services;
AppContext.ServiceProvider = ServiceProvider;
await host.RunAsync();
}
private static void RegisterSharedServices(this IServiceCollection services)
{
services.AddSingleton(AppContext);
services.AddSingleton<SessionSwitchDispatcher>();
services.AddSingleton<WinApiAdminService>();
services.AddSingleton<WinApiService>();
services.AddSingleton<WinElevatedProcessManager>();
services.AddTransient<RpcClientService>();
services.AddTransient<RpcServerService>();
services.AddSingleton<NvService>();
services.AddSingleton<WolService>();
services.AddSingleton<ColorControlBackgroundService>();
services.AddSingleton<ServiceManager>();
RpcServerService.ServiceTypes.Add(typeof(NvService));
}
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
{
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
return 1;
}
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
GlobalHandleException((Exception)e.ExceptionObject, "Unhandled exception");
}
private static void GlobalThreadExceptionHandler(object sender, ThreadExceptionEventArgs e)
{
GlobalHandleException(e.Exception, "Exception in thread");
}
private static void GlobalHandleException(Exception exception, string type)
{
if (UserExit)
{
return;
}
var trace = exception.ToLogString(Environment.StackTrace);
var message = $"{type}: {trace}";
Logger.Error(message);
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal static void SetTheme(bool useDarkMode)
{
_mainForm?.SetTheme(useDarkMode);
}
internal static NotifyIcon GetNotifyIcon()
{
return _mainForm?._notifyIconManager?.NotifyIcon;
}
}
static class Program
{
public const string TS_TASKNAME = "ColorControl";
public static string DataDir { get; private set; }
public static string ConfigFilename { get; private set; }
public static string LogFilename { get; private set; }
public static Config Config { get; private set; }
public static GlobalContext AppContext { get; private set; }
public static bool IsRestarting { get; private set; }
public static bool UserExit = false;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static Mutex _mutex;
public static MainForm _mainForm;
private static LoggingRule _loggingRule;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread] // autogenerated
public static void Main(string[] args)
{
// STA
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
DataDir = Utils.GetDataPath();
var runAsService = args.Contains("--service") || WinApiService.IsAdministratorStatic() && Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
var runElevated = args.Contains("--elevated");
InitLogger(runAsService, runElevated);
Logger.Debug($"Using data path: {DataDir}");
//Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
if (runAsService)
{
await RunService(args);
return;
}
await RunApp(args);
}
static async Task RunApp(string[] args)
{
var startTime = DateTime.Now;
var currentDomain = AppDomain.CurrentDomain;
// Handler for unhandled exceptions.
currentDomain.UnhandledException += GlobalUnhandledExceptionHandler;
// Handler for exceptions in threads behind forms.
Application.ThreadException += GlobalThreadExceptionHandler;
LoadConfig();
if (Config.UseGdiScaling)
{
var result = Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
Logger.Debug($"Result of setting SetHighDpiMode: {result}");
}
var mutexId = $"Global\\{typeof(MainForm).GUID}";
var startUpParams = StartUpParams.Parse(args);
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, startTime, mutexId);
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
AppContext.ServiceProvider = ServiceProvider;
MessageForms.Title = AppContext.ApplicationTitle;
var existingProcess = Utils.GetProcessByName("ColorControl");
try
{
if (await CommandLineHandler.HandleStartupParams(startUpParams, existingProcess))
{
return;
}
}
finally
{
Utils.CloseConsole();
}
_mutex = new Mutex(true, AppContext.MutexId, out var mutexCreated);
try
{
if (!mutexCreated)
{
if (existingProcess != null && existingProcess.Threads.Count > 0)
{
var thread = existingProcess.Threads[0];
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
return;
}
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
}
else
{
_mutex.WaitOne();
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (Debugger.IsAttached)
{
//Utils.StartService();
var winApiService = ServiceProvider.GetRequiredService<WinApiService>();
if (winApiService.IsAdministrator())
{
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
backgroundService.PipeName = PipeUtils.ElevatedPipe;
Task.Run(async () => await backgroundService.StartAsync(CancellationToken.None));
}
}
var worker = ServiceProvider.GetRequiredService<MainWorker>();
await worker.DoWork();
if (Debugger.IsAttached && !IsRestarting)
{
var winApiAdminService = ServiceProvider.GetRequiredService<WinApiAdminService>();
winApiAdminService.StopService();
}
}
catch (Exception ex)
{
MessageBox.Show("Error while initializing application: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
finally
{
if (mutexCreated)
{
_mutex?.Close();
}
}
}
public static void OpenDefaultUi()
{
var _ = Config.UiType switch
{
UiType.WebDefaultBrowser => OpenNewUi(),
UiType.WebEmbedded => OpenNewUiEmbedded(),
_ => OpenMainForm()
};
}
public static async Task OpenMainForm()
{
_mainForm ??= ServiceProvider.GetRequiredService<MainForm>();
_mainForm.OpenForm();
}
public static void Exit()
{
UserExit = true;
_mainForm?.Close();
if (AppContext.MainHandle != 0)
{
NativeMethods.SendMessageW(AppContext.MainHandle, NativeConstants.WM_CLOSE, 0, 0);
}
}
public static void Restart()
{
IsRestarting = true;
_mainForm?.CloseForRestart();
_mutex?.Close();
_mutex = null;
Application.Restart();
Environment.Exit(0);
}
public static async Task OpenNewUi()
{
await StartUiServer();
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StartProcess(GetNewUiUrl());
}
public static string GetNewUiUrl()
{
return $"http://localhost:{(Config.UiPort > 0 ? Config.UiPort : 5000)}";
}
public static async Task OpenNewUiEmbedded()
{
await StartUiServer();
BrowserWindow.CreateAndShow(GetNewUiUrl());
}
private static bool UiServerStarted;
private static async Task StartUiServer()
{
if (UiServerStarted)
{
return;
}
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
backgroundService.PipeName = PipeUtils.MainPipe;
await backgroundService.StartAsync(CancellationToken.None);
var _ = Task.Run(() => ColorControl.UI.Blazor.Start(Config));
UiServerStarted = true;
await Task.Delay(500);
}
private static void InitLogger(bool runAsService, bool runElevated)
{
var config = new LoggingConfiguration();
// Targets where to log to: File and Console
var logFileAppendix = runAsService ? "_svc" : runElevated ? "_elevated" : "";
LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
_loggingRule = new LoggingRule("*", LogLevel.Trace, LogLevel.Fatal, logfile);
_loggingRule.RuleName = "ColorControl";
// Rules for mapping loggers to targets
config.AddRule(_loggingRule);
// Apply config
LogManager.Configuration = config;
}
private static void LoadConfig()
{
ConfigFilename = Path.Combine(DataDir, "Settings.json");
try
{
if (File.Exists(ConfigFilename))
{
var data = File.ReadAllText(ConfigFilename);
Config = JsonConvert.DeserializeObject<Config>(data);
}
}
catch (Exception ex)
{
Logger.Error($"LoadConfig: {ex.Message}");
}
Config ??= new Config();
var minLogLevel = LogLevel.FromString(Config.LogLevel);
if (minLogLevel != LogLevel.Trace)
{
_loggingRule.SetLoggingLevels(minLogLevel, LogLevel.Fatal);
LogManager.ReconfigExistingLoggers();
}
}
public static IServiceProvider ServiceProvider { get; private set; }
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
//services.AddSingleton<NvService>();
services.RegisterSharedServices();
services.AddSingleton<MainWorker>();
services.AddSingleton<NotifyIconManager>();
services.AddSingleton<LgService>();
services.AddSingleton<GameService>();
services.AddSingleton<SamsungService>();
services.AddSingleton<AmdService>();
services.AddSingleton<PowerEventDispatcher>();
services.AddSingleton<ProcessEventDispatcher>();
services.AddSingleton<WindowMessageDispatcher>();
services.AddSingleton<KeyboardShortcutDispatcher>();
services.AddSingleton<MainForm>();
services.AddSingleton<LogWindow>();
services.AddSingleton<BrowserWindow>();
services.AddTransient<ColorProfileWindow>();
services.AddSingleton<RestartDetector>();
services.AddSingleton<InfoPanel>();
services.AddSingleton<OptionsPanel>();
services.AddSingleton<ElevationService>();
services.AddSingleton<UpdateManager>();
services.AddSingleton<OptionsService>();
services.AddSingleton<LoggingService>();
services.AddSingleton<AmdPanel>();
services.AddSingleton<NvPanel>();
services.AddSingleton<LgPanel>();
services.AddSingleton<SamsungPanel>();
services.AddSingleton<GamePanel>();
});
}
private static async Task RunService(string[] args)
{
var startTime = DateTime.Now;
Logger.Debug("RUNNING SERVICE");
AppContext = new GlobalContext(null, new StartUpParams(), DataDir, _loggingRule, null, startTime);
using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "ColorControl Service";
})
.ConfigureServices(services =>
{
services.RegisterSharedServices();
services.AddSingleton<PowerEventDispatcher>();
services.AddHostedService<ColorControlBackgroundService>();
})
.Build();
ServiceProvider = host.Services;
AppContext.ServiceProvider = ServiceProvider;
await host.RunAsync();
}
private static void RegisterSharedServices(this IServiceCollection services)
{
services.AddSingleton(AppContext);
services.AddSingleton<SessionSwitchDispatcher>();
services.AddSingleton<WinApiAdminService>();
services.AddSingleton<WinApiService>();
services.AddSingleton<WinElevatedProcessManager>();
services.AddTransient<RpcClientService>();
services.AddTransient<RpcServerService>();
services.AddSingleton<NvService>();
services.AddSingleton<WolService>();
services.AddSingleton<ColorControlBackgroundService>();
services.AddSingleton<ServiceManager>();
RpcServerService.ServiceTypes.Add(typeof(NvService));
RpcServerService.ServiceTypes.Add(typeof(GameService));
RpcServerService.ServiceTypes.Add(typeof(OptionsService));
RpcServerService.ServiceTypes.Add(typeof(LoggingService));
RpcServerService.ServiceTypes.Add(typeof(SamsungService));
RpcServerService.ServiceTypes.Add(typeof(LgService));
RpcServerService.ServiceTypes.Add(typeof(AmdService));
}
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
{
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
return 1;
}
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
GlobalHandleException((Exception)e.ExceptionObject, "Unhandled exception");
}
private static void GlobalThreadExceptionHandler(object sender, ThreadExceptionEventArgs e)
{
GlobalHandleException(e.Exception, "Exception in thread");
}
private static void GlobalHandleException(Exception exception, string type)
{
if (UserExit)
{
return;
}
var trace = exception.ToLogString(Environment.StackTrace);
var message = $"{type}: {trace}";
Logger.Error(message);
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal static void SetTheme(bool useDarkMode)
{
DarkModeUtils.SetDarkMode(useDarkMode);
WinApi.RefreshImmersiveColorPolicyState();
DarkModeUtils.InitWpfTheme();
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
DarkModeUtils.SetContextMenuForeColor(notifyIconManager.NotifyIcon.ContextMenuStrip, FormUtils.CurrentForeColor);
_mainForm?.SetTheme(useDarkMode);
}
internal static NotifyIcon GetNotifyIcon()
{
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
return notifyIconManager.NotifyIcon;
}
}
}
-396
View File
@@ -1,396 +0,0 @@
#region Copyright
/*******************************************************************************
Copyright(c) 2008 - 2009 Advanced Micro Devices, Inc. All Rights Reserved.
Copyright (c) 2002 - 2006 ATI Technologies Inc. All Rights Reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDED BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
File: ADL.cs
Purpose: Implements ADL interface
Description: Implements some of the methods defined in ADL interface.
********************************************************************************/
#endregion Copyright
#region Using
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ADL_CONTEXT_HANDLE = System.IntPtr;
using FARPROC = System.IntPtr;
using HMODULE = System.IntPtr;
#endregion Using
#region ATI.ADL
namespace ATI.ADL
{
#region Export Struct
#region ADLAdapterInfo
/// <summary> ADLAdapterInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfo
{
/// <summary>The size of the structure</summary>
int Size;
/// <summary> Adapter Index</summary>
internal int AdapterIndex;
/// <summary> Adapter UDID</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string UDID;
/// <summary> Adapter Bus Number</summary>
internal int BusNumber;
/// <summary> Adapter Driver Number</summary>
internal int DriverNumber;
/// <summary> Adapter Function Number</summary>
internal int FunctionNumber;
/// <summary> Adapter Vendor ID</summary>
internal int VendorID;
/// <summary> Adapter Adapter name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string AdapterName;
/// <summary> Adapter Display name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Adapter Present status</summary>
internal int Present;
/// <summary> Adapter Exist status</summary>
internal int Exist;
/// <summary> Adapter Driver Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPath;
/// <summary> Adapter Driver Ext Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPathExt;
/// <summary> Adapter PNP String</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string PNPString;
/// <summary> OS Display Index</summary>
internal int OSDisplayIndex;
}
/// <summary> ADLAdapterInfo Array</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfoArray
{
/// <summary> ADLAdapterInfo Array </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_ADAPTERS)]
internal ADLAdapterInfo[] ADLAdapterInfo;
}
#endregion ADLAdapterInfo
#region ADLDisplayInfo
/// <summary> ADLDisplayID Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayID
{
/// <summary> Display Logical Index </summary>
internal int DisplayLogicalIndex;
/// <summary> Display Physical Index </summary>
internal int DisplayPhysicalIndex;
/// <summary> Adapter Logical Index </summary>
internal int DisplayLogicalAdapterIndex;
/// <summary> Adapter Physical Index </summary>
internal int DisplayPhysicalAdapterIndex;
}
/// <summary> ADLDisplayInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayInfo
{
/// <summary> Display Index </summary>
internal ADLDisplayID DisplayID;
/// <summary> Display Controller Index </summary>
internal int DisplayControllerIndex;
/// <summary> Display Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Display Manufacturer Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayManufacturerName;
/// <summary> Display Type : < The Display type. CRT, TV,CV,DFP are some of display types,</summary>
internal int DisplayType;
/// <summary> Display output type </summary>
internal int DisplayOutputType;
/// <summary> Connector type</summary>
internal int DisplayConnector;
///<summary> Indicating the display info bits' mask.<summary>
internal int DisplayInfoMask;
///<summary> Indicating the display info value.<summary>
internal int DisplayInfoValue;
}
#endregion ADLDisplayInfo
#endregion Export Struct
#region ADL Class
/// <summary> ADL Class</summary>
internal static class ADL
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
#region Internal Constant
/// <summary> Define the maximum path</summary>
internal const int ADL_MAX_PATH = 256;
/// <summary> Define the maximum adapters</summary>
internal const int ADL_MAX_ADAPTERS = 40 /* 150 */;
/// <summary> Define the maximum displays</summary>
internal const int ADL_MAX_DISPLAYS = 40 /* 150 */;
/// <summary> Define the maximum device name length</summary>
internal const int ADL_MAX_DEVICENAME = 32;
/// <summary> Define the successful</summary>
internal const int ADL_SUCCESS = 0;
/// <summary> Define the failure</summary>
internal const int ADL_FAIL = -1;
/// <summary> Define the driver ok</summary>
internal const int ADL_DRIVER_OK = 0;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORTS = 8;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8;
/// <summary> Maximum number of ADLMOdes for the adapter </summary>
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
internal static Dictionary<Type, object> ADLDelegates = new Dictionary<Type, object>();
#endregion Internal Constant
#region Export Delegates
/// <summary> ADL Memory allocation function allows ADL to callback for memory allocation</summary>
/// <param name="size">input size</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate IntPtr ADL_Main_Memory_Alloc(int size);
// ///// <summary> ADL Create Function to create ADL Data</summary>
/// <param name="callback">Call back functin pointer which is ised to allocate memeory </param>
/// <param name="enumConnectedAdapters">If it is 1, then ADL will only retuen the physical exist adapters </param>
///// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters);
internal delegate int ADL2_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters, ref ADL_CONTEXT_HANDLE context);
/// <summary> ADL Destroy Function to free up ADL Data</summary>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Destroy();
internal delegate int ADL2_Main_Control_Destroy(ADL_CONTEXT_HANDLE context);
/// <summary> ADL Function to get the number of adapters</summary>
/// <param name="numAdapters">return number of adapters</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL2_Adapter_NumberOfAdapters_Get(ADL_CONTEXT_HANDLE context, ref int numAdapters);
/// <summary> ADL Function to get the GPU adapter information</summary>
/// <param name="info">return GPU adapter information</param>
/// <param name="inputSize">the size of the GPU adapter struct</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL2_Adapter_AdapterInfo_Get(ADL_CONTEXT_HANDLE context, IntPtr info, int inputSize);
/// <summary> Function to determine if the adapter is active or not.</summary>
/// <remarks>The function is used to check if the adapter associated with iAdapterIndex is active</remarks>
/// <param name="adapterIndex"> Adapter Index.</param>
/// <param name="status"> Status of the adapter. True: Active; False: Dsiabled</param>
/// <returns>Non zero is successfull</returns>
internal delegate int ADL2_Adapter_Active_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, ref int status);
/// <summary>Get display information based on adapter index</summary>
/// <param name="adapterIndex">Adapter Index</param>
/// <param name="numDisplays">return the total number of supported displays</param>
/// <param name="displayInfoArray">return ADLDisplayInfo Array for supported displays' information</param>
/// <param name="forceDetect">force detect or not</param>
/// <returns>return ADL Error Code</returns>
internal delegate int ADL2_Display_DisplayInfo_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, ref int numDisplays, out IntPtr displayInfoArray, int forceDetect);
internal delegate int ADL2_Display_Size_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, ref int lpWidth, ref int lpHeight, ref int lpDefaultWidth, ref int lpDefaultHeight, ref int lpMinWidth, ref int lpMinHeight, ref int lpMaxWidth, ref int lpMaxHeight, ref int lpStepWidth, ref int lpStepHeight);
internal delegate int ADL2_Display_PixelFormat_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, ref int pixelFormat);
internal delegate int ADL2_Display_PixelFormat_Set(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, int pixelFormat);
internal delegate int ADL2_Display_ColorDepth_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, ref int colorDepth);
internal delegate int ADL2_Display_ColorDepth_Set(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, int colorDepth);
internal delegate int ADL2_Display_DitherState_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, ref int ditherState);
internal delegate int ADL2_Display_DitherState_Set(ADL_CONTEXT_HANDLE context, int adapterIndex, int displayIndex, int ditherState);
internal delegate int ADL2_Display_HDRState_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, ADLDisplayID displayID, ref int support, ref int enable);
internal delegate int ADL2_Display_HDRState_Set(ADL_CONTEXT_HANDLE context, int adapterIndex, ADLDisplayID displayID, int enable);
#endregion Export Delegates
#region Class ADLImport
/// <summary> ADLImport class</summary>
private static class ADLImport
{
#region Internal Constant
/// <summary> Atiadlxx_FileName </summary>
internal const string Atiadlxx_FileName = "atiadlxx.dll";
/// <summary> Kernel32_FileName </summary>
internal const string Kernel32_FileName = "kernel32.dll";
#endregion Internal Constant
#region DLLImport
[DllImport(Kernel32_FileName)]
internal static extern HMODULE GetModuleHandle(string moduleName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Main_Control_IsFunctionValid(HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern FARPROC ADL_Main_Control_GetProcAddress(HMODULE module, string procName);
#endregion DLLImport
}
#endregion Class ADLImport
#region Class ADLCheckLibrary
/// <summary> ADLCheckLibrary class</summary>
private class ADLCheckLibrary
{
#region Private Members
private HMODULE ADLLibrary = System.IntPtr.Zero;
#endregion Private Members
#region Static Members
/// <summary> new a private instance</summary>
private static ADLCheckLibrary ADLCheckLibrary_ = new ADLCheckLibrary();
#endregion Static Members
#region Constructor
/// <summary> Constructor</summary>
private ADLCheckLibrary()
{
try
{
if (1 == ADLImport.ADL_Main_Control_IsFunctionValid(IntPtr.Zero, "ADL_Main_Control_Create"))
{
ADLLibrary = ADLImport.GetModuleHandle(ADLImport.Atiadlxx_FileName);
}
}
catch (DllNotFoundException) { }
catch (EntryPointNotFoundException) { }
catch (Exception) { }
}
#endregion Constructor
#region Destructor
/// <summary> Destructor to force calling ADL Destroy function before free up the ADL library</summary>
~ADLCheckLibrary()
{
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Destroy>();
if (del != null)
{
del();
}
}
}
#endregion Destructor
#region Static IsFunctionValid
/// <summary> Check the import function to see it exists or not</summary>
/// <param name="functionName"> function name</param>
/// <returns>return true, if function exists</returns>
internal static bool IsFunctionValid(string functionName)
{
var result = false;
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
if (1 == ADLImport.ADL_Main_Control_IsFunctionValid(ADLCheckLibrary_.ADLLibrary, functionName))
{
result = true;
}
}
return result;
}
#endregion Static IsFunctionValid
#region Static GetProcAddress
/// <summary> Get the unmanaged function pointer </summary>
/// <param name="functionName"> function name</param>
/// <returns>return function pointer, if function exists</returns>
internal static FARPROC GetProcAddress(string functionName)
{
var result = IntPtr.Zero;
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
result = ADLImport.ADL_Main_Control_GetProcAddress(ADLCheckLibrary_.ADLLibrary, functionName);
}
return result;
}
#endregion Static GetProcAddress
}
#endregion Class ADLCheckLibrary
#region Export Functions
#region ADL_Main_Memory_Alloc
/// <summary> Build in memory allocation function</summary>
internal static ADL_Main_Memory_Alloc ADL_Main_Memory_Alloc_Func = ADL_Main_Memory_Alloc_;
/// <summary> Build in memory allocation function</summary>
/// <param name="size">input size</param>
/// <returns>return the memory buffer</returns>
private static IntPtr ADL_Main_Memory_Alloc_(int size)
{
IntPtr result = Marshal.AllocCoTaskMem(size);
return result;
}
#endregion ADL_Main_Memory_Alloc
#region ADL_Main_Memory_Free
/// <summary> Build in memory free function</summary>
/// <param name="buffer">input buffer</param>
internal static void ADL_Main_Memory_Free(IntPtr buffer)
{
if (IntPtr.Zero != buffer)
{
Marshal.FreeCoTaskMem(buffer);
}
}
#endregion ADL_Main_Memory_Free
public static T GetDelegate<T>() where T : class
{
var type = typeof(T);
lock (ADLDelegates)
{
if (ADLDelegates.ContainsKey(type))
{
return ADLDelegates[type] as T;
}
var ptr = ADLCheckLibrary.GetProcAddress(type.Name);
if (ptr != FARPROC.Zero)
{
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, type);
ADLDelegates.Add(type, delegateValue);
return delegateValue as T;
}
}
Logger.Error($"ADL-function not supported: {type.Name}");
return null;
//throw new InvalidProgramException("ADL-function not supported");
}
#endregion Export Functions
}
#endregion ADL Class
}
#endregion ATI.ADL
-402
View File
@@ -1,402 +0,0 @@
using ATI.ADL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ADL_CONTEXT_HANDLE = System.IntPtr;
namespace ColorControl.Services.AMD
{
enum ADLPixelFormat
{
[Description("Unknown/unsupported")]
UNKNOWN = 0,
RGB_FULL_RANGE = 1,
YCRCB444 = 2,
YCRCB422 = 3,
RGB_LIMITED_RANGE = 4,
YCRCB420 = 8
}
enum ADLColorDepth
{
[Description("Unknown/unsupported")]
UNKNOWN = 0,
BPC6 = 1,
BPC8 = 2,
BPC10 = 3,
BPC12 = 4,
BPC14 = 5,
BPC16 = 6
}
enum ADLDitherState
{
DISABLED = 0,
DRIVER_DEFAULT = 1,
FM6 = 2,
FM8 = 3,
FM10 = 4,
DITH6 = 5,
DITH8 = 6,
DITH10 = 7,
DITH6_NO_FRAME_RAND = 8,
DITH8_NO_FRAME_RAND = 9,
DITH10_NO_FRAME_RAND = 10,
TRUN6 = 11,
TRUN8 = 12,
TRUN10 = 13,
TRUN8_DITH8 = 14,
TRUN10_DITH6 = 15,
TRUN10_FM8 = 16,
TRUN10_FM6 = 17,
TRUN10_DITH8_FM6 = 18,
DITH10_FM8 = 19,
DITH10_FM6 = 20,
TRUN8_DITH6 = 21,
TRUN8_FM6 = 22,
DITH8_FM6 = 23
}
class ADLWrapper
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static ADL_CONTEXT_HANDLE context = IntPtr.Zero;
public static bool Initialize()
{
int ADLRet = -1;
//Logger.Debug("Initialize");
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Create>();
if (del != null)
{
// Second parameter is 1: Get only the present adapters
ADLRet = del(ADL.ADL_Main_Memory_Alloc_Func, 1);
CheckError(ADLRet, nameof(ADL.ADL_Main_Control_Create));
}
var del2 = ADL.GetDelegate<ADL.ADL2_Main_Control_Create>();
if (del2 != null)
{
// Second parameter is 1: Get only the present adapters
ADLRet = del2(ADL.ADL_Main_Memory_Alloc_Func, 1, ref context);
CheckError(ADLRet, nameof(ADL.ADL2_Main_Control_Create));
}
return (ADL.ADL_SUCCESS == ADLRet);
}
public static void Uninitialze()
{
if (!ADL.ADLDelegates.Any())
{
return;
}
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Destroy>();
if (null != del)
{
del();
}
var del2 = ADL.GetDelegate<ADL.ADL2_Main_Control_Destroy>();
if (null != del2 && context != IntPtr.Zero)
{
del2(context);
}
}
public static ADLAdapterInfoArray? GetAdapters()
{
int ADLRet = -1;
int NumberOfAdapters = 0;
var delNumberOfAdapters = ADL.GetDelegate<ADL.ADL2_Adapter_NumberOfAdapters_Get>();
if (null != delNumberOfAdapters)
{
delNumberOfAdapters(context, ref NumberOfAdapters);
}
Logger.Debug("Number Of Adapters: " + NumberOfAdapters.ToString() + "\n");
if (NumberOfAdapters > 0)
{
// Get OS adpater info from ADL
ADLAdapterInfoArray OSAdapterInfoData;
OSAdapterInfoData = new ADLAdapterInfoArray();
var delAdapterInfo = ADL.GetDelegate<ADL.ADL2_Adapter_AdapterInfo_Get>();
if (null != delAdapterInfo)
{
IntPtr AdapterBuffer = IntPtr.Zero;
int size = Marshal.SizeOf(OSAdapterInfoData);
AdapterBuffer = Marshal.AllocCoTaskMem((int)size);
Marshal.StructureToPtr(OSAdapterInfoData, AdapterBuffer, false);
ADLRet = delAdapterInfo(context, AdapterBuffer, size);
if (ADL.ADL_SUCCESS == ADLRet)
{
OSAdapterInfoData = (ADLAdapterInfoArray)Marshal.PtrToStructure(AdapterBuffer, OSAdapterInfoData.GetType());
int IsActive = 0;
var delActive = ADL.GetDelegate<ADL.ADL2_Adapter_Active_Get>();
for (int i = 0; i < NumberOfAdapters; i++)
{
// Check if the adapter is active
if (null != delActive)
ADLRet = delActive(context, OSAdapterInfoData.ADLAdapterInfo[i].AdapterIndex, ref IsActive);
if (ADL.ADL_SUCCESS == ADLRet)
{
}
}
}
else
{
Logger.Debug("ADL_Adapter_AdapterInfo_Get() returned error code " + ADLRet.ToString());
}
// Release the memory for the AdapterInfo structure
if (IntPtr.Zero != AdapterBuffer)
{
Marshal.FreeCoTaskMem(AdapterBuffer);
}
}
return OSAdapterInfoData;
}
return null;
}
public static List<ADLDisplayInfo> GetAllDisplays(bool connected = true)
{
int ADLRet;
int NumberOfDisplays = 0;
var DisplayInfoData = new List<ADLDisplayInfo>();
var OSAdapterInfoData = GetAdapters();
if (OSAdapterInfoData == null || !OSAdapterInfoData.Value.ADLAdapterInfo.Any())
{
return DisplayInfoData;
}
var primaryAdapter = OSAdapterInfoData.Value.ADLAdapterInfo.First();
ADLDisplayInfo oneDisplayInfo = new ADLDisplayInfo();
var del = ADL.GetDelegate<ADL.ADL2_Display_DisplayInfo_Get>();
if (null != del)
{
IntPtr DisplayBuffer = IntPtr.Zero;
int j = 0;
// Force the display detection and get the Display Info. Use 0 as last parameter to NOT force detection
ADLRet = del(context, primaryAdapter.AdapterIndex, ref NumberOfDisplays, out DisplayBuffer, 1);
if (ADL.ADL_SUCCESS == ADLRet)
{
for (j = 0; j < NumberOfDisplays; j++)
{
oneDisplayInfo = (ADLDisplayInfo)Marshal.PtrToStructure(new IntPtr(DisplayBuffer.ToInt64() + j * Marshal.SizeOf(oneDisplayInfo)), oneDisplayInfo.GetType());
if (!connected || (1 == (oneDisplayInfo.DisplayInfoValue & 1)))
{
DisplayInfoData.Add(oneDisplayInfo);
}
}
Logger.Debug("Total Number of Displays supported: " + NumberOfDisplays.ToString());
Logger.Debug("DispID DispPhyId AdpID Type OutType CnctType Connected Mapped InfoValue DisplayName ");
for (j = 0; j < DisplayInfoData.Count; j++)
{
int InfoValue = DisplayInfoData[j].DisplayInfoValue;
string StrConnected = (1 == (InfoValue & 1)) ? "Yes" : "No ";
string StrMapped = (2 == (InfoValue & 2)) ? "Yes" : "No ";
int AdpID = DisplayInfoData[j].DisplayID.DisplayLogicalAdapterIndex;
string StrAdpID = (AdpID < 0) ? "--" : AdpID.ToString("d2");
Logger.Debug(DisplayInfoData[j].DisplayID.DisplayLogicalIndex.ToString() + " " +
DisplayInfoData[j].DisplayID.DisplayPhysicalIndex.ToString() + " " +
StrAdpID + " " +
DisplayInfoData[j].DisplayType.ToString() + " " +
DisplayInfoData[j].DisplayOutputType.ToString() + " " +
DisplayInfoData[j].DisplayConnector.ToString() + " " +
StrConnected + " " +
StrMapped + " " +
InfoValue.ToString("x4") + " " +
DisplayInfoData[j].DisplayName.ToString());
}
}
else
{
Logger.Debug("ADL_Display_DisplayInfo_Get() returned error code " + ADLRet.ToString());
}
// Release the memory for the DisplayInfo structure
if (IntPtr.Zero != DisplayBuffer)
Marshal.FreeCoTaskMem(DisplayBuffer);
}
return DisplayInfoData;
}
private static bool CheckError(int ADLRet, string functionName)
{
var result = ADLRet == ADL.ADL_SUCCESS;
if (!result)
{
Logger.Error($"Error calling ADL ({functionName}): {ADLRet}");
}
return result;
}
public static bool GetDisplayResolution(ADLDisplayInfo display, ref int horizontal, ref int vertical)
{
var screen = Screen.PrimaryScreen;
var bounds = screen.Bounds;
horizontal = bounds.Width;
vertical = bounds.Height;
Logger.Debug($"GetDisplayResolution: {horizontal}x{vertical}");
//var del = ADL.GetDelegate<ADL.ADL2_Display_Size_Get>();
//if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
//{
// int lpDefaultWidth, lpDefaultHeight, lpMinWidth, lpMinHeight, lpMaxWidth, lpMaxHeight, lpStepWidth, lpStepHeight;
// lpDefaultWidth = lpDefaultHeight = lpMinWidth = lpMinHeight = lpMaxWidth = lpMaxHeight = lpStepWidth = lpStepHeight = 0;
// var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref horizontal, ref vertical, ref lpDefaultWidth, ref lpDefaultHeight, ref lpMinWidth, ref lpMinHeight, ref lpMaxWidth, ref lpMaxHeight, ref lpStepWidth, ref lpStepHeight);
// return CheckError(ADLRet, nameof(ADL.ADL2_Display_Size_Get));
//}
return true;
}
public static bool GetDisplayPixelFormat(ADLDisplayInfo display, ref ADLPixelFormat pixelFormat)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_PixelFormat_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
int pixelFormatInt = 0;
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref pixelFormatInt);
pixelFormat = (ADLPixelFormat)pixelFormatInt;
return CheckError(ADLRet, nameof(ADL.ADL2_Display_PixelFormat_Get));
}
return false;
}
public static bool SetDisplayPixelFormat(ADLDisplayInfo display, ADLPixelFormat pixelFormat)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_PixelFormat_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)pixelFormat);
return CheckError(ADLRet, nameof(ADL.ADL2_Display_PixelFormat_Set));
}
return false;
}
public static bool GetDisplayColorDepth(ADLDisplayInfo display, ref ADLColorDepth colorDepth)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_ColorDepth_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
int colorDepthInt = 0;
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref colorDepthInt);
colorDepth = (ADLColorDepth)colorDepthInt;
return CheckError(ADLRet, nameof(ADL.ADL2_Display_ColorDepth_Get));
}
return false;
}
public static bool SetDisplayColorDepth(ADLDisplayInfo display, ADLColorDepth colorDepth)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_ColorDepth_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)colorDepth);
return CheckError(ADLRet, nameof(ADL.ADL2_Display_ColorDepth_Set));
}
return false;
}
public static bool GetDisplayDitherState(ADLDisplayInfo display, ref ADLDitherState ditherState)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_DitherState_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ditherStateInt = 0;
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref ditherStateInt);
ditherState = (ADLDitherState)ditherStateInt;
return CheckError(ADLRet, nameof(ADL.ADL2_Display_DitherState_Get));
}
return false;
}
public static bool SetDisplayDitherState(ADLDisplayInfo display, ADLDitherState ditherState)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_DitherState_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)ditherState);
return CheckError(ADLRet, nameof(ADL.ADL2_Display_DitherState_Set));
}
return false;
}
public static bool GetDisplayHDRState(ADLDisplayInfo display, ref bool supported, ref bool enabled)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_HDRState_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var supportedInt = 0;
var enabledInt = 0;
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID, ref supportedInt, ref enabledInt);
supported = supportedInt != 0;
enabled = enabledInt != 0;
return CheckError(ADLRet, nameof(ADL.ADL2_Display_HDRState_Get));
}
return false;
}
public static bool SetDisplayHDRState(ADLDisplayInfo display, bool enabled)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_HDRState_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID, enabled ? 1 : 0);
return CheckError(ADLRet, nameof(ADL.ADL2_Display_HDRState_Set));
}
return false;
}
}
}
+13 -14
View File
@@ -1,21 +1,20 @@
using ATI.ADL;
using System.Collections.Generic;
namespace ColorControl.Services.AMD
namespace ColorControl.Services.AMD;
class AmdDisplayInfo
{
class AmdDisplayInfo
public ADLDisplayInfo Display { get; }
public List<string> Values { get; set; }
public string InfoLine { get; set; }
public AmdDisplayInfo(ADLDisplayInfo display, List<string> values, string infoLine)
{
public ADLDisplayInfo Display { get; }
public List<string> Values { get; }
public string InfoLine { get; }
public AmdDisplayInfo(ADLDisplayInfo display, List<string> values, string infoLine)
{
Display = display;
Values = values;
InfoLine = infoLine;
}
Display = display;
Values = values;
InfoLine = infoLine;
}
}
File diff suppressed because it is too large. Load diff
-148
View File
@@ -1,148 +0,0 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Contracts;
using System;
using System.Collections.Generic;
using System.Text;
namespace ColorControl.Services.AMD
{
class AmdPreset : PresetBase
{
public bool applyColorData { get; set; }
public ADLColorDepth colorDepth { get; set; }
public ADLPixelFormat pixelFormat { get; set; }
public bool applyHDR { get; set; }
public bool HDREnabled { get; set; }
public bool toggleHDR { get; set; }
public bool applyDithering { get; set; }
public ADLDitherState ditherState { get; set; }
public DisplayConfig DisplayConfig { get; set; }
public bool primaryDisplay { get; set; }
public string displayName { get; set; }
public AmdPreset() : base()
{
applyColorData = true;
applyDithering = false;
ditherState = ADLDitherState.DRIVER_DEFAULT;
applyHDR = false;
HDREnabled = false;
toggleHDR = false;
primaryDisplay = true;
DisplayConfig = new DisplayConfig();
}
public AmdPreset(AmdPreset preset) : this()
{
id = GetNewId();
primaryDisplay = preset.primaryDisplay;
displayName = preset.displayName;
applyColorData = preset.applyColorData;
colorDepth = preset.colorDepth;
pixelFormat = preset.pixelFormat;
applyHDR = preset.applyHDR;
HDREnabled = preset.HDREnabled;
toggleHDR = preset.toggleHDR;
applyDithering = preset.applyDithering;
ditherState = preset.ditherState;
DisplayConfig = new DisplayConfig(DisplayConfig);
}
public AmdPreset Clone()
{
var preset = new AmdPreset(this);
return preset;
}
public static string[] GetColumnNames()
{
return new[] { "Name", "Display|140", "Color settings (BPC, format)|260", "Refresh rate|100", "Resolution|120", "Dithering", "HDR", "Shortcut", "Apply on startup" };
}
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>
{
name
};
var display = string.Format("{0}", primaryDisplay ? "Primary" : displayName);
values.Add(display);
var colorSettings = string.Format("{0}: {1}, {2}", applyColorData ? "Included" : "Excluded", colorDepth, pixelFormat);
values.Add(colorSettings);
values.Add(string.Format("{0}: {1}Hz", DisplayConfig.ApplyRefreshRate ? "Included" : "Excluded", DisplayConfig.RefreshRate));
values.Add(string.Format("{0}{1}", DisplayConfig.ApplyResolution ? "Included" : "Excluded", DisplayConfig.ApplyResolution ? ": " + DisplayConfig.GetResolutionDesc() : ""));
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", ditherState));
values.Add(string.Format("{0}: {1}", applyHDR ? "Included" : "Excluded", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled"));
values.Add(shortcut);
values.Add(string.Format("{0}", config?.AmdPresetId_ApplyOnStartup == id ? "Yes" : string.Empty));
return values;
}
public static List<AmdPreset> GetDefaultPresets()
{
var presets = new List<AmdPreset>();
var preset = new AmdPreset();
preset.colorDepth = ADLColorDepth.BPC8;
preset.pixelFormat = ADLPixelFormat.RGB_FULL_RANGE;
presets.Add(preset);
return presets;
}
public override string GetTextForMenuItem()
{
var sb = new StringBuilder();
if (!string.IsNullOrEmpty(name))
{
sb.Append(name);
sb.Append(" / ");
}
else
{
if (displayName != null)
{
sb.AppendFormat("Display: {0} / ", displayName);
}
if (applyColorData)
{
var colorSettings = string.Format("Format: {0}, {1}", colorDepth, pixelFormat);
sb.Append(colorSettings);
sb.Append(" / ");
}
if (DisplayConfig.ApplyRefreshRate)
{
sb.AppendFormat("{0}Hz", DisplayConfig.RefreshRate);
sb.Append(" / ");
}
if (DisplayConfig.ApplyResolution)
{
sb.AppendFormat("{0}", DisplayConfig.GetResolutionDesc());
sb.Append(" / ");
}
if (applyDithering)
{
sb.AppendFormat("Dithering: {0}", ditherState);
sb.Append(" / ");
}
if (applyHDR)
{
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
sb.Append(" / ");
}
}
return sb.ToString(0, Math.Max(0, sb.Length - 3));
}
}
}
+298 -53
View File
@@ -2,10 +2,14 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.AMD;
using ColorControl.Shared.Native;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -24,9 +28,13 @@ namespace ColorControl.Services.AMD
public static readonly int SHORTCUTID_AMDQA = -201;
private string _configFilename;
public AmdServiceConfig Config { get; private set; }
public AmdService(GlobalContext globalContext) : base(globalContext)
{
LoadPresets();
LoadConfig();
}
public static async Task<bool> ExecutePresetAsync(string idOrName)
@@ -57,7 +65,24 @@ namespace ColorControl.Services.AMD
{
base.InstallEventHandlers();
SetShortcuts(SHORTCUTID_AMDQA, _globalContext.Config.AmdQuickAccessShortcut);
SetShortcuts(SHORTCUTID_AMDQA, Config.QuickAccessShortcut);
}
private void LoadConfig()
{
_configFilename = Path.Combine(_dataPath, "AmdConfig.json");
try
{
if (File.Exists(_configFilename))
{
Config = JsonConvert.DeserializeObject<AmdServiceConfig>(File.ReadAllText(_configFilename));
}
}
catch (Exception ex)
{
Logger.Error($"LoadConfig: {ex.Message}");
}
Config ??= new AmdServiceConfig();
}
protected override List<AmdPreset> GetDefaultPresets()
@@ -65,6 +90,11 @@ namespace ColorControl.Services.AMD
return AmdPreset.GetDefaultPresets();
}
public override List<string> GetInfo()
{
return [$"{_presets.Count} presets"];
}
private void SavePresets()
{
Utils.WriteObject(_presetsFilename, _presets);
@@ -73,6 +103,7 @@ namespace ColorControl.Services.AMD
public void GlobalSave()
{
SavePresets();
SaveConfig();
}
public override bool HasDisplaysAttached(bool reinitialize = false)
@@ -110,19 +141,6 @@ namespace ColorControl.Services.AMD
return _currentDisplay;
}
public async Task<bool> ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return await ApplyPreset(preset);
}
else
{
return false;
}
}
public override async Task<bool> ApplyPreset(AmdPreset preset)
{
SetCurrentDisplay(preset);
@@ -151,6 +169,13 @@ namespace ColorControl.Services.AMD
//{
// ToggleHDR(config.DisplaySettingsDelay);
//}
if (preset.SDRBrightness.HasValue)
{
var deviceName = GetDisplayDeviceName(_currentDisplay);
SetSDRBrightness(deviceName, preset.SDRBrightness.Value);
}
}
if (preset.DisplayConfig.ApplyRefreshRate || preset.DisplayConfig.ApplyResolution)
@@ -259,9 +284,67 @@ namespace ColorControl.Services.AMD
return GetAvailableResolutionsInternalV2(deviceName);
}
public bool IsHDREnabled()
public bool SetMode(VirtualResolution resolution = null, Rational refreshRate = null, bool updateRegistry = false, AmdPreset preset = null)
{
var display = GetCurrentDisplay();
var display = preset == null ? GetCurrentDisplay() : GetPresetDisplay(preset);
if (display.Equals(default(ADLDisplayInfo)))
{
return false;
}
return SetMode(GetDisplayDeviceName(display), resolution, refreshRate, updateRegistry);
}
public bool SetMode(DisplayConfig displayConfig, bool updateRegistry = false, AmdPreset preset = null)
{
var display = preset == null ? GetCurrentDisplay() : GetPresetDisplay(preset);
if (display.DisplayID.DisplayPhysicalIndex == 0)
{
return false;
}
return SetMode(GetDisplayDeviceName(display), displayConfig, updateRegistry);
}
public ADLDisplayInfo GetPresetDisplay(AmdPreset preset)
{
return GetCurrentDisplay();
}
public List<Rational> GetAvailableRefreshRatesV2(AmdPreset preset = null)
{
if (preset != null)
{
SetCurrentDisplay(preset);
}
var display = _currentDisplay;
if (_currentDisplay.DisplayID.DisplayLogicalAdapterIndex == 0)
{
return [];
}
var desktopRect = preset?.DisplayConfig.ApplyResolution == true && preset?.DisplayConfig.Resolution.ActiveWidth > 0 ?
new Rectangle(0, 0, (int)preset.DisplayConfig.Resolution.ActiveWidth, (int)preset.DisplayConfig.Resolution.ActiveHeight) :
GetDesktopRect(display);
return GetAvailableRefreshRatesV2(Screen.PrimaryScreen.DeviceName, desktopRect.Width, desktopRect.Height);
}
private Rectangle GetDesktopRect(ADLDisplayInfo display)
{
// TODO: improve this
var screen = Screen.AllScreens.First();
return screen.Bounds;
}
public bool IsHDREnabled(ADLDisplayInfo display = default)
{
if (display.Equals(default(ADLDisplayInfo)))
{
display = GetCurrentDisplay();
}
var supported = false;
var enabled = false;
@@ -312,7 +395,7 @@ namespace ColorControl.Services.AMD
return Screen.AllScreens.FirstOrDefault(x => CCD.GetDisplayInfo(x.DeviceName)?.FriendlyName.Equals(display.DisplayName) ?? false);
}
public List<AmdDisplayInfo> GetDisplayInfos()
public List<AmdDisplayInfo> GetSimpleDisplayInfos()
{
List<ADLDisplayInfo> displays;
try
@@ -325,57 +408,132 @@ namespace ColorControl.Services.AMD
return null;
}
return displays.Select(d => new AmdDisplayInfo(d, null, null)).ToList();
//var list = new List<AmdDisplayInfo>();
//foreach (var display in displays)
//{
// var values = new List<string>
// {
// "Current settings"
// };
// var screen = GetScreenForDisplay(display);
// var name = GetFullDisplayName(display);
// values.Add(name);
// var colorDepth = ADLColorDepth.UNKNOWN;
// ADLWrapper.GetDisplayColorDepth(display, ref colorDepth);
// var pixelFormat = ADLPixelFormat.UNKNOWN;
// ADLWrapper.GetDisplayPixelFormat(display, ref pixelFormat);
// var colorSettings = $"{colorDepth.GetDescription()}, {pixelFormat.GetDescription()}";
// values.Add(colorSettings);
// var displayConfig = CCD.GetDisplayConfig(screen?.DeviceName);
// values.Add($"{displayConfig.RefreshRate}Hz");
// values.Add($"{displayConfig.GetResolutionDesc()}");
// var ditherState = GetDithering(display);
// values.Add(ditherState.GetDescription());
// var hdrEnabled = IsHDREnabled();
// values.Add(hdrEnabled ? "Yes" : "No");
// var infoLine = string.Format("{0}: {1}, {2}Hz, {3}, HDR: {4}", name, colorSettings, displayConfig.RefreshRate, displayConfig.GetResolutionDesc(), hdrEnabled ? "Yes" : "No");
// var displayInfo = new AmdDisplayInfo(display, values, infoLine);
// list.Add(displayInfo);
//}
//return list;
}
public List<AmdDisplayInfo> GetDisplayInfos()
{
var list = new List<AmdDisplayInfo>();
foreach (var display in displays)
var displayInfos = GetSimpleDisplayInfos();
foreach (var displayInfo in displayInfos)
{
var values = new List<string>
{
"Current settings"
};
var preset = GetPresetForDisplay(displayInfo, displayInfos.Count());
var screen = GetScreenForDisplay(display);
var name = GetFullDisplayName(display);
values.Add(name);
var colorDepth = ADLColorDepth.UNKNOWN;
ADLWrapper.GetDisplayColorDepth(display, ref colorDepth);
var pixelFormat = ADLPixelFormat.UNKNOWN;
ADLWrapper.GetDisplayPixelFormat(display, ref pixelFormat);
var colorSettings = $"{colorDepth.GetDescription()}, {pixelFormat.GetDescription()}";
values.Add(colorSettings);
var displayConfig = CCD.GetDisplayConfig(screen?.DeviceName);
values.Add($"{displayConfig.RefreshRate}Hz");
values.Add($"{displayConfig.GetResolutionDesc()}");
var ditherState = GetDithering(display);
values.Add(ditherState.GetDescription());
var hdrEnabled = IsHDREnabled();
values.Add(hdrEnabled ? "Yes" : "No");
var infoLine = string.Format("{0}: {1}, {2}Hz, {3}, HDR: {4}", name, colorSettings, displayConfig.RefreshRate, displayConfig.GetResolutionDesc(), hdrEnabled ? "Yes" : "No");
var displayInfo = new AmdDisplayInfo(display, values, infoLine);
displayInfo.Values = preset.GetDisplayValues(_globalContext.Config);
displayInfo.InfoLine = preset.InfoLine;
list.Add(displayInfo);
}
return list;
}
//List<ADLDisplayInfo> displays;
//try
//{
// displays = GetDisplays();
//}
//catch (Exception e)
//{
// Logger.Error("Error while getting displays: " + e.ToLogString());
// return null;
//}
//var list = new List<AmdDisplayInfo>();
//foreach (var display in displays)
//{
// var values = new List<string>
// {
// "Current settings"
// };
// var screen = GetScreenForDisplay(display);
// var name = GetFullDisplayName(display);
// values.Add(name);
// var colorDepth = ADLColorDepth.UNKNOWN;
// ADLWrapper.GetDisplayColorDepth(display, ref colorDepth);
// var pixelFormat = ADLPixelFormat.UNKNOWN;
// ADLWrapper.GetDisplayPixelFormat(display, ref pixelFormat);
// var colorSettings = $"{colorDepth.GetDescription()}, {pixelFormat.GetDescription()}";
// values.Add(colorSettings);
// var displayConfig = CCD.GetDisplayConfig(screen?.DeviceName);
// values.Add($"{displayConfig.RefreshRate}Hz");
// values.Add($"{displayConfig.GetResolutionDesc()}");
// var ditherState = GetDithering(display);
// values.Add(ditherState.GetDescription());
// var hdrEnabled = IsHDREnabled();
// values.Add(hdrEnabled ? "Yes" : "No");
// var infoLine = string.Format("{0}: {1}, {2}Hz, {3}, HDR: {4}", name, colorSettings, displayConfig.RefreshRate, displayConfig.GetResolutionDesc(), hdrEnabled ? "Yes" : "No");
// var displayInfo = new AmdDisplayInfo(display, values, infoLine);
// list.Add(displayInfo);
//}
//return list;
}
protected override void Initialize()
{
if (!ADLWrapper.Initialize())
{
throw new InvalidOperationException("Cannot load AMD API");
//throw new InvalidOperationException("Cannot load AMD API");
}
}
@@ -383,5 +541,92 @@ namespace ColorControl.Services.AMD
{
ADLWrapper.Uninitialze();
}
public List<AmdPreset> GetDisplayPresets()
{
try
{
var list = new List<AmdPreset>();
var displayInfos = GetSimpleDisplayInfos();
foreach (var displayInfo in displayInfos)
{
var preset = GetPresetForDisplay(displayInfo, displayInfos.Count());
displayInfo.Values = preset.GetDisplayValues(_globalContext.Config);
displayInfo.InfoLine = preset.InfoLine;
list.Add(preset);
}
return list;
}
catch (Exception e)
{
Logger.Error("Error while getting displays: " + e.ToLogString());
return null;
}
}
public AmdPreset GetPresetForDisplay(AmdDisplayInfo displayInfo, int displayCount = 1)
{
if (displayInfo == null)
{
return null;
}
var display = displayInfo.Display;
var colorDepth = ADLColorDepth.UNKNOWN;
ADLWrapper.GetDisplayColorDepth(display, ref colorDepth);
var pixelFormat = ADLPixelFormat.UNKNOWN;
ADLWrapper.GetDisplayPixelFormat(display, ref pixelFormat);
var ditherState = GetDithering(display);
var screen = GetScreenForDisplay(display);
var displayConfig = CCD.GetDisplayConfig(screen?.DeviceName);
var ccdDisplay = screen != null ? CCD.GetDisplayInfo(screen.DeviceName) : null;
var hdrEnabled = IsHDREnabled(display);
var preset = new AmdPreset
{
IsDisplayPreset = true,
displayName = display.DisplayName,
DisplayId = ccdDisplay?.DisplayId,
primaryDisplay = screen?.Primary == true,
colorDepth = colorDepth,
pixelFormat = pixelFormat,
ditherState = ditherState,
DisplayConfig = displayConfig,
SDRBrightness = hdrEnabled ? GetSDRBrightness(screen?.DeviceName) : null,
HDREnabled = hdrEnabled,
};
return preset;
}
private void SaveConfig()
{
Utils.WriteObject(_configFilename, Config);
}
public AmdServiceConfig GetConfig()
{
return Config;
}
public bool UpdateConfig(AmdServiceConfig config)
{
Config.Update(config);
SetShortcuts(SHORTCUTID_AMDQA, Config.QuickAccessShortcut);
return true;
}
}
}
@@ -1,7 +1,10 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using NWin32;
using Shared.Native;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -11,6 +14,9 @@ namespace ColorControl.Services.Common
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private int _lastSetSDRBrightness = -1;
private int _lastReadSDRBrightness = -1;
public GraphicsService(GlobalContext globalContext) : base(globalContext)
{
}
@@ -68,6 +74,43 @@ namespace ColorControl.Services.Common
return CCD.SetDisplayConfig(displayName, currentDisplayConfig, updateRegistry);
}
public void SetSDRBrightness(string displayName, int brightnessPercent)
{
var hmon = FormUtils.GetMonitorForDisplayName(displayName);
if (hmon == IntPtr.Zero)
{
hmon = NativeMethods.MonitorFromWindow(0, 1);
}
var brightness = 1 + (double)brightnessPercent / 20;
WinApi.DwmpSDRToHDRBoostPtr(hmon, brightness);
_lastSetSDRBrightness = brightnessPercent;
}
public int GetSDRBrightness(string displayName)
{
var whiteLevel = CCD.GetSDRWhiteLevel(displayName);
if (whiteLevel >= 1000)
{
var newBrightnessPercent = (int)(whiteLevel - 1000) / 50;
if (_lastSetSDRBrightness >= 0 && newBrightnessPercent == _lastReadSDRBrightness)
{
return _lastSetSDRBrightness;
}
_lastReadSDRBrightness = newBrightnessPercent;
return newBrightnessPercent;
}
return 0;
}
protected List<Rational> GetAvailableRefreshRatesV2(string displayName, int horizontal, int vertical)
{
var dxWrapper = new DXWrapper();
+5 -2
View File
@@ -1,6 +1,9 @@
namespace ColorControl.Services.Common;
using System.Collections.Generic;
internal interface IServiceBase
namespace ColorControl.Services.Common;
public interface IServiceBase
{
List<string> GetInfo();
void InstallEventHandlers();
}
@@ -0,0 +1,70 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Services;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ColorControl.Services.Common;
public class LoggingService
{
private readonly GlobalContext _globalContext;
private readonly ElevationService _elevationService;
private readonly WinApiService _winApiService;
public LoggingService(GlobalContext globalContext, ElevationService elevationService, WinApiService winApiService)
{
_globalContext = globalContext;
_elevationService = elevationService;
_winApiService = winApiService;
}
public bool SetLogLevel(string logLevelName)
{
var logLevel = LogLevel.FromString(logLevelName);
_globalContext.SetLogLevel(logLevel);
return true;
}
public async Task<List<string>> LoadLog(int type, int maxLines = 1000, int order = 0)
{
string logFile;
var lines = default(List<string>);
if (type == 0)
{
lines = Utils.ReadLines(Program.LogFilename);
}
else
{
var winApiService = Program.ServiceProvider.GetRequiredService<WinApiService>();
if (winApiService.IsServiceRunning())
{
var result = await PipeUtils.SendMessageAsync(SvcMessageType.GetLog);
logFile = result?.Data ?? "Cannot get log from service";
}
else
{
logFile = "The service is not installed or not running";
}
lines = logFile.Split("\r\n").ToList();
}
lines = lines.Where((s, index) => order == 1 ? index < maxLines : (order == 0 && index >= lines.Count - maxLines)).ToList();
if (order == 0)
{
lines.Reverse();
}
return lines;
}
}
+206 -192
View File
@@ -1,11 +1,11 @@
using ColorControl.Properties;
using ColorControl.Services.AMD;
using ColorControl.Services.GameLauncher;
using ColorControl.Services.LG;
using ColorControl.Services.NVIDIA;
using ColorControl.Services.Samsung;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.AMD;
using ColorControl.Shared.Contracts.Game;
using ColorControl.Shared.Contracts.LG;
using ColorControl.Shared.Contracts.NVIDIA;
using ColorControl.Shared.Contracts.Samsung;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Services;
using novideo_srgb;
@@ -18,239 +18,253 @@ namespace ColorControl.Services.Common;
public class NotifyIconManager
{
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
private readonly WinApiService _winApiService;
private readonly Config _config;
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
private readonly WinApiService _winApiService;
private readonly Config _config;
public NotifyIcon NotifyIcon { get; private set; }
public NotifyIcon NotifyIcon { get; private set; }
private ToolStripMenuItem _nvTrayMenu;
private ToolStripMenuItem _novideoTrayMenu;
private ToolStripMenuItem _amdTrayMenu;
private ToolStripMenuItem _lgTrayMenu;
private ToolStripMenuItem _samsungTrayMenu;
private ToolStripMenuItem _gameTrayMenu;
private ToolStripMenuItem _nvTrayMenu;
private ToolStripMenuItem _novideoTrayMenu;
private ToolStripMenuItem _amdTrayMenu;
private ToolStripMenuItem _lgTrayMenu;
private ToolStripMenuItem _samsungTrayMenu;
private ToolStripMenuItem _gameTrayMenu;
private ToolStripMenuItem _stopServiceAndExitMenuItem;
private ToolStripMenuItem _openNewUiMenuItem;
private ToolStripMenuItem _stopServiceAndExitMenuItem;
private ToolStripMenuItem _openWinFormsMenuItem;
private ToolStripMenuItem _openNewUiMenuItem;
private ToolStripMenuItem _openNewUiEmbeddedMenuItem;
public NotifyIconManager(GlobalContext globalContext, ServiceManager serviceManager, WinApiAdminService winApiAdminService, WinApiService winApiService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
_winApiService = winApiService;
_config = globalContext.Config;
}
public NotifyIconManager(GlobalContext globalContext, ServiceManager serviceManager, WinApiAdminService winApiAdminService, WinApiService winApiService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
_winApiService = winApiService;
_config = globalContext.Config;
}
public void Build()
{
if (_nvTrayMenu != null)
{
return;
}
public void Build()
{
if (_nvTrayMenu != null)
{
return;
}
_nvTrayMenu = new ToolStripMenuItem("NVIDIA presets");
_novideoTrayMenu = new ToolStripMenuItem("Novideo sRGB");
_amdTrayMenu = new ToolStripMenuItem("AMD presets");
_lgTrayMenu = new ToolStripMenuItem("LG presets");
_samsungTrayMenu = new ToolStripMenuItem("Samsung presets");
_gameTrayMenu = new ToolStripMenuItem("Game Launcher");
NotifyIcon = new NotifyIcon()
{
Icon = Resources.AppIcon,
ContextMenuStrip = new ContextMenuStrip(),
Visible = _config.MinimizeToTray
};
_nvTrayMenu = new ToolStripMenuItem("NVIDIA presets");
_novideoTrayMenu = new ToolStripMenuItem("Novideo sRGB");
_amdTrayMenu = new ToolStripMenuItem("AMD presets");
_lgTrayMenu = new ToolStripMenuItem("LG presets");
_samsungTrayMenu = new ToolStripMenuItem("Samsung presets");
_gameTrayMenu = new ToolStripMenuItem("Game Launcher");
NotifyIcon = new NotifyIcon()
{
Icon = Resources.AppIcon,
ContextMenuStrip = new ContextMenuStrip(),
Visible = _config.MinimizeToTray
};
SetText();
SetText();
NotifyIcon.ContextMenuStrip.Items.AddRange(new ToolStripItem[] {
_nvTrayMenu,
_novideoTrayMenu,
_amdTrayMenu,
_lgTrayMenu,
_samsungTrayMenu,
_gameTrayMenu,
new ToolStripSeparator(),
new ToolStripMenuItem("Open", null, OpenForm),
_openNewUiMenuItem = new ToolStripMenuItem("Open new UI", null, OpenNewUi),
new ToolStripSeparator(),
new ToolStripMenuItem("Restart", null, Restart),
_stopServiceAndExitMenuItem = new ToolStripMenuItem("Stop Service And Exit", null, StopServiceAndExit),
new ToolStripMenuItem("Exit", null, Exit)
});
NotifyIcon.ContextMenuStrip.Items.AddRange(new ToolStripItem[] {
_nvTrayMenu,
_novideoTrayMenu,
_amdTrayMenu,
_lgTrayMenu,
_samsungTrayMenu,
_gameTrayMenu,
new ToolStripSeparator(),
new ToolStripMenuItem("Open", null, OpenForm),
_openWinFormsMenuItem = new ToolStripMenuItem("Open WinForms UI", null, OpenWinFormsUi),
_openNewUiMenuItem = new ToolStripMenuItem("Open web UI (browser)", null, OpenNewUi),
_openNewUiEmbeddedMenuItem = new ToolStripMenuItem("Open web UI (embedded)", null, OpenNewUiEmbedded),
new ToolStripSeparator(),
new ToolStripMenuItem("Restart", null, Restart),
_stopServiceAndExitMenuItem = new ToolStripMenuItem("Stop Service And Exit", null, StopServiceAndExit),
new ToolStripMenuItem("Exit", null, Exit)
});
NotifyIcon.MouseDoubleClick += trayIcon_MouseDoubleClick;
NotifyIcon.ContextMenuStrip.Opened += trayIconContextMenu_Popup;
}
NotifyIcon.MouseDoubleClick += trayIcon_MouseDoubleClick;
NotifyIcon.ContextMenuStrip.Opened += trayIconContextMenu_Popup;
}
public void HideIcon()
{
if (NotifyIcon != null)
{
NotifyIcon.Visible = false;
}
}
public void HideIcon()
{
if (NotifyIcon != null)
{
NotifyIcon.Visible = false;
}
}
private void StopServiceAndExit(object sender, EventArgs e)
{
_winApiAdminService.StopService();
private void StopServiceAndExit(object sender, EventArgs e)
{
_winApiAdminService.StopService();
Program.Exit();
}
Program.Exit();
}
private void Exit(object sender, EventArgs e)
{
Program.Exit();
}
private void Exit(object sender, EventArgs e)
{
Program.Exit();
}
private void Restart(object sender, EventArgs e)
{
Program.Restart();
}
private void Restart(object sender, EventArgs e)
{
Program.Restart();
}
private void OpenForm(object sender, EventArgs e)
{
Program.OpenMainForm();
}
private void OpenForm(object sender, EventArgs e)
{
Program.OpenDefaultUi();
}
private async void OpenNewUi(object sender, EventArgs e)
{
await Program.OpenNewUi();
}
private void OpenWinFormsUi(object sender, EventArgs e)
{
Program.OpenMainForm();
}
private void trayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Program.OpenMainForm();
}
private async void OpenNewUi(object sender, EventArgs e)
{
await Program.OpenNewUi();
}
private void trayIconContextMenu_Popup(object sender, EventArgs e)
{
_nvTrayMenu.Visible = _serviceManager.NvService != null;
if (_nvTrayMenu.Visible)
{
var presets = _serviceManager.NvService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution || x.applyDriverSettings || x.applyOther || x.applyOverclocking || x.ApplyColorEnhancements || x.applyHdmiSettings);
private async void OpenNewUiEmbedded(object sender, EventArgs e)
{
await Program.OpenNewUiEmbedded();
}
UpdateTrayMenu(_nvTrayMenu, presets, TrayMenuItemNv_Click);
}
private void trayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Program.OpenDefaultUi();
}
_novideoTrayMenu.Visible = _serviceManager.NvService != null && MainWindow.IsInitialized();
if (_novideoTrayMenu.Visible)
{
MainWindow.UpdateContextMenu(_novideoTrayMenu);
}
private void trayIconContextMenu_Popup(object sender, EventArgs e)
{
_nvTrayMenu.Visible = _serviceManager.NvService != null;
if (_nvTrayMenu.Visible)
{
var presets = _serviceManager.NvService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution || x.applyDriverSettings || x.applyOther || x.applyOverclocking || x.ApplyColorEnhancements || x.applyHdmiSettings);
_amdTrayMenu.Visible = _serviceManager.AmdService != null;
if (_amdTrayMenu.Visible)
{
var presets = _serviceManager.AmdService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution);
UpdateTrayMenu(_nvTrayMenu, presets, TrayMenuItemNv_Click);
}
UpdateTrayMenu(_amdTrayMenu, presets, TrayMenuItemAmd_Click);
}
_novideoTrayMenu.Visible = _serviceManager.NvService != null && MainWindow.IsInitialized();
if (_novideoTrayMenu.Visible)
{
MainWindow.UpdateContextMenu(_novideoTrayMenu);
}
_lgTrayMenu.Visible = _serviceManager.LgService != null;
if (_lgTrayMenu.Visible)
{
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.steps.Any());
_amdTrayMenu.Visible = _serviceManager.AmdService != null;
if (_amdTrayMenu.Visible)
{
var presets = _serviceManager.AmdService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution);
_lgTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_amdTrayMenu, presets, TrayMenuItemAmd_Click);
}
UpdateTrayMenu(_lgTrayMenu, presets, TrayMenuItemLg_Click);
}
_lgTrayMenu.Visible = _serviceManager.LgService != null;
if (_lgTrayMenu.Visible)
{
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.steps.Any());
_samsungTrayMenu.Visible = _serviceManager.SamsungService != null;
if (_samsungTrayMenu.Visible)
{
var presets = _serviceManager.SamsungService.GetPresets().Where(x => x.Steps.Any());
_lgTrayMenu.DropDownItems.Clear();
_samsungTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_lgTrayMenu, presets, TrayMenuItemLg_Click);
}
UpdateTrayMenu(_samsungTrayMenu, presets, TrayMenuItemSamsung_Click);
}
_samsungTrayMenu.Visible = _serviceManager.SamsungService != null;
if (_samsungTrayMenu.Visible)
{
var presets = _serviceManager.SamsungService.GetPresets().Where(x => x.Steps.Any());
_gameTrayMenu.Visible = _serviceManager.GameService != null;
if (_gameTrayMenu.Visible)
{
var presets = _serviceManager.GameService.GetPresets();
_samsungTrayMenu.DropDownItems.Clear();
_gameTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_samsungTrayMenu, presets, TrayMenuItemSamsung_Click);
}
UpdateTrayMenu(_gameTrayMenu, presets, TrayMenuItemGame_Click);
}
_gameTrayMenu.Visible = _serviceManager.GameService != null;
if (_gameTrayMenu.Visible)
{
var presets = _serviceManager.GameService.GetPresets();
_stopServiceAndExitMenuItem.Visible = _winApiService.IsServiceRunning();
}
_gameTrayMenu.DropDownItems.Clear();
private void UpdateTrayMenu(ToolStripMenuItem menu, IEnumerable<PresetBase> presets, EventHandler eventHandler)
{
menu.DropDownItems.Clear();
UpdateTrayMenu(_gameTrayMenu, presets, TrayMenuItemGame_Click);
}
foreach (var preset in presets.OrderBy(p => p.name))
{
var name = preset.GetTextForMenuItem();
var keys = Keys.None;
_stopServiceAndExitMenuItem.Visible = _winApiService.IsServiceRunning();
}
if (!string.IsNullOrEmpty(preset.shortcut))
{
name += " " + preset.shortcut;
//keys = KeyboardShortcutDispatcher.ShortcutToKeys(preset.shortcut);
}
private void UpdateTrayMenu(ToolStripMenuItem menu, IEnumerable<PresetBase> presets, EventHandler eventHandler)
{
menu.DropDownItems.Clear();
var item = new ToolStripMenuItem(name, null, null, keys);
item.Tag = preset;
item.Click += eventHandler;
item.ForeColor = FormUtils.MenuItemForeColor;
menu.DropDownItems.Add(item);
}
}
foreach (var preset in presets.OrderBy(p => p.name))
{
var name = preset.GetTextForMenuItem();
var keys = Keys.None;
private async void TrayMenuItemNv_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (NvPreset)item.Tag;
if (!string.IsNullOrEmpty(preset.shortcut))
{
name += " " + preset.shortcut;
//keys = KeyboardShortcutDispatcher.ShortcutToKeys(preset.shortcut);
}
await _serviceManager.NvService?.ApplyPresetUi(preset);
}
var item = new ToolStripMenuItem(name, null, null, keys);
item.Tag = preset;
item.Click += eventHandler;
item.ForeColor = FormUtils.MenuItemForeColor;
menu.DropDownItems.Add(item);
}
}
private async void TrayMenuItemAmd_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (AmdPreset)item.Tag;
private async void TrayMenuItemNv_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (NvPreset)item.Tag;
await _serviceManager.AmdService?.ApplyPresetUi(preset);
}
await _serviceManager.NvService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemGame_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (GamePreset)item.Tag;
private async void TrayMenuItemAmd_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (AmdPreset)item.Tag;
await _serviceManager.GameService?.ApplyPresetUi(preset);
}
await _serviceManager.AmdService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemLg_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (LgPreset)item.Tag;
private async void TrayMenuItemGame_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (GamePreset)item.Tag;
await _serviceManager.LgService?.ApplyPresetUi(preset);
}
await _serviceManager.GameService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemSamsung_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (SamsungPreset)item.Tag;
private async void TrayMenuItemLg_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (LgPreset)item.Tag;
await _serviceManager.SamsungService?.ApplyPresetUi(preset);
}
await _serviceManager.LgService?.ApplyPresetUi(preset);
}
internal void SetText(string text = null)
{
var fullText = $"{_globalContext.ApplicationTitleAdmin}{(text == null ? "" : $"\n{text}")}";
private async void TrayMenuItemSamsung_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (SamsungPreset)item.Tag;
FormUtils.SetNotifyIconText(NotifyIcon, fullText.Trim());
}
await _serviceManager.SamsungService?.ApplyPresetUi(preset);
}
internal void SetText(string text = null)
{
var fullText = $"{_globalContext.ApplicationTitleAdmin}{(text == null ? "" : $"\n{text}")}";
FormUtils.SetNotifyIconText(NotifyIcon, fullText.Trim());
}
}
@@ -0,0 +1,224 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Services;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ColorControl.Services.Common;
public class OptionsService
{
private readonly GlobalContext _globalContext;
private readonly ElevationService _elevationService;
private readonly WinApiService _winApiService;
private readonly ServiceManager _serviceManager;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly WinApiAdminService _winApiAdminService;
public OptionsService(GlobalContext globalContext, ElevationService elevationService, WinApiService winApiService, ServiceManager serviceManager, KeyboardShortcutDispatcher keyboardShortcutDispatcher, WinApiAdminService winApiAdminService)
{
_globalContext = globalContext;
_elevationService = elevationService;
_winApiService = winApiService;
_serviceManager = serviceManager;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_winApiAdminService = winApiAdminService;
}
public Config GetConfig()
{
_globalContext.Config.AutoStart = _winApiService.TaskExists(Program.TS_TASKNAME);
return _globalContext.Config;
}
public bool SetConfig(Config config)
{
if (_globalContext.Config.MinimizeToTray != config.MinimizeToTray)
{
SetMinimizeToTray(config.MinimizeToTray);
}
_globalContext.Config.Update(config);
return true;
}
public List<ModuleDto> GetModules()
{
return _globalContext.Config.Modules.Select(m => new ModuleDto { DisplayName = m.DisplayName, IsActive = m.IsActive, Info = _serviceManager.GetServiceInfo(m.DisplayName) }).ToList();
}
public InfoDto GetInfo()
{
return new InfoDto
{
ApplicationTitle = _globalContext.ApplicationTitle,
ApplicationTitleAdmin = _globalContext.ApplicationTitleAdmin,
DataPath = _globalContext.DataPath,
StartTime = _globalContext.StartTime,
UpdateAvailable = _globalContext.UpdateAvailable,
LegalCopyright = _globalContext.LegalCopyright,
};
}
public bool SetStartAfterLogin(bool value)
{
_elevationService.RegisterScheduledTask(value);
return true;
}
public bool SetMinimizeOnClose(bool value)
{
_globalContext.Config.MinimizeOnClose = value;
return true;
}
public bool SetStartMinimized(bool value)
{
_globalContext.Config.StartMinimized = value;
return true;
}
public bool SetMinimizeToTray(bool value)
{
_globalContext.Config.MinimizeToTray = value;
Program.GetNotifyIcon().Visible = _globalContext.Config.MinimizeToTray;
return true;
}
public bool SetCheckForUpdates(bool value)
{
_globalContext.Config.CheckForUpdates = value;
return true;
}
public bool SetAutoInstallUpdates(bool value)
{
_globalContext.Config.AutoInstallUpdates = value;
return true;
}
public bool SetDarkMode(bool value)
{
_globalContext.Config.UseDarkMode = value;
Program.SetTheme(value);
return true;
}
public bool SetElevationMethod(ElevationMethod elevationMethod)
{
_elevationService.SetElevationMethod(elevationMethod);
return true;
}
public bool SetUiType(UiType uiType)
{
_globalContext.Config.UiType = uiType;
return true;
}
public bool SetUiPort(int uiPort)
{
if (uiPort is <= 0 or > 65535)
{
return false;
}
if (_globalContext.Config.UiPort == uiPort)
{
return true;
}
_globalContext.Config.UiPort = uiPort;
return true;
}
public bool SetProcessMonitorPollingInterval(int pollingInterval)
{
_globalContext.Config.ProcessMonitorPollingInterval = pollingInterval;
return true;
}
public bool SetUseRawInput(bool value)
{
_globalContext.Config.UseRawInput = value;
_keyboardShortcutDispatcher.SetUseRawInput(value);
return true;
}
public bool SetMinTmlAndMaxTml(bool value)
{
_globalContext.Config.SetMinTmlAndMaxTml = value;
return true;
}
public bool SetDisableErrorPopupWhenApplyingPreset(bool value)
{
_globalContext.Config.DisableErrorPopupWhenApplyingPreset = value;
return true;
}
public bool SetFixChromeFonts(bool value)
{
_globalContext.Config.FixChromeFonts = value;
var folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (value != _winApiService.IsChromeFixInstalled())
{
return _winApiAdminService.InstallChromeFix(value, folder);
}
return true;
}
public bool SetScreenSaverShortcut(string value)
{
_globalContext.Config.ScreenSaverShortcut = value;
return _keyboardShortcutDispatcher.RegisterShortcut(MainWorker.SHORTCUTID_SCREENSAVER, value);
}
public bool SetLogLevel(string logLevelName)
{
var logLevel = LogLevel.FromString(logLevelName);
_globalContext.SetLogLevel(logLevel);
return true;
}
public bool UpdateModule(Module updateModule)
{
var module = _globalContext.Config.Modules.FirstOrDefault(m => m.DisplayName == updateModule.DisplayName);
if (module == null)
{
return false;
}
module.IsActive = updateModule.IsActive;
_serviceManager.ActivateModule(module);
return true;
}
}
-232
View File
@@ -1,232 +0,0 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using NStandard;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace ColorControl.Services.Common
{
enum PresetTriggerType
{
[Description("None")]
None = 0,
[Description("Startup")]
Startup = 1,
[Description("Resume")]
Resume = 2,
[Description("Shutdown")]
Shutdown = 3,
[Description("Standby")]
Standby = 4,
[Description("Reserved5")]
Reserved5 = 5,
[Description("Process switch")]
ProcessSwitch = 6,
[Description("Screensaver start")]
ScreensaverStart = 7,
[Description("Screensaver stop")]
ScreensaverStop = 8,
}
[Flags]
enum PresetConditionType
{
[Description("None")]
None = 0,
[Description("SDR")]
SDR = 1,
[Description("HDR")]
HDR = 2,
[Description("Full Screen")]
FullScreen = 4,
[Description("Notifications Disabled")]
NotificationsDisabled = 8,
[Description("G-SYNC Disabled")]
GsyncDisabled = 16,
[Description("G-SYNC Enabled")]
GsyncEnabled = 32
}
class PresetTrigger
{
public PresetTriggerType Trigger { get; set; }
public List<string> IncludedProcesses { get; set; }
public List<string> ExcludedProcesses { get; set; }
public PresetConditionType Conditions { get; set; }
public PresetTrigger()
{
IncludedProcesses = new List<string>();
ExcludedProcesses = new List<string>();
}
public bool TriggerActive(PresetTriggerContext context)
{
var active = Conditions == PresetConditionType.None ||
(Conditions.HasFlag(PresetConditionType.SDR) ? !context.IsHDRActive : true) && (Conditions.HasFlag(PresetConditionType.HDR) ? context.IsHDRActive : true) &&
(Conditions.HasFlag(PresetConditionType.GsyncDisabled) ? !context.IsGsyncActive : true) && (Conditions.HasFlag(PresetConditionType.GsyncEnabled) ? context.IsGsyncActive : true);
if (Trigger == PresetTriggerType.ProcessSwitch && context.Triggers.Contains(Trigger))
{
var allProcesses = IncludedProcesses.Contains("*");
active = active && (allProcesses || (context.ChangedProcesses?.Any() ?? false));
if (active)
{
var included = allProcesses || context.ChangedProcesses.Any(cp => IncludedProcesses.Any(ip => cp.ProcessName.NormEquals(ip)));
var excluded = context.ChangedProcesses.Any(cp => ExcludedProcesses.Any(ep => cp.ProcessName.NormEquals(ep)));
var screenSizeCheck = (!Conditions.HasFlag(PresetConditionType.FullScreen) && !context.ForegroundProcessIsFullScreen) ||
(context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen &&
(allProcesses || IncludedProcesses.Any(ip => context.ForegroundProcess.ProcessName.NormEquals(ip))));
var notificationsDisabledCheck = !Conditions.HasFlag(PresetConditionType.NotificationsDisabled) || context.IsNotificationDisabled;
active = included && !excluded && screenSizeCheck && notificationsDisabledCheck;
}
}
else if ((Trigger == PresetTriggerType.ScreensaverStart || Trigger == PresetTriggerType.ScreensaverStop) && context.Triggers.Contains(Trigger))
{
active = active && (Trigger == PresetTriggerType.ScreensaverStart && context.ScreenSaverTransitionState == ScreenSaverTransitionState.Started ||
Trigger == PresetTriggerType.ScreensaverStop && context.ScreenSaverTransitionState == ScreenSaverTransitionState.Stopped);
}
else
{
active = active && context.Triggers.Contains(Trigger);
}
return active;
}
public override string ToString()
{
if (Trigger == PresetTriggerType.None)
{
return string.Empty;
}
var text = $"On {Trigger.GetDescription()}";
if (Trigger == PresetTriggerType.ProcessSwitch)
{
text += $"of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}";
}
return text += $"{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
}
public static string DisplayProcesses(IEnumerable<string> processes)
{
return processes.Contains("*") ? "all processes" : string.Join(", ", processes);
}
}
class PresetTriggerContext
{
public IEnumerable<PresetTriggerType> Triggers { get; set; } = new[] { PresetTriggerType.ProcessSwitch };
public bool IsHDRActive { get; set; }
public bool IsGsyncActive { get; set; }
public Process ForegroundProcess { get; set; }
public bool ForegroundProcessIsFullScreen { get; set; }
public List<Process> ChangedProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
public ScreenSaverTransitionState ScreenSaverTransitionState { get; set; }
}
internal abstract class PresetBase
{
public string name { get; set; }
public string shortcut { get; set; }
public List<PresetTrigger> Triggers { get; set; }
public string Description { get; set; }
public bool ShowInQuickAccess { get; set; }
public string IdOrName => name.IsNullOrEmpty() ? _id.ToString() : name;
private int _id;
protected PresetBase()
{
Triggers = new List<PresetTrigger>();
}
public int id
{
get
{
return _id;
}
set
{
if (value == 0 || ids.Contains(value))
{
value = GetNewId();
}
else
{
ids.Add(value);
}
_id = value;
}
}
public static List<int> ids = new List<int>();
public static int GetNewId()
{
int id;
do
{
id = new Random().Next();
}
while (ids.Contains(id));
ids.Add(id);
return id;
}
public abstract List<string> GetDisplayValues(Config config = null);
public virtual string GetTextForMenuItem()
{
return name;
}
public void UpdateTrigger(PresetTriggerType triggerType, PresetConditionType conditions, string includedProcesses = null, string excludedProcesses = null)
{
if (!Triggers.Any())
{
Triggers.Add(new PresetTrigger());
}
var trigger = Triggers.First();
trigger.Trigger = triggerType;
trigger.Conditions = conditions;
if (trigger.IncludedProcesses == null)
{
trigger.IncludedProcesses = new List<string>();
}
if (trigger.ExcludedProcesses == null)
{
trigger.ExcludedProcesses = new List<string>();
}
Utils.ParseWords(trigger.IncludedProcesses, includedProcesses);
Utils.ParseWords(trigger.ExcludedProcesses, excludedProcesses);
}
public PresetTrigger AddTrigger()
{
if (!Triggers.Any())
{
Triggers.Add(new PresetTrigger());
}
return Triggers.First();
}
}
}
@@ -1,4 +1,5 @@
using ColorControl.Shared.Contracts;
using System.Windows.Forms;
namespace ColorControl.Services.Common
@@ -1,4 +1,5 @@
using System;
using ColorControl.Shared.Contracts;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
+283 -246
View File
@@ -1,10 +1,10 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NStandard;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,307 +15,344 @@ using System.Threading.Tasks;
namespace ColorControl.Services.Common
{
abstract class ServiceBase<T> : IServiceBase where T : PresetBase, new()
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
abstract class ServiceBase<T> : IServiceBase where T : PresetBase, new()
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public event EventHandler<T> AfterApplyPreset;
public event EventHandler<T> AfterApplyPreset;
public abstract string ServiceName
{
get;
}
protected abstract string PresetsBaseFilename { get; }
public abstract string ServiceName
{
get;
}
protected abstract string PresetsBaseFilename { get; }
protected string _dataPath;
protected string _presetsFilename;
protected string _presetsBackupFilename;
protected bool _initialized = false;
protected List<T> _presets;
protected T _lastAppliedPreset;
protected string _loadPresetsError;
protected T _lastTriggeredPreset;
protected int _quickAccessShortcutId;
protected string _dataPath;
protected string _presetsFilename;
protected string _presetsBackupFilename;
protected bool _initialized = false;
protected List<T> _presets;
protected T _lastAppliedPreset;
protected string _loadPresetsError;
protected T _lastTriggeredPreset;
protected int _quickAccessShortcutId;
protected GlobalContext _globalContext;
protected GlobalContext _globalContext;
private List<JsonConverter> _jsonConverters;
private List<JsonConverter> _jsonConverters;
public ServiceBase(GlobalContext globalContext)
{
_globalContext = globalContext;
_dataPath = globalContext.DataPath;
_jsonConverters = new List<JsonConverter>();
public ServiceBase(GlobalContext globalContext)
{
_globalContext = globalContext;
_dataPath = globalContext.DataPath;
_jsonConverters = new List<JsonConverter>();
Initialize();
}
Initialize();
}
~ServiceBase()
{
Uninitialize();
}
~ServiceBase()
{
Uninitialize();
}
public List<T> GetPresets()
{
return _presets;
}
public abstract List<string> GetInfo();
public T GetPresetByIdOrName(string idOrName)
{
var preset = _presets.FirstOrDefault(p => p.name != null && p.name.Equals(idOrName, StringComparison.OrdinalIgnoreCase));
if (preset == null && int.TryParse(idOrName, out var id))
{
preset = _presets.FirstOrDefault(p => p.id == id);
}
public List<T> GetPresets()
{
return _presets;
}
return preset;
}
public T GetPresetByIdOrName(string idOrName)
{
var preset = _presets.FirstOrDefault(p => p.name != null && p.name.Equals(idOrName, StringComparison.OrdinalIgnoreCase));
if (preset == null && int.TryParse(idOrName, out var id))
{
preset = _presets.FirstOrDefault(p => p.id == id);
}
public abstract Task<bool> ApplyPreset(T preset);
return preset;
}
public async Task<bool> ApplyPresetUi(T preset)
{
if (preset == null)
{
return false;
}
try
{
var result = await ApplyPreset(preset);
if (!result)
{
throw new Exception($"Error while applying {ServiceName}-preset. At least one setting could not be applied. Check the log for details.");
}
public bool DeletePreset(int id)
{
var preset = _presets.FirstOrDefault(p => p.id == id);
return true;
}
catch (Exception e)
{
Logger.Error(e);
if (preset != null)
{
_presets.Remove(preset);
}
if (!_globalContext.Config.DisableErrorPopupWhenApplyingPreset)
{
MessageForms.ErrorOk($"Error applying {ServiceName}-preset ({preset.IdOrName}): {e.Message}");
}
return false;
}
}
return true;
}
public async Task<bool> ApplyPresetById(int id)
{
var preset = _presets.FirstOrDefault(p => p.id == id);
public Task<bool> ApplyPresetWithId(int id)
{
return ApplyPreset(id.ToString());
}
if (preset == null)
{
return false;
}
public async Task<bool> ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return await ApplyPreset(preset);
}
else
{
return false;
}
}
return await ApplyPresetUi(preset);
}
public abstract Task<bool> ApplyPreset(T preset);
protected void SetShortcuts(int quickAccessShortcutId = 0, string shortcut = null)
{
_quickAccessShortcutId = quickAccessShortcutId;
public async Task<bool> ApplyPresetUi(T preset)
{
if (preset == null)
{
return false;
}
try
{
var result = await ApplyPreset(preset);
if (!result)
{
throw new Exception($"Error while applying {ServiceName}-preset. At least one setting could not be applied. Check the log for details.");
}
var keyboardShortcutDispatcher = _globalContext.ServiceProvider.GetService<KeyboardShortcutDispatcher>();
return true;
}
catch (Exception e)
{
Logger.Error(e);
if (keyboardShortcutDispatcher == null)
{
return;
}
if (!_globalContext.Config.DisableErrorPopupWhenApplyingPreset)
{
MessageForms.ErrorOk($"Error applying {ServiceName}-preset ({preset.IdOrName}): {e.Message}");
}
return false;
}
}
if (quickAccessShortcutId != 0 && !shortcut.IsNullOrWhiteSpace())
{
keyboardShortcutDispatcher.RegisterShortcut(quickAccessShortcutId, shortcut);
}
public async Task<bool> ApplyPresetById(int id)
{
var preset = _presets.FirstOrDefault(p => p.id == id);
foreach (var preset in _presets)
{
keyboardShortcutDispatcher.RegisterShortcut(preset.id, preset.shortcut);
}
if (preset == null)
{
return false;
}
keyboardShortcutDispatcher.RegisterAsyncEventHandler(KeyboardShortcutDispatcher.Event_HotKey, HotKeyPressed);
}
return await ApplyPresetUi(preset);
}
public virtual void InstallEventHandlers()
{
protected void SetShortcuts(int quickAccessShortcutId = 0, string shortcut = null)
{
_quickAccessShortcutId = quickAccessShortcutId;
}
var keyboardShortcutDispatcher = _globalContext.ServiceProvider.GetService<KeyboardShortcutDispatcher>();
public T GetLastAppliedPreset()
{
return _lastAppliedPreset;
}
if (keyboardShortcutDispatcher == null)
{
return;
}
public virtual T CreateNewPreset()
{
var preset = new T();
preset.name = CreateNewPresetName();
if (quickAccessShortcutId != 0)
{
keyboardShortcutDispatcher.RegisterShortcut(quickAccessShortcutId, shortcut);
}
return preset;
}
foreach (var preset in _presets)
{
keyboardShortcutDispatcher.RegisterShortcut(preset.id, preset.shortcut);
}
public string CreateNewPresetName()
{
var name = "New preset";
string fullname;
var number = 1;
do
{
fullname = $"{name} ({number})";
number++;
} while (_presets.Any(x => x.name?.Equals(fullname) == true));
keyboardShortcutDispatcher.RegisterAsyncEventHandler(KeyboardShortcutDispatcher.Event_HotKey, HotKeyPressed);
}
return fullname;
}
public virtual void InstallEventHandlers()
{
protected virtual void Initialize()
{
}
}
public T GetLastAppliedPreset()
{
return _lastAppliedPreset;
}
protected virtual void Uninitialize()
{
public virtual T CreateNewPreset()
{
var preset = new T();
preset.name = CreateNewPresetName();
}
return preset;
}
protected void PresetApplied()
{
AfterApplyPreset?.Invoke(this, _lastAppliedPreset);
}
public string CreateNewPresetName()
{
var name = "New preset";
string fullname;
var number = 1;
do
{
fullname = $"{name} ({number})";
number++;
} while (_presets.Any(x => x.name?.Equals(fullname) == true));
protected void LoadPresets()
{
_presetsFilename = Path.Combine(_dataPath, PresetsBaseFilename);
_presetsBackupFilename = Path.Combine(_dataPath, $"{PresetsBaseFilename}.backup");
return fullname;
}
try
{
var presetsExists = File.Exists(_presetsFilename);
if (presetsExists)
{
try
{
var json = File.ReadAllText(_presetsFilename);
protected virtual void Initialize()
{
// Hack to convert incorrect triggers
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
}
_presets = JsonConvert.DeserializeObject<List<T>>(json, _jsonConverters.ToArray());
protected virtual void Uninitialize()
{
if (_presets != null)
{
if (File.Exists(_presetsBackupFilename))
{
File.Delete(_presetsBackupFilename);
}
File.Copy(_presetsFilename, _presetsBackupFilename);
}
}
catch (Exception ex1)
{
Logger.Error($"Error while loading presets, reverting to default presets: {ex1.Message}");
_loadPresetsError = ex1.Message;
}
}
if (!presetsExists || _presets == null)
{
Logger.Debug("Reverting to default presets");
try
{
_presets = GetDefaultPresets();
}
catch (Exception e)
{
Logger.Error($"Error loading default presets, reverting to empty list: {e.Message}");
}
}
}
catch (Exception ex)
{
Logger.Error($"General error while loading presets, reverting to empty list: {ex.Message}");
_loadPresetsError = ex.Message;
}
if (_presets == null)
{
_presets = new List<T>();
}
}
}
protected abstract List<T> GetDefaultPresets();
protected void PresetApplied()
{
if (_lastAppliedPreset != null)
{
_lastAppliedPreset.LastUsed = DateTime.Now;
}
protected void AddJsonConverter(JsonConverter jsonConverter)
{
_jsonConverters.Add(jsonConverter);
}
AfterApplyPreset?.Invoke(this, _lastAppliedPreset);
}
public void ToggleQuickAccessForm()
{
QuickAccessForm<T>.ToggleQuickAccessForm(this);
}
protected void LoadPresets()
{
_presetsFilename = Path.Combine(_dataPath, PresetsBaseFilename);
_presetsBackupFilename = Path.Combine(_dataPath, $"{PresetsBaseFilename}.backup");
protected async Task<PresetTriggerContext> CreateTriggerContext(ServiceManager serviceManager, ProcessChangedEventArgs context = null, bool? isHDRActive = null, IList<PresetTriggerType> triggerTypes = null)
{
triggerTypes ??= new[] { PresetTriggerType.ProcessSwitch };
try
{
var presetsExists = File.Exists(_presetsFilename);
if (presetsExists)
{
try
{
var json = File.ReadAllText(_presetsFilename);
var changedProcesses = new List<Process>();
if (context?.ForegroundProcess != null)
{
changedProcesses.Add(context.ForegroundProcess);
}
// Hack to convert incorrect triggers
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
var isGsyncActive = await serviceManager.HandleExternalServiceAsync("GsyncEnabled", new[] { "" });
_presets = JsonConvert.DeserializeObject<List<T>>(json, _jsonConverters.ToArray());
var triggerContext = new PresetTriggerContext
{
Triggers = triggerTypes,
IsHDRActive = isHDRActive ?? CCD.IsHDREnabled(),
IsGsyncActive = isGsyncActive,
ForegroundProcess = context?.ForegroundProcess,
ForegroundProcessIsFullScreen = context?.ForegroundProcessIsFullScreen ?? false,
IsNotificationDisabled = context?.IsNotificationDisabled ?? false,
ChangedProcesses = changedProcesses,
ScreenSaverTransitionState = context?.ScreenSaverTransitionState ?? ScreenSaverTransitionState.None
};
if (_presets != null)
{
if (File.Exists(_presetsBackupFilename))
{
File.Delete(_presetsBackupFilename);
}
File.Copy(_presetsFilename, _presetsBackupFilename);
}
}
catch (Exception ex1)
{
Logger.Error($"Error while loading presets, reverting to default presets: {ex1.Message}");
_loadPresetsError = ex1.Message;
}
}
if (!presetsExists || _presets == null)
{
Logger.Debug("Reverting to default presets");
try
{
_presets = GetDefaultPresets();
}
catch (Exception e)
{
Logger.Error($"Error loading default presets, reverting to empty list: {e.Message}");
}
}
}
catch (Exception ex)
{
Logger.Error($"General error while loading presets, reverting to empty list: {ex.Message}");
_loadPresetsError = ex.Message;
}
if (_presets == null)
{
_presets = new List<T>();
}
}
return triggerContext;
}
protected abstract List<T> GetDefaultPresets();
protected async Task ExecuteScreenSaverPresets(ServiceManager serviceManager, ProcessChangedEventArgs context, bool? isHDRActive = null)
{
await ExecuteEventPresets(serviceManager, new[] { PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop }, context, isHDRActive);
}
protected void AddJsonConverter(JsonConverter jsonConverter)
{
_jsonConverters.Add(jsonConverter);
}
public async Task ExecuteEventPresets(ServiceManager serviceManager, IList<PresetTriggerType> triggerTypes, ProcessChangedEventArgs context = null, bool? isHDRActive = null)
{
var triggerContext = await CreateTriggerContext(serviceManager, context, isHDRActive, triggerTypes);
public void ToggleQuickAccessForm()
{
QuickAccessForm<T>.ToggleQuickAccessForm(this);
}
var triggerPresets = _presets.Where(p => p.Triggers.Any(t => t.TriggerActive(triggerContext))).ToList();
protected async Task<PresetTriggerContext> CreateTriggerContext(ServiceManager serviceManager, ProcessChangedEventArgs context = null, bool? isHDRActive = null, IList<PresetTriggerType> triggerTypes = null)
{
triggerTypes ??= new[] { PresetTriggerType.ProcessSwitch };
if (!triggerPresets.Any())
{
return;
}
var changedProcesses = new List<Process>();
if (context?.ForegroundProcess != null)
{
changedProcesses.Add(context.ForegroundProcess);
}
Logger.Debug($"Executing event: {string.Join(',', triggerTypes)}, presets count: {triggerPresets.Count}");
var isGsyncActive = await serviceManager.HandleExternalServiceAsync("GsyncEnabled", new[] { "" });
foreach (var preset in triggerPresets)
{
Logger.Debug($"Executing event preset: {preset.name}");
var triggerContext = new PresetTriggerContext
{
Triggers = triggerTypes,
IsHDRActive = isHDRActive ?? CCD.IsHDREnabled(),
IsGsyncActive = isGsyncActive,
ForegroundProcess = context?.ForegroundProcess,
ForegroundProcessIsFullScreen = context?.ForegroundProcessIsFullScreen ?? false,
IsNotificationDisabled = context?.IsNotificationDisabled ?? false,
ChangedProcesses = changedProcesses,
ScreenSaverTransitionState = context?.ScreenSaverTransitionState ?? ScreenSaverTransitionState.None
};
await ApplyPreset(preset);
}
}
return triggerContext;
}
protected virtual async Task HotKeyPressed(object sender, KeyboardShortcutEventArgs e, CancellationToken _)
{
if (e.HotKeyId == _quickAccessShortcutId)
{
ToggleQuickAccessForm();
return;
}
protected async Task ExecuteScreenSaverPresets(ServiceManager serviceManager, ProcessChangedEventArgs context, bool? isHDRActive = null)
{
await ExecuteEventPresets(serviceManager, new[] { PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop }, context, isHDRActive);
}
await ApplyPresetById(e.HotKeyId);
}
}
public async Task ExecuteEventPresets(ServiceManager serviceManager, IList<PresetTriggerType> triggerTypes, ProcessChangedEventArgs context = null, bool? isHDRActive = null)
{
var triggerContext = await CreateTriggerContext(serviceManager, context, isHDRActive, triggerTypes);
var triggerPresets = _presets.Where(p => p.Triggers.Any(t => t.TriggerActive(triggerContext))).ToList();
if (!triggerPresets.Any())
{
return;
}
Logger.Debug($"Executing event: {string.Join(',', triggerTypes)}, presets count: {triggerPresets.Count}");
foreach (var preset in triggerPresets)
{
Logger.Debug($"Executing event preset: {preset.name}");
await ApplyPreset(preset);
}
}
protected virtual async Task HotKeyPressed(object sender, KeyboardShortcutEventArgs e, CancellationToken _)
{
if (e.HotKeyId == _quickAccessShortcutId)
{
ToggleQuickAccessForm();
return;
}
await ApplyPresetById(e.HotKeyId);
}
}
}
+69 -11
View File
@@ -21,6 +21,7 @@ public class ServiceManager
private readonly IServiceProvider _serviceProvider;
private readonly Config _config;
public Dictionary<string, object> Modules { get; } = new();
public Dictionary<string, IServiceBase> Services { get; } = new();
internal NvService NvService { get; set; }
internal LgService LgService { get; set; }
@@ -36,22 +37,53 @@ public class ServiceManager
_config = globalContext.Config;
}
public void LoadModules()
public void LoadModules(string displayName = null)
{
NvService = AddModule<NvService>("NVIDIA controller");
AmdService = AddModule<AmdService>("AMD controller");
LgService = AddModule<LgService>("LG controller");
SamsungService = AddModule<SamsungService>("Samsung controller");
GameService = AddModule<GameService>("Game launcher");
if (displayName == null || displayName == Module.NvModule)
{
NvService = AddModule<NvService>(Module.NvModule);
}
var names = Modules.Select(m => m.Key).ToList();
_config.Modules = _config.Modules.OrderBy(m => names.IndexOf(m.DisplayName)).ToList();
if (displayName == null || displayName == Module.AmdModule)
{
AmdService = AddModule<AmdService>(Module.AmdModule);
}
if (displayName == null || displayName == Module.LgModule)
{
LgService = AddModule<LgService>(Module.LgModule);
}
if (displayName == null || displayName == Module.SamsungModule)
{
SamsungService = AddModule<SamsungService>(Module.SamsungModule);
}
if (displayName == null || displayName == Module.GameModule)
{
GameService = AddModule<GameService>(Module.GameModule);
}
if (displayName == null)
{
var names = Modules.Select(m => m.Key).ToList();
_config.Modules = _config.Modules.OrderBy(m => names.IndexOf(m.DisplayName)).ToList();
}
}
private T AddModule<T>(string displayName) where T : class, IServiceBase
{
var moduleEx = new ModuleEx<T> { DisplayName = displayName };
Modules.Add(displayName, moduleEx);
ModuleEx<T> moduleEx;
if (Modules.TryGetValue(displayName, out var moduleObject))
{
moduleEx = moduleObject as ModuleEx<T>;
}
else
{
moduleEx = new ModuleEx<T> { DisplayName = displayName };
Modules.Add(displayName, moduleEx);
}
var existingModule = _config.Modules.FirstOrDefault(m => m.DisplayName == displayName);
@@ -65,7 +97,11 @@ public class ServiceManager
{
var service = moduleEx.CreateService(_serviceProvider);
service?.InstallEventHandlers();
if (service != null)
{
service.InstallEventHandlers();
Services.Add(displayName, service);
}
return service;
}
@@ -73,6 +109,16 @@ public class ServiceManager
return default;
}
public void ActivateModule(Module module)
{
if (!module.IsActive)
{
return;
}
LoadModules(module.DisplayName);
}
public async Task<bool> HandleExternalServiceAsync(string serviceName, string[] parameters)
{
if (string.IsNullOrEmpty(serviceName) || parameters.Length == 0)
@@ -128,5 +174,17 @@ public class ServiceManager
LgService?.GlobalSave();
GameService?.GlobalSave();
SamsungService?.GlobalSave();
Utils.WriteObject(Program.ConfigFilename, _config);
}
internal List<string> GetServiceInfo(string displayName)
{
if (Services.TryGetValue(displayName, out var service))
{
return service.GetInfo();
}
return new List<string>();
}
}
@@ -58,7 +58,7 @@ public class UpdateManager
HandleCheckForUpdates(latest);
}
private void HandleCheckForUpdates(dynamic latest)
private async void HandleCheckForUpdates(dynamic latest)
{
if (latest?.tag_name == null)
{
@@ -112,7 +112,7 @@ public class UpdateManager
if (_config.AutoInstallUpdates)
{
InstallUpdate();
await InstallUpdate();
return;
}
@@ -142,7 +142,7 @@ public class UpdateManager
}
}
public void InstallUpdate()
public async Task InstallUpdate()
{
if (_downloadUrl == null) { return; }
@@ -152,7 +152,7 @@ public class UpdateManager
ClientPath = new FileInfo(Application.ExecutablePath).Directory.FullName
};
var result = PipeUtils.SendMessage(message, 30000);
var result = await PipeUtils.SendMessageAsync(message, 30000);
if (result != null && !result.Result)
{
@@ -161,7 +161,7 @@ public class UpdateManager
return;
}
PipeUtils.SendMessage(SvcMessageType.RestartAfterUpdate);
await PipeUtils.SendMessageAsync(SvcMessageType.RestartAfterUpdate);
if (MessageForms.QuestionYesNo("Update installed successfully. Do you want to restart the application?") == DialogResult.Yes)
{
@@ -1,10 +1,10 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.Game;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
using nspector;
using System;
using System.Collections.Generic;
@@ -49,7 +49,6 @@ namespace ColorControl.Services.GameLauncher
foreach (var preset in _gameService.GetPresets())
{
AddOrUpdateItemGame(preset);
_keyboardShortcutDispatcher.RegisterShortcut(preset.id, preset.shortcut);
}
}
@@ -65,12 +64,12 @@ namespace ColorControl.Services.GameLauncher
private void edtShortcut_KeyDown(object sender, KeyEventArgs e)
{
((TextBox)sender).Text = _keyboardShortcutDispatcher.FormatKeyboardShortcut(e);
((TextBox)sender).Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(e);
}
private void edtShortcut_KeyUp(object sender, KeyEventArgs e)
{
_keyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
KeyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
}
private GamePreset GetSelectedGamePreset()
@@ -337,7 +336,7 @@ namespace ColorControl.Services.GameLauncher
{
Label = "Quick Access shortcut",
FieldType = FieldType.Shortcut,
Value = _config.GameQuickAccessShortcut
Value = _gameService.Config.QuickAccessShortcut
};
var applyExternallyLaunched = new FieldDefinition
@@ -359,9 +358,9 @@ namespace ColorControl.Services.GameLauncher
var shortcut = (string)quickAccessField.Value;
_config.GameQuickAccessShortcut = shortcut;
_gameService.Config.QuickAccessShortcut = shortcut;
_keyboardShortcutDispatcher.RegisterShortcut(GameService.SHORTCUTID_GAMEQA, _config.GameQuickAccessShortcut);
_keyboardShortcutDispatcher.RegisterShortcut(GameService.SHORTCUTID_GAMEQA, _gameService.Config.QuickAccessShortcut);
_gameService.Config.ApplyExternallyLaunched = applyExternallyLaunched.ValueAsBool;
@@ -1,106 +0,0 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Contracts;
using NWin32;
using System.Collections.Generic;
using System.ComponentModel;
namespace ColorControl.Services.GameLauncher
{
public enum GameStepType
{
[Description("Pre-launch steps")]
PreLaunch = 0,
[Description("Post-launch steps")]
PostLaunch = 1,
[Description("Finalize steps")]
Finalize = 2,
}
public enum GamePriorityClass
{
Idle = NativeConstants.IDLE_PRIORITY_CLASS,
BelowNormal = NativeConstants.BELOW_NORMAL_PRIORITY_CLASS,
Normal = NativeConstants.NORMAL_PRIORITY_CLASS,
AboveNormal = NativeConstants.ABOVE_NORMAL_PRIORITY_CLASS,
High = NativeConstants.HIGH_PRIORITY_CLASS,
RealTime = NativeConstants.REALTIME_PRIORITY_CLASS
}
public enum ProcessAutoAction
{
None = 0,
Suspend = 1,
Restart = 2
}
class AutoSettings
{
public bool AllowAutoApply { get; set; }
public ProcessAutoAction ProcessAutoAction { get; set; }
}
class GamePreset : PresetBase
{
public string Path { get; set; }
public string Parameters { get; set; }
public bool RunAsAdministrator { get; set; }
public List<string> PreLaunchSteps { get; set; }
public List<string> PostLaunchSteps { get; set; }
public List<string> FinalizeSteps { get; set; }
public uint ProcessAffinityMask { get; set; }
public uint ProcessPriorityClass { get; set; }
public AutoSettings AutoSettings { get; set; }
public GamePreset() : base()
{
PreLaunchSteps = new List<string>();
PostLaunchSteps = new List<string>();
FinalizeSteps = new List<string>();
AutoSettings = new AutoSettings();
}
public GamePreset(GamePreset preset) : this()
{
id = GetNewId();
name = preset.name;
Path = preset.Path;
Parameters = preset.Parameters;
RunAsAdministrator = preset.RunAsAdministrator;
PreLaunchSteps.AddRange(preset.PreLaunchSteps);
PostLaunchSteps.AddRange(preset.PostLaunchSteps);
FinalizeSteps.AddRange(preset.FinalizeSteps);
}
public GamePreset Clone()
{
var preset = new GamePreset(this);
preset.name += " (copy)";
return preset;
}
public static string[] GetColumnNames()
{
return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300", "Post-launch steps|300", "Finalize steps|300" };
}
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>
{
name,
Path,
Parameters,
string.Join(", ", PreLaunchSteps),
string.Join(", ", PostLaunchSteps),
string.Join(", ", FinalizeSteps)
};
return values;
}
}
}
@@ -1,5 +1,6 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts.Game;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Services;
using Newtonsoft.Json;
@@ -49,7 +50,12 @@ namespace ColorControl.Services.GameLauncher
{
base.InstallEventHandlers();
SetShortcuts(SHORTCUTID_GAMEQA, _globalContext.Config.GameQuickAccessShortcut);
SetShortcuts(SHORTCUTID_GAMEQA, Config.QuickAccessShortcut);
}
public override List<string> GetInfo()
{
return [$"{_presets.Count} presets"];
}
private async Task ProcessChanged(object sender, ProcessChangedEventArgs e, CancellationToken token)
@@ -149,19 +155,6 @@ namespace ColorControl.Services.GameLauncher
SaveConfig();
}
public async Task<bool> ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return await ApplyPreset(preset);
}
else
{
return false;
}
}
public override async Task<bool> ApplyPreset(GamePreset preset)
{
var result = await ExecutePreset(preset);
@@ -295,5 +288,48 @@ namespace ColorControl.Services.GameLauncher
{
Utils.WriteObject(_configFilename, Config);
}
public GameServiceConfig GetConfig()
{
return Config;
}
public bool UpdateConfig(GameServiceConfig config)
{
Config.Update(config);
SetShortcuts(SHORTCUTID_GAMEQA, Config.QuickAccessShortcut);
return true;
}
public bool UpdatePreset(GamePreset presetSpec)
{
var preset = _presets.FirstOrDefault(x => x.id == presetSpec.id);
if (preset != null)
{
preset.Update(presetSpec);
}
else
{
preset = new GamePreset(presetSpec);
_presets.Add(preset);
}
return true;
}
public string SelectPath()
{
var fileInfo = Utils.SelectFile();
if (fileInfo == null)
{
return null;
}
return fileInfo.FullName;
}
}
}
@@ -1,11 +0,0 @@
namespace ColorControl.Services.GameLauncher
{
class GameServiceConfig
{
public bool ApplyExternallyLaunched { get; set; }
public GameServiceConfig()
{
}
}
}
+72 -5
View File
@@ -1,5 +1,7 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.LG;
using ColorControl.Shared.Services;
using LgTv;
using Microsoft.Extensions.DependencyInjection;
@@ -167,8 +169,6 @@ namespace ColorControl.Services.LG
PictureSettings = new LgDevicePictureSettings();
ActionsOnGameBar = new List<string>(DefaultActionsOnGameBar);
_serviceManager = Program.ServiceProvider.GetRequiredService<ServiceManager>();
Name = name;
IpAddress = ipAddress;
MacAddress = macAddress;
@@ -283,6 +283,9 @@ namespace ColorControl.Services.LG
AddGenericPictureAction("autoVolume", typeof(OffToOn), category: "sound", title: "Auto Volume");
AddGenericPictureAction("virtualSurround", typeof(OffToOn), category: "sound", title: "Virtual Surround");
AddGenericPictureAction("screenShift", typeof(OffToOn), title: "Screen Shift");
AddGenericPictureAction("logoLuminanceAdjust", typeof(LogoLuminance), title: "Logo Luminance");
//AddGenericPictureAction("enableToastPopup", typeof(OffToOn), category: "option", title: "enableToastPopup");
AddSetConfigAction("tv.conti.supportUsedTime", typeof(BoolFalseToTrue), title: "Total Power On Time");
@@ -291,11 +294,55 @@ namespace ColorControl.Services.LG
AddLunaAction("GSR", typeof(BoolFalseToTrue), title: "Global Sticky/Stress Reduction (GSR)", ModelYear.Series2020);
}
public LgDevice(LgDeviceDto device) : this(device.Name, device.IpAddress, device.MacAddress, device.IsCustom, device.IsDummy)
{
UpdateOptions(device.Options);
}
private void UpdateOptions(LgDeviceOptions options)
{
UseSecureConnection = options.UseSecureConnection;
TurnScreenOffOnScreenSaver = options.TurnScreenOffOnScreenSaver;
TurnScreenOnAfterScreenSaver = options.TurnScreenOnAfterScreenSaver;
ScreenSaverMinimalDuration = options.ScreenSaverMinimalDuration;
HandleManualScreenSaver = options.HandleManualScreenSaver;
PowerOffOnScreenSaver = options.PowerOffOnScreenSaver;
PowerOnAfterScreenSaver = options.PowerOnAfterScreenSaver;
PowerOnAfterManualPowerOff = options.PowerOnAfterManualPowerOff;
PowerOnAfterResume = options.PowerOnAfterResume;
PowerOffOnStandby = options.PowerOffOnStandby;
PowerOffOnShutdown = options.PowerOffOnShutdown;
PowerOffByWindows = options.PowerOffByWindows;
PowerOnByWindows = options.PowerOnByWindows;
PowerOnAfterStartup = options.PowerOnAfterStartup;
TriggersEnabled = options.TriggersEnabled;
HDMIPortNumber = options.HDMIPortNumber;
}
~LgDevice()
{
ClearPowerOffTask();
}
public bool Update(LgDeviceDto device)
{
var reconnect = false;
Name = device.Name;
if (IpAddress != device.IpAddress)
{
IpAddress = device.IpAddress;
reconnect = true;
}
MacAddress = device.MacAddress;
IsCustom = device.IsCustom;
IsDummy = device.IsDummy;
UpdateOptions(device.Options);
return reconnect;
}
private void AddInvokableAction(string name, Func<Dictionary<string, object>, Task<bool>> function)
{
var action = new InvokableAction
@@ -314,7 +361,8 @@ namespace ColorControl.Services.LG
var action = new InvokableAction
{
Name = preset.name,
Preset = preset
Preset = preset,
Advanced = true
};
_invokableActions.Add(action);
@@ -774,9 +822,14 @@ namespace ColorControl.Services.LG
executeKey = false;
}
if (parameters != null && await _serviceManager.HandleExternalServiceAsync(key, parameters))
if (parameters != null)
{
executeKey = false;
_serviceManager ??= Program.ServiceProvider.GetRequiredService<ServiceManager>();
if (await _serviceManager.HandleExternalServiceAsync(key, parameters))
{
executeKey = false;
}
}
if (executeKey)
@@ -1291,5 +1344,19 @@ namespace ColorControl.Services.LG
var x = PerformActionOnScreenSaver();
}
internal async Task<bool> ExecuteInvokableAction(InvokableActionDto<LgPreset> invokableAction, List<string> parameters)
{
var action = _invokableActions.FirstOrDefault(a => a.Name == invokableAction.Name);
if (action == null)
{
return false;
}
await ExecuteActionAsync(action, parameters?.ToArray() ?? []);
return true;
}
}
}
+290 -289
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts.LG;
using ColorControl.Shared.Forms;
using NWin32;
using System;
@@ -9,356 +10,356 @@ using System.Windows.Forms;
namespace ColorControl.Services.LG
{
partial class LgGameBar : Form
{
private class TbSetting
{
public TrackBar TrackBar { get; set; }
public string Name { get; set; }
public Type EnumType { get; set; }
public string PropertyName { get; set; }
}
partial class LgGameBar : Form
{
private class TbSetting
{
public TrackBar TrackBar { get; set; }
public string Name { get; set; }
public Type EnumType { get; set; }
public string PropertyName { get; set; }
}
private LgService _lgService;
private LgDevice _lgDevice;
private ToolTip _toolTip;
private bool _settingChanged = false;
private bool _mouseHovering = false;
private List<TrackBar> _trackBars = new List<TrackBar>();
private List<string> _lastActions = new List<string>();
private LgService _lgService;
private LgDevice _lgDevice;
private ToolTip _toolTip;
private bool _settingChanged = false;
private bool _mouseHovering = false;
private List<TrackBar> _trackBars = new List<TrackBar>();
private List<string> _lastActions = new List<string>();
public LgGameBar(LgService lgService)
{
_lgService = lgService;
public LgGameBar(LgService lgService)
{
_lgService = lgService;
_lgDevice = _lgService.SelectedDevice;
_lgDevice = _lgService.SelectedDevice;
_lgDevice.PictureSettingsChangedEvent += LgDevice_PictureSettingsChangedEvent;
_lgDevice.PictureSettingsChangedEvent += LgDevice_PictureSettingsChangedEvent;
InitializeComponent();
InitializeComponent();
_toolTip = new ToolTip();
}
_toolTip = new ToolTip();
}
private void GenerateComponents()
{
var actions = _lgDevice.GetActionsForGameBar();
var actionNames = actions.Select(a => a.Name).ToList();
private void GenerateComponents()
{
var actions = _lgDevice.GetActionsForGameBar();
var actionNames = actions.Select(a => a.Name).ToList();
if (_lastActions.Count == actions.Count && _lastActions.All(a => actionNames.Contains(a)))
{
return;
}
if (_lastActions.Count == actions.Count && _lastActions.All(a => actionNames.Contains(a)))
{
return;
}
flowPanel.Controls.Clear();
_trackBars.Clear();
flowPanel.Controls.Clear();
_trackBars.Clear();
foreach (var action in actions)
{
CreateGroup(action.Title, action.Name, (int)action.MinValue, (int)action.MaxValue, action.EnumType);
}
foreach (var action in actions)
{
CreateGroup(action.Title, action.Name, (int)action.MinValue, (int)action.MaxValue, action.EnumType);
}
_lastActions.Clear();
_lastActions.AddRange(actionNames);
_lastActions.Clear();
_lastActions.AddRange(actionNames);
Width = 28 + (_trackBars.Count() * 56);
}
Width = 28 + (_trackBars.Count() * 56);
}
private TrackBar CreateGroup(string title, string settingName = null, int min = 0, int max = 100, Type enumType = null)
{
if (settingName == null)
{
settingName = title.ToLowerInvariant();
}
private TrackBar CreateGroup(string title, string settingName = null, int min = 0, int max = 100, Type enumType = null)
{
if (settingName == null)
{
settingName = title.ToLowerInvariant();
}
var panel = new Panel();
panel.Width = 50;
panel.Height = flowPanel.Height;
var label = new Label();
label.Text = title;
label.ForeColor = Color.White;
label.Parent = panel;
var panel = new Panel();
panel.Width = 50;
panel.Height = flowPanel.Height;
var label = new Label();
label.Text = title;
label.ForeColor = Color.White;
label.Parent = panel;
if (min == 0 && max == 0 && enumType != null)
{
max = Enum.GetValues(enumType).Length - 1;
}
if (min == 0 && max == 0 && enumType != null)
{
max = Enum.GetValues(enumType).Length - 1;
}
var trackBar = CreateTrackBar(min, max);
trackBar.Top = label.Height;
trackBar.Height = panel.Height - trackBar.Top - 4;
trackBar.Parent = panel;
trackBar.Scroll += tbTrackBar_Scroll;
var margin = trackBar.Margin;
margin.Top = 0;
margin.Bottom = 0;
trackBar.Margin = margin;
trackBar.ValueChanged += TrackBar_ValueChanged;
var trackBar = CreateTrackBar(min, max);
trackBar.Top = label.Height;
trackBar.Height = panel.Height - trackBar.Top - 4;
trackBar.Parent = panel;
trackBar.Scroll += tbTrackBar_Scroll;
var margin = trackBar.Margin;
margin.Top = 0;
margin.Bottom = 0;
trackBar.Margin = margin;
trackBar.ValueChanged += TrackBar_ValueChanged;
var setting = new TbSetting
{
TrackBar = trackBar,
Name = settingName,
EnumType = enumType,
PropertyName = Utils.FirstCharUpperCase(settingName)
};
var setting = new TbSetting
{
TrackBar = trackBar,
Name = settingName,
EnumType = enumType,
PropertyName = Utils.FirstCharUpperCase(settingName)
};
trackBar.Tag = setting;
trackBar.Tag = setting;
panel.Parent = flowPanel;
panel.Parent = flowPanel;
_trackBars.Add(trackBar);
_trackBars.Add(trackBar);
return trackBar;
}
return trackBar;
}
private void TrackBar_ValueChanged(object sender, EventArgs e)
{
//var trackBar = sender as TrackBar;
private void TrackBar_ValueChanged(object sender, EventArgs e)
{
//var trackBar = sender as TrackBar;
//if (_toolTip != null)
//{
// _toolTip.SetToolTip(trackBar, trackBar.Value.ToString());
//}
}
//if (_toolTip != null)
//{
// _toolTip.SetToolTip(trackBar, trackBar.Value.ToString());
//}
}
private TrackBar CreateTrackBar(int min, int max)
{
var trackBar = new TrackBar();
trackBar.BackColor = Color.Black;
trackBar.Orientation = Orientation.Vertical;
trackBar.TickFrequency = (max >= 50) ? 5 : 1;
trackBar.TickStyle = TickStyle.Both;
trackBar.Minimum = min;
trackBar.Maximum = max;
private TrackBar CreateTrackBar(int min, int max)
{
var trackBar = new TrackBar();
trackBar.BackColor = Color.Black;
trackBar.Orientation = Orientation.Vertical;
trackBar.TickFrequency = (max >= 50) ? 5 : 1;
trackBar.TickStyle = TickStyle.Both;
trackBar.Minimum = min;
trackBar.Maximum = max;
return trackBar;
}
return trackBar;
}
private void LgDevice_PictureSettingsChangedEvent(object sender, EventArgs e)
{
if (_settingChanged)
{
_settingChanged = false;
return;
}
private void LgDevice_PictureSettingsChangedEvent(object sender, EventArgs e)
{
if (_settingChanged)
{
_settingChanged = false;
return;
}
if (!Visible)
{
return;
}
if (!Visible)
{
return;
}
FormUtils.BeginInvokeCheck(this, UpdateValues);
}
FormUtils.BeginInvokeCheck(this, UpdateValues);
}
private void UpdateValues()
{
var dynType = _lgDevice.PictureSettings.GetType();
var actions = _lgDevice.GetInvokableActions();
foreach (var trackbar in _trackBars.Where(t => !t.Focused))
{
var setting = (TbSetting)trackbar.Tag;
private void UpdateValues()
{
var dynType = _lgDevice.PictureSettings.GetType();
var actions = _lgDevice.GetInvokableActions();
foreach (var trackbar in _trackBars.Where(t => !t.Focused))
{
var setting = (TbSetting)trackbar.Tag;
if (setting.PropertyName == null)
{
continue;
}
if (setting.PropertyName == null)
{
continue;
}
var prop = dynType.GetProperty(setting.PropertyName);
var prop = dynType.GetProperty(setting.PropertyName);
if (prop != null)
{
var value = prop.GetValue(_lgDevice.PictureSettings);
if (prop != null)
{
var value = prop.GetValue(_lgDevice.PictureSettings);
if (value is int intValue)
{
trackbar.Value = intValue;
}
}
else
{
var action = actions.FirstOrDefault(a => a.Name == setting.Name);
if (action != null)
{
trackbar.Value = action.CurrentValue;
}
}
}
}
if (value is int intValue)
{
trackbar.Value = intValue;
}
}
else
{
var action = actions.FirstOrDefault(a => a.Name == setting.Name);
if (action != null)
{
trackbar.Value = action.CurrentValue;
}
}
}
}
private void RemoteControlForm_Load(object sender, EventArgs e)
{
Left = _lgService.Config.GameBarLeft;
Top = _lgService.Config.GameBarTop;
}
private void RemoteControlForm_Load(object sender, EventArgs e)
{
Left = _lgService.Config.GameBarLeft;
Top = _lgService.Config.GameBarTop;
}
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
var _ = _lgService.ApplyPreset(preset);
}
}
var _ = _lgService.ApplyPreset(preset);
}
}
private void btnClose_Click(object sender, EventArgs e)
{
Hide();
}
private void btnClose_Click(object sender, EventArgs e)
{
Hide();
}
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
var intPtr = new UIntPtr(2);
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
}
}
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
var intPtr = new UIntPtr(2);
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
}
}
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
{
//var key = e.KeyCode;
//HandleKey(key);
}
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
{
//var key = e.KeyCode;
//HandleKey(key);
}
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void HandleKey(Keys key)
{
var cvt = new KeysConverter();
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
}
private void HandleKey(Keys key)
{
var cvt = new KeysConverter();
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
}
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
return;
}
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
return;
}
HandleKey(e.KeyCode);
}
HandleKey(e.KeyCode);
}
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
{
ResetAutoHide();
}
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
{
ResetAutoHide();
}
private void RemoteControlForm_Shown(object sender, EventArgs e)
{
GenerateComponents();
UpdateInfo();
ResetAutoHide();
}
private void RemoteControlForm_Shown(object sender, EventArgs e)
{
GenerateComponents();
UpdateInfo();
ResetAutoHide();
}
private void UpdateInfo()
{
lblTvName.Text = $"Device: {_lgDevice?.Name ?? "No tv selected"}";
lblProcessName.Text = $"Current process: {_lgService.CurrentProcessEvent?.ForegroundProcess?.ProcessName ?? _lgService.CurrentProcessEvent?.LastFullScreenProcessName ?? "No full screen process"}";
private void UpdateInfo()
{
lblTvName.Text = $"Device: {_lgDevice?.Name ?? "No tv selected"}";
lblProcessName.Text = $"Current process: {_lgService.CurrentProcessEvent?.ForegroundProcess?.ProcessName ?? _lgService.CurrentProcessEvent?.LastFullScreenProcessName ?? "No full screen process"}";
UpdateValues();
}
UpdateValues();
}
private void LgGameBar_Deactivate(object sender, EventArgs e)
{
_lgService.Config.GameBarLeft = Left;
_lgService.Config.GameBarTop = Top;
Hide();
}
private void LgGameBar_Deactivate(object sender, EventArgs e)
{
_lgService.Config.GameBarLeft = Left;
_lgService.Config.GameBarTop = Top;
Hide();
}
private void tmrHide_Tick(object sender, EventArgs e)
{
_lgService.Config.GameBarLeft = Left;
_lgService.Config.GameBarTop = Top;
private void tmrHide_Tick(object sender, EventArgs e)
{
_lgService.Config.GameBarLeft = Left;
_lgService.Config.GameBarTop = Top;
if (_mouseHovering)
{
return;
}
if (_mouseHovering)
{
return;
}
Hide();
}
Hide();
}
private async void tbTrackBar_Scroll(object sender, EventArgs e)
{
_settingChanged = true;
ResetAutoHide();
private async void tbTrackBar_Scroll(object sender, EventArgs e)
{
_settingChanged = true;
ResetAutoHide();
var trackBar = sender as TrackBar;
var setting = (TbSetting)trackBar.Tag;
var trackBar = sender as TrackBar;
var setting = (TbSetting)trackBar.Tag;
string value;
if (setting.EnumType == null)
{
value = trackBar.Value.ToString();
}
else
{
var enumValue = Enum.Parse(setting.EnumType, trackBar.Value.ToString());
value = enumValue.ToString();
}
string value;
if (setting.EnumType == null)
{
value = trackBar.Value.ToString();
}
else
{
var enumValue = Enum.Parse(setting.EnumType, trackBar.Value.ToString());
value = enumValue.ToString();
}
try
{
if (setting.Name == "audioVolume")
{
await _lgDevice.SetVolume(Utils.ParseInt(value));
}
else
{
await _lgDevice.SetSystemSettings(setting.Name, value);
}
}
catch (Exception) { }
try
{
if (setting.Name == "audioVolume")
{
await _lgDevice.SetVolume(Utils.ParseInt(value));
}
else
{
await _lgDevice.SetSystemSettings(setting.Name, value);
}
}
catch (Exception) { }
if (_toolTip != null)
{
_toolTip.Show(value, trackBar, 10, 50, 1500);
}
}
if (_toolTip != null)
{
_toolTip.Show(value, trackBar, 10, 50, 1500);
}
}
private void LgGameBar_Activated(object sender, EventArgs e)
{
GenerateComponents();
UpdateInfo();
ResetAutoHide();
}
private void LgGameBar_Activated(object sender, EventArgs e)
{
GenerateComponents();
UpdateInfo();
ResetAutoHide();
}
private void LgGameBar_Move(object sender, EventArgs e)
{
ResetAutoHide();
}
private void LgGameBar_Move(object sender, EventArgs e)
{
ResetAutoHide();
}
private void ResetAutoHide()
{
tmrHide.Enabled = false;
tmrHide.Interval = _lgService.Config.GameBarShowingTime;
tmrHide.Enabled = true;
}
private void ResetAutoHide()
{
tmrHide.Enabled = false;
tmrHide.Interval = _lgService.Config.GameBarShowingTime;
tmrHide.Enabled = true;
}
private void LgGameBar_MouseEnter(object sender, EventArgs e)
{
_mouseHovering = true;
}
private void LgGameBar_MouseEnter(object sender, EventArgs e)
{
_mouseHovering = true;
}
private void LgGameBar_MouseLeave(object sender, EventArgs e)
{
_mouseHovering = false;
}
private void LgGameBar_MouseLeave(object sender, EventArgs e)
{
_mouseHovering = false;
}
private void flowPanel_MouseEnter(object sender, EventArgs e)
{
_mouseHovering = true;
}
}
private void flowPanel_MouseEnter(object sender, EventArgs e)
{
_mouseHovering = true;
}
}
}
File diff suppressed because it is too large. Load diff
-118
View File
@@ -1,118 +0,0 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Contracts;
using LgTv;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace ColorControl.Services.LG
{
class LgPreset : PresetBase
{
public static List<LgApp> LgApps { get; set; }
public static List<LgDevice> LgDevices { get; set; }
public string appId { get; set; }
public List<string> steps { get; set; }
public string DeviceMacAddress { get; set; }
public dynamic AppParams { get; set; }
[JsonIgnore]
public int X { get; set; }
[JsonIgnore]
public int Y { get; set; }
public LgPreset() : base()
{
steps = new List<string>();
}
public LgPreset(LgPreset preset) : this()
{
id = GetNewId();
name = preset.name + " (copy)";
appId = preset.appId;
foreach (var step in preset.steps)
{
steps.Add(step);
}
}
public LgPreset(string name, string appId, IEnumerable<string> steps = null, dynamic appParams = null) : this()
{
this.name = name;
this.appId = appId;
if (steps != null)
{
this.steps.AddRange(steps);
}
AppParams = appParams;
}
public LgPreset Clone()
{
var preset = new LgPreset(this);
return preset;
}
public static List<string> GetColumnNames()
{
return new List<string>() { "Name", "Device|140", "App|200", "Steps|400", "Shortcut", "Trigger" };
}
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>();
values.Add(name);
var deviceString = "Global";
if (!string.IsNullOrEmpty(DeviceMacAddress))
{
if (LgDevices != null)
{
var device = LgDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress));
if (device != null)
{
deviceString = device.Name;
}
else
{
deviceString = "Unknown: device not found";
}
}
else
{
deviceString = DeviceMacAddress;
}
}
values.Add(deviceString);
var app = appId;
if (LgApps != null)
{
var item = LgApps.FirstOrDefault(x => x.appId.Equals(appId));
if (item != null)
{
app = item.title + " (" + appId + ")";
}
}
values.Add(app);
values.Add(GetStepsDisplay());
values.Add(shortcut);
if (Triggers.Any())
{
values.Add(Triggers.First().ToString());
}
return values;
}
public string GetStepsDisplay()
{
return steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
}
}
}
+224 -16
View File
@@ -1,6 +1,7 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.LG;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
@@ -8,6 +9,7 @@ using LgTv;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Win32;
using Newtonsoft.Json;
using NStandard;
using NWin32;
using System;
using System.Collections.Generic;
@@ -77,7 +79,8 @@ namespace ColorControl.Services.LG
_sessionSwitchDispatcher = sessionSwitchDispatcher;
_processEventDispatcher = processEventDispatcher;
_serviceManager = serviceManager;
LgPreset.LgApps = _lgApps;
LgPreset.GetDeviceName += LgPreset_GetDeviceName;
LgPreset.GetAppName += LgPreset_GetAppName;
_restartDetector = restartDetector;
_winApiService = winApiService;
@@ -90,6 +93,30 @@ namespace ColorControl.Services.LG
LoadRemoteControlButtons();
}
private string LgPreset_GetAppName(string appId)
{
var name = appId;
var item = _lgApps.FirstOrDefault(x => x.appId.Equals(appId));
if (item != null)
{
name = item.title + " (" + appId + ")";
}
return name;
}
private string LgPreset_GetDeviceName(string macAddress)
{
var device = Devices?.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(macAddress));
if (device != null)
{
return device.Name;
}
return "Unknown: device not found";
}
~LgService()
{
GlobalSave();
@@ -106,6 +133,11 @@ namespace ColorControl.Services.LG
}
}
public override List<string> GetInfo()
{
return [$"{_presets.Count} presets", $"{Devices?.Count ?? 0} devices"];
}
public static async Task<bool> ExecutePresetAsync(string presetName)
{
try
@@ -169,8 +201,6 @@ namespace ColorControl.Services.LG
Logger.Error($"LoadConfig: {ex.Message}");
}
Config ??= new LgServiceConfig();
LgPreset.LgDevices = Config.Devices;
}
private void LoadRemoteControlButtons()
@@ -349,19 +379,6 @@ namespace ColorControl.Services.LG
//
}
public async Task<bool> ApplyPreset(string presetName)
{
var preset = _presets.FirstOrDefault(p => p.name.Equals(presetName, StringComparison.OrdinalIgnoreCase));
if (preset != null)
{
return await ApplyPreset(preset);
}
else
{
return false;
}
}
public override async Task<bool> ApplyPreset(LgPreset preset)
{
return await ApplyPreset(preset, false);
@@ -1016,5 +1033,196 @@ namespace ColorControl.Services.LG
_gameBarForm?.Hide();
}
}
public List<LgDeviceDto> GetDevices()
{
if (Devices == null)
{
return new List<LgDeviceDto>();
}
return Devices.Select(d => new LgDeviceDto
{
Name = d.Name,
MacAddress = d.MacAddress,
IpAddress = d.IpAddress,
IsCustom = d.IsCustom,
IsDummy = d.IsDummy,
Options = new LgDeviceOptions
{
HandleManualScreenSaver = d.HandleManualScreenSaver,
HDMIPortNumber = d.HDMIPortNumber,
PowerOffByWindows = d.PowerOffByWindows,
PowerOffOnScreenSaver = d.PowerOffOnScreenSaver,
PowerOffOnShutdown = d.PowerOffOnShutdown,
PowerOffOnStandby = d.PowerOffOnStandby,
PowerOnAfterManualPowerOff = d.PowerOnAfterManualPowerOff,
PowerOnAfterResume = d.PowerOnAfterResume,
PowerOnAfterScreenSaver = d.PowerOnAfterScreenSaver,
PowerOnAfterStartup = d.PowerOnAfterStartup,
PowerOnByWindows = d.PowerOnByWindows,
ScreenSaverMinimalDuration = d.ScreenSaverMinimalDuration,
TriggersEnabled = d.TriggersEnabled,
TurnScreenOffOnScreenSaver = d.TurnScreenOffOnScreenSaver,
TurnScreenOnAfterScreenSaver = d.TurnScreenOnAfterScreenSaver,
UseSecureConnection = d.UseSecureConnection
},
IsConnected = d.IsConnected(),
IsSelected = d == SelectedDevice
}).ToList();
}
public async Task<bool> UpdateDevice(LgDeviceDto deviceSpec)
{
if (Devices == null)
{
return false;
}
var device = Devices.FirstOrDefault(d => d.MacAddress == deviceSpec.MacAddress);
if (device == null)
{
device = new LgDevice(deviceSpec);
Devices.Add(device);
}
else
{
var reconnect = device.Update(deviceSpec);
if (reconnect)
{
await device.Connect();
}
}
return true;
}
public async Task<bool> TestDevice(LgDeviceDto deviceSpec)
{
if (Devices == null)
{
return false;
}
try
{
var device = new LgDevice(deviceSpec);
return await device.TestConnection();
}
catch
{
return false;
}
}
public LgServiceConfigDto GetConfig()
{
return new LgServiceConfigDto
{
DefaultButtonDelay = Config.DefaultButtonDelay,
DeviceSearchKey = Config.DeviceSearchKey,
//Devices = GetDevices(),
PowerOnAfterStartup = Config.PowerOnAfterStartup,
PowerOnRetries = Config.PowerOnRetries,
PreferredMacAddress = Config.PreferredMacAddress,
QuickAccessShortcut = Config.QuickAccessShortcut,
SetSelectedDeviceByPowerOn = Config.SetSelectedDeviceByPowerOn,
ShowAdvancedActions = Config.ShowAdvancedActions,
ShutdownDelay = Config.ShutdownDelay,
GameBarShortcut = Config.GameBarShortcut,
GameBarShowingTime = Config.GameBarShowingTime
};
}
public bool UpdateConfig(LgServiceConfigDto config)
{
Config.Update(config);
SetShortcuts(SHORTCUTID_LGQA, Config.QuickAccessShortcut);
return true;
}
public bool UpdatePreset(LgPreset specPreset)
{
var currentPreset = _presets.FirstOrDefault(p => p.id == specPreset.id);
if (currentPreset != null)
{
currentPreset.Update(specPreset);
return true;
}
var newPreset = new LgPreset(specPreset);
newPreset.name = specPreset.name;
_presets.Add(newPreset);
return true;
}
public List<InvokableActionDto<LgPreset>> GetInvokableActions()
{
return SelectedDevice?.GetInvokableActions(Config.ShowAdvancedActions).Select(a => new InvokableActionDto<LgPreset>
{
Advanced = a.Advanced,
Category = Utils.FirstCharUpperCase(a.Category ?? "Misc"),
CurrentValue = a.CurrentValue,
EnumType = a.EnumType,
MaxValue = a.MaxValue,
MinValue = a.MinValue,
Name = a.Name,
NumberOfValues = a.NumberOfValues,
Preset = a.Preset,
Title = a.Title ?? Utils.FirstCharUpperCase(a.Name),
ValueLabels = a.ValueLabels
}).ToList();
}
public async Task<bool> ExecuteInvokableAction(InvokableActionDto<LgPreset> invokableAction, List<string> parameters = null)
{
var device = SelectedDevice;
if (invokableAction.SelectedDevice != null)
{
device = MatchDevice(invokableAction.SelectedDevice) ?? SelectedDevice;
}
if (invokableAction.Preset != null)
{
await SelectedDevice?.ExecutePreset(invokableAction.Preset, false, Config);
return true;
}
return await device?.ExecuteInvokableAction(invokableAction, parameters);
}
public bool SetSelectedDevice(LgDeviceDto deviceDto)
{
if (deviceDto == null)
{
return false;
}
var device = MatchDevice(deviceDto);
if (device == null)
{
return false;
}
SelectedDevice = device;
return true;
}
private LgDevice MatchDevice(LgDeviceDto deviceDto)
{
return Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress);
}
}
}
+46 -32
View File
@@ -1,37 +1,51 @@
using System.Collections.Generic;
using ColorControl.Shared.Contracts.LG;
using System.Collections.Generic;
namespace ColorControl.Services.LG
{
class LgServiceConfig
{
public bool PowerOnAfterStartup { get; set; }
public int PowerOnDelayAfterResume { get; set; }
public int PowerOnRetries { get; set; }
public string PreferredMacAddress { get; set; }
public string DeviceSearchKey { get; set; }
public List<LgDevice> Devices { get; set; }
public bool ShowRemoteControl { get; set; }
public bool ShowAdvancedActions { get; set; }
public string GameBarShortcut { get; set; }
public int GameBarTop { get; set; }
public int GameBarLeft { get; set; }
public int ShutdownDelay { get; set; }
public bool SetSelectedDeviceByPowerOn { get; set; }
public string QuickAccessShortcut { get; set; }
public int DefaultButtonDelay { get; set; }
public int GameBarShowingTime { get; set; }
class LgServiceConfig
{
public bool PowerOnAfterStartup { get; set; }
public int PowerOnDelayAfterResume { get; set; }
public int PowerOnRetries { get; set; }
public string PreferredMacAddress { get; set; }
public string DeviceSearchKey { get; set; }
public List<LgDevice> Devices { get; set; }
public bool ShowRemoteControl { get; set; }
public bool ShowAdvancedActions { get; set; }
public string GameBarShortcut { get; set; }
public int GameBarTop { get; set; }
public int GameBarLeft { get; set; }
public int ShutdownDelay { get; set; }
public bool SetSelectedDeviceByPowerOn { get; set; }
public string QuickAccessShortcut { get; set; }
public int DefaultButtonDelay { get; set; }
public int GameBarShowingTime { get; set; }
public LgServiceConfig()
{
PowerOnDelayAfterResume = 5000;
ShutdownDelay = 1000;
DeviceSearchKey = "[LG]";
PowerOnRetries = 10;
Devices = new List<LgDevice>();
GameBarTop = 100;
GameBarLeft = 100;
DefaultButtonDelay = 200;
GameBarShowingTime = 5000;
}
}
public LgServiceConfig()
{
PowerOnDelayAfterResume = 5000;
ShutdownDelay = 1000;
DeviceSearchKey = "[LG]";
PowerOnRetries = 10;
Devices = new List<LgDevice>();
GameBarTop = 100;
GameBarLeft = 100;
DefaultButtonDelay = 200;
GameBarShowingTime = 5000;
}
internal void Update(LgServiceConfigDto config)
{
PowerOnAfterStartup = config.PowerOnAfterStartup;
ShutdownDelay = config.ShutdownDelay;
DeviceSearchKey = config.DeviceSearchKey;
PowerOnRetries = config.PowerOnRetries;
DefaultButtonDelay = config.DefaultButtonDelay;
GameBarShowingTime = config.GameBarShowingTime;
ShowAdvancedActions = config.ShowAdvancedActions;
QuickAccessShortcut = config.QuickAccessShortcut;
SetSelectedDeviceByPowerOn = config.SetSelectedDeviceByPowerOn;
}
}
}
+193 -192
View File
@@ -1,4 +1,5 @@
using LgTv;
using ColorControl.Shared.Contracts.LG;
using LgTv;
using NWin32;
using System;
using System.Collections.Generic;
@@ -9,234 +10,234 @@ using System.Windows.Forms;
namespace ColorControl.Services.LG
{
partial class RemoteControlForm : Form
{
private LgService _lgService;
private List<LgPreset> _buttons;
private LgWebOsMouseService _mouseService;
private int _maxX, _maxY;
private int _posX, _posY;
partial class RemoteControlForm : Form
{
private LgService _lgService;
private List<LgPreset> _buttons;
private LgWebOsMouseService _mouseService;
private int _maxX, _maxY;
private int _posX, _posY;
public RemoteControlForm(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
public RemoteControlForm(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
InitializeComponent();
InitializeComponent();
pnlMouse.MouseWheel += new MouseEventHandler(pnlMouse_MouseWheel);
}
pnlMouse.MouseWheel += new MouseEventHandler(pnlMouse_MouseWheel);
}
private void RemoteControlForm_Load(object sender, EventArgs e)
{
foreach (var preset in _buttons)
{
var button = new Button();
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderColor = Color.DarkGray;
button.FlatAppearance.MouseOverBackColor = Color.DarkGray;
button.Text = preset.name;
button.Click += ButtonClick;
button.Tag = preset;
private void RemoteControlForm_Load(object sender, EventArgs e)
{
foreach (var preset in _buttons)
{
var button = new Button();
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderColor = Color.DarkGray;
button.FlatAppearance.MouseOverBackColor = Color.DarkGray;
button.Text = preset.name;
button.Click += ButtonClick;
button.Tag = preset;
button.Parent = flowPanel;
}
}
button.Parent = flowPanel;
}
}
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
var _ = _lgService.ApplyPreset(preset);
}
}
var _ = _lgService.ApplyPreset(preset);
}
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
var intPtr = new UIntPtr(2);
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
}
}
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
var intPtr = new UIntPtr(2);
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
}
}
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
{
//var key = e.KeyCode;
//HandleKey(key);
}
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
{
//var key = e.KeyCode;
//HandleKey(key);
}
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void HandleKey(Keys key)
{
if (!chkDirectMode.Checked)
{
return;
}
var cvt = new KeysConverter();
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
private void HandleKey(Keys key)
{
if (!chkDirectMode.Checked)
{
return;
}
var cvt = new KeysConverter();
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
var preset = _buttons.FirstOrDefault(p => p.shortcut != null && p.shortcut.Equals(shortcut));
if (preset != null)
{
var _ = _lgService.ApplyPreset(preset);
}
}
var preset = _buttons.FirstOrDefault(p => p.shortcut != null && p.shortcut.Equals(shortcut));
if (preset != null)
{
var _ = _lgService.ApplyPreset(preset);
}
}
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
chkDirectMode.Checked = !chkDirectMode.Checked;
return;
}
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
chkDirectMode.Checked = !chkDirectMode.Checked;
return;
}
HandleKey(e.KeyCode);
}
HandleKey(e.KeyCode);
}
private async void chkDirectMode_CheckedChanged(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
// MessageForms.InfoOk(
//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
private async void chkDirectMode_CheckedChanged(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
// MessageForms.InfoOk(
//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
//Keyboard keys:
//- arrow keys: navigate the menu
//- backspace: go back
//- escape: exit
//- S: open picture settings
//- media keys: volume up/down/mute
//Keyboard keys:
//- arrow keys: navigate the menu
//- backspace: go back
//- escape: exit
//- S: open picture settings
//- media keys: volume up/down/mute
//Mouse:
//- left button: activate/open item
//- right button: go back
//- wheel: scroll through menus (only vertically)
//Mouse:
//- left button: activate/open item
//- right button: go back
//- wheel: scroll through menus (only vertically)
//Press F1 to stop Direct Mode."
// );
//Press F1 to stop Direct Mode."
// );
if (_mouseService == null)
{
await _lgService.GetMouseAsync().ContinueWith((task) => BeginInvoke(SetMouseService, new[] { task }));
}
}
else
{
Cursor.Clip = Rectangle.Empty;
}
}
if (_mouseService == null)
{
await _lgService.GetMouseAsync().ContinueWith((task) => BeginInvoke(SetMouseService, new[] { task }));
}
}
else
{
Cursor.Clip = Rectangle.Empty;
}
}
private void SetMouseService(Task<LgWebOsMouseService> mouseServiceTask)
{
_mouseService = mouseServiceTask.Result;
private void SetMouseService(Task<LgWebOsMouseService> mouseServiceTask)
{
_mouseService = mouseServiceTask.Result;
_maxX = 1920;
_maxY = 1080;
_posX = _maxX / 2;
_posY = _maxY / 2;
}
_maxX = 1920;
_maxY = 1080;
_posX = _maxX / 2;
_posY = _maxY / 2;
}
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
{
}
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
{
}
private void pnlMouse_MouseEnter(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
Cursor.Clip = RectangleToScreen(pnlMouse.Bounds);
}
}
private void pnlMouse_MouseEnter(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
Cursor.Clip = RectangleToScreen(pnlMouse.Bounds);
}
}
private void RemoteControlForm_FormClosed(object sender, FormClosedEventArgs e)
{
Cursor.Clip = Rectangle.Empty;
}
private void RemoteControlForm_FormClosed(object sender, FormClosedEventArgs e)
{
Cursor.Clip = Rectangle.Empty;
}
private void pnlMouse_MouseUp(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
if (e.Button == MouseButtons.Left)
{
_mouseService.Click();
}
else
{
HandleKey(Keys.Back);
}
}
}
private void pnlMouse_MouseUp(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
if (e.Button == MouseButtons.Left)
{
_mouseService.Click();
}
else
{
HandleKey(Keys.Back);
}
}
}
private void HandleMouse(MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var scaleX = (double)e.X / pnlMouse.Width;
var scaleY = (double)e.Y / pnlMouse.Height;
private void HandleMouse(MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var scaleX = (double)e.X / pnlMouse.Width;
var scaleY = (double)e.Y / pnlMouse.Height;
var relX = scaleX * _maxX;
var relY = scaleY * _maxY;
var relX = scaleX * _maxX;
var relY = scaleY * _maxY;
var dx = (int)relX - _posX;
var dy = (int)relY - _posY;
var dx = (int)relX - _posX;
var dy = (int)relY - _posY;
_posX = Math.Min(_posX + dx, _maxX);
_posY = Math.Min(_posY + dy, _maxY);
_posX = Math.Min(_posX + dx, _maxX);
_posY = Math.Min(_posY + dy, _maxY);
lblPos.Text = $"Pos: {_posX}, {_posY}";
lblPos.Text = $"Pos: {_posX}, {_posY}";
//var cx = flowPanel.Width / 2;
//var cy = flowPanel.Height / 2;
//var cx = flowPanel.Width / 2;
//var cy = flowPanel.Height / 2;
//var dx = (e.X - cx) / 10;
//var dy = (e.Y - cy) / 10;
//var dx = (e.X - cx) / 10;
//var dy = (e.Y - cy) / 10;
_mouseService.Move(dx, dy);
}
}
_mouseService.Move(dx, dy);
}
}
private void RemoteControlForm_Shown(object sender, EventArgs e)
{
chkDirectMode.Checked = true;
}
private void RemoteControlForm_Shown(object sender, EventArgs e)
{
chkDirectMode.Checked = true;
}
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
{
HandleMouse(e);
}
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
{
HandleMouse(e);
}
private void pnlMouse_MouseWheel(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var dy = e.Delta * SystemInformation.MouseWheelScrollLines / 120;
private void pnlMouse_MouseWheel(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var dy = e.Delta * SystemInformation.MouseWheelScrollLines / 120;
if (dy != 0)
{
_mouseService.Scroll(0, dy);
}
}
}
}
if (dy != 0)
{
_mouseService.Scroll(0, dy);
}
}
}
}
}
+142 -141
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts.LG;
using NLog;
using System;
using System.Collections.Generic;
@@ -8,181 +9,181 @@ using System.Windows.Forms;
namespace ColorControl.Services.LG
{
partial class RemoteControlPanel : UserControl
{
private static int Range = 35;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
partial class RemoteControlPanel : UserControl
{
private static int Range = 35;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private LgService _lgService;
private List<LgPreset> _buttons;
private RemoteControlForm _remoteControlForm;
private Bitmap _backupImage;
private LgPreset _lastHoveredPreset;
private LgService _lgService;
private List<LgPreset> _buttons;
private RemoteControlForm _remoteControlForm;
private Bitmap _backupImage;
private LgPreset _lastHoveredPreset;
public RemoteControlPanel(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
public RemoteControlPanel(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
InitializeComponent();
InitializeComponent();
_backupImage = new Bitmap(pbRemote.Image);
_backupImage = new Bitmap(pbRemote.Image);
var txtCoords = Utils.GetResourceFile("RC_coords.txt");
var txtCoords = Utils.GetResourceFile("RC_coords.txt");
foreach (var line in txtCoords.Split('\n'))
{
var columns = line.Split(';');
foreach (var line in txtCoords.Split('\n'))
{
var columns = line.Split(';');
var name = columns[0];
var preset = _buttons.FirstOrDefault(p => p.name.Equals(name));
if (preset != null)
{
preset.X = int.Parse(columns[1]);
preset.Y = int.Parse(columns[2]);
}
}
}
var name = columns[0];
var preset = _buttons.FirstOrDefault(p => p.name.Equals(name));
if (preset != null)
{
preset.X = int.Parse(columns[1]);
preset.Y = int.Parse(columns[2]);
}
}
}
private void RemoteControlPanel_Load(object sender, EventArgs e)
{
}
private void RemoteControlPanel_Load(object sender, EventArgs e)
{
}
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
var _ = _lgService.ApplyPreset(preset);
}
}
var _ = _lgService.ApplyPreset(preset);
}
}
private void pbRemote_Click(object sender, EventArgs e)
{
if (e is MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
private void pbRemote_Click(object sender, EventArgs e)
{
if (e is MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
if (preset != null)
{
if (preset.steps.Contains("POWER") && _lgService.SelectedDevice?.CurrentState != LgDevice.PowerState.Active)
{
Logger.Debug("Executing WOL instead of POWER");
if (preset != null)
{
if (preset.steps.Contains("POWER") && _lgService.SelectedDevice?.CurrentState != LgDevice.PowerState.Active)
{
Logger.Debug("Executing WOL instead of POWER");
var wolPreset = _buttons.FirstOrDefault(p => p.name.Equals("Wol"));
if (wolPreset != null)
{
preset = wolPreset;
}
}
var wolPreset = _buttons.FirstOrDefault(p => p.name.Equals("Wol"));
if (wolPreset != null)
{
preset = wolPreset;
}
}
var _ = _lgService.ApplyPreset(preset);
}
}
}
var _ = _lgService.ApplyPreset(preset);
}
}
}
private (int, int) GetCoordsInImage(int curX, int curY)
{
var width = pbRemote.Width;
var aspect = 0.27D;
var currentAspect = (double)pbRemote.Width / pbRemote.Height;
if (currentAspect > aspect)
{
width = (int)(aspect * pbRemote.Height);
curX = curX - ((pbRemote.Width - width) / 2);
}
else
{
var height = (int)(pbRemote.Width / aspect);
curY = curY - ((pbRemote.Height - height) / 2);
}
private (int, int) GetCoordsInImage(int curX, int curY)
{
var width = pbRemote.Width;
var aspect = 0.27D;
var currentAspect = (double)pbRemote.Width / pbRemote.Height;
if (currentAspect > aspect)
{
width = (int)(aspect * pbRemote.Height);
curX = curX - ((pbRemote.Width - width) / 2);
}
else
{
var height = (int)(pbRemote.Width / aspect);
curY = curY - ((pbRemote.Height - height) / 2);
}
var factor = 405D / width;
var factor = 405D / width;
var posX = (int)(curX * factor);
var posY = (int)(curY * factor);
var posX = (int)(curX * factor);
var posY = (int)(curY * factor);
return (posX, posY);
}
return (posX, posY);
}
private void btnRcDirect_Click(object sender, EventArgs e)
{
}
private void btnRcDirect_Click(object sender, EventArgs e)
{
}
private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
var refresh = false;
var refresh = false;
if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset))
{
var image = (Bitmap)pbRemote.Image;
if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset))
{
var image = (Bitmap)pbRemote.Image;
for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++)
{
for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++)
{
image.SetPixel(x, y, _backupImage.GetPixel(x, y));
for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++)
{
for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++)
{
image.SetPixel(x, y, _backupImage.GetPixel(x, y));
}
}
}
}
refresh = true;
}
refresh = true;
}
if (preset != null && preset != _lastHoveredPreset)
{
var image = (Bitmap)pbRemote.Image;
var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2);
if (preset != null && preset != _lastHoveredPreset)
{
var image = (Bitmap)pbRemote.Image;
var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2);
using (var g = Graphics.FromImage(image))
{
using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White)))
{
g.FillEllipse(cloud_brush, rec);
}
}
using (var g = Graphics.FromImage(image))
{
using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White)))
{
g.FillEllipse(cloud_brush, rec);
}
}
//for (var y = preset.Y - 30; y < preset.Y + 30; y++)
//{
// for (var x = preset.X - 30; x < preset.X + 30; x++)
// {
// var pixel = image.GetPixel(x, y);
//for (var y = preset.Y - 30; y < preset.Y + 30; y++)
//{
// for (var x = preset.X - 30; x < preset.X + 30; x++)
// {
// var pixel = image.GetPixel(x, y);
// image.SetPixel(x, y, Color.FromArgb(150, 0, 0));
// image.SetPixel(x, y, Color.FromArgb(150, 0, 0));
// }
//}
// }
//}
refresh = true;
}
refresh = true;
}
if (refresh)
{
pbRemote.Refresh();
}
if (refresh)
{
pbRemote.Refresh();
}
_lastHoveredPreset = preset;
}
_lastHoveredPreset = preset;
}
private void button1_Click(object sender, EventArgs e)
{
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
{
var buttons = _lgService.GetRemoteControlButtons();
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
_remoteControlForm.Show();
}
else
{
_remoteControlForm.Show();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
{
var buttons = _lgService.GetRemoteControlButtons();
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
_remoteControlForm.Show();
}
else
{
_remoteControlForm.Show();
}
}
}
}
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using ColorControl.Shared.Contracts.NVIDIA;
using Newtonsoft.Json;
using NvAPIWrapper.Display;
using System;
using System.Collections.Generic;
@@ -1,4 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts.NVIDIA;
using ColorControl.Shared.Forms;
using System;
using System.Linq;
+1 -1
View File
@@ -1,6 +1,6 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.NVIDIA;
using ColorControl.Shared.Forms;
using NvAPIWrapper.GPU;
using NvAPIWrapper.Native;
using NvAPIWrapper.Native.GPU;
+13 -14
View File
@@ -112,7 +112,6 @@ namespace ColorControl.Services.NVIDIA
foreach (var preset in _nvService.GetPresets())
{
AddOrUpdateItem(preset);
_keyboardShortcutDispatcher.RegisterShortcut(preset.id, preset.shortcut);
}
}
finally
@@ -444,7 +443,7 @@ namespace ColorControl.Services.NVIDIA
FormUtils.BuildMenuItemAndSub(mnuNvPresetsColorEnhancements, "Hue",
$"{preset.ColorEnhancementSettings.HueAngle}° (click to change)", nvPresetHueMenuItem_Click);
FormUtils.BuildMenuItemAndSub(mnuNvOtherSettings, "SDR brightness",
FormUtils.BuildMenuItemAndSub(miNvHDR, "SDR brightness",
(preset.SDRBrightness.HasValue ? $"{preset.SDRBrightness.Value}%" : "Unset") + " (click to change)", nvPresetBrightnessMenuItem_Click);
FormUtils.BuildDropDownMenu(mnuNvOtherSettings, "Scaling", typeof(Scaling), preset, "scaling", nvPresetScalingMenuItem_Click, unchanged: true, skipValues: new object[] { Scaling.Customized })
@@ -571,7 +570,7 @@ namespace ColorControl.Services.NVIDIA
}
else
{
(presetSetting, isDefault) = settingMeta.ToIntValue(driverSetting.ValueText);
(presetSetting, isDefault, _) = settingMeta.ToIntValue(driverSetting.ValueText);
}
item.Font = _menuItemFonts[!isDefault];
@@ -596,7 +595,7 @@ namespace ColorControl.Services.NVIDIA
subItemUnchanged.Font = _menuItemFonts[subItemUnchanged.Checked];
}
if (NvService.RangeDriverSettings.Contains(driverSetting.SettingId))
if (NvSettingConstants.RangeDriverSettings.Contains(driverSetting.SettingId))
{
var value = settingMeta.DwordValues.FirstOrDefault(v => v.ValueName == driverSetting.ValueText);
@@ -604,7 +603,7 @@ namespace ColorControl.Services.NVIDIA
var subItem = FormUtils.BuildMenuItem(item.DropDownItems, subItemName, "", onClick: driverSettingFrameRateNvMenuItem_Click);
var metaValue = settingMeta.DwordValues.FirstOrDefault(v => v.Value == presetSetting);
subItem.Text = $"{metaValue?.ValueName ?? (driverSetting.SettingId == NvService.DRS_FRAME_RATE_LIMITER_V3 ? "??FPS" : "default")} (click to change)";
subItem.Text = $"{metaValue?.ValueName ?? (driverSetting.SettingId == NvSettingConstants.DRS_FRAME_RATE_LIMITER_V3 ? "??FPS" : "default")} (click to change)";
subItem.Tag = value?.Value ?? 0;
subItem.Checked = presetSetting != defaultValue;
@@ -812,7 +811,7 @@ namespace ColorControl.Services.NVIDIA
return;
}
_nvService.SetSDRBrightness(preset.Display, value.Value);
_nvService.SetSDRBrightness(preset.Display.Name, value.Value);
UpdateDisplayInfoItems();
return;
@@ -1271,7 +1270,7 @@ namespace ColorControl.Services.NVIDIA
private void edtShortcut_KeyDown(object sender, KeyEventArgs e)
{
((TextBox)sender).Text = _keyboardShortcutDispatcher.FormatKeyboardShortcut(e);
((TextBox)sender).Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(e);
}
private void btnSetShortcut_Click(object sender, EventArgs e)
@@ -1310,7 +1309,7 @@ namespace ColorControl.Services.NVIDIA
private void edtShortcut_KeyUp(object sender, KeyEventArgs e)
{
_keyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
KeyboardShortcutDispatcher.HandleKeyboardShortcutUp(e);
}
private void edtShortcut_TextChanged(object sender, EventArgs e)
@@ -1369,7 +1368,7 @@ namespace ColorControl.Services.NVIDIA
return;
}
preset.UpdateDriverSetting(settingItem, intValue);
preset.UpdateDriverSetting(settingItem.SettingId, intValue);
AddOrUpdateItem();
}
@@ -1388,7 +1387,7 @@ namespace ColorControl.Services.NVIDIA
uint presetValue;
if (preset.IsDisplayPreset)
{
(presetValue, _) = settingMeta.ToIntValue(settingItem.ValueText);
(presetValue, _, _) = settingMeta.ToIntValue(settingItem.ValueText);
}
else
{
@@ -1412,7 +1411,7 @@ namespace ColorControl.Services.NVIDIA
return;
}
preset.UpdateDriverSetting(settingItem, settingValue);
preset.UpdateDriverSetting(settingItem.SettingId, settingValue);
AddOrUpdateItem();
}
@@ -1519,7 +1518,7 @@ namespace ColorControl.Services.NVIDIA
{
Label = "Quick Access shortcut",
FieldType = FieldType.Shortcut,
Value = _config.NvQuickAccessShortcut
Value = _nvService.Config.QuickAccessShortcut
};
var enableOcField = new FieldDefinition
@@ -1548,9 +1547,9 @@ namespace ColorControl.Services.NVIDIA
var shortcut = (string)quickAccessField.Value;
_config.NvQuickAccessShortcut = shortcut;
_nvService.Config.QuickAccessShortcut = shortcut;
_keyboardShortcutDispatcher.RegisterShortcut(NvService.SHORTCUTID_NVQA, _config.NvQuickAccessShortcut);
_keyboardShortcutDispatcher.RegisterShortcut(NvService.SHORTCUTID_NVQA, _nvService.Config.QuickAccessShortcut);
_nvService.Config.ShowOverclocking = enableOcField.ValueAsBool;
_nvService.Config.ApplyNovideoOnStartup = enableNovideoField.ValueAsBool;
+160 -144
View File
@@ -3,7 +3,6 @@ using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.NVIDIA;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
using Microsoft.Extensions.DependencyInjection;
@@ -23,10 +22,8 @@ using NvAPIWrapper.Native.General.Structures;
using NvAPIWrapper.Native.GPU.Structures;
using NvAPIWrapper.Native.Helpers;
using NvAPIWrapper.Native.Interfaces.Display;
using NWin32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -39,40 +36,6 @@ using static ColorControl.Shared.Native.CCD;
namespace ColorControl.Services.NVIDIA
{
enum NvDitherState
{
[Description("Auto")]
Auto = 0,
[Description("Enabled")]
Enabled = 1,
[Description("Disabled")]
Disabled = 2
}
enum NvDitherBits
{
[Description("6-bit")]
Bits6 = 0,
[Description("8-bit")]
Bits8 = 1,
[Description("10-bit")]
Bits10 = 2
}
enum NvDitherMode
{
[Description("Spatial Dynamic")]
SpatialDynamic = 0,
[Description("Spatial Static")]
SpatialStatic = 1,
[Description("Spatial Dynamic 2x2")]
SpatialDynamic2x2 = 2,
[Description("Spatial Static 2x2")]
SpatialStatic2x2 = 3,
[Description("Temporal")]
Temporal = 4
}
class NvService : GraphicsService<NvPreset>
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
@@ -166,54 +129,13 @@ namespace ColorControl.Services.NVIDIA
private DrsSettingsMetaService _meta;
private List<SettingItem> _settings = new List<SettingItem>();
private int _lastSetSDRBrightness = -1;
private int _lastReadSDRBrightness = -1;
private SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1);
public const uint DRS_GSYNC_APPLICATION_MODE = 294973784;
public const uint DRS_VSYNC_CONTROL = 11041231;
//public const uint DRS_ULTRA_LOW_LATENCY = 0x10835000;
public const uint DRS_PRERENDERED_FRAMES = 8102046;
public const uint DRS_FRAME_RATE_LIMITER_V3 = 277041154;
public const uint DRS_RTX_HDR_PEAK_BRIGHTNESS = 0x00DD48FC;
public const uint DRS_RTX_HDR_MIDDLE_GREY = 0x00DD48FD;
public const uint DRS_RTX_HDR_CONTRAST = 0x00DD48FE;
public const uint DRS_RTX_HDR_SATURATION = 0x00DD48FF;
public const uint DRS_RTX_DYNAMIC_VIBRANCE_INTENSITY = 0x00ABAB22;
public const uint DRS_RTX_DYNAMIC_VIBRANCE_SATURATION_BOOST = 0x00ABAB13;
public const uint DRS_ANISOTROPIC_FILTERING_SETTING = 0x101E61A9;
public const uint DRS_ANISOTROPIC_FILTER_OPTIMIZATION = 0x0084CD70;
public const uint DRS_ANISOTROPIC_FILTER_SAMPLE_OPTIMIZATION = 0x00E73211;
public const uint DRS_TEXTURE_FILTERING_QUALITY = 0x00CE2691;
public const uint DRS_TEXTURE_FILTERING_NEGATIVE_LOD_BIAS = 0x0019BB68;
public const uint DRS_RBAR_FEATURE = 0X000F00BA;
public const uint DRS_RBAR_OPTIONS = 0X000F00BB;
public const uint DRS_RBAR_SIZE_LIMIT = 0X000F00FF;
public static uint[] RangeDriverSettings = [DRS_FRAME_RATE_LIMITER_V3, DRS_RTX_HDR_PEAK_BRIGHTNESS, DRS_RTX_HDR_MIDDLE_GREY, DRS_RTX_HDR_CONTRAST, DRS_RTX_HDR_SATURATION, DRS_RTX_DYNAMIC_VIBRANCE_INTENSITY, DRS_RTX_DYNAMIC_VIBRANCE_SATURATION_BOOST];
public uint DriverVersion { get; private set; }
public bool OutputModeAvailable => DriverVersion >= 52500;
private static readonly List<uint> _driverSettingIds = new()
{
DRS_GSYNC_APPLICATION_MODE,
DRS_VSYNC_CONTROL,
DRS_PRERENDERED_FRAMES,
DRS_FRAME_RATE_LIMITER_V3,
DRS_ANISOTROPIC_FILTERING_SETTING,
DRS_ANISOTROPIC_FILTER_OPTIMIZATION,
DRS_TEXTURE_FILTERING_QUALITY,
DRS_TEXTURE_FILTERING_NEGATIVE_LOD_BIAS,
DRS_ANISOTROPIC_FILTER_SAMPLE_OPTIMIZATION,
DRS_RBAR_FEATURE,
DRS_RBAR_OPTIONS,
DRS_RBAR_SIZE_LIMIT
};
private static readonly List<uint> _driverSettingIds = new();
private readonly WinApiService _winApiService;
private readonly RpcClientService _rpcClientService;
private readonly PowerEventDispatcher _powerEventDispatcher;
@@ -229,7 +151,7 @@ namespace ColorControl.Services.NVIDIA
_powerEventDispatcher = powerEventDispatcher;
_serviceManager = serviceManager;
_rpcClientService.Name = nameof(NvService);
NvPreset.NvService = this;
NvPreset.GetDriverSettingsDescription += GetDriverSettingsDescription;
AddJsonConverter(new ColorDataConverter());
LoadConfig();
@@ -238,7 +160,7 @@ namespace ColorControl.Services.NVIDIA
public override void InstallEventHandlers()
{
SetShortcuts(SHORTCUTID_NVQA, _globalContext.Config.NvQuickAccessShortcut);
SetShortcuts(SHORTCUTID_NVQA, Config.QuickAccessShortcut);
GetDisplayInfos(false);
@@ -254,6 +176,11 @@ namespace ColorControl.Services.NVIDIA
//_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
}
public override List<string> GetInfo()
{
return [$"{_presets.Count} presets"];
}
public static async Task<bool> ExecutePresetAsync(string idOrName)
{
try
@@ -372,17 +299,23 @@ namespace ColorControl.Services.NVIDIA
}
}
public async Task<bool> ApplyPreset(string idOrName)
public bool UpdatePreset(NvPreset specPreset)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
var currentPreset = _presets.FirstOrDefault(p => p.id == specPreset.id);
if (currentPreset != null)
{
return await ApplyPreset(preset);
}
else
{
return false;
currentPreset.name = specPreset.name;
currentPreset.Update(specPreset);
return true;
}
var newPreset = new NvPreset(specPreset);
newPreset.name = specPreset.name;
_presets.Add(newPreset);
return true;
}
private async Task<Display> WaitForDisplayAsync(NvPreset preset)
@@ -465,6 +398,11 @@ namespace ColorControl.Services.NVIDIA
{
SetOutputMode(preset.HdrSettings.OutputMode.Value, display);
}
if (preset.SDRBrightness.HasValue)
{
SetSDRBrightness(display.Name, preset.SDRBrightness.Value);
}
}
if (preset.DisplayConfig.ApplyRefreshRate || preset.DisplayConfig.ApplyResolution)
@@ -477,11 +415,6 @@ namespace ColorControl.Services.NVIDIA
if (preset.applyOther)
{
if (preset.SDRBrightness.HasValue)
{
SetSDRBrightness(display, preset.SDRBrightness.Value);
}
if (preset.ColorProfileSettings.ProfileName != null)
{
CCD.SetDisplayDefaultColorProfile(display.Name, preset.ColorProfileSettings.ProfileName, _globalContext.Config.SetMinTmlAndMaxTml);
@@ -517,6 +450,11 @@ namespace ColorControl.Services.NVIDIA
SetDriverSettings(preset.driverSettings);
}
if (preset.ApplyNovideoSettings && !newHdrEnabled)
{
SetNovideoSettings(preset.DisplayId, preset.NovideoSettings);
}
if (preset.applyOverclocking)
{
result = ApplyOverclocking(preset.ocSettings);
@@ -532,6 +470,11 @@ namespace ColorControl.Services.NVIDIA
return result;
}
private void SetNovideoSettings(string displayId, NovideoSettings novideoSettings)
{
MainWindow.ApplySettings(displayId, novideoSettings.ApplyClamp, (int)novideoSettings.ColorSpace);
}
public bool ApplyOverclocking(List<NvGpuOcSettings> settings)
{
if (!_winApiService.IsAdministrator())
@@ -604,7 +547,7 @@ namespace ColorControl.Services.NVIDIA
return result;
}
private void SetDriverSettings(Dictionary<uint, uint> settings)
public bool SetDriverSettings(Dictionary<uint, uint> settings, string profileName = null)
{
var convertedSettings = new List<KeyValuePair<uint, string>>();
@@ -618,7 +561,9 @@ namespace ColorControl.Services.NVIDIA
}
}
_drs.StoreSettingsToProfile(_baseProfileName, convertedSettings);
_drs.StoreSettingsToProfile(profileName ?? _baseProfileName, convertedSettings);
return true;
}
public void SetDriverSetting(uint settingId, uint value)
@@ -675,11 +620,7 @@ namespace ColorControl.Services.NVIDIA
var currentColorData = GetCurrentColorData(display);
var settingRange = colorData.DynamicRange == ColorDataDynamicRange.Auto ? colorData.ColorFormat == ColorDataFormat.RGB ? ColorDataDynamicRange.VESA : ColorDataDynamicRange.CEA : colorData.DynamicRange;
var settingSpace = colorData.Colorimetry;
return colorData.ColorFormat != currentColorData.ColorFormat || colorData.ColorDepth != currentColorData.ColorDepth || settingRange != currentColorData.DynamicRange || settingSpace != currentColorData.Colorimetry;
return currentColorData.IsDifferent(colorData);
}
public void SetHDRState(Display display, bool enabled)
@@ -693,43 +634,6 @@ namespace ColorControl.Services.NVIDIA
CCD.SetHDRState(enabled, display.Name);
}
public void SetSDRBrightness(Display display, int brightnessPercent)
{
var hmon = FormUtils.GetMonitorForDisplayName(display.Name);
if (hmon == IntPtr.Zero)
{
hmon = NativeMethods.MonitorFromWindow(0, 1);
}
var brightness = 1 + (double)brightnessPercent / 20;
WinApi.DwmpSDRToHDRBoostPtr(hmon, brightness);
_lastSetSDRBrightness = brightnessPercent;
}
public int GetSDRBrightness(Display display)
{
var whiteLevel = CCD.GetSDRWhiteLevel(display.Name);
if (whiteLevel >= 1000)
{
var newBrightnessPercent = (int)(whiteLevel - 1000) / 50;
if (_lastSetSDRBrightness >= 0 && newBrightnessPercent == _lastReadSDRBrightness)
{
return _lastSetSDRBrightness;
}
_lastReadSDRBrightness = newBrightnessPercent;
return newBrightnessPercent;
}
return 0;
}
public void SetDigitalVibranceLevel(Display display, int level)
{
var dvControl = display.DigitalVibranceControl;
@@ -941,6 +845,9 @@ namespace ColorControl.Services.NVIDIA
{
bits = preset.ditheringBits;
mode = preset.ditheringMode;
setRegistryKey |= preset.SetDitherRegistryKey;
restartDriver |= preset.RestartDriver;
}
if (setRegistryKey)
@@ -1489,6 +1396,30 @@ namespace ColorControl.Services.NVIDIA
}
}
public List<NvPreset> GetDisplayPresets()
{
try
{
var list = new List<NvPreset>();
var displayInfos = GetSimpleDisplayInfos();
foreach (var displayInfo in displayInfos)
{
var preset = GetPresetForDisplay(displayInfo, true, displayInfos.Count());
preset.MonitorConnectionType = preset?.Display?.DisplayDevice?.ConnectionType ?? NvAPIWrapper.Native.GPU.MonitorConnectionType.Unknown;
list.Add(preset);
}
return list;
}
catch (Exception e)
{
Logger.Error("Error while getting displays: " + e.ToLogString());
return null;
}
}
public NvPreset GetPresetForDisplay(string name, bool driverSettings = false)
{
var displays = GetSimpleDisplayInfos();
@@ -1517,7 +1448,7 @@ namespace ColorControl.Services.NVIDIA
preset.colorData = GetCurrentColorData(display);
preset.ColorEnhancementSettings = GetCurrentColorEnhancements(display);
preset.HdmiInfoFrameSettings = GetHdmiSettings(display);
preset.SDRBrightness = GetSDRBrightness(display);
preset.SDRBrightness = GetSDRBrightness(display.Name);
preset.scaling = GetScaling(display);
preset.ColorProfileSettings.ProfileName = CCD.GetDisplayDefaultColorProfile(display.Name, preset.HDREnabled ? COLORPROFILESUBTYPE.CPST_EXTENDED_DISPLAY_COLOR_MODE : COLORPROFILESUBTYPE.CPST_STANDARD_DISPLAY_COLOR_MODE);
@@ -1565,7 +1496,7 @@ namespace ColorControl.Services.NVIDIA
continue;
}
var (presetSetting, isDefault) = settingMeta.ToIntValue(setting.ValueText);
var (presetSetting, isDefault, _) = settingMeta.ToIntValue(setting.ValueText);
if (isDefault)
{
@@ -1598,9 +1529,54 @@ namespace ColorControl.Services.NVIDIA
return _meta.GetSettingMeta(settingId);
}
public List<NvSettingItemDto> GetDriverSettings(string profileName = null)
{
if (profileName == null)
{
_drs.GetProfileNames(ref profileName, false);
}
var applications = new Dictionary<string, string>();
var settings = profileName == null ? GetVisibleSettings() : _drs.GetSettingsForProfile(profileName, SettingViewMode.Normal, ref applications, true);
return settings.Where(s => s.GroupName != null).Select(s =>
{
var settingMeta = _meta.GetSettingMeta(s.SettingId);
var intValue = settingMeta.ToIntValue(s.ValueText);
var type = settingMeta.SettingType == nspector.Native.NVAPI2.NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE ? NvSettingType.BinaryType : NvSettingType.DwordType;
var values = type == NvSettingType.BinaryType ?
settingMeta.BinaryValues?.Select(bv => new NvSettingItemValue { Value = BitConverter.ToUInt32(bv.Value), ValueName = bv.ValueName }) :
settingMeta.DwordValues?.Select(bv => new NvSettingItemValue { Value = bv.Value, ValueName = bv.ValueName });
return new NvSettingItemDto
{
GroupName = s.GroupName,
IsApiExposed = s.IsApiExposed,
IsSettingHidden = s.IsSettingHidden,
IsStringValue = s.IsStringValue,
SettingId = s.SettingId,
SettingText = s.SettingText,
State = (int)s.State,
ValueRaw = s.ValueRaw,
ValueText = s.ValueText,
Value = intValue.intValue,
DefaultValue = intValue.defaultValue,
SettingType = type,
Values = values?.ToList()
};
}).ToList();
}
public List<string> GetDriverProfileNames()
{
var baseProfileName = "";
return DrsServiceLocator.SettingService.GetProfileNames(ref baseProfileName).Where(s => !s.StartsWith("0x")).OrderBy(s => s).ToList();
}
public bool IsGsyncEnabled()
{
var setting = GetSettings().FirstOrDefault(s => s.SettingId == DRS_GSYNC_APPLICATION_MODE);
var setting = GetSettings().FirstOrDefault(s => s.SettingId == NvSettingConstants.DRS_GSYNC_APPLICATION_MODE);
if (setting == null)
{
return false;
@@ -1667,10 +1643,9 @@ namespace ColorControl.Services.NVIDIA
DrsSessionScope.DestroyGlobalSession();
var applications = default(Dictionary<string, string>);
var normalSettings = _drs.GetSettingsForProfile(_baseProfileName, SettingViewMode.Normal, ref applications, true);
var normalSettings = _drs.GetSettingsForProfile(_baseProfileName, SettingViewMode.IncludeScannedSetttings, ref applications, true);
_settings = new List<SettingItem>();
_settings.AddRange(normalSettings);
_settings = new List<SettingItem>(normalSettings);
_driverSettingIds.Clear();
_driverSettingIds.AddRange(_settings.Where(s => !s.IsStringValue && !s.SettingText.Contains("SLI") && s.GroupName != null && s.GroupName != "Unknown").Select(s => s.SettingId));
@@ -1760,6 +1735,20 @@ namespace ColorControl.Services.NVIDIA
Utils.WriteObject(_configFilename, Config);
}
public NvServiceConfig GetConfig()
{
return Config;
}
public bool UpdateConfig(NvServiceConfig config)
{
Config.Update(config);
SetShortcuts(SHORTCUTID_NVQA, Config.QuickAccessShortcut);
return true;
}
internal Display ResolveDisplay(NvPreset preset)
{
if (preset.Display == null)
@@ -1817,5 +1806,32 @@ namespace ColorControl.Services.NVIDIA
{
return (uint)((Marshal.SizeOf(typeof(T))) | version << 16);
}
private string GetDriverSettingsDescription(Dictionary<uint, uint> driverSettings, bool useNewLines = false)
{
if (driverSettings.Count == 0)
{
return "None";
}
var values = new List<string>();
foreach (var driverSetting in driverSettings)
{
var settingMeta = GetSettingMeta(driverSetting.Key);
var value = settingMeta.ToFriendlyName(intValue: driverSetting.Value, displayDefault: true);
if (value == null)
{
continue;
}
values.Add($"{settingMeta.SettingName}: {value}");
}
return string.Join(useNewLines ? "\r\n" : ", ", values);
}
}
}
@@ -1,12 +0,0 @@
namespace ColorControl.Services.NVIDIA
{
class NvServiceConfig
{
public bool ShowOverclocking { get; set; }
public bool ApplyNovideoOnStartup { get; set; }
public NvServiceConfig()
{
}
}
}
@@ -1,19 +1,20 @@
using System.Collections.Generic;
using ColorControl.Shared.Contracts.Samsung;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ColorControl.Services.Samsung
{
internal interface ISamsungService
{
SamsungServiceConfig Config { get; }
List<SamsungDevice> Devices { get; }
SamsungDevice SelectedDevice { get; set; }
string ServiceName { get; }
internal interface ISamsungService
{
SamsungServiceConfig Config { get; }
List<SamsungDevice> Devices { get; }
SamsungDevice SelectedDevice { get; set; }
string ServiceName { get; }
Task<bool> ApplyPreset(SamsungPreset preset);
Task<bool> ApplyPreset(string idOrName);
SamsungDevice GetPresetDevice(SamsungPreset preset);
void GlobalSave();
Task RefreshDevices(bool connect = true, bool afterStartUp = false);
}
Task<bool> ApplyPreset(SamsungPreset preset);
Task<bool> ApplyPreset(string idOrName);
SamsungDevice GetPresetDevice(SamsungPreset preset);
void GlobalSave();
Task<bool> RefreshDevices(bool connect = true, bool afterStartUp = false);
}
}
@@ -1,50 +0,0 @@
namespace ColorControl.Services.Samsung
{
public enum ButtonType
{
KEY_POWER,
KEY_UP,
KEY_DOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_CHUP,
KEY_CHDOWN,
KEY_ENTER,
KEY_RETURN,
KEY_HOME,
KEY_CH_LIST,
KEY_MENU,
KEY_SOURCE,
KEY_GUIDE,
KEY_TOOLS,
KEY_INFO,
KEY_RED,
KEY_GREEN,
KEY_YELLOW,
KEY_BLUE,
KEY_PANNEL_CHDOWN,
KEY_VOLUP,
KEY_VOLDOWN,
KEY_MUTE,
KEY_0,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_DTV,
KEY_HDMI,
KEY_CONTENTS,
KEY_TV_MODE,
KEY_PC_MODE,
KEY_SUB_TITLE,
KEY_AD,
KEY_PMODE,
KEY_ASPECT,
KEY_AMBIENT
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,109 +0,0 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Contracts;
using System.Collections.Generic;
using System.Linq;
namespace ColorControl.Services.Samsung
{
class SamsungPreset : PresetBase
{
public string AppId { get; set; }
public List<string> Steps { get; set; }
public string DeviceMacAddress { get; set; }
public static List<SamsungDevice> SamsungDevices { get; internal set; }
public SamsungPreset() : base()
{
Steps = new List<string>();
}
public SamsungPreset(SamsungPreset preset) : this()
{
id = GetNewId();
name = preset.name;
AppId = preset.AppId;
DeviceMacAddress = preset.DeviceMacAddress;
Steps.AddRange(preset.Steps);
}
public SamsungPreset(string name, string appId, IEnumerable<string> steps = null) : this()
{
this.name = name;
AppId = appId;
if (steps != null)
{
Steps.AddRange(steps);
}
}
public SamsungPreset Clone()
{
var preset = new SamsungPreset(this);
preset.name += " (copy)";
return preset;
}
public static string[] GetColumnNames()
{
return new[] { "Name", "Device|140", "App|200", "Steps|400", "Shortcut", "Trigger" };
}
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>();
values.Add(name);
var deviceString = "Global";
if (!string.IsNullOrEmpty(DeviceMacAddress))
{
if (SamsungDevices != null)
{
var device = SamsungDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress));
if (device != null)
{
deviceString = device.Name;
}
else
{
deviceString = "Unknown: device not found";
}
}
else
{
deviceString = DeviceMacAddress;
}
}
values.Add(deviceString);
var app = AppId;
//if (LgApps != null)
//{
// var item = LgApps.FirstOrDefault(x => x.appId.Equals(appId));
// if (item != null)
// {
// app = item.title + " (" + appId + ")";
// }
//}
values.Add(app);
values.Add(GetStepsDisplay());
values.Add(shortcut);
if (Triggers.Any())
{
values.Add(Triggers.First().ToString());
}
return values;
}
public string GetStepsDisplay()
{
return Steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
}
}
}
+171 -19
View File
@@ -1,5 +1,7 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.Samsung;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
@@ -55,11 +57,23 @@ namespace ColorControl.Services.Samsung
_processEventDispatcher = processEventDispatcher;
_winApiAdminService = winApiAdminService;
SamTvConnection.SyncContext = _appContext.SynchronizationContext;
SamsungPreset.GetDeviceName += SamsungPreset_GetDeviceName;
LoadConfig();
LoadPresets();
}
private string SamsungPreset_GetDeviceName(string macAddress)
{
var device = Devices?.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(macAddress));
if (device != null)
{
return device.Name;
}
return "Unknown: device not found";
}
public static async Task<bool> ExecutePresetAsync(string presetName)
{
try
@@ -99,6 +113,11 @@ namespace ColorControl.Services.Samsung
_processEventDispatcher.RegisterAsyncEventHandler(ProcessEventDispatcher.Event_ProcessChanged, ProcessChanged);
}
public override List<string> GetInfo()
{
return [$"{_presets.Count} presets", $"{Devices?.Count ?? 0} devices"];
}
private async Task PowerStateChanged(object sender, PowerStateChangedEventArgs e, CancellationToken token)
{
await RefreshDevices(afterStartUp: true);
@@ -129,8 +148,6 @@ namespace ColorControl.Services.Samsung
Logger.Error($"LoadConfig: {ex.Message}");
}
Config ??= new SamsungServiceConfig();
SamsungPreset.SamsungDevices = Config.Devices;
}
private async Task PowerModeResume(object sender, PowerStateChangedEventArgs e, CancellationToken _)
@@ -209,7 +226,7 @@ namespace ColorControl.Services.Samsung
await ExecuteEventPresets(_serviceManager, new[] { triggerType }).ConfigureAwait(true);
}
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
public async Task<bool> RefreshDevices(bool connect = true, bool afterStartUp = false)
{
Devices = Config.Devices;
@@ -249,9 +266,7 @@ namespace ColorControl.Services.Samsung
tempDevice.Wake();
await Task.Delay(4000);
await RefreshDevices();
return;
return await RefreshDevices();
}
if (connect && SelectedDevice != null)
@@ -265,6 +280,8 @@ namespace ColorControl.Services.Samsung
//var _ = SelectedDevice.ConnectAsync();
}
}
return true;
}
private async void SelectedDevice_Connected(object sender)
@@ -308,19 +325,6 @@ namespace ColorControl.Services.Samsung
return device;
}
public async Task<bool> ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return await ApplyPreset(preset, _appContext);
}
else
{
return false;
}
}
public override async Task<bool> ApplyPreset(SamsungPreset preset)
{
return await ApplyPreset(preset, _appContext);
@@ -771,5 +775,153 @@ namespace ColorControl.Services.Samsung
{
await ExecuteEventPresets(_serviceManager, new[] { triggerType });
}
public List<SamsungDeviceDto> GetDevices()
{
return Devices?.Select(d => new SamsungDeviceDto
{
Name = d.Name,
MacAddress = d.MacAddress,
IpAddress = d.IpAddress,
IsCustom = d.IsCustom,
IsDummy = d.IsDummy,
Token = d.Token,
Options = d.Options,
IsConnected = d.IsConnected(),
IsSelected = d == SelectedDevice
}).ToList();
}
public async Task<bool> UpdateDevice(SamsungDeviceDto deviceSpec)
{
if (Devices == null)
{
return false;
}
var device = Devices.FirstOrDefault(d => d.MacAddress == deviceSpec.MacAddress);
if (device == null)
{
device = new SamsungDevice(deviceSpec);
Devices.Add(device);
}
else
{
var reconnect = device.Update(deviceSpec);
if (reconnect)
{
await device.ConnectAsync(true);
}
}
return true;
}
public async Task<bool> TestDevice(SamsungDeviceDto deviceSpec)
{
if (Devices == null)
{
return false;
}
try
{
var device = new SamsungDevice(deviceSpec);
return await device.TestConnectionAsync();
}
catch
{
return false;
}
}
public SamsungServiceConfigDto GetConfig()
{
return new SamsungServiceConfigDto
{
DefaultButtonDelay = Config.DefaultButtonDelay,
DeviceSearchKey = Config.DeviceSearchKey,
Devices = GetDevices(),
PowerOnAfterStartup = Config.PowerOnAfterStartup,
PowerOnRetries = Config.PowerOnRetries,
PreferredMacAddress = Config.PreferredMacAddress,
QuickAccessShortcut = Config.QuickAccessShortcut,
SetSelectedDeviceByPowerOn = Config.SetSelectedDeviceByPowerOn,
ShowAdvancedActions = Config.ShowAdvancedActions,
ShutdownDelay = Config.ShutdownDelay,
};
}
public bool UpdateConfig(SamsungServiceConfigDto config)
{
Config.Update(config);
SetShortcuts(SHORTCUTID_SAMSUNGQA, Config.QuickAccessShortcut);
return true;
}
public bool UpdatePreset(SamsungPreset specPreset)
{
var currentPreset = _presets.FirstOrDefault(p => p.id == specPreset.id);
if (currentPreset != null)
{
currentPreset.Update(specPreset);
return true;
}
var newPreset = new SamsungPreset(specPreset);
newPreset.name = specPreset.name;
_presets.Add(newPreset);
return true;
}
public List<InvokableActionDto<SamsungPreset>> GetInvokableActions()
{
return SelectedDevice?.GetInvokableActions(Config.ShowAdvancedActions).Select(a => new InvokableActionDto<SamsungPreset>
{
Advanced = a.Advanced,
Category = Utils.FirstCharUpperCase(a.Category ?? "Misc"),
CurrentValue = a.CurrentValue,
EnumType = a.EnumType,
MaxValue = a.MaxValue,
MinValue = a.MinValue,
Name = a.Name,
NumberOfValues = a.NumberOfValues,
Preset = a.Preset,
Title = a.Title ?? Utils.FirstCharUpperCase(a.Name),
ValueLabels = a.ValueLabels
}).ToList();
}
public async Task<bool> ExecuteInvokableAction(InvokableActionDto<SamsungPreset> invokableAction)
{
return await SelectedDevice?.ExecuteInvokableAction(invokableAction, _appContext, Config);
}
public bool SetSelectedDevice(SamsungDeviceDto deviceDto)
{
if (deviceDto == null)
{
return false;
}
var device = Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress);
if (device == null)
{
return false;
}
SelectedDevice = device;
return true;
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using ColorControl.Shared.Contracts.Samsung;
using System.Collections.Generic;
namespace ColorControl.Services.Samsung
{
@@ -23,5 +24,19 @@ namespace ColorControl.Services.Samsung
DefaultButtonDelay = 500;
Devices = new List<SamsungDevice>();
}
internal void Update(SamsungServiceConfigDto config)
{
PowerOnAfterStartup = config.PowerOnAfterStartup;
PowerOnRetries = config.PowerOnRetries;
DefaultButtonDelay = config.DefaultButtonDelay;
PreferredMacAddress = config.PreferredMacAddress;
DeviceSearchKey = config.DeviceSearchKey;
ShutdownDelay = config.ShutdownDelay;
SetSelectedDeviceByPowerOn = config.SetSelectedDeviceByPowerOn;
QuickAccessShortcut = config.QuickAccessShortcut;
DefaultButtonDelay = config.DefaultButtonDelay;
ShowAdvancedActions = config.ShowAdvancedActions;
}
}
}
@@ -55,7 +55,7 @@ namespace ColorControl.Svc
var ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(securityIdentifier, PipeAccessRights.ReadWrite, AccessControlType.Allow));
Logger.Debug("EXECUTING BACKGROUND");
Logger.Debug($"EXECUTING IN BACKGROUND: {PipeName}");
while (!stoppingToken.IsCancellationRequested)
{
+21
View File
@@ -0,0 +1,21 @@
<src:BaseWindow x:Class="ColorControl.XForms.BrowserWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
mc:Ignorable="d"
Title="ColorControl"
ResizeMode="CanResize"
Width="1280"
Height="720"
WindowStartupLocation="CenterOwner"
SnapsToDevicePixels="True"
Style="{DynamicResource CustomWindowStyle}"
xmlns:src="clr-namespace:ColorControl.Shared.XForms;assembly=Shared"
d:DesignHeight="720" d:DesignWidth="1280" Loaded="Window_Loaded">
<DockPanel>
<wv2:WebView2 Name="webView" DefaultBackgroundColor="#212529"
Source="{Binding Url}"/>
</DockPanel>
</src:BaseWindow>
+87
View File
@@ -0,0 +1,87 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.XForms;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Navigation;
namespace ColorControl.XForms;
public partial class BrowserWindow : BaseWindow
{
private static BrowserWindow _window;
private readonly GlobalContext _globalContext;
public string Url { get; private set; }
public BrowserWindow(GlobalContext globalContext)
{
InitializeComponent();
DataContext = this;
_globalContext = globalContext;
Title = _globalContext.ApplicationTitleAdmin;
Width = _globalContext.Config.XFormWidth;
Height = _globalContext.Config.XFormHeight;
}
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri)
{
UseShellExecute = true,
};
System.Diagnostics.Process.Start(processStartInfo);
}
public static void CreateAndShow(string url, bool show = true)
{
if (Application.Current == null)
{
new Application();
}
_window ??= Program.ServiceProvider.GetRequiredService<BrowserWindow>();
if (show)
{
_window.Url = url;
_window.WindowState = WindowState.Normal;
_window.Show();
_window.Topmost = true;
_window.Topmost = false;
}
}
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
_globalContext.Config.XFormWidth = Width;
_globalContext.Config.XFormHeight = Height;
Hide();
}
protected override void OnStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
Hide();
}
base.OnStateChanged(e);
}
private void Close_Click(object sender, RoutedEventArgs e)
{
Close();
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
+65
View File
@@ -46,5 +46,70 @@
<dj:NLogViewer x:Name="logViewerService" />
</TabItem>
</TabControl>
<GroupBox DockPanel.Dock="Top" Header="Log settings" Padding="6" Height="100">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"><Run Text="Log level:"/></TextBlock>
<ComboBox Width="117" Margin="6,0,0,0" SelectedIndex="{Binding LogLevelIndex}" SelectionChanged="LogLevel_Changed">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="Debug"/>
<ComboBoxItem Content="Info"/>
<ComboBoxItem Content="Warning"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Fatal"/>
</ComboBox>
</StackPanel>
</GroupBox>
<GroupBox DockPanel.Dock="Top" Header="Log settings" Padding="6" Height="100">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"><Run Text="Log level:"/></TextBlock>
<ComboBox Width="117" Margin="6,0,0,0" SelectedIndex="{Binding LogLevelIndex}" SelectionChanged="LogLevel_Changed">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="Debug"/>
<ComboBoxItem Content="Info"/>
<ComboBoxItem Content="Warning"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Fatal"/>
</ComboBox>
</StackPanel>
</GroupBox>
<GroupBox DockPanel.Dock="Top" Header="Log settings" Padding="6" Height="100">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"><Run Text="Log level:"/></TextBlock>
<ComboBox Width="117" Margin="6,0,0,0" SelectedIndex="{Binding LogLevelIndex}" SelectionChanged="LogLevel_Changed">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="Debug"/>
<ComboBoxItem Content="Info"/>
<ComboBoxItem Content="Warning"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Fatal"/>
</ComboBox>
</StackPanel>
</GroupBox>
<GroupBox DockPanel.Dock="Top" Header="Log settings" Padding="6" Height="100">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"><Run Text="Log level:"/></TextBlock>
<ComboBox Width="117" Margin="6,0,0,0" SelectedIndex="{Binding LogLevelIndex}" SelectionChanged="LogLevel_Changed">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="Debug"/>
<ComboBoxItem Content="Info"/>
<ComboBoxItem Content="Warning"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Fatal"/>
</ComboBox>
</StackPanel>
</GroupBox>
<GroupBox DockPanel.Dock="Top" Header="Log settings" Padding="6" Height="100">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"><Run Text="Log level:"/></TextBlock>
<ComboBox Width="117" Margin="6,0,0,0" SelectedIndex="{Binding LogLevelIndex}" SelectionChanged="LogLevel_Changed">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="Debug"/>
<ComboBoxItem Content="Info"/>
<ComboBoxItem Content="Warning"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Fatal"/>
</ComboBox>
</StackPanel>
</GroupBox>
</DockPanel>
</src:BaseWindow>
+11 -14
View File
@@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Navigation;
@@ -79,7 +80,7 @@ namespace ColorControl.XForms
base.OnStateChanged(e);
}
private void RawLog_Click(object sender, RoutedEventArgs e)
private async void RawLog_Click(object sender, RoutedEventArgs e)
{
var context = Shared.Common.GlobalContext.CurrentContext;
@@ -98,9 +99,7 @@ namespace ColorControl.XForms
return;
}
var message = new SvcMessage { MessageType = SvcMessageType.GetLog };
var result = PipeUtils.SendMessage(message);
var result = await PipeUtils.SendMessageAsync(SvcMessageType.GetLog);
var logData = result?.Data ?? "Cannot get log from service";
@@ -118,11 +117,11 @@ namespace ColorControl.XForms
_winApiAdminService.StartProcess(logFile);
}
private void LoadOlder_Click(object sender, RoutedEventArgs e)
private async void LoadOlder_Click(object sender, RoutedEventArgs e)
{
var viewer = GetCurrentViewer();
var lines = LoadLog();
var lines = await LoadLog();
viewer.LoadLines(lines);
}
@@ -141,7 +140,7 @@ namespace ColorControl.XForms
context.SetLogLevel(logLevel);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
var viewer = GetCurrentViewer();
@@ -152,12 +151,12 @@ namespace ColorControl.XForms
var context = Shared.Common.GlobalContext.CurrentContext;
var lines = LoadLog();
var lines = await LoadLog();
viewer.LoadLines(lines, context.StartTime);
}
private List<string> LoadLog()
private async Task<List<string>> LoadLog()
{
string logFile;
@@ -170,9 +169,7 @@ namespace ColorControl.XForms
if (winApiService.IsServiceRunning())
{
var message = new SvcMessage { MessageType = SvcMessageType.GetLog };
var result = PipeUtils.SendMessage(message);
var result = await PipeUtils.SendMessageAsync(SvcMessageType.GetLog);
logFile = result?.Data ?? "Cannot get log from service";
}
@@ -189,7 +186,7 @@ namespace ColorControl.XForms
return tabControl.SelectedIndex == 0 ? logViewerApplication : logViewerService;
}
private void DeleteLog_Click(object sender, RoutedEventArgs e)
private async void DeleteLog_Click(object sender, RoutedEventArgs e)
{
if (MessageForms.QuestionYesNo("Are you sure you want to delete the log? This will clear all logging.") != System.Windows.Forms.DialogResult.Yes)
{
@@ -208,7 +205,7 @@ namespace ColorControl.XForms
}
else
{
PipeUtils.SendMessage(SvcMessageType.ClearLog);
await PipeUtils.SendMessageAsync(SvcMessageType.ClearLog);
logViewerService.ClearCommand.Execute(null);
}
}
File diff suppressed because it is too large. Load diff
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff. Show more