Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to use seperated TDP when device power on Battery Mode #833

Merged
merged 1 commit into from
Nov 22, 2023

Conversation

quangmach
Copy link
Contributor

@quangmach quangmach commented Nov 18, 2023

I add an option to use seperated TDP when device power on Battery Mode. I tested and it works nicely on my device, reusing all current UI component, UI still clean

Summary by CodeRabbit

  • New Features

    • Introduced power management settings for adjusting Thermal Design Power (TDP) when the device is on battery.
    • Added UI components for TDP settings, including a toggle switch and a slider for on-battery configuration.
    • Implemented event handlers to manage power mode changes and update TDP settings accordingly.
  • Enhancements

    • Improved power management logic to dynamically adjust TDP based on power status.
  • Documentation

    • Added new localized strings for TDP on battery settings descriptions.
  • Bug Fixes

    • Ensured TDP settings are correctly applied when switching between power states.

Copy link
Contributor

coderabbitai bot commented Nov 18, 2023

Walkthrough

The software has been updated to manage Thermal Design Power (TDP) settings more effectively, especially when devices are running on battery power. New UI elements and event handlers have been added to allow users to toggle TDP settings and adjust power limits for battery operation. The application now responds to power mode changes, ensuring that TDP settings are appropriately applied based on whether the device is plugged in or on battery.

Changes

File Path Change Summary
.../Managers/PerformanceManager.cs Added handling for power mode changes and TDP settings adjustment based on power status.
.../Misc/Profile.cs Introduced properties for managing TDP settings on battery.
.../Properties/Resources.Designer.cs
.../Properties/Resources.resx
Added new resources for TDP on battery settings localization.
.../Views/Pages/ProfilesPage.xaml
.../Views/Pages/ProfilesPage.xaml.cs
.../Views/QuickPages/QuickProfilesPage.xaml
.../Views/QuickPages/QuickProfilesPage.xaml.cs
Updated UI and code-behind to include TDP on battery settings with new event handlers and UI elements.

🐇 In the heart of November, chill and gray,
Our code hops forth, in battery's thrall,
With sliders and toggles, it's a power play,
Ensuring our devices won't stall. 🍂🔋


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.
  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Note: For conversation with the bot, please use the review comments on code diffs or files.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between b9f46a2 and f1a69bb.
Files selected for processing (8)
  • HandheldCompanion/Managers/PerformanceManager.cs (4 hunks)
  • HandheldCompanion/Misc/Profile.cs (1 hunks)
  • HandheldCompanion/Properties/Resources.Designer.cs (1 hunks)
  • HandheldCompanion/Properties/Resources.resx (1 hunks)
  • HandheldCompanion/Views/Pages/ProfilesPage.xaml (1 hunks)
  • HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs (4 hunks)
  • HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml (1 hunks)
  • HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs (8 hunks)
Files skipped from review due to trivial changes (1)
  • HandheldCompanion/Properties/Resources.Designer.cs
Additional comments: 17
HandheldCompanion/Properties/Resources.resx (1)
  • 2360-2365: The new entries for TDP settings on battery are correctly added to the .resx file. These will be used to display the appropriate UI text for the new feature. Make sure that the corresponding keys are used in the code to fetch these values for localization purposes.
HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml (2)
  • 235-294: The addition of the TDP on battery feature is well implemented with a clear UI design that includes a toggle switch and a slider for user interaction. The use of data binding and event handlers is consistent with the rest of the file. However, ensure that the visibility of the TDP on battery settings is correctly managed based on the state of the TDPToggle. If the TDPToggle is off, the TDP on battery settings should not be visible to the user.

  • 231-291: The TickPlacement and ValueChanged event are correctly set for the TDPOnBatterySlider. It's important to ensure that the ValueChanged event handler TDPOnBatterySlider_ValueChanged is implemented in the code-behind to handle the user's input and update the TDP settings accordingly.

HandheldCompanion/Views/Pages/ProfilesPage.xaml (1)
  • 657-724: The implementation of the "TDP On Battery limit" feature in the UI seems consistent with the rest of the application's design and functionality. The use of x:Static for resource strings ensures localization support, and the data bindings for the toggle switch and slider appear to be set up correctly to respond to user interactions.

However, there are a few points to consider:

  • Ensure that the TDPOnBatteryToggle toggle switch correctly reflects the state of the TDPOnBatteryEnabled property from the Profile class.
  • Verify that the TDPOnBatterySlider slider is properly bound to the TDPOnBatteryValues property and that the value changes are being saved to the profile settings.
  • Confirm that the TDPOnBatterySlider_ValueChanged event handler is implemented to handle the slider's value changes.
  • Check that the TDPOnBatteryToggle_Toggled event handler is implemented to handle the toggle switch state changes.
  • Make sure that the IsEnabled bindings for the TDPOnBatteryToggle and the content grid are working as expected, enabling and disabling the UI elements based on the TDPToggle state.

The code is well-structured, and the use of styles and resources should make it easy to maintain and ensure a consistent look and feel across the application.

HandheldCompanion/Managers/PerformanceManager.cs (5)
  • 6-14: The import of Microsoft.Win32 and System.Windows.Forms is necessary for handling system events related to power mode changes. This is a good use of system libraries to extend functionality.

  • 102-110: The addition of an event handler for SystemEvents.PowerModeChanged is a good practice to dynamically adjust settings based on the power source. It's important to ensure that the application properly unsubscribes from this event when it's closed or no longer needed to prevent memory leaks.

  • 121-122: Setting MaxDegreeOfParallelism to half of the processor count is a reasonable default for parallel operations, but it should be verified that this does not negatively impact performance on systems with a high number of processor cores.

  • 124-127: The SystemEvents_PowerModeChanged method correctly triggers a UI update through ProfilesPage.RequestUpdate(). It's important to ensure that the UI update method is thread-safe since PowerModeChanged can be raised from a non-UI thread.

  • 151-174: The logic to determine TDPOverrideValues based on whether the device is plugged in or on battery is sound. However, it's important to ensure that SystemInformation.PowerStatus.PowerLineStatus is reliable across different devices and that the TDPOnBatteryValues are validated before use.

HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs (2)
  • 139-145: The use of ScopedLock on updateLock is good for thread safety when updating shared resources. This ensures that the TDP (Thermal Design Power) settings are not concurrently modified, which could lead to inconsistent states. However, it's important to verify that updateLock is properly released in all scenarios to avoid deadlocks.

  • 403-419: The code correctly sets the minimum and maximum values for the TDP sliders based on the device's capabilities. It's important to ensure that SettingsManager.GetInt is a thread-safe operation since it's being called from within the UI thread's context. If it involves shared resources or complex logic, consider caching the values or ensuring thread safety within the SettingsManager.

HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs (6)
  • 11-14: The addition of the Microsoft.Win32 namespace is appropriate for handling system events related to power mode changes. This is a necessary import for the functionality being implemented.

  • 271-285: The conditional adjustments to TDPSlider and TDPOnBatterySlider values based on power status and profile settings are a key part of the feature. This logic ensures that the TDP settings are adjusted correctly when the power mode changes. The use of PluggedInStatus to determine which slider to adjust is correct and aligns with the feature's requirements.

  • 308-314: The use of ScopedLock on updateLock is good for thread safety, ensuring that the UI updates do not conflict with each other. This is important when dealing with asynchronous operations that may affect shared resources.

  • 391-403: The initialization of currentProfile.TDPOnBatteryValues in the CreateProfile function is correct and necessary for the new feature. This ensures that the TDP settings for battery mode are set up when a new profile is created.

  • 499-505: The code correctly initializes the TDPOnBatteryValues for the new profile based on the current device's normal TDP (nTDP). This is consistent with the initialization of TDPOverrideValues and is necessary for the new feature to work correctly.

  • 601-631: The event handlers for TDPOnBatteryToggle and TDPOnBatterySlider are implemented correctly. They update the currentProfile with the new TDP settings when the user interacts with the UI elements. This is a crucial part of the feature, ensuring that user preferences are captured and applied.

Comment on lines 123 to +127
// power
public bool TDPOverrideEnabled { get; set; }
public double[] TDPOverrideValues { get; set; }
public bool TDPOnBatteryEnabled { get; set; }
public double[] TDPOnBatteryValues { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of TDPOnBatteryEnabled and TDPOnBatteryValues properties is consistent with the feature being implemented. However, ensure that these properties are properly serialized and deserialized when the Profile objects are saved and loaded. Also, consider initializing TDPOverrideValues and TDPOnBatteryValues to prevent null reference exceptions when they are accessed before being set.

+   public double[] TDPOverrideValues { get; set; } = new double[0];
+   public double[] TDPOnBatteryValues { get; set; } = new double[0];

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// power
public bool TDPOverrideEnabled { get; set; }
public double[] TDPOverrideValues { get; set; }
public bool TDPOnBatteryEnabled { get; set; }
public double[] TDPOnBatteryValues { get; set; }
// power
public bool TDPOverrideEnabled { get; set; }
public double[] TDPOverrideValues { get; set; } = new double[0];
public bool TDPOnBatteryEnabled { get; set; }
public double[] TDPOnBatteryValues { get; set; } = new double[0];

Comment on lines 585 to +614
RequestUpdate();
}

private void TDPOnBatteryToggle_Toggled(object sender, RoutedEventArgs e)
{
// wait until lock is released
if (updateLock)
return;

selectedProfile.TDPOnBatteryEnabled = TDPOnBatteryToggle.IsOn;
RequestUpdate();
}

private void TDPOnBatterySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!TDPOnBatterySlider.IsInitialized)
return;

// wait until lock is released
if (updateLock)
return;

selectedProfile.TDPOnBatteryValues = new double[3]
{
(int)TDPOnBatterySlider.Value,
(int)TDPOnBatterySlider.Value,
(int)TDPOnBatterySlider.Value
};
RequestUpdate();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RequestUpdate method is called after setting the TDP values, which is good for ensuring that changes are queued for submission. However, the cast to (int) may not be necessary if the slider values are already double. If the intention is to store integers, it's fine, but if the underlying TDP settings can accept double values, consider removing the cast to avoid unnecessary precision loss.

-            (int)TDPSlider.Value,
-            (int)TDPSlider.Value,
-            (int)TDPSlider.Value
+            TDPSlider.Value,
+            TDPSlider.Value,
+            TDPSlider.Value

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
RequestUpdate();
}
private void TDPOnBatteryToggle_Toggled(object sender, RoutedEventArgs e)
{
// wait until lock is released
if (updateLock)
return;
selectedProfile.TDPOnBatteryEnabled = TDPOnBatteryToggle.IsOn;
RequestUpdate();
}
private void TDPOnBatterySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!TDPOnBatterySlider.IsInitialized)
return;
// wait until lock is released
if (updateLock)
return;
selectedProfile.TDPOnBatteryValues = new double[3]
{
(int)TDPOnBatterySlider.Value,
(int)TDPOnBatterySlider.Value,
(int)TDPOnBatterySlider.Value
};
RequestUpdate();
}
private void TDPOnBatterySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!TDPOnBatterySlider.IsInitialized)
return;
// wait until lock is released
if (updateLock)
return;
selectedProfile.TDPOnBatteryValues = new double[3]
{
TDPOnBatterySlider.Value,
TDPOnBatterySlider.Value,
TDPOnBatterySlider.Value
};
RequestUpdate();
}

Comment on lines 262 to +265
private void HotkeysManager_CommandExecuted(string listener)
{
// UI thread (async)
bool PluggedInStatus = System.Windows.Forms.SystemInformation.PowerStatus.PowerLineStatus == System.Windows.Forms.PowerLineStatus.Online;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable PluggedInStatus is used to check the power line status, which is essential for the feature being implemented. However, it's important to note that System.Windows.Forms.SystemInformation.PowerStatus is a part of the Windows Forms namespace, which is generally not used in WPF applications. If the application is primarily WPF, it would be better to use a more consistent approach for checking power status that aligns with WPF conventions.

// Instead of using System.Windows.Forms, consider using System.Management to query the power status
bool PluggedInStatus = new System.Management.ManagementObjectSearcher("root\\CIMV2",
    "SELECT * FROM Win32_Battery").Get().Count > 0;

@quangmach
Copy link
Contributor Author

Added UI components
Screenshot 2023-11-18 170727
Screenshot 2023-11-18 171118

@quangmach
Copy link
Contributor Author

UI when disabled/turn off
Screenshot 2023-11-18 171405
Screenshot 2023-11-18 171429
Screenshot 2023-11-18 171444

@MSeys
Copy link
Contributor

MSeys commented Nov 18, 2023

Maybe align the translations better to the existing TDP limit option.
E.g.:
"Thermal Power (TDP) limit on Battery"
"Limits processor power for less total power when on battery"

@Valkirie
Copy link
Owner

Hey thank you very much for your involvement here. To be fair I'd suggest we implement support for two separate power profiles based on plugged or unplugged scenario. What do you think?

@quangmach
Copy link
Contributor Author

quangmach commented Nov 20, 2023

Hey thank you very much for your involvement here. To be fair I'd suggest we implement support for two separate power profiles based on plugged or unplugged scenario. What do you think?

Do you mean the Power Profiles of windows, those Balanced & Performance? Isn't that part windows handle?

@Valkirie
Copy link
Owner

Yes, when I first thought about this feature, my plan was to have two power profiles associated to each profile. A plugged and unplugged one.

@quangmach
Copy link
Contributor Author

Yes, when I first thought about this feature, my plan was to have two power profiles associated to each profile. A plugged and unplugged one.

So what you plan is Profiles > Plug & Unplug Profile > each has App-specific profiles under? And what I did is Profiles > App-specific Profiles contain Plug & Unplug TDP slider?

I did try to did that first but need to change quite alot more than this one as It needs to change upper code and may messing up UI :D

@quangmach
Copy link
Contributor Author

But if you already have plan the job and that has additional feature or usecase better then just ignore this one and close this PR as I think it may conflict your work plan. I'm happy with that

@MSeys
Copy link
Contributor

MSeys commented Nov 21, 2023

I'm pretty sure what Valkirie meant is that he had the initial idea as he explained. It's not an "actual" plan, it's an idea that you could use to reshape your PR so it could potentially be integrated.

@quangmach
Copy link
Contributor Author

I'm pretty sure what Valkirie meant is that he had the initial idea as he explained. It's not an "actual" plan, it's an idea that you could use to reshape your PR so it could potentially be integrated.

Ohh, I think I can deal with it, just need more details from him

@Valkirie
Copy link
Owner

Glad you're on board. I'm still ensure what's the cleanest UI approach but from a backoffice perspective I'd just make it so a profile has two power profiles bounds to it within a dictionary maybe. Key could be a bool (plugged, unplugged) and value would be PowerProfile.

Either we duplicate the power profile selection UI and have one named plugged and the other unplugged. Either we need to think of something else..

@quangmach
Copy link
Contributor Author

Ok so you meant we should have all those profile settings like (CPU Count, TDP, AutoTDP, EPP, GPU Clock) should all separated between Plug & Unplug. The issue I can first see is we make it's nest 1 deeper dictionary but then it's bad for backward compatibility. The other option is having additional OnBattery for each settings in dictionary and it'll be used by a Toggle UseOnBattery settings (Placed above "Use per-game profile" toggle) and when On Battery just like I did with TDP but it adds up a lot more code than the nest deeper dictionary. What's your opinion? As it's your app I don't want to messing it up lol

Regarding UI, I think adding a Tab under "Power settings" Expandable Panel but I could not find any Tab in your app that I can reuse (I'm bad at UI)

@CasperH2O
Copy link
Collaborator

@quangmach the layout editor has tabs, we can even add an icon with a power plug and a battery. See below.

As for my thoughts on this feature. I think we should jut have two tabs that both have the same list of options. What I'm not sure about is how to handle the default state so to speak (not the Default profile). Do we want users to have to configure both tabs of settings, or if the plugged in settings are disabled, use the battery one or is it really disabled and what if you just want everything the same as "the other" tab?

afbeelding

@Valkirie Valkirie merged commit a95132c into Valkirie:main Nov 22, 2023
1 check passed
Valkirie added a commit that referenced this pull request Nov 22, 2023
Valkirie added a commit that referenced this pull request Nov 22, 2023
@Valkirie
Copy link
Owner

My bad, used the Github mobile app to review something and ended up merging this. Reverted it.

@quangmach
Copy link
Contributor Author

@quangmach the layout editor has tabs, we can even add an icon with a power plug and a battery. See below.

As for my thoughts on this feature. I think we should jut have two tabs that both have the same list of options. What I'm not sure about is how to handle the default state so to speak (not the Default profile). Do we want users to have to configure both tabs of settings, or if the plugged in settings are disabled, use the battery one or is it really disabled and what if you just want everything the same as "the other" tab?

afbeelding

I think if the Togggle "Use On Battery different settings" On, it will show up the same panel for battery settings. Otherwise it will be the same as currently. User can choose to set & apply it for global or per-game like now too. I will try to utilise thay tab, draft it and show how it might look in here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants