diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8519726
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,14 @@
+* text=auto
+*.c text
+*.h text
+*.cpp text
+*.hpp text
+*.cc text
+*.txt text
+*.sln text eol=crlf
+*.csproj text eol=crlf
+*.csproj.user text eol=crlf
+*.editorconfig text eol=crlf
+app.config text eol=crlf
+packages.config text eol=crlf
+*.cs text
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a8001cd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,91 @@
+/out/
+/build/
+/cmake-build-debug/
+/cmake-build-release/
+/install/
+
+
+
+
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+.idea/vcs.xml
+.idea/modules.xml
+.idea/misc.xml
+.idea/*.iml
+.idea/.name
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..86125c9
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "rainmeter-plugin-sdk"]
+ path = rainmeter-plugin-sdk
+ url = https://github.com/rainmeter/rainmeter-plugin-sdk
+ branch = master
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
new file mode 100644
index 0000000..d5f97c9
--- /dev/null
+++ b/.idea/cmake.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/Emerson_Pinter.xml b/.idea/copyright/Emerson_Pinter.xml
new file mode 100644
index 0000000..b97087e
--- /dev/null
+++ b/.idea/copyright/Emerson_Pinter.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..672e8fd
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..95a53b2
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.21)
+
+set(PROJECT_NAME "rainmeter_lhws")
+set(PROJECT_VERSION 0.2.0)
+set(PROJECT_DESCRIPTION "LibreHardwareService plugin for Rainmeter")
+set(PROJECT_HOMEPAGE_URL "https://github.com/epinter/rainmeter-lhws")
+enable_language(CXX)
+set(CMAKE_CXX_STANDARD 17)
+
+set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+
+project (${PROJECT_NAME} VERSION ${PROJECT_VERSION})
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+add_subdirectory(src)
+
+if (PROJECT_IS_TOP_LEVEL)
+ message("Configuring CMAKE_INSTALL_PREFIX to ${CMAKE_CURRENT_SOURCE_DIR}/install")
+ set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "Installation directory" FORCE)
+ message("CMAKE_INSTALL_PREFIX = ${CMAKE_CURRENT_SOURCE_DIR}/install")
+
+ include(GNUInstallDirs)
+
+ install(TARGETS ${MAINTARGET}
+ EXPORT ${PROJECT_NAME}Targets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ )
+endif()
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a612ad9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..abad8b6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,168 @@
+# LibreHardwareService plugin for Rainmeter
+
+[](https://github.com/epinter/rainmeter-lhws/releases/latest)
+[](https://github.com/epinter/rainmeter-lhws/releases/latest)
+[](https://github.com/epinter/rainmeter-lhws/releases/latest)
+[](https://github.com/epinter/rainmeter-lhws/blob/main/LICENSE)
+
+## Introduction
+
+This plugin collects hardware sensors from [LibreHardwareService](https://github.com/epinter/LibreHardwareService), allowing skins to access all hardware sensors exposed by [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor), SMART status, and RTSS framerate and frametime.
+
+## Requirements
+
+[LibreHardwareService](https://github.com/epinter/LibreHardwareService) and [Visual C++ Redistributable 2022](https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist).
+
+## Usage
+
+Copy LibreHardwareService.dll to %APPDATA%/Rainmeter/Plugins and setup measures like the examples below.
+
+## Building
+
+This library depends on [LibHardwareService](https://github.com/epinter/lhwservice) and Boost. The dependencies are found using CMake find_package, so [vcpkg](https://vcpkg.io) is strongly recommended for Boost. Scripts for building can be found in cmake directory.
+Visual Studio 2022 and CMake are required.
+
+## Measure Examples
+
+Hardware names are the ones returned by LibreHardwareMonitor or ShowSensors.exe from LibreHardwareService.
+
+Motherboard:
+```
+[MBTemp]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#mbSubhardwareName#
+SensorName=Temperature #2
+SensorType=Temperature
+```
+
+CPU usage:
+```
+[CPUTotalLoad]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#cpuHardwareName#
+SensorName=CPU Total
+SensorType=Load
+MinValue=0
+MaxValue=100
+```
+
+RTSS fps:
+```
+[Fps]
+Measure=Plugin
+Plugin=LibreHardwareService
+SensorName=RTSS
+SensorType=Framerate
+```
+
+RTSS Frametime (milliseconds):
+```
+[Frametime]
+Measure=Plugin
+Plugin=LibreHardwareService
+SensorName=RTSS
+SensorType=frametime
+```
+
+Storage activity:
+```
+[Drive1Load]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive1HwName#
+SensorName=Total Activity
+SensorType=Load
+```
+
+Storage Temperature:
+```
+[Drive1Temp]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive1HwName#
+SensorName=Temperature
+SensorType=Temperature
+```
+Storage Temperature:
+```
+[Drive2Temp]
+Measure=Plugin
+Plugin=LibreHardwareService
+SensorIdentifier=/hdd/1/temperature/0
+```
+
+SMART SATA status, returns one of the following strings:
+- "Pre-fail" if the smart Prefail bit is set;
+- "End-of-Life" if the smart Advisory bit is set;
+- "Warning" if smart attributes 0x05, 0xC5, or 0xC6 have raw values above 10
+- "OK" if none of above is detected
+```
+[SmartDrive1]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive1HwName#
+HardwareType=Storage
+HwStatusType=storage_smart_ata
+HwStatusItem=Status
+```
+
+SMART NVME:
+```
+[SmartDrive2availableSpare]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive2HwName#
+HardwareType=Storage
+HwStatusType=storage_smart_nvme
+HwStatusItem=availableSpare
+```
+
+SMART NVME:
+```
+[SmartDrive2criticalWarning]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive2HwName#
+HardwareType=Storage
+HwStatusType=storage_smart_nvme
+HwStatusItem=criticalWarning
+```
+
+SMART NVME:
+```
+[SmartDrive2percentageUsed]
+Measure=Plugin
+Plugin=LibreHardwareService
+HardwareName=#drive2HwName#
+HardwareType=Storage
+HwStatusType=storage_smart_nvme
+HwStatusItem=percentageUsed
+```
+
+NVME attributes available:
+```
+availableSpare
+availableSpareThreshold
+controllerBusyTime
+criticalCompositeTemperatureTime
+criticalWarning
+dataUnitRead
+dataUnitWritten
+errorInfoLogEntryCount
+hostReadCommands
+hostWriteCommands
+mediaErrors
+percentageUsed
+powerCycle
+powerOnHours
+temperature
+unsafeShutdowns
+warningCompositeTemperatureTime
+```
+
+## License
+
+This software is licensed under the terms of [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/).
+
diff --git a/cmake/build-release-x64.ps1 b/cmake/build-release-x64.ps1
new file mode 100644
index 0000000..abf6df5
--- /dev/null
+++ b/cmake/build-release-x64.ps1
@@ -0,0 +1,23 @@
+$ErrorActionPreference = "Stop"
+
+$projectRoot = Resolve-Path "$PSScriptRoot/.."
+
+
+#if lhwservice is not built and installed at the parent directory of this project, build will fail
+$lhwservicePath = Resolve-Path "../../lhwservice/install"
+
+if(!(Test-Path "$projectRoot/out/release-msvc-x64")) {
+ mkdir "$projectRoot/out/release-msvc-x64"
+}
+
+$cwd = Get-Location
+Set-Location "$projectRoot/out/release-msvc-x64"
+
+write-host "Starting cmake"
+write-host $pwd
+
+cmake "$projectRoot" -G "Visual Studio 17 2022" -T v143,host=x64 -A x64 -DCMAKE_TOOLCHAIN_FILE="$($Env:VCPKG_ROOT)/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static-md -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH="$lhwservicePath" -DCMAKE_BUILD_TYPE=Release
+cmake --build . --config Release
+cmake --build . --config Release --target package
+
+Set-Location $cwd
diff --git a/cmake/build-release-x86.ps1 b/cmake/build-release-x86.ps1
new file mode 100644
index 0000000..c99e7bd
--- /dev/null
+++ b/cmake/build-release-x86.ps1
@@ -0,0 +1,23 @@
+$ErrorActionPreference = "Stop"
+
+$projectRoot = Resolve-Path "$PSScriptRoot/.."
+
+
+#if lhwservice is not built and installed at the parent directory of this project, build will fail
+$lhwservicePath = Resolve-Path "../../lhwservice/install"
+
+if(!(Test-Path "$projectRoot/out/release-msvc-x86")) {
+ mkdir "$projectRoot/out/release-msvc-x86"
+}
+
+$cwd = Get-Location
+Set-Location "$projectRoot/out/release-msvc-x86"
+
+write-host "Starting cmake"
+write-host $pwd
+
+cmake "$projectRoot" -G "Visual Studio 17 2022" -T v143,host=x86 -A Win32 -DCMAKE_TOOLCHAIN_FILE="$($Env:VCPKG_ROOT)/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x86-windows-static-md -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH="$lhwservicePath" -DCMAKE_BUILD_TYPE=Release
+cmake --build . --config Release
+cmake --build . --config Release --target package
+
+Set-Location $cwd
diff --git a/external/include/README.md b/external/include/README.md
new file mode 100644
index 0000000..8bd96dc
--- /dev/null
+++ b/external/include/README.md
@@ -0,0 +1 @@
+The RTSS headers are licensed and copyrighted by their respective owners. Information can be found at https://www.guru3d.com/files-details/rtss-rivatuner-statistics-server-download.html.
diff --git a/external/include/RTSSHooksTypes.h b/external/include/RTSSHooksTypes.h
new file mode 100644
index 0000000..b08d16b
--- /dev/null
+++ b/external/include/RTSSHooksTypes.h
@@ -0,0 +1,23 @@
+#ifndef _RTSSHOOKSTYPES_H_INCLUDED_
+#define _RTSSHOOKSTYPES_H_INCLUDED_
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+typedef struct VIDEO_CAPTURE_PARAM
+{
+ DWORD dwVersion;
+ char szFilename[MAX_PATH];
+ DWORD dwFramerate;
+ DWORD dwFramesize;
+ DWORD dwFormat;
+ DWORD dwQuality;
+ DWORD dwThreads;
+ BOOL bCaptureOSD;
+ DWORD dwAudioCaptureFlags;
+ DWORD dwVideoCaptureFlagsEx;
+ DWORD dwAudioCaptureFlags2;
+ DWORD dwPrerecordSizeLimit;
+ DWORD dwPrerecordTimeLimit;
+} VIDEO_CAPTURE_PARAM, *LPVIDEO_CAPTURE_PARAM;
+//////////////////////////////////////////////////////////////////////
+#endif
+//////////////////////////////////////////////////////////////////////
diff --git a/external/include/RTSSSharedMemory.h b/external/include/RTSSSharedMemory.h
new file mode 100644
index 0000000..89729c4
--- /dev/null
+++ b/external/include/RTSSSharedMemory.h
@@ -0,0 +1,645 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// This header file defines statistics server's shared memory format
+//
+/////////////////////////////////////////////////////////////////////////////
+#ifndef _RTSS_SHARED_MEMORY_INCLUDED_
+#define _RTSS_SHARED_MEMORY_INCLUDED_
+/////////////////////////////////////////////////////////////////////////////
+#include "RTSSHooksTypes.h"
+/////////////////////////////////////////////////////////////////////////////
+// v1.0 memory structure
+typedef struct RTSS_SHARED_MEMORY_V_1_0
+{
+ DWORD dwSignature;
+ //signature allows applications to verify status of shared memory
+
+ //The signature can be set to:
+ //'RTSS' - statistics server's memory is initialized and contains
+ // valid data
+ //0xDEAD - statistics server's memory is marked for deallocation and
+ // no longer contain valid data
+ //otherwise the memory is not initialized
+ DWORD dwVersion;
+ //structure version ((major<<16) + minor)
+ //must be set to 0x00010000 for v1.0 structure
+ DWORD dwTime0;
+ //start time of framerate measurement period (in milliseconds)
+
+ //Take a note that this field must contain non-zero value to calculate
+ //framerate properly!
+ DWORD dwTime1;
+ //end time of framerate measurement period (in milliseconds)
+ DWORD dwFrames;
+ //amount of frames rendered during (dwTime1 - dwTime0) period
+
+ //to calculate framerate use the following formula:
+ //1000.0f * dwFrames / (dwTime1 - dwTime0)
+
+} RTSS_SHARED_MEMORY_V_1_0, *LPRTSS_SHARED_MEMORY_V_1_0;
+/////////////////////////////////////////////////////////////////////////////
+#define OSDFLAG_UPDATED 0x00000001
+ //use this flag to force the server to update OSD
+/////////////////////////////////////////////////////////////////////////////
+// v1.1 memory structure
+typedef struct RTSS_SHARED_MEMORY_V_1_1
+{
+ DWORD dwSignature;
+ //signature allows applications to verify status of shared memory
+
+ //The signature can be set to:
+ //'RTSS' - statistics server's memory is initialized and contains
+ // valid data
+ //0xDEAD - statistics server's memory is marked for deallocation and
+ // no longer contain valid data
+ //otherwise the memory is not initialized
+ DWORD dwVersion;
+ //structure version ((major<<16) + minor)
+ //must be set to 0x00010001 for v1.1 structure
+ DWORD dwTime0;
+ //start time of framerate measurement period (in milliseconds)
+
+ //Take a note that this field must contain non-zero value to calculate
+ //framerate properly!
+ DWORD dwTime1;
+ //end time of framerate measurement period (in milliseconds)
+ DWORD dwFrames;
+ //amount of frames rendered during (dwTime1 - dwTime0) period
+
+ //to calculate framerate use the following formula:
+ //1000.0f * dwFrames / (dwTime1 - dwTime0)
+
+ DWORD dwOSDFlags;
+ //bitmask, containing combination of OSDFLAG_... flags
+
+ //Note: set OSDFLAG_UPDATED flag as soon as you change any OSD related
+ //field
+ DWORD dwOSDX;
+ //OSD X-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the right side of the screen)
+ DWORD dwOSDY;
+ //OSD Y-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the bottom side of the screen)
+ DWORD dwOSDPixel;
+ //OSD pixel zooming ratio
+ DWORD dwOSDColor;
+ //OSD color in RGB format
+ char szOSD[256];
+ //OSD text
+ char szOSDOwner[32];
+ //OSD owner ID
+
+ //Use this field to capture OSD and prevent other applications from
+ //using OSD when it is already in use by your application.
+ //You should change this field only if it is empty (i.e. when OSD is
+ //not owned by any application) or if it is set to your own application's
+ //ID (i.e. when you own OSD)
+ //You shouldn't change any OSD related feilds until you own OSD
+
+} RTSS_SHARED_MEMORY_V_1_1, *LPRTSS_SHARED_MEMORY_V_1_1;
+/////////////////////////////////////////////////////////////////////////////
+// v1.2 memory structure
+typedef struct RTSS_SHARED_MEMORY_V_1_2
+{
+ DWORD dwSignature;
+ //signature allows applications to verify status of shared memory
+
+ //The signature can be set to:
+ //'RTSS' - statistics server's memory is initialized and contains
+ // valid data
+ //0xDEAD - statistics server's memory is marked for deallocation and
+ // no longer contain valid data
+ //otherwise the memory is not initialized
+ DWORD dwVersion;
+ //structure version ((major<<16) + minor)
+ //must be set to 0x00010002 for v1.2 structure
+ DWORD dwTime0;
+ //start time of framerate measurement period (in milliseconds)
+
+ //Take a note that this field must contain non-zero value to calculate
+ //framerate properly!
+ DWORD dwTime1;
+ //end time of framerate measurement period (in milliseconds)
+ DWORD dwFrames;
+ //amount of frames rendered during (dwTime1 - dwTime0) period
+
+ //to calculate framerate use the following formula:
+ //1000.0f * dwFrames / (dwTime1 - dwTime0)
+
+ DWORD dwOSDFlags;
+ //bitmask, containing combination of OSDFLAG_... flags
+
+ //Note: set OSDFLAG_UPDATED flag as soon as you change any OSD related
+ //field
+ DWORD dwOSDX;
+ //OSD X-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the right side of the screen)
+ DWORD dwOSDY;
+ //OSD Y-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the bottom side of the screen)
+ DWORD dwOSDPixel;
+ //OSD pixel zooming ratio
+ DWORD dwOSDColor;
+ //OSD color in RGB format
+ char szOSD[256];
+ //primary OSD slot text
+ char szOSDOwner[32];
+ //primary OSD slot owner ID
+
+ //Use this field to capture OSD slot and prevent other applications from
+ //using OSD when it is already in use by your application.
+ //You should change this field only if it is empty (i.e. when OSD slot is
+ //not owned by any application) or if it is set to your own application's
+ //ID (i.e. when you own OSD slot)
+ //You shouldn't change any OSD related feilds until you own OSD slot
+
+ char szOSD1[256];
+ //OSD slot 1 text
+ char szOSD1Owner[32];
+ //OSD slot 1 owner ID
+ char szOSD2[256];
+ //OSD slot 2 text
+ char szOSD2Owner[32];
+ //OSD slot 2 owner ID
+ char szOSD3[256];
+ //OSD slot 3 text
+ char szOSD3Owner[32];
+ //OSD slot 3 owner ID
+} RTSS_SHARED_MEMORY_V_1_2, *LPRTSS_SHARED_MEMORY_V_1_2;
+/////////////////////////////////////////////////////////////////////////////
+#define STATFLAG_RECORD 0x00000001
+/////////////////////////////////////////////////////////////////////////////
+// v1.3 memory structure
+typedef struct RTSS_SHARED_MEMORY_V_1_3
+{
+ DWORD dwSignature;
+ //signature allows applications to verify status of shared memory
+
+ //The signature can be set to:
+ //'RTSS' - statistics server's memory is initialized and contains
+ // valid data
+ //0xDEAD - statistics server's memory is marked for deallocation and
+ // no longer contain valid data
+ //otherwise the memory is not initialized
+ DWORD dwVersion;
+ //structure version ((major<<16) + minor)
+ //must be set to 0x00010003 for v1.3 structure
+ DWORD dwTime0;
+ //start time of framerate measurement period (in milliseconds)
+
+ //Take a note that this field must contain non-zero value to calculate
+ //framerate properly!
+ DWORD dwTime1;
+ //end time of framerate measurement period (in milliseconds)
+ DWORD dwFrames;
+ //amount of frames rendered during (dwTime1 - dwTime0) period
+
+ //to calculate framerate use the following formula:
+ //1000.0f * dwFrames / (dwTime1 - dwTime0)
+
+ DWORD dwOSDFlags;
+ //bitmask, containing combination of OSDFLAG_... flags
+
+ //Note: set OSDFLAG_UPDATED flag as soon as you change any OSD related
+ //field
+ DWORD dwOSDX;
+ //OSD X-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the right side of the screen)
+ DWORD dwOSDY;
+ //OSD Y-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the bottom side of the screen)
+ DWORD dwOSDPixel;
+ //OSD pixel zooming ratio
+ DWORD dwOSDColor;
+ //OSD color in RGB format
+ char szOSD[256];
+ //primary OSD slot text
+ char szOSDOwner[32];
+ //primary OSD slot owner ID
+
+ //Use this field to capture OSD slot and prevent other applications from
+ //using OSD when it is already in use by your application.
+ //You should change this field only if it is empty (i.e. when OSD slot is
+ //not owned by any application) or if it is set to your own application's
+ //ID (i.e. when you own OSD slot)
+ //You shouldn't change any OSD related feilds until you own OSD slot
+
+ char szOSD1[256];
+ //OSD slot 1 text
+ char szOSD1Owner[32];
+ //OSD slot 1 owner ID
+ char szOSD2[256];
+ //OSD slot 2 text
+ char szOSD2Owner[32];
+ //OSD slot 2 owner ID
+ char szOSD3[256];
+ //OSD slot 3 text
+ char szOSD3Owner[32];
+ //OSD slot 3 owner ID
+
+ DWORD dwStatFlags;
+ //bitmask containing combination of STATFLAG_... flags
+ DWORD dwStatTime0;
+ //statistics record period start time
+ DWORD dwStatTime1;
+ //statistics record period end time
+ DWORD dwStatFrames;
+ //total amount of frames rendered during statistics record period
+ DWORD dwStatCount;
+ //amount of min/avg/max measurements during statistics record period
+ DWORD dwStatFramerateMin;
+ //minimum instantaneous framerate measured during statistics record period
+ DWORD dwStatFramerateAvg;
+ //average instantaneous framerate measured during statistics record period
+ DWORD dwStatFramerateMax;
+ //maximum instantaneous framerate measured during statistics record period
+} RTSS_SHARED_MEMORY_V_1_3, *LPRTSS_SHARED_MEMORY_V_1_3;
+/////////////////////////////////////////////////////////////////////////////
+
+// WARNING! The following API usage flags are deprecated and valid in 2.9
+// and older shared memory layout only
+
+#define APPFLAG_DEPRECATED_DD 0x00000010
+#define APPFLAG_DEPRECATED_D3D8 0x00000100
+#define APPFLAG_DEPRECATED_D3D9 0x00001000
+#define APPFLAG_DEPRECATED_D3D9EX 0x00002000
+#define APPFLAG_DEPRECATED_OGL 0x00010000
+#define APPFLAG_DEPRECATED_D3D10 0x00100000
+#define APPFLAG_DEPRECATED_D3D11 0x01000000
+
+#define APPFLAG_DEPRECATED_API_USAGE_MASK (APPFLAG_DD | APPFLAG_D3D8 | APPFLAG_D3D9 | APPFLAG_D3D9EX | APPFLAG_OGL | APPFLAG_D3D10 | APPFLAG_D3D11)
+
+// The following API usage flags are valid in 2.10 and newer shared memory
+// layout only
+
+#define APPFLAG_OGL 0x00000001
+#define APPFLAG_DD 0x00000002
+#define APPFLAG_D3D8 0x00000003
+#define APPFLAG_D3D9 0x00000004
+#define APPFLAG_D3D9EX 0x00000005
+#define APPFLAG_D3D10 0x00000006
+#define APPFLAG_D3D11 0x00000007
+#define APPFLAG_D3D12 0x00000008
+#define APPFLAG_D3D12AFR 0x00000009
+#define APPFLAG_VULKAN 0x0000000A
+
+#define APPFLAG_API_USAGE_MASK 0x0000FFFF
+
+#define APPFLAG_ARCHITECTURE_X64 0x00010000
+#define APPFLAG_ARCHITECTURE_UWP 0x00020000
+
+#define APPFLAG_PROFILE_UPDATE_REQUESTED 0x10000000
+/////////////////////////////////////////////////////////////////////////////
+#define SCREENCAPTUREFLAG_REQUEST_CAPTURE 0x00000001
+#define SCREENCAPTUREFLAG_REQUEST_CAPTURE_OSD 0x00000010
+/////////////////////////////////////////////////////////////////////////////
+#define VIDEOCAPTUREFLAG_REQUEST_CAPTURE_START 0x00000001
+#define VIDEOCAPTUREFLAG_REQUEST_CAPTURE_PROGRESS 0x00000002
+#define VIDEOCAPTUREFLAG_REQUEST_CAPTURE_STOP 0x00000004
+#define VIDEOCAPTUREFLAG_REQUEST_CAPTURE_MASK 0x00000007
+#define VIDEOCAPTUREFLAG_REQUEST_CAPTURE_OSD 0x00000010
+
+#define VIDEOCAPTUREFLAG_INTERNAL_RESIZE 0x00010000
+/////////////////////////////////////////////////////////////////////////////
+#define PROCESS_PERF_COUNTER_ID_RAM_USAGE 0x00000001
+#define PROCESS_PERF_COUNTER_ID_D3DKMT_VRAM_USAGE_LOCAL 0x00000100
+#define PROCESS_PERF_COUNTER_ID_D3DKMT_VRAM_USAGE_SHARED 0x00000101
+/////////////////////////////////////////////////////////////////////////////
+
+// v2.0 memory structure
+typedef struct RTSS_SHARED_MEMORY
+{
+ DWORD dwSignature;
+ //signature allows applications to verify status of shared memory
+
+ //The signature can be set to:
+ //'RTSS' - statistics server's memory is initialized and contains
+ // valid data
+ //0xDEAD - statistics server's memory is marked for deallocation and
+ // no longer contain valid data
+ //otherwise the memory is not initialized
+ DWORD dwVersion;
+ //structure version ((major<<16) + minor)
+ //must be set to 0x0002xxxx for v2.x structure
+
+ DWORD dwAppEntrySize;
+ //size of RTSS_SHARED_MEMORY_OSD_ENTRY for compatibility with future versions
+ DWORD dwAppArrOffset;
+ //offset of arrOSD array for compatibility with future versions
+ DWORD dwAppArrSize;
+ //size of arrOSD array for compatibility with future versions
+
+ DWORD dwOSDEntrySize;
+ //size of RTSS_SHARED_MEMORY_APP_ENTRY for compatibility with future versions
+ DWORD dwOSDArrOffset;
+ //offset of arrApp array for compatibility with future versions
+ DWORD dwOSDArrSize;
+ //size of arrOSD array for compatibility with future versions
+
+ DWORD dwOSDFrame;
+ //Global OSD frame ID. Increment it to force the server to update OSD for all currently active 3D
+ //applications.
+
+ //next fields are valid for v2.14 and newer shared memory format only
+
+ LONG dwBusy;
+ //set bit 0 when you're writing to shared memory and reset it when done
+
+ //WARNING: do not forget to reset it, otherwise you'll completely lock OSD updates for all clients
+
+
+ //next fields are valid for v2.15 and newer shared memory format only
+
+ DWORD dwDesktopVideoCaptureFlags;
+ DWORD dwDesktopVideoCaptureStat[5];
+ //shared copy of desktop video capture flags and performance stats for 64-bit applications
+
+ //next fields are valid for v2.16 and newer shared memory format only
+
+ DWORD dwLastForegroundApp;
+ //last foreground application entry index
+ DWORD dwLastForegroundAppProcessID;
+ //last foreground application process ID
+
+ //next fields are valid for v2.18 and newer shared memory format only
+
+ DWORD dwProcessPerfCountersEntrySize;
+ //size of RTSS_SHARED_MEMORY_PROCESS_PERF_COUNTER_ENTRY for compatibility with future versions
+ DWORD dwProcessPerfCountersArrOffset;
+ //offset of arrPerfCounters array for compatibility with future versions (relative to application entry)
+
+ //next fields are valid for v2.19 and newer shared memory format only
+
+ LARGE_INTEGER qwLatencyMarkerSetTimestamp;
+ LARGE_INTEGER qwLatencyMarkerResetTimestamp;
+
+ //OSD slot descriptor structure
+
+ typedef struct RTSS_SHARED_MEMORY_OSD_ENTRY
+ {
+ char szOSD[256];
+ //OSD slot text
+ char szOSDOwner[256];
+ //OSD slot owner ID
+
+ //next fields are valid for v2.7 and newer shared memory format only
+
+ char szOSDEx[4096];
+ //extended OSD slot text
+
+ //next fields are valid for v2.12 and newer shared memory format only
+
+ BYTE buffer[262144];
+ //OSD slot data buffer
+
+ //next fields are valid for v2.20 and newer shared memory format only
+
+ char szOSDEx2[32768];
+ //additional 32KB extended OSD slot text
+
+ } RTSS_SHARED_MEMORY_OSD_ENTRY, *LPRTSS_SHARED_MEMORY_OSD_ENTRY;
+
+ //process performance counter structure
+
+ typedef struct RTSS_SHARED_MEMORY_PROCESS_PERF_COUNTER_ENTRY
+ {
+ DWORD dwID;
+ //performance counter ID, PROCESS_PERF_COUNTER_ID_XXX
+ DWORD dwParam;
+ //performance counter parameters specific to performance counter ID
+ //PROCESS_PERF_COUNTER_ID_D3DKMT_VRAM_USAGE_LOCAL : contains GPU location (PCI bus, device and function)
+ //PROCESS_PERF_COUNTER_ID_D3DKMT_VRAM_USAGE_SHARED : contains GPU location (PCI bus, device and function)
+ DWORD dwData;
+ //performance counter data
+ } RTSS_SHARED_MEMORY_PROCESS_PERF_COUNTER_ENTRY, *LPRTSS_SHARED_MEMORY_PROCESS_PERF_COUNTER_ENTRY;
+
+ //application descriptor structure
+
+ typedef struct RTSS_SHARED_MEMORY_APP_ENTRY
+ {
+ //application identification related fields
+
+ DWORD dwProcessID;
+ //process ID
+ char szName[MAX_PATH];
+ //process executable name
+ DWORD dwFlags;
+ //application specific flags
+
+ //instantaneous framerate related fields
+
+ DWORD dwTime0;
+ //start time of framerate measurement period (in milliseconds)
+
+ //Take a note that this field must contain non-zero value to calculate
+ //framerate properly!
+ DWORD dwTime1;
+ //end time of framerate measurement period (in milliseconds)
+ DWORD dwFrames;
+ //amount of frames rendered during (dwTime1 - dwTime0) period
+ DWORD dwFrameTime;
+ //frame time (in microseconds)
+
+
+ //to calculate framerate use the following formulas:
+
+ //1000.0f * dwFrames / (dwTime1 - dwTime0) for framerate calculated once per second
+ //or
+ //1000000.0f / dwFrameTime for framerate calculated once per frame
+
+ //framerate statistics related fields
+
+ DWORD dwStatFlags;
+ //bitmask containing combination of STATFLAG_... flags
+ DWORD dwStatTime0;
+ //statistics record period start time
+ DWORD dwStatTime1;
+ //statistics record period end time
+ DWORD dwStatFrames;
+ //total amount of frames rendered during statistics record period
+ DWORD dwStatCount;
+ //amount of min/avg/max measurements during statistics record period
+ DWORD dwStatFramerateMin;
+ //minimum instantaneous framerate measured during statistics record period
+ DWORD dwStatFramerateAvg;
+ //average instantaneous framerate measured during statistics record period
+ DWORD dwStatFramerateMax;
+ //maximum instantaneous framerate measured during statistics record period
+
+ //OSD related fields
+
+ DWORD dwOSDX;
+ //OSD X-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the right side of the screen)
+ DWORD dwOSDY;
+ //OSD Y-coordinate (coordinate wrapping is allowed, i.e. -5 defines 5
+ //pixel offset from the bottom side of the screen)
+ DWORD dwOSDPixel;
+ //OSD pixel zooming ratio
+ DWORD dwOSDColor;
+ //OSD color in RGB format
+ DWORD dwOSDFrame;
+ //application specific OSD frame ID. Don't change it directly!
+
+ DWORD dwScreenCaptureFlags;
+ char szScreenCapturePath[MAX_PATH];
+
+ //next fields are valid for v2.1 and newer shared memory format only
+
+ DWORD dwOSDBgndColor;
+ //OSD background color in RGB format
+
+ //next fields are valid for v2.2 and newer shared memory format only
+
+ DWORD dwVideoCaptureFlags;
+ char szVideoCapturePath[MAX_PATH];
+ DWORD dwVideoFramerate;
+ DWORD dwVideoFramesize;
+ DWORD dwVideoFormat;
+ DWORD dwVideoQuality;
+ DWORD dwVideoCaptureThreads;
+
+ DWORD dwScreenCaptureQuality;
+ DWORD dwScreenCaptureThreads;
+
+ //next fields are valid for v2.3 and newer shared memory format only
+
+ DWORD dwAudioCaptureFlags;
+
+ //next fields are valid for v2.4 and newer shared memory format only
+
+ DWORD dwVideoCaptureFlagsEx;
+
+ //next fields are valid for v2.5 and newer shared memory format only
+
+ DWORD dwAudioCaptureFlags2;
+
+ DWORD dwStatFrameTimeMin;
+ DWORD dwStatFrameTimeAvg;
+ DWORD dwStatFrameTimeMax;
+ DWORD dwStatFrameTimeCount;
+
+ DWORD dwStatFrameTimeBuf[1024];
+ DWORD dwStatFrameTimeBufPos;
+ DWORD dwStatFrameTimeBufFramerate;
+
+ //next fields are valid for v2.6 and newer shared memory format only
+
+ LARGE_INTEGER qwAudioCapturePTTEventPush;
+ LARGE_INTEGER qwAudioCapturePTTEventRelease;
+
+ LARGE_INTEGER qwAudioCapturePTTEventPush2;
+ LARGE_INTEGER qwAudioCapturePTTEventRelease2;
+
+ //next fields are valid for v2.8 and newer shared memory format only
+
+ DWORD dwPrerecordSizeLimit;
+ DWORD dwPrerecordTimeLimit;
+
+ //next fields are valid for v2.13 and newer shared memory format only
+
+ LARGE_INTEGER qwStatTotalTime;
+ DWORD dwStatFrameTimeLowBuf[1024];
+ DWORD dwStatFramerate1Dot0PercentLow;
+ DWORD dwStatFramerate0Dot1PercentLow;
+
+ //next fields are valid for v2.17 and newer shared memory format only
+
+ DWORD dw1Dot0PercentLowBufPos;
+ DWORD dw0Dot1PercentLowBufPos;
+
+ //next fields are valid for v2.18 and newer shared memory format only
+
+ DWORD dwProcessPerfCountersFlags;
+ DWORD dwProcessPerfCountersCount;
+ DWORD dwProcessPerfCountersSamplingPeriod;
+ DWORD dwProcessPerfCountersSamplingTime;
+ DWORD dwProcessPerfCountersTimestamp;
+
+ //next fields are valid for v2.19 and newer shared memory format only
+
+ LARGE_INTEGER qwLatencyMarkerPresentTimestamp;
+
+ //next fields are valid for v2.20 and newer shared memory format only
+
+ DWORD dwResolutionX;
+ DWORD dwResolutionY;
+
+ //WARNING: next fields should never (!!!) be accessed directly, use the offsets to access them in order to provide
+ //compatibility with future versions
+
+ RTSS_SHARED_MEMORY_PROCESS_PERF_COUNTER_ENTRY arrPerfCounters[256];
+
+ } RTSS_SHARED_MEMORY_APP_ENTRY, *LPRTSS_SHARED_MEMORY_APP_ENTRY;
+
+ //WARNING: next fields should never (!!!) be accessed directly, use the offsets to access them in order to provide
+ //compatibility with future versions
+
+ RTSS_SHARED_MEMORY_OSD_ENTRY arrOSD[8];
+ //array of OSD slots
+ RTSS_SHARED_MEMORY_APP_ENTRY arrApp[256];
+ //array of application descriptors
+
+ //next fields are valid for v2.9 and newer shared memory format only
+
+ //WARNING: due to design flaw there is no offset available for this field, so it must be calculated manually as
+ //dwAppArrOffset + dwAppArrSize * dwAppEntrySize
+
+ VIDEO_CAPTURE_PARAM autoVideoCaptureParam;
+
+} RTSS_SHARED_MEMORY, *LPRTSS_SHARED_MEMORY;
+/////////////////////////////////////////////////////////////////////////////
+typedef struct RTSS_EMBEDDED_OBJECT
+{
+ DWORD dwSignature;
+ //embedded object signature
+ DWORD dwSize;
+ //embedded object size in bytes
+ LONG dwWidth;
+ //embedded object width in pixels (if positive) or in chars (if negative)
+ LONG dwHeight;
+ //embedded object height in pixels (if positive) or in chars (if negative)
+ LONG dwMargin;
+ //embedded object margin in pixels
+} RTSS_EMBEDDED_OBJECT, *LPRTSS_EMBEDDED_OBJECT;
+/////////////////////////////////////////////////////////////////////////////
+#define RTSS_EMBEDDED_OBJECT_GRAPH_SIGNATURE 'GR00'
+/////////////////////////////////////////////////////////////////////////////
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FILLED 1
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE 2
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMETIME 4
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_BAR 8
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_BGND 16
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_VERTICAL 32
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_MIRRORED 64
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_AUTOSCALE 128
+
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE_MIN 256
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE_AVG 512
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE_MAX 1024
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE_1DOT0_PERCENT_LOW 2048
+#define RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_FRAMERATE_0DOT1_PERCENT_LOW 4096
+/////////////////////////////////////////////////////////////////////////////
+#pragma warning (disable : 4200)
+
+typedef struct RTSS_EMBEDDED_OBJECT_GRAPH
+{
+ RTSS_EMBEDDED_OBJECT header;
+ //embedded object header
+
+ DWORD dwFlags;
+ //bitmask containing RTSS_EMBEDDED_OBJECT_GRAPH_FLAG_XXX flags
+ FLOAT fltMin;
+ //graph mininum value
+ FLOAT fltMax;
+ //graph maximum value
+ DWORD dwDataCount;
+ //count of data samples in fltData array
+ FLOAT fltData[0];
+ //graph data samples array
+
+} RTSS_EMBEDDED_OBJECT_GRAPH, *LPRTSS_EMBEDDED_OBJECT_GRAPH;
+
+#pragma warning (default : 4200)
+/////////////////////////////////////////////////////////////////////////////
+#endif //_RTSS_SHARED_MEMORY_INCLUDED_
\ No newline at end of file
diff --git a/rainmeter-plugin-sdk b/rainmeter-plugin-sdk
new file mode 160000
index 0000000..a32273c
--- /dev/null
+++ b/rainmeter-plugin-sdk
@@ -0,0 +1 @@
+Subproject commit a32273c2c4092f8d5d8f2ad010623559198bbdc4
diff --git a/src/API.cpp b/src/API.cpp
new file mode 100644
index 0000000..fa03809
--- /dev/null
+++ b/src/API.cpp
@@ -0,0 +1,88 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 15/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include
+#include
+#include "API.h"
+
+std::wstring Rainmeter::API::readString(const std::wstring &option, const std::wstring &defaultValue, const bool &replaceMeasures) {
+ return std::wstring{RmReadString(rm, option.c_str(), defaultValue.c_str(), replaceMeasures)};
+}
+
+double Rainmeter::API::readFormula(const std::wstring &option, const double &defaultValue) {
+ return RmReadFormula(rm, option.c_str(), defaultValue);
+}
+
+std::wstring Rainmeter::API::replaceVariables(const std::wstring &str) {
+ return std::wstring{RmReplaceVariables(rm, str.c_str())};
+}
+
+std::wstring Rainmeter::API::pathToAbsolute(const std::wstring &relativePath) {
+ return std::wstring{RmPathToAbsolute(rm, relativePath.c_str())};
+}
+
+LPVOID Rainmeter::API::get(Rainmeter::API::GetType type) const {
+ return RmGet(rm, type);
+}
+
+std::wstring Rainmeter::API::readPath(const std::wstring &option, const std::wstring &defaultValue) {
+ std::wstring relativePath = readString(option, defaultValue, true);
+ return pathToAbsolute(relativePath);
+}
+
+int Rainmeter::API::readInt(const std::wstring &option, const int &defaultValue) {
+ return static_cast(readFormula(option, defaultValue));
+}
+
+double Rainmeter::API::readDouble(const std::wstring &option, const double &defaultValue) {
+ return static_cast(readFormula(option, defaultValue));
+}
+
+std::wstring Rainmeter::API::readMeasureName() const {
+ return std::wstring{static_cast(get(GetType::RMG_MEASURENAME))};
+}
+
+std::wstring Rainmeter::API::getSettingsFile() const {
+ return std::wstring{static_cast(get(GetType::RMG_SETTINGSFILE))};
+}
+
+void *Rainmeter::API::getSkin() const {
+ return static_cast(get(GetType::RMG_SKIN));
+}
+
+std::wstring Rainmeter::API::getSkinName() const {
+ return std::wstring{static_cast(get(GetType::RMG_SKINNAME))};
+}
+
+void *Rainmeter::API::getSkinWindow() const {
+ return static_cast(get(GetType::RMG_SKINWINDOWHANDLE));
+}
+
+void Rainmeter::API::log(int level, const std::wstring &message) {
+ RmLog(rm, level, message.c_str());
+}
+
+void Rainmeter::API::logError(const std::wstring &message) {
+ log(LogLevel::LOG_ERROR, message);
+}
+
+void Rainmeter::API::logNotice(const std::wstring &message) {
+ log(LogLevel::LOG_NOTICE, message);
+}
+
+void Rainmeter::API::logDebug(const std::wstring &message) {
+ log(LogLevel::LOG_DEBUG, message);
+}
+
+void Rainmeter::API::logWarning(const std::wstring &message) {
+ log(LogLevel::LOG_WARNING, message);
+}
diff --git a/src/API.h b/src/API.h
new file mode 100644
index 0000000..3e188ec
--- /dev/null
+++ b/src/API.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 15/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace Rainmeter {
+ class API {
+ public:
+ enum LogLevel {
+ LOG_ERROR = 1,
+ LOG_WARNING = 2,
+ LOG_NOTICE = 3,
+ LOG_DEBUG = 4
+ };
+
+ enum GetType {
+ RMG_MEASURENAME = 0,
+ RMG_SKIN = 1,
+ RMG_SETTINGSFILE = 2,
+ RMG_SKINNAME = 3,
+ RMG_SKINWINDOWHANDLE = 4
+ };
+
+ private:
+ void *rm;
+
+ [[maybe_unused]] [[nodiscard]] void *get(GetType type) const;
+
+ public:
+ explicit API(void *rainmeter) {
+ rm = rainmeter;
+ }
+
+ API(API const &aApi) {
+ rm = aApi.rm;
+ }
+
+ [[maybe_unused]] [[nodiscard]] std::wstring
+ readString(std::wstring const &option, std::wstring const &defaultValue, bool const &replaceMeasures = true);
+
+ [[maybe_unused]] [[nodiscard]] double readFormula(std::wstring const &option, double const &defaultValue);
+
+ [[maybe_unused]] [[nodiscard]] std::wstring replaceVariables(std::wstring const &str);
+
+ [[maybe_unused]] [[nodiscard]] std::wstring pathToAbsolute(std::wstring const &relativePath);
+
+ [[maybe_unused]] [[nodiscard]] std::wstring readPath(std::wstring const &option, std::wstring const &defaultValue);
+
+ [[maybe_unused]] [[nodiscard]] int readInt(std::wstring const &option, int const &defaultValue);
+
+ [[maybe_unused]] [[nodiscard]] double readDouble(std::wstring const &option, double const &defaultValue);
+
+ [[maybe_unused]] [[nodiscard]] std::wstring readMeasureName() const;
+
+ [[maybe_unused]] [[nodiscard]] std::wstring getSettingsFile() const;
+
+ [[maybe_unused]] [[nodiscard]] void *getSkin() const;
+
+ [[maybe_unused]] [[nodiscard]] std::wstring getSkinName() const;
+
+ [[maybe_unused]] [[nodiscard]] void *getSkinWindow() const;
+
+ [[maybe_unused]] void log(int level, std::wstring const &message);
+
+ [[maybe_unused]] void logError(std::wstring const &message);
+
+ [[maybe_unused]] void logNotice(std::wstring const &message);
+
+ [[maybe_unused]] void logDebug(std::wstring const &message);
+
+ [[maybe_unused]] void logWarning(std::wstring const &message);
+ };
+}
diff --git a/src/BackgroundTimer.h b/src/BackgroundTimer.h
new file mode 100644
index 0000000..acd3850
--- /dev/null
+++ b/src/BackgroundTimer.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 30/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace LhwsPlugin {
+ class BackgroundTimer {
+ private:
+ std::chrono::milliseconds time{};
+ std::mutex mutex{};
+ std::function func;
+ std::atomic runThread{true};
+ std::condition_variable cv{};
+
+ void timer() {
+ std::unique_lock lock{mutex};
+ while (runThread) {
+ mutex.unlock();
+ func();
+ mutex.lock();
+ auto now = std::chrono::steady_clock::now();
+ cv.wait_until(lock, now + time, [this] { return !runThread.load(); });
+ }
+ }
+
+ std::thread timerThread{[this]() { timer(); }};
+
+ public:
+ explicit BackgroundTimer(std::chrono::milliseconds const &time, std::function function) : time(time),
+ func(std::move(
+ function)) {}
+
+ ~BackgroundTimer() {
+ runThread = false;
+ cv.notify_all();
+ timerThread.join();
+ }
+
+ void setTime(const std::chrono::milliseconds &aTime) {
+ BackgroundTimer::time = aTime;
+ }
+ };
+} // LhwsPlugin
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..268dac7
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.21)
+set(CMAKE_CXX_STANDARD 17)
+
+if (MSVC)
+ string(TOLOWER ${CMAKE_VS_PLATFORM_NAME} ARCHITECTURE)
+else ()
+ string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} ARCHITECTURE)
+endif ()
+if (ARCHITECTURE STREQUAL "amd64" OR ARCHITECTURE STREQUAL "x86_64")
+ set(ARCHITECTURE "x64")
+elseif (ARCHITECTURE STREQUAL "win32")
+ set(ARCHITECTURE "x86")
+endif ()
+message("architecture: ${ARCHITECTURE}")
+
+if(ARCHITECTURE STREQUAL x86)
+ set(RAINMETER_ARCH x32)
+else()
+ set(RAINMETER_ARCH x64)
+endif()
+
+set(MAINTARGET "LibreHardwareService")
+set(MAINTARGET "${MAINTARGET}" PARENT_SCOPE)
+
+message("TARGET=${MAINTARGET}")
+
+set(SOURCE_FILES Plugin.cpp Measure.cpp API.cpp RtssAppStat.cpp)
+
+
+if (BUILD_SHARED_LIBS)
+ add_library(${MAINTARGET} SHARED ${SOURCE_FILES})
+else ()
+ add_library(${MAINTARGET} STATIC ${SOURCE_FILES})
+endif ()
+
+target_compile_features(${MAINTARGET} PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ message("Compiler is MSVC")
+ if (BUILD_SHARED_LIBS)
+ target_compile_options(${MAINTARGET} PRIVATE /W4 /WX /EHs)
+ else ()
+ target_compile_options(${MAINTARGET} PRIVATE /W4 /WX /EHs "$<$:/MD>" "$<$:/MDd>"
+ "$<$:/O2>" "$<$:/Od>" /fp:fast)
+ target_compile_definitions(${MAINTARGET} PRIVATE _Boost_USE_STATIC_LIBS BOOST_ALL_NO_LIB)
+ set_property(TARGET ${MAINTARGET} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL")
+ endif ()
+else ()
+ message("UNSUPPORTED COMPILER")
+endif ()
+
+set(Boost_USE_MULTITHREADED ON)
+set(Boost_NO_WARN_NEW_VERSIONS ON)
+
+find_package(Boost 1.79.0 REQUIRED COMPONENTS locale)
+find_package(lhwservice CONFIG REQUIRED)
+
+target_include_directories(${MAINTARGET} PRIVATE
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${CMAKE_SOURCE_DIR}/external/include"
+ "${CMAKE_SOURCE_DIR}/rainmeter-plugin-sdk/API"
+ lhwservice
+ ${Boost_INCLUDE_DIR})
+
+target_link_libraries(${MAINTARGET} PRIVATE
+ lhwservice
+ ${Boost_LIBRARIES}
+ "${CMAKE_SOURCE_DIR}/rainmeter-plugin-sdk/API/${RAINMETER_ARCH}/Rainmeter.lib")
+
+
+set(CPACK_GENERATOR ZIP)
+include(CPack)
\ No newline at end of file
diff --git a/src/Constants.h b/src/Constants.h
new file mode 100644
index 0000000..f4cfc71
--- /dev/null
+++ b/src/Constants.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 24/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#define ATASMART_PREFAIL L"Pre-fail"
+#define ATASMART_WARNING L"Warning"
+#define ATASMART_ENDOFLIFE L"End-of-Life"
+#define ATASMART_OK L"OK"
+
+#define OPTION_HWSTATUSTYPE L"HwStatusType"
+#define OPTION_HWSTATUSITEM L"HwStatusItem"
+#define OPTION_SENSORNAME L"SensorName"
+#define OPTION_SENSORTYPE L"SensorType"
+#define OPTION_HARDWARENAME L"HardwareName"
+#define OPTION_HARDWARETYPE L"HardwareType"
+#define OPTION_SENSORID L"SensorIdentifier"
+#define OPTION_STALEAFTERSECS L"StaleAfterSecs"
+
+#define SMART_ITEM_STATUS "Status"
+#define SMARTTYPE_ATA L"storage_smart_ata"
+#define SMARTTYPE_NVME L"storage_smart_nvme"
+
+#define WARNING_THRESHOLD_ATASMART 10
+#define VALUE_STALE_SECS 5
+#define MIN_UPDATE_INTERVAL 1000
+#define MAX_UPDATE_INTERVAL 5000
\ No newline at end of file
diff --git a/src/Measure.cpp b/src/Measure.cpp
new file mode 100644
index 0000000..47fb925
--- /dev/null
+++ b/src/Measure.cpp
@@ -0,0 +1,293 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 15/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "API.h"
+#include "Measure.h"
+#include "Constants.h"
+#include "RtssAppStat.h"
+#include "BackgroundTimer.h"
+
+namespace LhwsPlugin {
+ Measure::Measure(Rainmeter::API const &rm) : api(rm) {
+ }
+
+ void Measure::reload([[maybe_unused]] double maxValue) {
+ loadOptions();
+ }
+
+ double Measure::update() {
+ calculateUpdateInterval();
+ return lastValue;
+ }
+
+ void Measure::logError(std::wstring const &message) {
+ api.log(Rainmeter::API::LogLevel::LOG_ERROR, message);
+ }
+
+ void Measure::logError(const std::string &message) {
+ logError(boost::locale::conv::utf_to_utf(message));
+ }
+
+ long long Measure::unixTime() {
+ std::chrono::system_clock::time_point time = std::chrono::system_clock::now();
+ return std::chrono::duration_cast(time.time_since_epoch()).count();
+ }
+
+ long long rawValueToLong(std::vector const &bytes) {
+ long long ret = 0;
+ for (auto i = bytes.end() - 1; i >= bytes.begin(); i--) {
+ ret |= static_cast(*i) << ((i - bytes.begin())) * 8;
+ }
+ return ret;
+ }
+
+ double Measure::updateSmartAta() {
+ std::vector smartAttributes{};
+ try {
+ smartAttributes = hardwareService.getSmartAttributes(
+ boost::locale::conv::utf_to_utf(hardwareName),
+ boost::locale::conv::utf_to_utf(hardwareType));
+ } catch (std::exception const &e) {
+ logError(e.what());
+ return -1.0f;
+ }
+ bool prefail = false;
+ bool advisory = false;
+ bool warning = false;
+ for (auto const &a: smartAttributes) {
+ long long rawValue = rawValueToLong(a.getRawValue());
+
+ if (!a.getRawValue().empty()) {
+ if ((a.getId() == 0x05 || a.getId() == 0xC5 || a.getId() == 0xC6) && rawValue > WARNING_THRESHOLD_ATASMART) {
+ warning = true;
+ logError((boost::format("warning: id:'%1%'; name:'%2%';") % a.getId() % a.getName()).str());
+ }
+
+ if (a.isPrefail()) {
+ prefail = true;
+ logError((boost::format("prefail: id:'%1%'; name:'%2%';") % a.getId() % a.getName()).str());
+ }
+
+ if (a.isAdvisory()) {
+ advisory = true;
+ }
+ }
+ }
+
+ if (boost::iequals(hwStatusItem, SMART_ITEM_STATUS)) {
+ if (prefail) {
+ stringValue = ATASMART_PREFAIL;
+ } else if (advisory) {
+ stringValue = ATASMART_ENDOFLIFE;
+ } else if (warning) {
+ stringValue = ATASMART_WARNING;
+ } else {
+ stringValue = ATASMART_OK;
+ }
+ } else {
+ for (auto const &a: smartAttributes) {
+ if (!a.getRawValue().empty() && boost::iequals(hwStatusItem, a.getName())) {
+ return static_cast(a.getCurrentValue());
+ }
+ }
+ }
+
+ return -1.0f;
+ }
+
+ double Measure::updateSmartNvme() {
+ try {
+ lhws::NvmeSmart nvmeSmart = hardwareService.getNvmeSmart(boost::locale::conv::utf_to_utf(hardwareName),
+ boost::locale::conv::utf_to_utf(hardwareType));
+ if (boost::iequals(hwStatusItem, "availableSpare")) {
+ return static_cast(nvmeSmart.getAvailableSpare());
+ }
+ if (boost::iequals(hwStatusItem, "availableSpareThreshold")) {
+ return static_cast(nvmeSmart.getAvailableSpareThreshold());
+ }
+ if (boost::iequals(hwStatusItem, "controllerBusyTime")) {
+ return static_cast(nvmeSmart.getControllerBusyTime());
+ }
+ if (boost::iequals(hwStatusItem, "criticalCompositeTemperatureTime")) {
+ return static_cast(nvmeSmart.getCriticalCompositeTemperatureTime());
+ }
+ if (boost::iequals(hwStatusItem, "criticalWarning")) {
+ return static_cast(nvmeSmart.getCriticalWarning());
+ }
+ if (boost::iequals(hwStatusItem, "dataUnitRead")) {
+ return static_cast(nvmeSmart.getDataUnitRead());
+ }
+ if (boost::iequals(hwStatusItem, "dataUnitWritten")) {
+ return static_cast(nvmeSmart.getDataUnitWritten());
+ }
+ if (boost::iequals(hwStatusItem, "errorInfoLogEntryCount")) {
+ return static_cast(nvmeSmart.getErrorInfoLogEntryCount());
+ }
+ if (boost::iequals(hwStatusItem, "hostReadCommands")) {
+ return static_cast(nvmeSmart.getHostReadCommands());
+ }
+ if (boost::iequals(hwStatusItem, "hostWriteCommands")) {
+ return static_cast(nvmeSmart.getHostWriteCommands());
+ }
+ if (boost::iequals(hwStatusItem, "mediaErrors")) {
+ return static_cast(nvmeSmart.getMediaErrors());
+ }
+ if (boost::iequals(hwStatusItem, "percentageUsed")) {
+ return static_cast(nvmeSmart.getPercentageUsed());
+ }
+ if (boost::iequals(hwStatusItem, "powerCycle")) {
+ return static_cast(nvmeSmart.getPowerCycle());
+ }
+ if (boost::iequals(hwStatusItem, "powerOnHours")) {
+ return static_cast(nvmeSmart.getPowerOnHours());
+ }
+ if (boost::iequals(hwStatusItem, "temperature")) {
+ return static_cast(nvmeSmart.getTemperature());
+ }
+ if (boost::iequals(hwStatusItem, "unsafeShutdowns")) {
+ return static_cast(nvmeSmart.getUnsafeShutdowns());
+ }
+ if (boost::iequals(hwStatusItem, "warningCompositeTemperatureTime")) {
+ return static_cast(nvmeSmart.getWarningCompositeTemperatureTime());
+ }
+ } catch (std::exception const &e) {
+ logError(e.what());
+ }
+ return -1.0f;
+ }
+
+ double Measure::updateRtss() {
+ RtssAppStat stat = RtssAppStat::getStatLastForeground();
+ if (boost::iequals(sensorType, "framerate")) {
+ return stat.getFramerate();
+ } else if (boost::iequals(sensorType, "frametime")) {
+ return (stat.getFrametime() / 1000.0);
+ } else if (boost::iequals(sensorType, "frametimeMax")) {
+ return (stat.getFrametimeMax() / 1000.0);
+ } else if (boost::iequals(sensorType, "frametimeMin")) {
+ return (stat.getFrametimeMin() / 1000.0);
+ } else if (boost::iequals(sensorType, "frametimeAvg")) {
+ return (stat.getFrametimeAvg() / 1000.0);
+ }
+ return -1.0f;
+ }
+
+ double Measure::updateSensor() {
+ bool stale = false;
+ try {
+ if (staleAfterSecs > 0) {
+ int sensorUpdateInterval = hardwareService.getSensorsMetadata().getUpdateInterval();
+ long long lastUpdate = hardwareService.getSensorsMetadata().getLastUpdate();
+ long long now = unixTime();
+
+ if (lastUpdate < (now - sensorUpdateInterval - staleAfterSecs)) {
+ stale = true;
+ }
+ } else {
+ stale = true;
+ }
+ } catch (std::exception const &e) {
+ logError(e.what());
+ return -1.0f;
+ }
+
+ try {
+ lhws::Sensor sensor;
+ if(sensorId.empty()) {
+ sensor = hardwareService.getSensorByName(
+ boost::locale::conv::utf_to_utf(sensorName),
+ boost::locale::conv::utf_to_utf(sensorType),
+ boost::locale::conv::utf_to_utf(hardwareName));
+ } else {
+ sensor = hardwareService.getSensorById(boost::locale::conv::utf_to_utf(sensorId));
+ }
+
+ return sensor.getValue();
+ } catch (std::exception const &e) {
+ logError(e.what());
+ }
+ if (!stale) {
+ return lastValue;
+ }
+ return -1.0f;
+ }
+
+ void Measure::doUpdate() {
+ double value = -1.0;
+ if (!hwStatusType.empty() && !hwStatusItem.empty()) {
+ if (boost::iequals(hwStatusType, SMARTTYPE_ATA)) {
+ value = updateSmartAta();
+ } else if (boost::iequals(hwStatusType, SMARTTYPE_NVME)) {
+ value = updateSmartNvme();
+ }
+ } else if (boost::iequals(sensorName, "rtss")) {
+ value = updateRtss();
+ } else if ((!sensorName.empty() && !sensorType.empty() && !hardwareName.empty()) || !sensorId.empty()) {
+ value = updateSensor();
+ } else {
+ value = -1.0f;
+ }
+
+ Measure::lastValue = value;
+ }
+
+ void Measure::calculateUpdateInterval() {
+ auto now = std::chrono::steady_clock::now();
+
+ auto realInterval = now - previousUpdate;
+ long long realIntervalMs = std::chrono::duration_cast(realInterval).count();
+
+ if (realIntervalMs < MIN_UPDATE_INTERVAL) {
+ realIntervalMs = MIN_UPDATE_INTERVAL;
+ }
+ if (realIntervalMs > MAX_UPDATE_INTERVAL) {
+ realIntervalMs = MAX_UPDATE_INTERVAL;
+ }
+
+ auto currentInterval = std::chrono::milliseconds(static_cast(
+ std::round(static_cast(realIntervalMs) / 500.0) * 500)
+ );
+
+ previousUpdate = std::chrono::steady_clock::now();
+
+ setUpdateInterval(currentInterval);
+ }
+
+ void Measure::loadOptions() {
+ hwStatusType = api.readString(OPTION_HWSTATUSTYPE, L"");
+ hwStatusItem = api.readString(OPTION_HWSTATUSITEM, L"");
+ sensorName = api.readString(OPTION_SENSORNAME, L"");
+ sensorType = api.readString(OPTION_SENSORTYPE, L"");
+ hardwareName = api.readString(OPTION_HARDWARENAME, L"");
+ hardwareType = api.readString(OPTION_HARDWARETYPE, L"");
+ sensorId = api.readString(OPTION_SENSORID, L"");
+ staleAfterSecs = api.readInt(OPTION_STALEAFTERSECS, VALUE_STALE_SECS);
+
+ if (staleAfterSecs < 0) {
+ staleAfterSecs = 0;
+ }
+ }
+
+ void Measure::setUpdateInterval(std::chrono::milliseconds &aInterval) {
+ updateInterval = aInterval;
+ timer.setTime(updateInterval);
+ }
+}
\ No newline at end of file
diff --git a/src/Measure.h b/src/Measure.h
new file mode 100644
index 0000000..0cdf8c7
--- /dev/null
+++ b/src/Measure.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 15/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include "lhwservice/HardwareService.h"
+#include "Constants.h"
+#include "API.h"
+#include "BackgroundTimer.h"
+
+namespace LhwsPlugin {
+ class Measure {
+ private:
+ lhws::HardwareService hardwareService{};
+ std::wstring sensorIdentifier{};
+ std::wstring hardwareName{};
+ std::wstring hardwareType{};
+ std::wstring sensorName{};
+ std::wstring sensorType{};
+ std::wstring hwStatusType{};
+ std::wstring hwStatusItem{};
+ std::wstring stringValue{};
+ std::wstring sensorId{};
+ std::wstring fps{};
+ double lastValue = -1.0f;
+ int staleAfterSecs = VALUE_STALE_SECS;
+ Rainmeter::API api;
+ std::mutex mtxValue{};
+ std::chrono::milliseconds updateInterval{};
+ std::chrono::time_point previousUpdate{std::chrono::steady_clock::now()};
+
+ BackgroundTimer timer{std::chrono::milliseconds(1000), [this]() {
+ if (!mtxValue.try_lock()) {
+ return;
+ }
+ std::scoped_lock lock(std::adopt_lock, mtxValue);
+ doUpdate();
+ }};
+
+ void loadOptions();
+
+ public:
+ explicit Measure(Rainmeter::API const &rm);
+
+ [[nodiscard]] std::wstring getString() {
+ return stringValue;
+ }
+
+ void reload(double maxValue);
+
+ double update();
+
+ void logError(std::wstring const &message);
+
+ void logError(std::string const &message);
+
+ double updateSmartAta();
+
+ double updateSmartNvme();
+
+ double updateRtss();
+
+ double updateSensor();
+
+ static long long unixTime();
+
+ void doUpdate();
+
+ void calculateUpdateInterval();
+
+ void setUpdateInterval(std::chrono::milliseconds &aInterval);
+ };
+}
\ No newline at end of file
diff --git a/src/Plugin.cpp b/src/Plugin.cpp
new file mode 100644
index 0000000..8b62142
--- /dev/null
+++ b/src/Plugin.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 15/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include
+#include
+#include
+#include "API.h"
+#include "Measure.h"
+#include "RainmeterAPI.h"
+
+namespace LhwsPlugin {
+ PLUGIN_EXPORT void Initialize([[maybe_unused]] void **data, [[maybe_unused]] void *rm) {
+ Rainmeter::API api(rm);
+ auto *measure = new Measure(api);
+ *data = measure;
+ }
+
+ PLUGIN_EXPORT void Reload([[maybe_unused]] void *data, [[maybe_unused]] void *rm,
+ [[maybe_unused]] double *maxValue) { // NOLINT(readability-non-const-parameter)
+ auto *measure = static_cast(data);
+ measure->reload(*maxValue);
+ }
+
+ PLUGIN_EXPORT double Update([[maybe_unused]] void *data) {
+ auto *measure = static_cast(data);
+ return measure->update();
+ }
+
+ PLUGIN_EXPORT LPCWSTR GetString([[maybe_unused]] void *data) {
+ auto *measure = static_cast(data);
+ std::wstring wstr = measure->getString();
+ if (wstr.empty()) {
+ return nullptr;
+ }
+
+ int stringSize = static_cast(wstr.size() * sizeof(wchar_t));
+ auto *str = new wchar_t[stringSize + 1];
+ wcsncpy_s(str, stringSize, wstr.c_str(), stringSize);
+ return str;
+ }
+
+ PLUGIN_EXPORT void Finalize([[maybe_unused]] void *data) {
+ delete (static_cast(data));
+ }
+}
\ No newline at end of file
diff --git a/src/RtssAppStat.cpp b/src/RtssAppStat.cpp
new file mode 100644
index 0000000..73b4e5d
--- /dev/null
+++ b/src/RtssAppStat.cpp
@@ -0,0 +1,106 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 28/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include
+#include
+#include
+#include
+#include "RtssAppStat.h"
+
+namespace LhwsPlugin {
+ RtssAppStat::RtssAppStat(std::string appName, unsigned long processId, unsigned long framerate, unsigned long frametime,
+ unsigned long framerateMin, unsigned long framerateAvg, unsigned long framerateMax,
+ unsigned long frametimeMin, unsigned long frametimeAvg, unsigned long frametimeMax)
+ : appName(std::move(appName)), processId(processId), framerate(framerate), frametime(frametime),
+ framerateMin(framerateMin),
+ framerateAvg(framerateAvg), framerateMax(framerateMax), frametimeMin(frametimeMin), frametimeAvg(frametimeAvg),
+ frametimeMax(frametimeMax) {}
+
+ RtssAppStat RtssAppStat::getStatLastForeground() {
+ HANDLE hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, "RTSSSharedMemoryV2");
+
+ if (hMapFile) {
+ LPVOID pMapAddr = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
+ auto pMem = (LPRTSS_SHARED_MEMORY) pMapAddr;
+
+ if (pMem) {
+ if ((pMem->dwSignature == 'RTSS') && (pMem->dwVersion >= 0x00020000)) {
+ auto appEntry =
+ reinterpret_cast(
+ ((LPBYTE) pMem + pMem->dwAppArrOffset + pMem->dwLastForegroundApp * pMem->dwAppEntrySize)
+ );
+ if (appEntry->dwTime0 != 0 && appEntry->dwFrames != 0 &&
+ pMem->dwLastForegroundAppProcessID == appEntry->dwProcessID) {
+ return {appEntry->szName,
+ appEntry->dwProcessID,
+ static_cast(std::round(
+ 1000.0 * appEntry->dwFrames / (appEntry->dwTime1 - appEntry->dwTime0))),
+ appEntry->dwFrameTime,
+ appEntry->dwStatFramerateMin,
+ appEntry->dwStatFramerateAvg,
+ appEntry->dwStatFramerateMax,
+ appEntry->dwStatFrameTimeMin,
+ appEntry->dwStatFrameTimeAvg,
+ appEntry->dwStatFrameTimeMax
+ };
+ }
+ }
+
+ UnmapViewOfFile(pMapAddr);
+ }
+
+ CloseHandle(hMapFile);
+ }
+
+ return {};
+ }
+
+ const std::string &RtssAppStat::getAppName() const {
+ return appName;
+ }
+
+ unsigned long RtssAppStat::getProcessId() const {
+ return processId;
+ }
+
+ unsigned long RtssAppStat::getFramerate() const {
+ return framerate;
+ }
+
+ unsigned long RtssAppStat::getFrametime() const {
+ return frametime;
+ }
+
+ unsigned long RtssAppStat::getFramerateMin() const {
+ return framerateMin;
+ }
+
+ unsigned long RtssAppStat::getFramerateAvg() const {
+ return framerateAvg;
+ }
+
+ unsigned long RtssAppStat::getFramerateMax() const {
+ return framerateMax;
+ }
+
+ unsigned long RtssAppStat::getFrametimeMin() const {
+ return frametimeMin;
+ }
+
+ unsigned long RtssAppStat::getFrametimeAvg() const {
+ return frametimeAvg;
+ }
+
+ unsigned long RtssAppStat::getFrametimeMax() const {
+ return frametimeMax;
+ }
+}
\ No newline at end of file
diff --git a/src/RtssAppStat.h b/src/RtssAppStat.h
new file mode 100644
index 0000000..0f7d491
--- /dev/null
+++ b/src/RtssAppStat.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 Emerson Pinter - All Rights Reserved
+
+//
+// Created by epinter on 28/05/2022.
+//
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include
+
+namespace LhwsPlugin {
+ class RtssAppStat {
+ private:
+ std::string appName{};
+ unsigned long processId{};
+ unsigned long framerate{};
+ unsigned long frametime{};
+ unsigned long framerateMin{};
+ unsigned long framerateAvg{};
+ unsigned long framerateMax{};
+ unsigned long frametimeMin{};
+ unsigned long frametimeAvg{};
+ unsigned long frametimeMax{};
+ public:
+ RtssAppStat(std::string appName, unsigned long processId, unsigned long framerate, unsigned long frametime,
+ unsigned long framerateMin, unsigned long framerateAvg, unsigned long framerateMax, unsigned long frametimeMin,
+ unsigned long frametimeAvg, unsigned long frametimeMax);
+
+ RtssAppStat() = default;
+
+ static RtssAppStat getStatLastForeground();
+
+ [[nodiscard]] [[maybe_unused]] const std::string &getAppName() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getProcessId() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFramerate() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFrametime() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFramerateMin() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFramerateAvg() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFramerateMax() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFrametimeMin() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFrametimeAvg() const;
+
+ [[nodiscard]] [[maybe_unused]] unsigned long getFrametimeMax() const;
+ };
+}
\ No newline at end of file