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

Move files from EA to Public. #1036

Merged
merged 1 commit into from
Apr 19, 2024
Merged

Conversation

CasperH2O
Copy link
Collaborator

@CasperH2O CasperH2O commented Apr 19, 2024

Summary by CodeRabbit

  • New Features

    • Introduced environment variable-based configuration for file paths.
    • Enhanced GPU management with updated threading and telemetry handling.
    • Updated processor management with new synchronization techniques and removed outdated timer functionalities.
    • Improved UI responsiveness and thread safety across various components.
    • Added full motion control support for gaming platforms in documentation.
  • Bug Fixes

    • Removed redundant initialization and methods across device and GPU classes to streamline operations.
    • Adjusted UI elements and conditions in controllers and settings pages for better user interaction.
  • Refactor

    • Transitioned certain classes to static and updated method access to improve performance and maintainability.
    • Implemented new locking mechanisms and thread safety protocols across multiple system components.
  • Documentation

    • Updated gaming platform links to support enhanced motion control features.
  • Style

    • Performed code formatting for better readability and consistency across various files.

@CasperH2O CasperH2O merged commit 33a981d into Valkirie:main Apr 19, 2024
1 check failed
Copy link
Contributor

coderabbitai bot commented Apr 19, 2024

Walkthrough

The recent updates to the HandheldCompanion project focus on enhancing thread safety, refining synchronization, and optimizing performance. Changes include the transformation of classes to static, significant method revisions, and improved error handling. The updates span across various components such as GPUs, devices, and UI elements, aiming to streamline operations and increase the robustness of the application.

Changes

File Path Change Summary
.../ADLX/ADLXBackend.cs, .../IGCL/IGCLBackend.cs Converted to static classes; removed methods and updated function pointers.
App.xaml, .../Views/.../*.xaml.cs Cosmetic changes and code formatting.
App.xaml.cs, .../HandheldCompanion.json Environment variables and paths updated.
.../Devices/AYANEO/*.cs Significant method and constructor changes; added synchronization.
.../GraphicsProcessingUnit/*.cs Threading and telemetry handling updates; removed redundant methods.
.../Managers/*.cs Updated event handling, status management, and initialization processes.
.../Processors/*.cs Removed timers, updated synchronization methods, and streamlined update processes.
.../Misc/MotherboardInfo.cs, .../Platforms/LibreHardwareMonitor.cs Added synchronization and caching; restructured data handling.
HandheldCompanion.csproj, README.md Added new package reference and updated links.

🐇✨
In the code's forest, deep and vast,
A rabbit hopped, making changes fast.
Threads now safe, with locks so tight,
Performance peaks, sheer delight.
Hop, hop, hop, on soft green patches,
Code now flows without any catches. 🌟🌿
🐇✨


Recent Review Details

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between cf38ec9 and 549bdfc.
Files selected for processing (39)
  • HandheldCompanion/ADLX/ADLXBackend.cs (2 hunks)
  • HandheldCompanion/App.xaml (1 hunks)
  • HandheldCompanion/App.xaml.cs (1 hunks)
  • HandheldCompanion/Devices/AYANEO/AYANEODevice.cs (2 hunks)
  • HandheldCompanion/Devices/AYANEO/AYANEODeviceCEc.cs (3 hunks)
  • HandheldCompanion/Devices/AYANEO/AYANEODeviceCEii.cs (1 hunks)
  • HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs (11 hunks)
  • HandheldCompanion/GraphicsProcessingUnit/GPU.cs (5 hunks)
  • HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs (6 hunks)
  • HandheldCompanion/HandheldCompanion.csproj (1 hunks)
  • HandheldCompanion/HandheldCompanion.json (2 hunks)
  • HandheldCompanion/IGCL/IGCLBackend.cs (5 hunks)
  • HandheldCompanion/Managers/ControllerManager.cs (9 hunks)
  • HandheldCompanion/Managers/GPUManager.cs (9 hunks)
  • HandheldCompanion/Managers/MultimediaManager.cs (1 hunks)
  • HandheldCompanion/Managers/ProfileManager.cs (1 hunks)
  • HandheldCompanion/Misc/MotherboardInfo.cs (1 hunks)
  • HandheldCompanion/Platforms/LibreHardwareMonitor.cs (2 hunks)
  • HandheldCompanion/Processors/AMDProcessor.cs (4 hunks)
  • HandheldCompanion/Processors/IntelProcessor.cs (4 hunks)
  • HandheldCompanion/Processors/Processor.cs (5 hunks)
  • HandheldCompanion/UI/UISounds.cs (1 hunks)
  • HandheldCompanion/Utils/CrossThreadLock.cs (1 hunks)
  • HandheldCompanion/Utils/WPFUtils.cs (1 hunks)
  • HandheldCompanion/Views/Classes/GamepadWindow.cs (1 hunks)
  • HandheldCompanion/Views/Pages/ControllerPage.xaml (2 hunks)
  • HandheldCompanion/Views/Pages/ControllerPage.xaml.cs (7 hunks)
  • HandheldCompanion/Views/Pages/LayoutPage.xaml.cs (3 hunks)
  • HandheldCompanion/Views/Pages/PerformancePage.xaml.cs (1 hunks)
  • HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs (9 hunks)
  • HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs (6 hunks)
  • HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs (32 hunks)
  • HandheldCompanion/Views/QuickPages/QuickOverlayPage.xaml (1 hunks)
  • HandheldCompanion/Views/QuickPages/QuickOverlayPage.xaml.cs (1 hunks)
  • HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml (1 hunks)
  • HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs (1 hunks)
  • HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs (1 hunks)
  • HandheldCompanion/Views/Windows/MainWindow.xaml.cs (1 hunks)
  • README.md (1 hunks)
Files not summarized due to errors (3)
  • HandheldCompanion/Managers/ProfileManager.cs: Error: Message exceeds token limit
  • HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml: Error: Message exceeds token limit
  • HandheldCompanion/Views/Windows/MainWindow.xaml.cs: Error: Message exceeds token limit
Files skipped from review due to trivial changes (8)
  • HandheldCompanion/App.xaml
  • HandheldCompanion/Managers/MultimediaManager.cs
  • HandheldCompanion/Utils/WPFUtils.cs
  • HandheldCompanion/Views/Classes/GamepadWindow.cs
  • HandheldCompanion/Views/Pages/PerformancePage.xaml.cs
  • HandheldCompanion/Views/QuickPages/QuickOverlayPage.xaml
  • HandheldCompanion/Views/QuickPages/QuickOverlayPage.xaml.cs
  • HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs
Additional Context Used
LanguageTool (64)
README.md (64)

Near line 10: Possible spelling mistake found.
Context: ...lerometer) or external sensor. Settings availible for racing, 1st and 3rd person gaming a...


Near line 11: Possible spelling mistake found.
Context: ... person gaming and emulator support.

  • Quicktools overlay, with easy access to various se...

Near line 11: Possible spelling mistake found.
Context: ...ith easy access to various settings and informatio such as TDP, Auto TDP, GPU, Screen Hz, ...


Near line 11: Possible spelling mistake found.
Context: ... such as TDP, Auto TDP, GPU, Screen Hz, Framelimiter, Resolution, Brightness, Volume, Powerm...


Near line 11: Possible spelling mistake found.
Context: ...imiter, Resolution, Brightness, Volume, Powermode control and battery level.

  • Virtual c...

Near line 12: Possible spelling mistake found.
Context: ...org/wiki/Xbox_360_controller) and [Sony DualShock 4 Controller](https://en.wikipedia.org/...


Near line 14: Possible spelling mistake found.
Context: ...ctive game and applying of settings.

  • Gamepad remapping to mouse and keyboard, gamepa...

Near line 14: Possible spelling mistake found.
Context: ...amepad remapping to mouse and keyboard, gamepad joystick and trigger deadzone adjusteme...


Near line 14: Possible spelling mistake found.
Context: ... keyboard, gamepad joystick and trigger deadzone adjustements.

  • PS Remote Play support...

Near line 14: Possible spelling mistake found.
Context: ..., gamepad joystick and trigger deadzone adjustements.

  • PS Remote Play support with DS4 con...

Near line 22: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...tion controls (UMC) to any game.

  • You want to add high-precision motion controls to y...

Near line 23: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ed.com/controller/update/dec15).

  • You want to play your Sony Playstation 4 library th...

Near line 23: Possible spelling mistake found.
Context: ...e/dec15).

  • You want to play your Sony Playstation 4 library through [PlayStation Now](htt...

Near line 24: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ay.dl.playstation.net/remoteplay/>).


Near line 24: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...l.playstation.net/remoteplay/>).


Near line 24: Possible spelling mistake found.
Context: ... your Wii, WiiU and [Switch](https...


Near line 26: The official name of this popular video platform is spelled with a capital “T”.
Context: ...hrough UDP motion control protocol.

[Youtube Channel](https://www.youtube.com/channe...


Near line 33: Possible spelling mistake found.
Context: ...d64).

Supported Devices

  • ASUS ROG Ally
  • AOKZOE A1
  • AYA Neo and its di...

Near line 34: Possible spelling mistake found.
Context: ...Supported Devices

  • ASUS ROG Ally
  • AOKZOE A1
  • AYA Neo and its different version...

Near line 37: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...nd its different versions

  • AYA Neo Air and it's different versions
  • AYA Neo 2, G...

Near line 39: Possible spelling mistake found.
Context: ... AYA Neo 2, Geek, 2S Geek 1S

  • AYA Neo KUN
  • ONEXPLAYER MINI and its different ve...

Near line 40: Possible spelling mistake found.
Context: ...o 2, Geek, 2S Geek 1S

  • AYA Neo KUN
  • ONEXPLAYER MINI and its different versions (Intel,...

Near line 40: Possible spelling mistake found.
Context: ...and its different versions (Intel, AMD, Gundam)

  • GPD WIN Max 2 (INTEL, AMD)
  • GPD W...

Near line 46: Possible spelling mistake found.
Context: ... GPD Win 4

  • Steam Deck (LCD, OLED)
  • Ayn Loki (all models)
  • Lenovo Legion Go
    ...

Near line 67: Possible spelling mistake found.
Context: ...33e620-c629-4f63-b337-9b10138988b7)

DroiX, are trus...


Near line 67: Unpaired symbol: ‘[’ seems to be missing
Context: ...0-c629-4f63-b337-9b10138988b7)

DroiX, are trust...


Near line 67: Possible spelling mistake found.
Context: ...aming handhelds including GPD, AYA NEO, ONEXPLAYER & AOKZOE. Shipping worldwide with local...


Near line 67: Possible spelling mistake found.
Context: ...ds including GPD, AYA NEO, ONEXPLAYER & AOKZOE. Shipping worldwide with local return c...


Near line 69: Possible spelling mistake found.
Context: ... for your purchase. Try them today!

[Droix Discord](https://go.droix.co.uk/discord...


Near line 81: Possible spelling mistake found.
Context: ...57d-3a68-4d8d-b62e-9129b577d022)

QuickTools

On the fly adjustment of TDP (global...


Near line 83: Possible spelling mistake found.
Context: ...ys and motion control profile settings. Summonable with a user defined button combination ...


Near line 83: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...rol profile settings. Summonable with a user defined button combination (including certaind ...


Near line 83: Possible spelling mistake found.
Context: ...r defined button combination (including certaind supported devices mapped special keys)....


Near line 85: Possible spelling mistake found.
Context: .../889a1a1c-4775-4261-a173-c275eb4071ad)
Quicktools power control with Into the Breach.

...


Near line 88: Possible spelling mistake found.
Context: .../ec35272e-4c9e-4386-9b0f-3b4e3aa0cf6d)
Quicktools profile settings with Borderlands Pre-S...


Near line 93: Possible spelling mistake found.
Context: ...e virtual touchpad is used to mimic the DualShock 4 physical touchpad and grants maximum ...


Near line 93: Possible spelling mistake found.
Context: ...and games that make specific use of the Steampad touchpads.

![Touchpad](https://thumb...


Near line 97: Unpaired symbol: ‘]’ seems to be missing
Context: ...rtual Touchpad input demonstration with [PS Remote Play](https://remoteplay.dl.pl...


Near line 105: Possible spelling mistake found.
Context: ... positions. The following 3D models are availible.

  • OEM controller (Ayaneo Pro, Ayane...

Near line 106: Possible spelling mistake found.
Context: ...els are availible.

  • OEM controller (Ayaneo Pro, Ayaneo Next, OneXPlayer Mini)
    -...

Near line 106: Possible spelling mistake found.
Context: ...lible.

  • OEM controller (Ayaneo Pro, Ayaneo Next, OneXPlayer Mini)
  • Emulated co...

Near line 106: Possible spelling mistake found.
Context: ...EM controller (Ayaneo Pro, Ayaneo Next, OneXPlayer Mini)

  • Emulated controller (DualSho...

Near line 107: Possible spelling mistake found.
Context: ...XPlayer Mini)

  • Emulated controller (DualShock 4, Xbox 360)
  • Xbox One controller
    ...

Near line 109: Possible spelling mistake found.
Context: ...Xbox 360)

  • Xbox One controller
  • ZDO+ controller
  • Fisher-Price controlle...

Near line 111: Possible spelling mistake found.
Context: ...oller

  • Fisher-Price controller
  • Machenike HG510
  • 8BitDo Lite 2
  • Nintendo...

Near line 123: Consider adding a comma here.
Context: .../znHuywFz5M).

Questions & Support

Please respect that the GitHub issue tracker i...


Near line 127: Possible spelling mistake found.
Context: ...t, please consider making a donation to BenjaminLSR via [PayPal](https://www.paypal.com/pa...


Near line 129: Possible spelling mistake found.
Context: ...inLSR).

Handheld Companion relies on ViGEmBus driver and ViGEmClient libraries as ...


Near line 129: Possible spelling mistake found.
Context: ...mpanion relies on ViGEmBus driver and ViGEmClient libraries as well as HidHide kernel-...


Near line 129: Possible spelling mistake found.
Context: ... and ViGEmClient libraries as well as HidHide kernel-mode filter driver. Therefore, ...


Near line 129: This verb may not be in the correct form. Consider using a different form for this context.
Context: ...Therefore, we strongly encourage you in donating to Nefarius via [PayPal](https://payp...


Near line 129: Possible spelling mistake found.
Context: ...e strongly encourage you in donating to Nefarius via [PayPal](https://paypal.me/Nefariu...


Near line 133: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...
Run the install.exe as administrator and you'll be set!

Credits & Librarie...


Near line 136: Possible spelling mistake found.
Context: ...ll be set!

Credits & Libraries


Near line 136: Unpaired symbol: ‘]’ seems to be missing
Context: ...

Credits & Libraries


Near line 136: Possible spelling mistake found.
Context: ...

Credits & Libraries


Near line 137: Possible spelling mistake found.
Context: ...](https://github.com/ViGEm/ViGEmBus)


Near line 137: Unpaired symbol: ‘]’ seems to be missing
Context: ...ub.com/ViGEm/ViGEmBus)


Near line 137: Possible spelling mistake found.
Context: ...b.com/ViGEm/ViGEmBus)


Near line 138: Possible spelling mistake found.
Context: ...ttps://github.com/ViGEm/ViGEmClient)

  • SharpDX : [https://github.com/sharpdx/SharpDX](...

Near line 138: Unpaired symbol: ‘]’ seems to be missing
Context: ...hub.com/ViGEm/ViGEmClient)

  • SharpDX : [https://github.com/sharpdx/SharpDX](http...

Near line 139: Unpaired symbol: ‘]’ seems to be missing
Context: ...SharpDX)

  • Godot Engine Illustration : [Juan Linietsky, Fernando Miguel Calabró]...

Near line 139: Possible spelling mistake found.
Context: ...tion : [Juan Linietsky, Fernando Miguel Calabró](https://github.com/godotengine/tps-dem...


Near line 145: Possible spelling mistake found.
Context: ... is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a co...

Additional comments not posted (78)
HandheldCompanion/HandheldCompanion.json (1)

16-16: Ensure the environment variable %APP_BASE_DIRECTORY% is consistently set across all deployment environments to avoid runtime errors.

HandheldCompanion/Devices/AYANEO/AYANEODevice.cs (2)

Line range hint 7-7: The removal of prevPowerStatus and prevBatteryLevelPercentage initialization in the constructor might affect state management if these properties are used elsewhere in the code.

Verification successful

The search for prevPowerStatus and prevBatteryLevelPercentage in the codebase did not return any results, indicating that these properties are not used elsewhere. Therefore, their removal from the AYANEODevice constructor is unlikely to affect other functionalities or integrations within the project.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for usages of prevPowerStatus and prevBatteryLevelPercentage in the project
rg "prevPowerStatus|prevBatteryLevelPercentage" --type cs

Length of output: 59


Line range hint 7-7: The removal of PowerManager_PowerStatusChanged method could lead to unhandled power status changes if this method was previously handling critical functionality.

Verification successful

Based on the script execution and its results, there are no indications of the PowerManager_PowerStatusChanged method being used elsewhere in the codebase. This suggests that the removal of this method from the AYANEODevice class does not result in any orphaned event subscriptions. Therefore, the initial concern about unhandled power status changes due to the removal of this method appears to be unfounded.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Check if there are any event subscriptions to PowerManager_PowerStatusChanged that are now orphaned
ast-grep --lang csharp --pattern $'class $_ { $$$ PowerManager_PowerStatusChanged($_, $_) { $$$ } $$$ }'

Length of output: 162

HandheldCompanion/Processors/IntelProcessor.cs (2)

46-46: The use of lock on updateLock for synchronization in SetTDPLimit is a good practice for thread safety, but ensure that updateLock is properly initialized and not accessed from multiple threads before being set.

Verification successful

The initialization and usage of updateLock in IntelProcessor have been verified. The lock object updateLock is properly initialized in the parent class Processor and is correctly used for synchronization in IntelProcessor. This setup ensures thread safety and adheres to good practices in object-oriented programming.

  • File: Processor.cs
    • updateLock is initialized as a protected object, ensuring it is accessible in all subclasses including IntelProcessor.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Check for proper initialization and potential early access to updateLock across the project
rg "updateLock" --type cs

Length of output: 3473



Script:

#!/bin/bash
# Check the initialization of `updateLock` in IntelProcessor.cs
rg "updateLock" HandheldCompanion/Processors/IntelProcessor.cs

Length of output: 115



Script:

#!/bin/bash
# Check if `updateLock` is inherited from a parent class in IntelProcessor.cs
rg "class IntelProcessor" -A 10 HandheldCompanion/Processors/IntelProcessor.cs

Length of output: 371



Script:

#!/bin/bash
# Check the initialization of `updateLock` in the parent class Processor
rg "updateLock" HandheldCompanion/Processors/Processor.cs

Length of output: 98


71-71: Similar to SetTDPLimit, ensure that the lock in SetGPUClock is used correctly and that updateLock is robust against concurrent modifications.

Verification successful

The review of the SetGPUClock method implementations in both IntelProcessor.cs and AMDProcessor.cs shows that both classes use the updateLock object to synchronize access to shared resources, which is consistent with the concern raised in the original review comment about ensuring robustness against concurrent modifications.

In IntelProcessor.cs, the method locks on updateLock and then calls platform.set_gfx_clk to set the GPU clock, followed by a call to the base class method SetGPUClock. Similarly, in AMDProcessor.cs, the method also locks on updateLock and performs various operations depending on the processor family, including setting GPU clocks and calling the base class method.

Given this consistent use of updateLock across different processor implementations to ensure thread safety when setting the GPU clock, it appears that the concern in the review comment has been addressed properly in the codebase.

Conclusion:

  • The usage of updateLock in the SetGPUClock method across different parts of the project is consistent and adheres to good practices for handling concurrency.
  • The implementations in both IntelProcessor.cs and AMDProcessor.cs demonstrate proper synchronization which should protect against concurrent modifications as required.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Confirm that updateLock handling in SetGPUClock is consistent with other parts of the project
rg "SetGPUClock" --type cs

Length of output: 957



Script:

#!/bin/bash
# Extract the implementation of SetGPUClock in IntelProcessor.cs and AMDProcessor.cs
rg "SetGPUClock" --type cs -C 10

Length of output: 12075

HandheldCompanion/Processors/Processor.cs (2)

24-24: Initialization of updateLock here is crucial for thread safety in derived classes like IntelProcessor. Ensure that this lock is used consistently across all methods that require synchronization.


50-50: Logging an error when failing to retrieve the processor family is a good practice. However, ensure that this does not expose sensitive information in logs.

Consider masking or generalizing sensitive information before logging.

HandheldCompanion/Utils/CrossThreadLock.cs (1)

76-79: The addition of the IsEntered method in CrossThreadLock is useful for checking the lock status. Ensure that this method is used appropriately to prevent logic errors in lock management.

HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs (2)

48-65: The use of CrossThreadLock and the try-finally block in SetProfile method is a robust pattern for managing concurrency. Ensure that updateLock is released in all exit paths of the method.


93-94: Adding checks with IsEntered to prevent update loops in event handlers is a good defensive programming practice. Ensure that these checks are consistently applied in all similar scenarios.

Also applies to: 106-107, 121-122

HandheldCompanion/Devices/AYANEO/AYANEODeviceCEii.cs (2)

102-106: The method ECRamDirectWrite now takes an additional offset parameter. Ensure that this change is compatible with all calls to this method throughout the project.

Verification successful

The method ECRamDirectWrite has been updated in the AYANEODeviceCEii class to include an optional offset parameter. This change is localized to the AYANEODeviceCEii class and does not affect other classes, as they use a different method signature defined in IDevice.cs. All calls to ECRamDirectWrite within the AYANEODeviceCEii class are compatible with the new signature. Calls in other classes match the signature in IDevice.cs and are not affected by the changes in AYANEODeviceCEii.

  • AYANEODeviceCEii.cs: Uses the new signature with the optional offset parameter.
  • Other device classes (OneXPlayer, GPD, AOKZOE): Use the signature from IDevice.cs which does not include an offset parameter.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Check for all calls to ECRamDirectWrite to ensure they pass the correct parameters
rg "ECRamDirectWrite" --type cs

Length of output: 3172



Script:

#!/bin/bash
# Check the method signatures of ECRamDirectWrite in other classes to see if they include the optional offset parameter
rg "public .* ECRamDirectWrite\(.*\)" --type cs

Length of output: 309


108-116: The methods CEcControl_RgbHoldControl and CEcControl_RgbReleaseControl manage RGB functionality. Ensure that these methods are called in the correct sequence to avoid leaving the device in an inconsistent state.

HandheldCompanion/App.xaml.cs (1)

40-40: Setting the APP_BASE_DIRECTORY environment variable at the start of the application ensures that it is available throughout the application lifecycle. Confirm that this variable is used consistently wherever paths are constructed.

HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs (3)

91-93: Ensure that the initialization check is consistent across all methods to prevent operations on an uninitialized object.


159-164: Ensure that the TelemetryData is thread-safe when accessed or modified, as it is being updated in a timer callback which could be executed concurrently.


176-176: The change to make Stop method synchronous might affect how the method behaves in the context of the entire application, especially if it was previously designed to handle asynchronous operations.

HandheldCompanion/Processors/AMDProcessor.cs (2)

74-74: The use of lock for synchronization is appropriate here to ensure thread safety when updating TDP limits.


100-100: Similarly, the use of lock in SetGPUClock ensures that GPU clock settings are changed atomically, preventing race conditions.

HandheldCompanion/GraphicsProcessingUnit/GPU.cs (4)

30-30: Adjusting the telemetry and update intervals is a significant change that could affect the performance and responsiveness of the GPU monitoring functionality. Ensure that these values are tested under various conditions.


47-50: Introduction of static locking objects like updateLock, telemetryLock, and functionLock is crucial for ensuring that concurrent access to shared resources is managed correctly.


54-73: The Execute method's implementation using Task.Run and locks ensures that GPU operations are performed safely without blocking the main thread. However, the handling of exceptions could be improved by logging or handling specific types of exceptions rather than swallowing them.

Consider adding logging for exceptions to help with debugging issues.


100-104: Proper stopping of timers in the Stop method is good practice to clean up resources and prevent leaks or unintended execution after the object is supposed to be stopped.

HandheldCompanion/UI/UISounds.cs (1)

57-59: Adding a check for DeviceNumber before playing the sound is a good defensive programming practice to ensure that the sound device is properly initialized.

HandheldCompanion/Platforms/LibreHardwareMonitor.cs (3)

13-13: Introduction of updateLock to ensure thread safety when accessing and updating the computer object is a necessary change to prevent data corruption and ensure consistency.


63-68: Properly locking around the computer.Close() call is crucial to ensure that no other operations are being performed on the computer object during its closure.


75-93: The locking mechanism in UpdateTimer_Elapsed is well-implemented to prevent concurrent modifications during the hardware data update process. This is essential for maintaining data integrity.

README.md (1)

24-24: Updating the link for Switch to https://ryujinx.org/ reflects the current URLs and enhances the accuracy of the information provided to users.

HandheldCompanion/ADLX/ADLXBackend.cs (1)

6-6: Conversion to static class approved.

This change aligns with the PR's objective to enhance manageability and scalability by moving towards a more stateless design.

HandheldCompanion/Devices/AYANEO/AYANEODeviceCEc.cs (4)

79-86: Enhanced synchronization in Open method.

The addition of CEcControl_RgbHoldControl within a lock in the Open method enhances thread safety, aligning with the PR's objectives to improve synchronization mechanisms.


89-95: Enhanced synchronization in Close method.

The addition of CEcControl_RgbReleaseControl within a lock in the Close method enhances thread safety, aligning with the PR's objectives to improve synchronization mechanisms.


233-236: Addition of CEcControl_RgbHoldControl method.

This method supports the new synchronization strategy by managing RGB control during device operation.


238-241: Addition of CEcControl_RgbReleaseControl method.

This method supports the new synchronization strategy by managing RGB control during device shutdown.

HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs (3)

23-23: Introduction of CrossThreadLock for updateLock seems appropriate for managing thread safety in UI updates.


42-89: The use of TryEnter and Exit with updateLock within a try-finally block ensures that the lock is released properly, preventing potential deadlocks. However, consider adding more detailed comments explaining the purpose of locking in this context, especially for future maintainability.


111-112: The pattern used to prevent update loops by checking updateLock.IsEntered() before proceeding with updates is a good practice to avoid unnecessary re-entrancy. This pattern is consistently used across different event handlers, which is good for maintaining predictable behavior.

Also applies to: 124-125, 240-241, 253-254, 266-267, 279-280

HandheldCompanion/Misc/MotherboardInfo.cs (2)

42-52: The Collect method now includes a check to load data from cache before querying WMI, which is a good performance optimization. Ensure that the cache invalidation strategy is robust to handle potential stale data issues.


331-351: The loadCache method uses JSON serialization to load the cache from a file, which is a standard approach. Ensure that error handling is in place for file access and JSON parsing exceptions to enhance robustness.

HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs (3)

24-25: Introduction of a protected AdlxTelemetryData instance is a good practice for encapsulating telemetry data within the class. This allows safer access from multiple threads.


135-145: The logic to ensure mutual exclusivity between different GPU features (RSR, Integer Scaling, Image Sharpening) is well-implemented. This prevents conflicts between features that cannot be enabled simultaneously. Consider adding more comments to explain why these features are mutually exclusive, for better maintainability.

Also applies to: 161-168


187-197: The GetTelemetry method correctly checks initialization status and uses a thread-safe pattern to update telemetry data. This is crucial for avoiding race conditions in multi-threaded environments.

HandheldCompanion/Managers/GPUManager.cs (2)

16-28: The introduction of nullable event handlers (Initialized, Hooked, Unhooked) is a good practice in C# 8.0 and later for better handling of scenarios where no subscribers are present. This avoids potential NullReferenceException.


205-218: The Start method now checks if the manager is already initialized before proceeding, which prevents re-initialization errors. The method also handles event subscriptions and GPU initialization status effectively. Ensure that all potential exceptions during initialization are handled to prevent partial initialization states.

HandheldCompanion/Views/Pages/ControllerPage.xaml.cs (2)

34-34: Renaming ControllerManager.Working to ControllerManager.StatusChanged and changing the method signature to use ControllerManagerStatus enum improves code readability and type safety.


162-183: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [165-190]

The ControllerManager_Working method handles different controller statuses to update UI elements accordingly. Using enums (ControllerManagerStatus.Busy, Succeeded, Failed) instead of magic numbers enhances clarity. Consider adding error handling for the dialog display logic to manage exceptions that might occur during dialog interaction.

HandheldCompanion/Views/Pages/LayoutPage.xaml.cs (1)

30-30: Initialization of updateLock using object is appropriate for the lock statement.

HandheldCompanion/Views/Windows/MainWindow.xaml.cs (1)

1-824: Reviewed the MainWindow.xaml.cs file. Ensure proper resource management and thread safety, especially with multiple threads and external device interactions. Consider reviewing the use of threads and possibly refactoring to use Task for better manageability.

HandheldCompanion/Managers/ProfileManager.cs (2)

36-50: Consider initializing ProfilesPath directly in its declaration to simplify the static constructor.

Moving the initialization of ProfilesPath and the directory creation to the declaration of ProfilesPath could simplify the static constructor and make the initialization process more straightforward.


96-107: Validate the state change logic in the Stop method.

The Stop method sets IsInitialized to false and disposes of profileWatcher without checking if it was initialized or not. This could lead to potential null reference exceptions. Verify that profileWatcher is initialized before calling Dispose on it.

Verification successful

The verification process has confirmed that profileWatcher is initialized in the ProfileManager class before it is used in the Stop method. This initialization ensures that profileWatcher is not null when Dispose is called, under normal circumstances where Stop is called after the initialization. Therefore, the concern raised in the review comment about a potential null reference exception does not apply in the typical lifecycle of the ProfileManager.

  • profileWatcher is initialized in ProfileManager.cs before any event subscriptions and before it is used in the Stop method.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Check for any potential null reference issues in the usage of `profileWatcher` across the project.
rg --type cs "profileWatcher"

Length of output: 595

HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml (13)

22-22: Ensure proper data binding for IsEnabled property.

Please verify that the CanChangePreset property properly notifies changes since it's used in a OneWay binding which will not update the UI if the property does not raise PropertyChanged events.


61-61: Check the binding for CPUCoreEnabled.

Ensure that the CPUCoreEnabled property is correctly implemented in the ViewModel to support TwoWay binding, as this control is interactive and should reflect both UI and ViewModel state changes.


65-65: Visibility binding should be verified.

Confirm that the BooleanToVisibilityConverter is correctly converting the CPUCoreEnabled boolean value to visibility states. This is crucial for dynamic UI changes based on the ViewModel state.


131-131: Check the binding for TDPOverrideEnabled.

The TDPOverrideEnabled property must properly support TwoWay binding and notify changes to ensure the UI reflects the current state and updates the ViewModel upon user interaction.


135-135: Verify visibility conversion logic.

Ensure that the BooleanToVisibilityConverter is accurately converting the TDPOverrideEnabled property to control the visibility of related UI elements.


203-203: Confirm the binding setup for AutoTDPEnabled.

The AutoTDPEnabled property should be correctly implemented to support TwoWay binding, ensuring that changes in the UI toggle state are propagated back to the ViewModel.


207-207: Check the visibility binding for AutoTDPEnabled.

Verify that the BooleanToVisibilityConverter is correctly applied to the AutoTDPEnabled property to manage the visibility of associated controls based on this toggle's state.


272-272: Ensure correct implementation of EPPOverrideEnabled.

The EPPOverrideEnabled property needs to support TwoWay binding and should properly notify changes to keep the UI and ViewModel in sync, especially since this is a user-interactive control.


276-276: Verify the visibility conversion for EPPOverrideEnabled.

Confirm that the visibility of UI elements tied to the EPPOverrideEnabled property is correctly managed by the BooleanToVisibilityConverter, reflecting the appropriate state based on this property.


352-352: Check the binding for CPUOverrideEnabled.

Ensure that the CPUOverrideEnabled property is implemented to support TwoWay binding and notifies changes, as it controls interactive UI elements that should reflect the current configuration state.


356-356: Verify visibility management for CPUOverrideEnabled.

It's important to verify that the BooleanToVisibilityConverter is correctly applied to the CPUOverrideEnabled property to control the visibility of related UI elements based on this toggle's state.


422-422: Ensure proper binding for GPUOverrideEnabled.

The GPUOverrideEnabled property must support TwoWay binding and should notify changes to ensure that the UI correctly reflects the current state and updates the ViewModel when the user interacts with the toggle.


426-426: Check the visibility conversion for GPUOverrideEnabled.

Verify that the BooleanToVisibilityConverter is correctly converting the GPUOverrideEnabled property to visibility states, which is essential for showing or hiding related UI elements based on this toggle's state.

HandheldCompanion/Views/Pages/ControllerPage.xaml (2)

166-166: Verify the visibility setting for VirtualDevices.

Ensure that the Visibility property of VirtualDevices is correctly managed in the ViewModel to reflect the intended UI state, as it's set to Collapsed by default.


226-226: Check the visibility setting for PhysicalDevices.

Confirm that the Visibility property of PhysicalDevices is appropriately controlled in the ViewModel, especially since it's crucial for showing or hiding this section based on specific conditions.

HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs (1)

Line range hint 54-54: Ensure that the removal of ProfileManager_Deleted does not leave any unhandled references or event subscriptions.

HandheldCompanion/Managers/ControllerManager.cs (2)

59-65: The introduction of ControllerManagerStatus enum enhances the readability and maintainability of the controller status management.


749-753: Ensure that the UpdateStatus method is called appropriately throughout the code to reflect the actual status of controller management.

Verification successful

The review of the UpdateStatus method usage in the ControllerManager class has been completed based on the provided script output. The method is correctly utilized with the new ControllerManagerStatus enum in various scenarios within the ControllerManager.cs file. The instances found in the script output show that the method is called with appropriate enum values (Failed, Busy, Succeeded), which aligns with the expected behavior for reflecting the actual status of controller management.

  • ControllerManager.cs: UpdateStatus(ControllerManagerStatus.Failed); (multiple occurrences)
  • ControllerManager.cs: UpdateStatus(ControllerManagerStatus.Busy);
  • ControllerManager.cs: UpdateStatus(ControllerManagerStatus.Succeeded);
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for all occurrences of `UpdateStatus` to verify correct usage.
rg --type cs "UpdateStatus"

Length of output: 3341

HandheldCompanion/IGCL/IGCLBackend.cs (6)

7-7: Converted IGCLBackend to a static class.

This change aligns with the PR's objective to enhance manageability and scalability by moving towards a more stateless design.


413-426: Updated function pointers to nullable types.

This is a good practice in .NET to allow for the checking of null before invocation, which can prevent potential runtime errors if the DLL functions are not properly loaded.


438-438: Added new status DLL_INITIALIZE_SUCCESS.

This status addition is useful for more granular control and better error handling during the initialization process.


444-497: Refactored static constructor to initialize function pointers based on DLL status.

The refactoring ensures that function pointers are only set if the DLL is successfully loaded, which is crucial for stability and reliability.


514-524: Modified Initialize method to check for DLL_INITIALIZE_SUCCESS before calling InitializeIgcl.

This modification adds an additional layer of safety by ensuring that initialization routines are only called if the DLL has been successfully loaded.


526-531: Updated Terminate method to check for DLL_INITIALIZE_SUCCESS before calling CloseIgcl.

Similar to the Initialize method, this change ensures that termination routines are only executed if the DLL was initialized successfully, preventing potential errors during the cleanup process.

HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs (5)

43-44: Introduce new locks for managing concurrency.

The introduction of profileLock and graphicLock is a positive change for managing concurrency in the application. Ensure that these locks are used consistently throughout the code to prevent race conditions.


143-146: Ensure UI updates are performed on the UI thread.

Using Application.Current.Dispatcher.Invoke to update UI elements ensures that changes are made on the UI thread, which is crucial for thread safety in WPF applications.


151-158: Optimize repeated calls to Application.Current.Dispatcher.Invoke.

Consider combining these UI updates into a single Dispatcher.Invoke call to reduce overhead and improve performance, especially since all these updates are related to GPU scaling settings.


726-727: Prevent update loops by checking lock status.

Checking the lock status before proceeding with updates is a good practice to avoid update loops. This pattern is used effectively here and should be consistently applied throughout the application.


1042-1043: Handle multiple locks to prevent update loops in graphics settings.

The use of both profileLock and graphicLock to prevent update loops when updating graphics settings is a prudent approach to ensure thread safety and data integrity.

HandheldCompanion/HandheldCompanion.csproj (1)

153-153: Added package reference to "Fastenshtein" version 1.0.0.8.

This addition aligns with the PR's objective to enhance string comparison functionalities. Ensure that the new dependency is compatible with other project components and that its license is compatible with your project's licensing.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • 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/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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.

Actionable comments posted: 12

Actionable comments outside the diff hunks (3)
README.md (3)

Line range hint 10-10: Correct the spelling of "available" in the description of motion control settings.

- Settings availible for racing, 1st and 3rd person gaming and emulator support.
+ Settings available for racing, 1st and 3rd person gaming and emulator support.

Line range hint 11-11: Correct the spelling of "information" in the description of Quicktools overlay.

- with easy access to various settings and informatio such as TDP, Auto TDP, GPU, Screen Hz, ...
+ with easy access to various settings and information such as TDP, Auto TDP, GPU, Screen Hz, ...

Line range hint 37-37: Correct the use of "it's" to "its" in the description of supported devices.

- AYA Neo Air and it's different versions
+ AYA Neo Air and its different versions

Comment on lines +515 to +662
UseHighDPIAwareness.IsOn = selectedProfile.HighDPIAware;
// Compatibility settings
UseFullscreenOptimizations.IsOn = selectedProfile.FullScreenOptimization;
UseHighDPIAwareness.IsOn = selectedProfile.HighDPIAware;

// Layout settings
Toggle_ControllerLayout.IsOn = selectedProfile.LayoutEnabled;
// Layout settings
Toggle_ControllerLayout.IsOn = selectedProfile.LayoutEnabled;

// power profile
PowerProfile powerProfile = PowerProfileManager.GetProfile(selectedProfile.PowerProfile);
powerProfile.Check(this);
// power profile
PowerProfile powerProfile = PowerProfileManager.GetProfile(selectedProfile.PowerProfile);
powerProfile.Check(this);

// display warnings
WarningContent.Text = EnumUtils.GetDescriptionFromEnumValue(selectedProfile.ErrorCode);
// display warnings
WarningContent.Text = EnumUtils.GetDescriptionFromEnumValue(selectedProfile.ErrorCode);

switch (selectedProfile.ErrorCode)
{
default:
case ProfileErrorCode.None:
WarningBorder.Visibility = Visibility.Collapsed;
cB_Whitelist.IsEnabled = true;
switch (selectedProfile.ErrorCode)
{
default:
case ProfileErrorCode.None:
WarningBorder.Visibility = Visibility.Collapsed;
cB_Whitelist.IsEnabled = true;

// wrapper
cB_Wrapper_Injection.IsEnabled = true;
cB_Wrapper_Redirection.IsEnabled = true;
break;
// wrapper
cB_Wrapper_Injection.IsEnabled = true;
cB_Wrapper_Redirection.IsEnabled = true;
break;

case ProfileErrorCode.Running: // application is running
case ProfileErrorCode.MissingExecutable: // profile has no executable
case ProfileErrorCode.MissingPath: // profile has no path
case ProfileErrorCode.Default: // profile is default
WarningBorder.Visibility = Visibility.Visible;
cB_Whitelist.IsEnabled = false;
cB_Wrapper.IsEnabled = false;

// wrapper
cB_Wrapper_Injection.IsEnabled = false;
cB_Wrapper_Redirection.IsEnabled = false;
break;
case ProfileErrorCode.Running: // application is running
case ProfileErrorCode.MissingExecutable: // profile has no executable
case ProfileErrorCode.MissingPath: // profile has no path
case ProfileErrorCode.Default: // profile is default
WarningBorder.Visibility = Visibility.Visible;
cB_Whitelist.IsEnabled = false;
cB_Wrapper.IsEnabled = false;

// wrapper
cB_Wrapper_Injection.IsEnabled = false;
cB_Wrapper_Redirection.IsEnabled = false;
break;

case ProfileErrorCode.MissingPermission:
WarningBorder.Visibility = Visibility.Visible;
cB_Whitelist.IsEnabled = true;
case ProfileErrorCode.MissingPermission:
WarningBorder.Visibility = Visibility.Visible;
cB_Whitelist.IsEnabled = true;

// wrapper
cB_Wrapper_Injection.IsEnabled = true;
cB_Wrapper_Redirection.IsEnabled = false;
break;
}
// wrapper
cB_Wrapper_Injection.IsEnabled = true;
cB_Wrapper_Redirection.IsEnabled = false;
break;
}

// update dropdown lists
cB_Profiles.Items.Refresh();
cb_SubProfilePicker.Items.Refresh();
});
// update dropdown lists
cB_Profiles.Items.Refresh();
cb_SubProfilePicker.Items.Refresh();
});
}
finally
{
profileLock.Exit();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Review complex method with multiple UI updates and locking.

This method is quite complex and performs multiple UI updates. It's crucial to ensure that the locking mechanism (profileLock) is correctly implemented to avoid deadlocks or unnecessary blocking. Additionally, consider refactoring this method to improve readability and maintainability.

Comment on lines +1323 to +1392
if (!isEnabled)
{
selectedProfile.RSREnabled = false;
selectedProfile.IntegerScalingEnabled = false;

RSRToggle.IsOn = false;
IntegerScalingToggle.IsOn = false;
RSRToggle.IsOn = false;
IntegerScalingToggle.IsOn = false;
}
}
}
break;
// RSR is incompatible with RIS and IS
case UpdateGraphicsSettingsSource.RadeonSuperResolution:
{
selectedProfile.RSREnabled = isEnabled;
if (isEnabled)
break;
// RSR is incompatible with RIS and IS
case UpdateGraphicsSettingsSource.RadeonSuperResolution:
{
selectedProfile.RISEnabled = false;
selectedProfile.IntegerScalingEnabled = false;

RISToggle.IsOn = false;
IntegerScalingToggle.IsOn = false;

// RSR does not support ScalingMode.Center
if (selectedProfile.ScalingMode == 2)
selectedProfile.RSREnabled = isEnabled;
if (isEnabled)
{
selectedProfile.ScalingMode = 1;
GPUScalingComboBox.SelectedIndex = 1;
selectedProfile.RISEnabled = false;
selectedProfile.IntegerScalingEnabled = false;

RISToggle.IsOn = false;
IntegerScalingToggle.IsOn = false;

// RSR does not support ScalingMode.Center
if (selectedProfile.ScalingMode == 2)
{
selectedProfile.ScalingMode = 1;
GPUScalingComboBox.SelectedIndex = 1;
}
}
}
}
break;
// Image Sharpening is incompatible with RSR
case UpdateGraphicsSettingsSource.RadeonImageSharpening:
{
selectedProfile.RISEnabled = isEnabled;
if (isEnabled)
break;
// Image Sharpening is incompatible with RSR
case UpdateGraphicsSettingsSource.RadeonImageSharpening:
{
selectedProfile.RSREnabled = false;
selectedProfile.RISEnabled = isEnabled;
if (isEnabled)
{
selectedProfile.RSREnabled = false;

RSRToggle.IsOn = false;
RSRToggle.IsOn = false;
}
}
}
break;
break;

// Integer Scaling is incompatible with RSR
case UpdateGraphicsSettingsSource.IntegerScaling:
{
selectedProfile.IntegerScalingEnabled = isEnabled;
if (isEnabled)
// Integer Scaling is incompatible with RSR
case UpdateGraphicsSettingsSource.IntegerScaling:
{
selectedProfile.RSREnabled = false;
selectedProfile.IntegerScalingEnabled = isEnabled;
if (isEnabled)
{
selectedProfile.RSREnabled = false;

RSRToggle.IsOn = false;
RSRToggle.IsOn = false;
}
}
}
break;
break;
}
}
finally
{
graphicLock.Exit();
Copy link
Contributor

Choose a reason for hiding this comment

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

Review method handling graphics settings updates with conditional logic.

This method handles various graphics settings and applies conditional logic based on the settings source. It's important to ensure that the logic is correct and that incompatible settings are appropriately managed. Consider simplifying this method to improve clarity and reduce the potential for errors.

Comment on lines +54 to +94
public static void Start()
{
// monitor profile files
profileWatcher = new FileSystemWatcher
{
Path = ProfilesPath,
EnableRaisingEvents = true,
IncludeSubdirectories = true,
Filter = "*.json",
NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size
};
profileWatcher.Created += ProfileCreated;
profileWatcher.Deleted += ProfileDeleted;

// process existing profiles
string[] fileEntries = Directory.GetFiles(ProfilesPath, "*.json", SearchOption.AllDirectories);
foreach (string fileName in fileEntries)
ProcessProfile(fileName, false);

// check for default profile
if (!HasDefault())
{
Layout deviceLayout = IDevice.GetCurrent().DefaultLayout.Clone() as Layout;
Profile defaultProfile = new()
{
Name = DefaultName,
Default = true,
Enabled = false,
Layout = deviceLayout,
LayoutTitle = LayoutTemplate.DefaultLayout.Name,
LayoutEnabled = true
};

UpdateOrCreateProfile(defaultProfile, UpdateSource.Creation);
}

IsInitialized = true;
Initialized?.Invoke();

LogManager.LogInformation("{0} has started", "ProfileManager");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure proper error handling in the Start method.

The Start method lacks error handling for operations that could potentially fail, such as file operations and event subscriptions. Consider adding try-catch blocks around these operations to handle possible exceptions and ensure the application remains stable.

Comment on lines +127 to +151
public static Profile GetProfileFromPath(string path, bool ignoreStatus)
{
// check if favorite sub profile exists for path
Profile profile = subProfiles.FirstOrDefault(pr => pr.Path == path && pr.IsFavoriteSubProfile);

// get main profile from path instead
if (profile is null)
profile = profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase));

if (profile is null)
{
// otherwise, get profile from executable
string fileName = Path.GetFileName(path);
profile = profiles.Values.FirstOrDefault(a => a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));

if (profile is null)
return GetDefault();
}

// ignore profile status (enabled/disabled)
if (ignoreStatus)
return profile;

return profile.Enabled ? profile : GetDefault();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize the retrieval of profiles in GetProfileFromPath.

The method GetProfileFromPath performs multiple searches on the profiles collection, which could be optimized. Consider consolidating the conditions into a single LINQ query to reduce complexity and improve performance.

- Profile profile = subProfiles.FirstOrDefault(pr => pr.Path == path && pr.IsFavoriteSubProfile);
- if (profile is null)
-     profile = profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase));
- if (profile is null)
-     profile = profiles.Values.FirstOrDefault(a => a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
+ Profile profile = subProfiles.FirstOrDefault(pr => pr.Path == path && pr.IsFavoriteSubProfile)
+     ?? profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase) || a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));

Committable 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
public static Profile GetProfileFromPath(string path, bool ignoreStatus)
{
// check if favorite sub profile exists for path
Profile profile = subProfiles.FirstOrDefault(pr => pr.Path == path && pr.IsFavoriteSubProfile);
// get main profile from path instead
if (profile is null)
profile = profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase));
if (profile is null)
{
// otherwise, get profile from executable
string fileName = Path.GetFileName(path);
profile = profiles.Values.FirstOrDefault(a => a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
if (profile is null)
return GetDefault();
}
// ignore profile status (enabled/disabled)
if (ignoreStatus)
return profile;
return profile.Enabled ? profile : GetDefault();
}
public static Profile GetProfileFromPath(string path, bool ignoreStatus)
{
// check if favorite sub profile exists for path
string fileName = Path.GetFileName(path);
Profile profile = subProfiles.FirstOrDefault(pr => pr.Path == path && pr.IsFavoriteSubProfile)
?? profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase) || a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
if (profile is null)
return GetDefault();
// ignore profile status (enabled/disabled)
if (ignoreStatus)
return profile;
return profile.Enabled ? profile : GetDefault();
}

Comment on lines +207 to +240
public static void CycleSubProfiles(bool previous = false)
{
if (currentProfile == null)
return;
// called using previousSubProfile/nextSubProfile hotkeys
List<Profile> subProfilesList = new();
subProfilesList.Add(GetProfileForSubProfile(currentProfile)); // adds main profile as sub profile
subProfilesList.AddRange(GetSubProfilesFromPath(currentProfile.Path, false).ToList()); // adds all sub profiles

// if profile does not have sub profiles -> do nothing
if (subProfilesList.Count <= 1)
return;

// get index of currently applied profile
int currentIndex = subProfilesList.IndexOf(currentProfile);
int newIndex = currentIndex;

// previous? decrement, next? increment
if (previous)
newIndex -= 1;
else
newIndex += 1;

// ensure index is within list bounds, wrap if needed
newIndex = (newIndex + subProfilesList.Count) % subProfilesList.Count;

// if for whatever reason index is out of bound -> return
if (newIndex < 0 || newIndex >= subProfilesList.Count)
return;

// apply profile
Profile profileToApply = subProfilesList[newIndex];
UpdateOrCreateProfile(profileToApply);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Review the logic for cycling subprofiles in CycleSubProfiles.

The method CycleSubProfiles contains complex logic for cycling through profiles, which might be error-prone. Specifically, the method modifies the index without checking if the list is empty, which could lead to an ArgumentOutOfRangeException. Add checks to ensure the list is not empty before modifying the index.

Comment on lines +274 to +276
lock (updateLock)
{
// UI thread (async)
// UI thread
Copy link
Contributor

Choose a reason for hiding this comment

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

Tip

Codebase Verification

The review comment suggested ensuring that updateLock is used consistently and safely throughout the LayoutPage.xaml.cs class. However, the script output indicates that updateLock is declared and used in only one lock statement. This raises a concern about whether updateLock is sufficiently utilized for synchronization purposes across the class, especially if there are other operations that require thread safety.

  • The declaration of updateLock and its use in a lock statement are the only occurrences found.
  • No further usage of updateLock in other parts of the class was detected, which might suggest insufficient synchronization if other parts of the class are also accessing shared resources.

Given this, it seems appropriate to flag this for further review or adjustments by the development team.

Analysis chain

Switching from ScopedLock to lock simplifies synchronization. Ensure updateLock is used consistently and safely throughout the class.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for all occurrences of `updateLock` to ensure it's used safely and consistently.
rg --type cs 'updateLock' -- 'HandheldCompanion/Views/Pages/LayoutPage.xaml.cs'

Length of output: 145

Comment on lines +14 to +15
protected new ctl_telemetry_data TelemetryData = new();

Copy link
Contributor

Choose a reason for hiding this comment

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

Initialization of TelemetryData should be moved inside the constructor to ensure it's properly initialized with the rest of the object's state.

- protected new ctl_telemetry_data TelemetryData = new();
+ protected new ctl_telemetry_data TelemetryData;

Committable 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
protected new ctl_telemetry_data TelemetryData = new();
protected new ctl_telemetry_data TelemetryData;

Comment on lines +113 to +122
private ctl_telemetry_data GetTelemetry()
{
if (!IsInitialized)
return TelemetryData;

return Execute(() =>
{
return IGCLBackend.GetTelemetry(deviceIdx);
}, TelemetryData);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The method GetTelemetry should handle the case where TelemetryData is not initialized before use. Consider initializing TelemetryData in the constructor.

+ if (TelemetryData == null) TelemetryData = new ctl_telemetry_data();

Committable 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
private ctl_telemetry_data GetTelemetry()
{
if (!IsInitialized)
return TelemetryData;
return Execute(() =>
{
return IGCLBackend.GetTelemetry(deviceIdx);
}, TelemetryData);
}
private ctl_telemetry_data GetTelemetry()
{
if (TelemetryData == null) TelemetryData = new ctl_telemetry_data();
if (!IsInitialized)
return TelemetryData;
return Execute(() =>
{
return IGCLBackend.GetTelemetry(deviceIdx);
}, TelemetryData);
}

Comment on lines +49 to +83
SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
SliderSensitivityY.Value = ProfilesPage.selectedProfile.MotionSensivityY;
tb_ProfileAimingDownSightsMultiplier.Value = ProfilesPage.selectedProfile.AimingSightsMultiplier;
Toggle_FlickStick.IsOn = ProfilesPage.selectedProfile.FlickstickEnabled;
tb_ProfileFlickDuration.Value = ProfilesPage.selectedProfile.FlickstickDuration * 1000;
tb_ProfileStickSensitivity.Value = ProfilesPage.selectedProfile.FlickstickSensivity;

// todo: improve me ?
ProfilesPageHotkey.inputsChord.State = ProfilesPage.selectedProfile.AimingSightsTrigger.Clone() as ButtonState;
ProfilesPageHotkey.DrawInput();

// temp
StackCurve.Children.Clear();
foreach (KeyValuePair<double, double> elem in ProfilesPage.selectedProfile.MotionSensivityArray)
{
Tag = elem.Key,
Width = 8,
MaxHeight = StackCurve.Height,
Height = height,
VerticalAlignment = VerticalAlignment.Bottom,
Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"],
BorderThickness = new Thickness(0),
BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"],
IsEnabled = false // prevent the control from being clickable
};

StackCurve.Children.Add(thumb);
}
});
// skip first item ?
if (elem.Key == 0)
continue;

double height = elem.Value * StackCurve.Height;
Thumb thumb = new Thumb
{
Tag = elem.Key,
Width = 8,
MaxHeight = StackCurve.Height,
Height = height,
VerticalAlignment = VerticalAlignment.Bottom,
Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"],
BorderThickness = new Thickness(0),
BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"],
IsEnabled = false // prevent the control from being clickable
};

StackCurve.Children.Add(thumb);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The UI update logic inside the Dispatcher.Invoke call is extensive. While the logic appears correct, it's quite dense and could benefit from being broken down into smaller, more manageable methods. This would improve readability and maintainability.

+ private void UpdateUIWithProfileSettings()
+ {
+     SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
+     // Add other UI updates here...
+ }
+
- Application.Current.Dispatcher.Invoke(() =>
- {
-     SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
-     // Other UI updates...
- });
+ Application.Current.Dispatcher.Invoke(UpdateUIWithProfileSettings);

Committable 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
SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
SliderSensitivityY.Value = ProfilesPage.selectedProfile.MotionSensivityY;
tb_ProfileAimingDownSightsMultiplier.Value = ProfilesPage.selectedProfile.AimingSightsMultiplier;
Toggle_FlickStick.IsOn = ProfilesPage.selectedProfile.FlickstickEnabled;
tb_ProfileFlickDuration.Value = ProfilesPage.selectedProfile.FlickstickDuration * 1000;
tb_ProfileStickSensitivity.Value = ProfilesPage.selectedProfile.FlickstickSensivity;
// todo: improve me ?
ProfilesPageHotkey.inputsChord.State = ProfilesPage.selectedProfile.AimingSightsTrigger.Clone() as ButtonState;
ProfilesPageHotkey.DrawInput();
// temp
StackCurve.Children.Clear();
foreach (KeyValuePair<double, double> elem in ProfilesPage.selectedProfile.MotionSensivityArray)
{
Tag = elem.Key,
Width = 8,
MaxHeight = StackCurve.Height,
Height = height,
VerticalAlignment = VerticalAlignment.Bottom,
Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"],
BorderThickness = new Thickness(0),
BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"],
IsEnabled = false // prevent the control from being clickable
};
StackCurve.Children.Add(thumb);
}
});
// skip first item ?
if (elem.Key == 0)
continue;
double height = elem.Value * StackCurve.Height;
Thumb thumb = new Thumb
{
Tag = elem.Key,
Width = 8,
MaxHeight = StackCurve.Height,
Height = height,
VerticalAlignment = VerticalAlignment.Bottom,
Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"],
BorderThickness = new Thickness(0),
BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"],
IsEnabled = false // prevent the control from being clickable
};
StackCurve.Children.Add(thumb);
}
SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
SliderSensitivityY.Value = ProfilesPage.selectedProfile.MotionSensivityY;
tb_ProfileAimingDownSightsMultiplier.Value = ProfilesPage.selectedProfile.AimingSightsMultiplier;
Toggle_FlickStick.IsOn = ProfilesPage.selectedProfile.FlickstickEnabled;
tb_ProfileFlickDuration.Value = ProfilesPage.selectedProfile.FlickstickDuration * 1000;
tb_ProfileStickSensitivity.Value = ProfilesPage.selectedProfile.FlickstickSensivity;
// todo: improve me ?
ProfilesPageHotkey.inputsChord.State = ProfilesPage.selectedProfile.AimingSightsTrigger.Clone() as ButtonState;
ProfilesPageHotkey.DrawInput();
// temp
StackCurve.Children.Clear();
foreach (KeyValuePair<double, double> elem in ProfilesPage.selectedProfile.MotionSensivityArray)
{
// skip first item ?
if (elem.Key == 0)
continue;
double height = elem.Value * StackCurve.Height;
Thumb thumb = new Thumb
{
Tag = elem.Key,
Width = 8,
MaxHeight = StackCurve.Height,
Height = height,
VerticalAlignment = VerticalAlignment.Bottom,
Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"],
BorderThickness = new Thickness(0),
BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"],
IsEnabled = false // prevent the control from being clickable
};
StackCurve.Children.Add(thumb);
}
private void UpdateUIWithProfileSettings()
{
SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX;
// Add other UI updates here...
}
Application.Current.Dispatcher.Invoke(UpdateUIWithProfileSettings);

Comment on lines +260 to +283
private static object queryCacheValue(ManagementObjectCollection collection, string query, [CallerArgumentExpression("collection")] string collectionName = "")
{
get
if (!cache.ContainsKey($"{collectionName}-{query}"))
{
if (!string.IsNullOrEmpty(_Manufacturer))
return _Manufacturer;

foreach (ManagementObject queryObj in baseboardCollection)
if (collection is not null)
{
var query = queryObj["Manufacturer"];
if (query is not null)
foreach (ManagementObject queryObj in collection)
{
_Manufacturer = query.ToString();
break;
object queryResult = queryObj[query];
if (queryResult is not null)
{
cache.Add($"{collectionName}-{query}", queryResult);
writeCache();
break;
}
}
}

return _Manufacturer;
}
}

public static string Model
{
get
{
foreach (ManagementObject queryObj in baseboardCollection)
{
var query = queryObj["Model"];
if (query is not null)
return query.ToString();
}

return string.Empty;
}
if (cache.TryGetValue($"{collectionName}-{query}", out var result))
return result;

return string.Empty;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The queryCacheValue method efficiently handles caching by checking the cache before querying WMI and caching the results. This reduces redundant WMI queries and improves performance. However, consider handling exceptions that may occur during WMI querying to prevent application crashes.

+ try
+ {
+     foreach (ManagementObject queryObj in collection)
+     {
+         object queryResult = queryObj[query];
+         if (queryResult is not null)
+         {
+             cache.Add($"{collectionName}-{query}", queryResult);
+             writeCache();
+             break;
+         }
+     }
+ }
+ catch (Exception ex)
+ {
+     // Log or handle the exception
+ }

Committable 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
private static object queryCacheValue(ManagementObjectCollection collection, string query, [CallerArgumentExpression("collection")] string collectionName = "")
{
get
if (!cache.ContainsKey($"{collectionName}-{query}"))
{
if (!string.IsNullOrEmpty(_Manufacturer))
return _Manufacturer;
foreach (ManagementObject queryObj in baseboardCollection)
if (collection is not null)
{
var query = queryObj["Manufacturer"];
if (query is not null)
foreach (ManagementObject queryObj in collection)
{
_Manufacturer = query.ToString();
break;
object queryResult = queryObj[query];
if (queryResult is not null)
{
cache.Add($"{collectionName}-{query}", queryResult);
writeCache();
break;
}
}
}
return _Manufacturer;
}
}
public static string Model
{
get
{
foreach (ManagementObject queryObj in baseboardCollection)
{
var query = queryObj["Model"];
if (query is not null)
return query.ToString();
}
return string.Empty;
}
if (cache.TryGetValue($"{collectionName}-{query}", out var result))
return result;
return string.Empty;
}
private static object queryCacheValue(ManagementObjectCollection collection, string query, [CallerArgumentExpression("collection")] string collectionName = "")
{
if (!cache.ContainsKey($"{collectionName}-{query}"))
{
if (collection is not null)
{
try
{
foreach (ManagementObject queryObj in collection)
{
object queryResult = queryObj[query];
if (queryResult is not null)
{
cache.Add($"{collectionName}-{query}", queryResult);
writeCache();
break;
}
}
}
catch (Exception ex)
{
// Log or handle the exception
}
}
}
if (cache.TryGetValue($"{collectionName}-{query}", out var result))
return result;
return string.Empty;
}

@CasperH2O CasperH2O deleted the EAtoPublicFiles branch April 25, 2024 09:38
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.

1 participant