Compare commits

3 Commits

Author SHA1 Message Date
53809959cd its bitchier about unused results now 2026-05-11 09:56:25 +00:00
f5ff072c04 no 10g 2026-05-10 18:09:58 +02:00
4dee888d09 9070xt update, easier than expected tbh 2026-05-06 19:25:25 +02:00
49 changed files with 2773 additions and 2536 deletions

View File

@@ -1,56 +1,4 @@
BasedOnStyle: LLVM
# Indentation (Linux kernel style)
UseTab: Always
TabWidth: 8
IndentWidth: 8
ContinuationIndentWidth: 8
# Keep parameters on one line as much as possible
BinPackParameters: true
BinPackArguments: true
# Allow long lines (important!)
ColumnLimit: 0 # or set to something large like 120
# Prevent breaking after return type / before function name
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
# Keep function declarations/definitions on one line if possible
PenaltyBreakBeforeFirstCallParameter: 1000000
PenaltyReturnTypeOnItsOwnLine: 1000000
# Braces: K&R style everywhere, INCLUDING functions (your modification)
BreakBeforeBraces: Attach
# Control specific brace wrapping behavior
BraceWrapping:
AfterFunction: false # <-- key change (same line for functions)
AfterControlStatement: false
AfterStruct: false
AfterEnum: false
AfterUnion: false
AfterClass: false
BeforeElse: false
# Spaces
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
# Pointer alignment (kernel prefers this style)
PointerAlignment: Right
# Keep simple statements compact
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
# For-loop style similar to kernel
AllowShortBlocksOnASingleLine: Never
# Don't align weirdly
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
SortIncludes: CaseSensitive
---
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 0

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

105
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,105 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<RiderCodeStyleSettings>
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_NESTED_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BETWEEN_CLOSING_ANGLE_BRACKETS_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
</RiderCodeStyleSettings>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

7
.idea/dictionaries/project.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>drath</w>
</words>
</dictionary>
</component>

345
.idea/editor.xml generated Normal file
View File

@@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BackendCodeEditorSettings">
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CDeclarationWithImplicitIntType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CommentTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConstevalIfIsAlwaysConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractClassWithoutSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractVirtualFunctionCallInCtor/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAccessSpecifierWithNoDeclarations/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAwaiterTypeIsNotClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBooleanIncrementExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatBadCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatLegacyCode/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatMixedArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooFewArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCompileTimeConstantCanBeReplacedWithBooleanConstant/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConceptNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConditionalExpressionCanBeSimplified/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstValueFunctionReturnType/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCoroutineCallResolveError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAArrayIndexOutOfBounds/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantConditions/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantFunctionResult/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantParameter/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFADeletedPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAEndlessLoop/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInfiniteRecursion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInvalidatedMemory/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALoopConditionNotUpdated/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAMemoryLeak/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANotInitializedField/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANullDereference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFATimeOver/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableFunctionCall/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreadVariable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnusedValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesLocal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesUncapturedLocal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationSpecifierWithoutDeclarators/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorDisambiguatedAsFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorUsedBeforeInitialization/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultCaseNotHandledInSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultInitializationWithNoUserConstructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultIsUsedAsIdentifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultedSpecialMemberFunctionIsImplicitlyDeleted/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefinitionsOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeletingVoidPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTemplateWithoutTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTypeWithoutTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedEntity/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedOverridenMethod/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedRegisterStorageClassSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDereferenceOperatorLimitExceeded/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDiscardedPostfixOperatorResult/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenSyntaxError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUndocumentedParameter/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUnresolvedReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEmptyDeclaration/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersOrder/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersPlacement/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceDoStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceForStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceFunctionDeclarationStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceIfStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceNestedNamespacesStyle/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingDestructorStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingFunctionStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceTypeAliasCodeStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceWhileStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityAssignedButNoRead/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityUsedOnlyInUnevaluatedContext/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnumeratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEqualOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEvaluationFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExplicitSpecializationInNonNamespaceScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExpressionWithoutSideEffects/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalFunctionInFinalClass/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalNonOverridingVirtualFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForLoopCanBeReplacedWithWhile/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForwardEnumDeclarationWithoutUnderlyingType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionDoesntReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionResultShouldBeUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionalStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHeaderHasBeenAlreadyIncluded/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHiddenFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHidingFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIdenticalOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIfCanBeReplacedByConstexprIf/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberInitializersOrder/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMismatchedClassTags/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingIncludeGuard/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingKeywordThrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppModulePartitionWithSeveralPartitionUnits/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtAddressOfClassRValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtBindingRValueToLvalueReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtCopyElisionInCopyInitDeclarator/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtDoubleUserConversionInCopyInit/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtNotInitializedStaticConstLocalVar/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtReinterpretCastFromNullptr/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterWideLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMustBePublicVirtualToImplementInterface/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMutableSpecifierOnReferenceMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNoDiscardExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNodiscardFunctionWithoutReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExceptionSafeResourceAcquisition/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConversionOperator/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConvertingConstructor/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineFunctionDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineVariableDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPassValueParameterByConstReference/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerConversionDropsQualifiers/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerToIntegralConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPolymorphicClassWithNonVirtualPublicDestructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyErroneousEmptyStatements/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUnintendedObjectSlicing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderIsNotIncluded/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderNotFound/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfBadFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfExtraArg/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfMissedArg/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfRiskyFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrivateSpecialMemberFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRangeBasedForIncompatibleReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedefinitionOfDefaultArgumentInOverrideFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBooleanExpressionArgument/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantCastExpression/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantComplexityInComparison/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConditionalExpression/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConstSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantControlFlowJump/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantDereferencingAndTakingAddress/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElaboratedTypeSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeyword/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeywordInsideCompoundStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyDeclaration/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantExportKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantFwdClassOrEnumSpecifier/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantInlineSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantLambdaParameterList/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantMemberInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantNamespaceDefinition/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantParentheses/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifierADL/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnMemberAllocationFunction/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnThreadLocalLocalVariable/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateArguments/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantVoidArgumentList/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantZeroInitializerInAggregateInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReinterpretCastFromVoidPtr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRemoveRedundantBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceMemsetWithZeroInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceTieWithStructuredBinding/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReturnNoValueInNonVoidFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSmartPointerVsMakeFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSomeObjectMembersMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSpecialFunctionWithoutNoexceptSpecification/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticAssertFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppThrowExpressionCanBeReplacedWithRethrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScope/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScopeInitStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTypeAliasNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedDependentBaseClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedNonStaticDataMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnionMemberOfReferenceType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAssociativeContains/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAuto/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAutoForNumeric/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseElementsView/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseEraseAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseFamiliarTemplateSyntaxForGenericLambdas/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseRangeAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStdSize/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStructuredBinding/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseTypeTraitAlias/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUserDefinedLiteralSuffixDoesNotStartWithUnderscore/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUsingResultOfAssignmentAsCondition/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVariableCanBeMadeConstexpr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionCallInsideCtor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionInFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVolatileParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWarningDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongIncludesOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongSlashesInIncludeDirective/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroConstantCanBeReplacedWithNullptr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroValuedExpressionUsedAsNullPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IdentifierTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_NESTED_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BETWEEN_CLOSING_ANGLE_BRACKETS_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
</component>
</project>

View File

@@ -0,0 +1,11 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

2
.idea/linuxSmartie.iml generated Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CIDR" type="CPP_MODULE" version="4" />

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakePythonSetting">
<option name="pythonIntegrationState" value="YES" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/linuxSmartie.iml" filepath="$PROJECT_DIR$/.idea/linuxSmartie.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

54
CMakeLists.txt Normal file
View File

@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.20)
project(linuxSmartie
VERSION 1.0
LANGUAGES CXX
)
# Use C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Collect sources
file(GLOB SOURCES
src/*.cpp
main.cpp
)
# Define executable
add_executable(linuxSmartie ${SOURCES})
# Include directories
target_include_directories(linuxSmartie
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Compiler warnings
target_compile_options(linuxSmartie
PRIVATE
-O2
-Wall
-Wextra
-Wpedantic
-Werror
-fdiagnostics-color=always
)
find_package(libwebsockets QUIET)
find_package(ZLIB QUIET)
if(libwebsockets_FOUND)
target_link_libraries(linuxSmartie PRIVATE websockets)
target_compile_definitions(linuxSmartie PRIVATE ENABLE_HOME_ASSISTANT)
else()
message(STATUS "no libwebsockets found, disabling hass")
endif()
if(ZLIB_FOUND)
target_link_libraries(linuxSmartie PRIVATE z)
target_compile_definitions(linuxSmartie PRIVATE ENABLE_ZLIB)
else()
message(STATUS "no zlib found, disabling saving logs to gz")
endif()

View File

@@ -1,92 +1,27 @@
CC := cc
PKG_CONFIG := pkg-config
# wrapper around cmake, see CMakeLists.txt for compile options
.PHONY: configure build clean nuke install install_service
TARGET := build/linuxSmartie
BUILD_DIR ?= build
BITCHY_ERRORS := \
-Wconversion \
-Wsign-conversion \
-Wshadow \
-Wformat=2 \
-Wundef \
-Wstrict-prototypes \
-Wmissing-prototypes \
-Wold-style-definition \
-Wwrite-strings \
-Wvla \
-Wcast-qual \
-Wcast-align \
-Wpointer-arith \
-Wswitch-enum \
build: configure
cmake --build ${BUILD_DIR} -j
CFLAGS := \
-std=c23 \
-Iinclude \
-Ibuild \
-D_GNU_SOURCE \
-D_POSIX_C_SOURCE=200809L \
-Wall \
-Wextra \
-Wpedantic \
-Werror \
-pedantic-errors \
-MMD -MP \
-O2 \
$(BITCHY_ERRORS)
LDLIBS := \
$(shell $(PKG_CONFIG) --libs libusb-1.0) \
-lm
SOURCES := $(wildcard src/*.c)
OBJECTS := $(patsubst src/%.c,build/src/%.o,$(SOURCES))
DEPS := $(OBJECTS:.o=.d)
VERSION_H := build/version.h
# glowny target, linkuje linuxSmartie, potrzebuje ${OBJECTS}, jest na samej górze wiec jest domyslny
$(TARGET): $(OBJECTS) $(VERSION_H)
$(CC) $(OBJECTS) -o $@ $(LDLIBS)
# Compile source files
build/src/%.o: src/%.c $(VERSION_H)
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Generate version.h from Git
$(VERSION_H): include/version.h.in
@mkdir -p $(dir $@)
@version=$$(git describe --always --long --dirty 2>/dev/null || echo unknown); \
sed "s/@VCS_TAG@/$$version/" $< > $@.tmp; \
if ! cmp -s $@.tmp $@ 2>/dev/null; then \
mv $@.tmp $@; \
else \
rm -f $@.tmp; \
fi
# Include automatically generated header dependencies
-include $(DEPS)
.PHONY: all clean install uninstall debug
all: $(TARGET)
debug: CFLAGS += -O0 -g
debug: $(TARGET)
configure:
cmake -S . -B ${BUILD_DIR}
clean:
rm -rf build
cmake --build $(BUILD_DIR) --target clean
install: $(TARGET)
install -Dm755 $(TARGET) $(DESTDIR)$(HOME)/.local/bin/linuxSmartie
nuke: clean
rm -rf $(BUILD_DIR)
service_install: install
install: build
mkdir -p ${HOME}/.local/bin
install ${BUILD_DIR}/linuxSmartie ${HOME}/.local/bin
install_service: install
mkdir -p ~/.config/systemd/user
cp linuxSmartie.service ~/.config/systemd/user/linuxSmartie.service
systemctl --user daemon-reload
systemctl --user enable linuxSmartie
systemctl --user start linuxSmartie
uninstall:
rm -f $(DESTDIR)$(HOME)/.local/bin/linuxSmartie

View File

@@ -1,23 +1,29 @@
# linuxSmartie
Alphanumeric LCD controller software for Linux (kind of LCDSmartie for Windows but no GUI, no config file, no plugins, no Windows).
- Hardcoded to work with 4x40 character display because that's what I have. Uses Matrix Orbital protocol over serial. Should work with most other display sizes by modifying `src/lcd.c`. If you're trying to make it work with anything else, good luck.
- Originally written in C++26, then in C++20, and now finally in C23 because GOD C++ IS A MESS. Used to be vaguely C99 at some point but meson didn't like it and ig writing C23 I'm consistent with esp-idf v6.0.
- Vaguely inspired by suckless software philosophy, as in:
~~Written with C++26 (`-std=c++2c`), will need at least gcc 14 to compile.~~ Uhm actually written with C++20 now, I was only really using `std::print` from 26. Let us not fall for memes. Will need gcc 10 to compile. GCC actually made C++20 the default since v16.
We can't go lower than C++20, we use too much shit introduced by it (std::format, std::chrono stuff mostly).
Hardcoded to work with 4x40 character display because that's what I have and I don't plan on using anything else. Uses Matrix Orbital protocol over serial. Should work with most other display sizes by modifying `src/LCDDisplay.cpp`. Best of luck if you try to make it work with anything else.
Vaguely inspired by suckless software philosophy, as in:
## The config is the source code
Same as plugins. Just write in more features scrub, learn some C, damn
Same as plugins. Just write in more features scrub, learn some C++, damn
## TODOs
- GPU power is jumpy, average? Use accumulator instead?
- Use `std::string_view` or `const std::string &` when possible, its faster, no copy
- Extra screens support perhaps? just halt sensors monitoring thread and print another struct on display if needed, have a semaphore or sth
- Maybe not even screens, keep the 1st line and change lines 2-4
- Media playback screen (use mpris, get playback info with mpris)
- In the lcd lib, reimplement all the features I have in picoSmartie + maybe finally add keypad support for both so I can toggle backlight (?) or change screens if I want any
- In the LCDDisplay lib, reimplement all the features I have in picoSmartie + maybe finally add keypad support for both so I can toggle backlight (?) or change screens if I want any
- Screens could be automatic tbh, like if it detects media playing it switches to media screen, if in media screen and GPU power > 80W it switches to main (hwmon) screen
- Try to be C++, avoid globals, wrap in namespaces, use naming conventions, use classes, avoid pure C way of doing things unless it makes more sense (which it often does tbh, better for reading small files)
- **FUCK C++, HUGE REWRITE FROM SCRATCH IN C LIKE GOD ALWAYS INTENDED** but fuuuuck display thread would be so painful without std::string. And I really would need to start a whole ass project from a clean slate since I made it a point to use a lot of C++ features which now bite me in the ass.
- Fuck it man, nvm let's leave it. Make it C++17 or 20 or sth so it compiles on yo mama or sth.
## LCD layout (on Windows)
## LCD layout
It looks something like this
```
****************************************
@@ -27,6 +33,7 @@ It looks something like this
1,200V 1186MHz 3150 2/9MHz 0,112V
****************************************
```
(on LCDSmartie)
### Ok but what does it all mean
In general left side is CPU/system stuff, right side is GPU stuff
@@ -52,20 +59,20 @@ Text04="$dll(hwinfo,1,Vcore,3)V $Right($dll(hwinfo,1,Average Effective Clock,a)M
### So what's still missing
- Per-core CPU load, doable but annoying
- FPS counter, maybe possible? Not really on wayland, by design
- Active core count, probably impossible but loadAvg is better
- FPS counter, maybe possible?
- Active core count, probably impossible but loadAvg is better?
- GPU board power, probably impossible, it was estimated on windows anyway
- CPU cache/iod, likely impossible, zenpower doesnt show it
- GPU clock limit, probably impossible, gpu_metrics doesnt show it?
### Shit needed to compile on Fedora (44).
V simple:
```bash
sudo dnf -y install gcc make libusb1-devel
make -j
```
### Shit needed to compile on Debian 13
You're probably on some meme immutable distro so use toolbox. The [custom toolbox](https://git.drath.cc/drath/dev-toolbx) you have already has everything pre-installed.
```bash
sudo apt install build-essential cmake libwebsockets-dev nlohmann-json3-dev
make configure
make build
```
You're probably on some meme immutable distro so use Debian 13 on distrobox. Ubuntu 24.04 should also work. Might set up a distrobox container file, with distrobox-assemble (?)
### Additional notes
@@ -73,4 +80,4 @@ You're probably on some meme immutable distro so use toolbox. The [custom toolbo
- On Fedora Workstation it can be added by appending it to `/etc/kernel/cmdline`.
- On atomic desktops (Silverblue/Kinoite), `rpm-ostree kargs --append-if-missing=acpi_enforce_resources=lax`
- You need to be able to read RAPL as user. Create in `/etc/tmpfiles.d/intelrapl.conf` with content `z- /sys/class/powercap/intel-rapl:0/energy_uj 0440 root uucp - -`. Adjust uucp to whatever group applicable, like `dialout`.
- ModemManager might steal the port sometimes. Either disable the service or setup an udev rule: `SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", ENV{ID_MM_DEVICE_IGNORE}="1"`
- `compile_commands.json` is so clang understands this project, so we can edit it with helix/neovim. Generated with `bear -- make`, make sure to run `make clean` beforehand.

7
distrobox.ini Normal file
View File

@@ -0,0 +1,7 @@
[linuxsmartie]
additional_packages="build-essential cmake"
additional_packages="libwebsockets-dev nlohmann-json3-dev"
image=debian-toolbox:13
init=false
replace=true
start_now=true

48
include/LCDDisplay.hpp Normal file
View File

@@ -0,0 +1,48 @@
#ifndef LCDDISPLAY_HPP
#define LCDDISPLAY_HPP
#define LCD_LINES 4
#define LCD_COLS 40
#include <array>
#include <cstdint>
#include <string>
class LCDDisplay {
private:
int lcd = -1;
uint32_t baud = 0;
std::string dev;
std::array<char, LCD_LINES * LCD_COLS + 8> writebuf{}; // 4x40 chars + 2x4 setcursor commands
static constexpr bool dualDisp = (LCD_LINES * LCD_COLS > 80); // is it dual display device or not, evauluated from the size at compile time
int open(bool reopen = false);
void write(const uint8_t* cmdbuf, uint8_t len);
void close();
// we avoid using these, use buffer (even if these are faster for small writes, perhaps a rewrite is in order)
void writeStr(const std::string& str);
void writeStr(uint8_t row, uint8_t col, const std::string& str);
public:
explicit LCDDisplay(const std::string& dev, uint32_t baud = 0010002); // from termios-baud.h, B115200
~LCDDisplay();
int Init();
void clear();
void setContrast(uint8_t contrast);
void setBacklight(bool enabled);
void setBacklight(uint8_t brightness);
void setCursor(uint8_t row, uint8_t col);
void setCustomChar(uint8_t index, std::array<uint8_t, 8> cchar);
void writeBuf(uint8_t row, uint8_t col, const std::string& str);
void clearBuf();
void flushBuf();
char bar(uint8_t char_id, double value);
// returns custom char no. c, small enough to define in header
static constexpr char cc(const uint8_t c) { return static_cast<char>(c % 8); }
};
#endif

View File

@@ -1,67 +0,0 @@
#ifndef LINUXSMARTIE_AIO_H
#define LINUXSMARTIE_AIO_H
#include <libusb-1.0/libusb.h>
#define AIO_USB_VID 0x1b1cU
#define AIO_USB_PID 0x0c13U
#define AIO_COLOR_CYAN { 0, 255, 255 }
#define AIO_COLOR_WHITE { 255, 255, 255 }
#define AIO_COLOR_OFF { 0, 0, 0 }
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct aio_params_t {
struct {
uint8_t min_duty;
uint8_t max_duty;
bool fan_stop;
float setpoint;
struct {
float Ki;
float Kd;
} ctrl;
} fans;
};
enum aio_pump_mode_t {
AIO_PUMP_MODE_QUIET,
AIO_PUMP_MODE_BALANCED,
AIO_PUMP_MODE_EXTREME,
AIO_PUMP_MODE_UNKNOWN
};
enum aio_color_mode_t : uint8_t {
AIO_COLOR_PULSE,
AIO_COLOR_SHIFT,
AIO_COLOR_FIXED,
AIO_COLOR_BLINKING
};
struct rgb_t {
uint8_t r;
uint8_t g;
uint8_t b;
};
struct curve_point_t {
uint8_t temp;
uint8_t duty;
};
struct aio_t {
libusb_device_handle* handle;
enum aio_pump_mode_t pump_mode;
float water_temp;
int16_t pump_counter;
uint16_t pump_speed;
uint16_t fan_speed[2];
};
void aio_detect(struct aio_t *aio);
void aio_update(struct aio_t *aio);
void aio_close(const struct aio_t *aio);
#endif

71
include/app.hpp Normal file
View File

@@ -0,0 +1,71 @@
#ifndef APP_HPP
#define APP_HPP
#include <atomic>
#include <cpu.hpp>
#include <gpu.hpp>
#include <mutex>
#include <semaphore>
#include <stop_token>
#include <sys.hpp>
#include <thread>
class Application {
public:
struct Config {
struct Lcd {
uint16_t refresh_interval_ms;
uint8_t contrast;
std::string port;
};
struct HomeAssistant {
std::string url;
std::string entity;
std::string token;
};
Lcd lcd;
HomeAssistant hass;
std::string cpu_pciaddr;
std::string gpu_pciaddr;
std::string sys_sensor;
std::string sys_ifname;
std::string home_dir;
};
explicit Application(Config config);
~Application();
int Run();
private:
struct Sensors {
sys::TimeDate td;
double loadavg[3] = {0.0f};
sys::Memory ram;
sys::Disk home;
sys::Network net;
sys::sysSensors sys;
cpu::cpuSensors cpu;
gpu::gpuSensors gpu;
double hass_state = 0.0f;
};
struct Control {
std::binary_semaphore display{0};
std::mutex sensors; // making sure the sensors struct isnt accessed by multiple threads at once
sigset_t sigset;
};
pid_t m_mainPid{};
int m_ret = 0;
Config m_config{}; // m_ is prefix for member variable
Control m_control{};
Sensors m_sensors{};
void hassThread(const std::stop_token& st);
void sensorsThread(const std::stop_token& st);
void displayThread(const std::stop_token& st);
void Crash(int ret);
};
#endif

View File

@@ -1,30 +0,0 @@
#ifndef LINUXSMARTIE_CPU_H
#define LINUXSMARTIE_CPU_H
#include <stdint.h>
enum cpu_module_t : uint8_t {
unknown,
k10temp,
zenpower
};
struct cpu_sensors_t {
enum cpu_module_t module;
uint8_t num_cores;
int16_t tdie;
int16_t tctl;
int16_t tccd1;
// double pcore;
// double psoc;
float ptotal;
// double vcore;
// double vsoc;
uint32_t avg_freq;
char hwmon[64];
};
void cpu_detect(struct cpu_sensors_t *t, const char *pci_dev);
void cpu_update(struct cpu_sensors_t *c);
#endif

33
include/cpu.hpp Normal file
View File

@@ -0,0 +1,33 @@
#ifndef CPU_HPP
#define CPU_HPP
#include <cstdint>
#include <string>
namespace cpu {
enum Module : uint8_t { unknown,
k10temp,
zenpower };
struct cpuSensors {
Module module = unknown;
uint8_t num_cores = 0;
int16_t tdie = 0;
int16_t tctl = 0;
int16_t tccd1 = 0;
// double pcore = 0.0;
// double psoc = 0.0;
float ptotal = 0.0;
// double vcore = 0.0;
// double vsoc = 0.0;
uint32_t avg_freq = 0;
std::string hwmon;
};
int detect(cpuSensors* target, const std::string& pci);
void update(cpuSensors* c);
} // namespace cpu
#endif

View File

@@ -1,41 +0,0 @@
#ifndef LINUXSMARTIE_GPU_H
#define LINUXSMARTIE_GPU_H
#include <stdint.h>
struct gpu_sensors_t {
uint64_t vram_used;
// uint64_t vram_total = 0;
float vcore;
uint16_t clock_core;
uint16_t clock_mem;
// uint8_t fan_pwm = 0;
uint16_t fan_speed;
uint16_t power_avg;
uint16_t power_cap;
// uint16_t power_cap_max = 0;
uint8_t temp_edge;
uint8_t temp_hotspot;
uint8_t temp_mem;
uint8_t temp_vrm_gfx;
uint8_t temp_vrm_soc;
uint8_t temp_vrm_mem;
bool has_gpu_metrics;
uint8_t usage_core;
uint8_t usage_media;
uint8_t usage_umc;
char hwmon[64];
};
void gpu_detect(struct gpu_sensors_t *t, const char *pci);
void gpu_update(struct gpu_sensors_t *g);
#endif

85
include/gpu.hpp Normal file
View File

@@ -0,0 +1,85 @@
#ifndef GPU_HPP
#define GPU_HPP
#include <cstdint>
#include <string>
/* clean abstraction for gpu_metrics or metrics in filesystem in sane units */
namespace gpu {
struct gpuSensors {
uint64_t vram_used = 0;
// uint64_t vram_total = 0;
float vcore = 0.0f;
uint16_t clock_core = 0;
uint16_t clock_mem = 0;
// uint8_t fan_pwm = 0;
uint16_t fan_speed = 0;
uint16_t power_avg = 0;
uint16_t power_cap = 0;
// uint16_t power_cap_max = 0;
uint8_t temp_edge = 0;
uint8_t temp_hotspot = 0;
uint8_t temp_mem = 0;
uint8_t temp_vrm_gfx = 0;
uint8_t temp_vrm_soc = 0;
uint8_t temp_vrm_mem = 0;
bool hasGpuMetrics = false;
uint8_t usage_core = 0;
uint8_t usage_media = 0;
uint8_t usage_umc = 0;
std::string hwmon;
};
int detect(gpuSensors* target, const std::string& pci);
void update(gpuSensors* g);
} // namespace gpu
// class ThrottleStatus(IntFlag):
// # linux/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
// PPT0 = 1 << 0
// PPT1 = 1 << 1
// PPT2 = 1 << 2
// PPT3 = 1 << 3
// SPL = 1 << 4
// FPPT = 1 << 5
// SPPT = 1 << 6
// SPPT_APU = 1 << 7
// TDC_GFX = 1 << 16
// TDC_SOC = 1 << 17
// TDC_MEM = 1 << 18
// TDC_VDD = 1 << 19
// TDC_CVIP = 1 << 20
// EDC_CPU = 1 << 21
// EDC_GFX = 1 << 22
// APCC = 1 << 23
// TEMP_GPU = 1 << 32
// TEMP_CORE = 1 << 33
// TEMP_MEM = 1 << 34
// TEMP_EDGE = 1 << 35
// TEMP_HOTSPOT = 1 << 36
// TEMP_SOC = 1 << 37
// TEMP_VR_GFX = 1 << 38
// TEMP_VR_SOC = 1 << 39
// TEMP_VR_MEM0 = 1 << 40
// TEMP_VR_MEM1 = 1 << 41
// TEMP_LIQUID0 = 1 << 42
// TEMP_LIQUID1 = 1 << 43
// VRHOT0 = 1 << 44
// VRHOT1 = 1 << 45
// PROCHOT_CPU = 1 << 46
// PROCHOT_GFX = 1 << 47
// PPM = 1 << 56
// FIT = 1 << 57
#endif

View File

@@ -1,7 +1,7 @@
#ifndef GPU_METRICS_H
#define GPU_METRICS_H
#ifndef GPU_METRICS_HPP
#define GPU_METRICS_HPP
#include <stdint.h>
#include <cstdint>
/* STRUCTS COPIED WHOLESALE FROM LINUX SOURCE CODE, formatted and wrapped around a namespace
https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/kgd_pp_interface.h */
@@ -16,6 +16,8 @@ https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/kgd_pp
#define MAX_XCC 8
#define NUM_XCP 8
namespace metrics {
struct metrics_table_header {
uint16_t structure_size;
uint8_t format_revision;
@@ -793,5 +795,6 @@ struct gpu_metrics_v3_0 {
/* Metrics table alpha filter time constant [us] */
uint32_t time_filter_alphavalue;
};
} // namespace metrics
#endif

37
include/hass.hpp Normal file
View File

@@ -0,0 +1,37 @@
#ifndef HASS_HPP
#define HASS_HPP
#ifdef ENABLE_HOME_ASSISTANT
#include <libwebsockets.h>
#include <cstdint>
#include <string>
class HomeAssistant {
public:
struct Config {
std::string address;
std::string entity;
std::string token;
};
explicit HomeAssistant(const Config& config);
~HomeAssistant();
float state = 0.0f;
int connect();
void disconnect() const;
void service(int timeout_ms = 0) const;
private:
uint16_t id = 2;
std::string tx;
lws_context* context = nullptr;
lws* websock = nullptr;
void event_handler(lws* wsi, lws_callback_reasons reason, char* in);
static int callback(lws* wsi, lws_callback_reasons reason, void* user, void* in, size_t len);
Config m_config;
};
#endif
#endif

View File

@@ -1,37 +0,0 @@
#ifndef LINUXSMARTIE_LCD_H
#define LINUXSMARTIE_LCD_H
#define LCD_LINES 4
#define LCD_COLS 40
#include <stdint.h>
struct lcd_t {
int fd;
uint32_t baud;
struct {
char cchar[8][11]; // 8 custom chars, each 11 bytes (3 byte command + 8 byte glyph)
char text[LCD_LINES * LCD_COLS + 8]; // number of chars on the lcd + 2*4 byte set cursor commands
} writebuf;
char port[96];
};
typedef uint8_t lcd_glyph_t[8];
void lcd_close(struct lcd_t *lcd);
void lcd_reinit(struct lcd_t *lcd);
void lcd_splash(struct lcd_t *lcd);
void lcd_clear(struct lcd_t *lcd);
void lcd_set_contrast(struct lcd_t *lcd, uint8_t contrast);
void lcd_set_backlight(struct lcd_t *lcd, bool enabled);
void lcd_set_brightness(struct lcd_t *lcd, uint8_t brightness);
void lcd_set_cursor(struct lcd_t *lcd, uint8_t row, uint8_t col);
void lcd_set_custom_char(struct lcd_t *lcd, uint8_t index, const lcd_glyph_t *glyph);
void lcd_write_buf(struct lcd_t *lcd, uint8_t row, uint8_t col, const char *str);
void lcd_writef(struct lcd_t *lcd, uint8_t x, uint8_t y, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
void lcd_clear_buf(struct lcd_t *lcd);
void lcd_flush_buf(struct lcd_t *lcd);
char lcd_bar(struct lcd_t *lcd, uint8_t char_id, double value);
int lcd_init(struct lcd_t *lcd);
#endif

View File

@@ -1,36 +0,0 @@
#ifndef LINUXSMARTIE_LOG_H
#define LINUXSMARTIE_LOG_H
/* ── kill switch ────────────────────────────────────────────────────────── */
/* comment out to disable all logging */
#define APPLOG_ENABLED
/* ─────────────────────────────────────────────────────────────────────── */
#ifdef APPLOG_ENABLED
typedef enum {
LOG_INF,
LOG_WRN,
LOG_ERR,
LOG_DBG,
LOG_START,
} log_level_t;
/* __PRETTY_FUNCTION__ is a GCC/Clang extension that gives the full
signature: "voi*d foo(int x)" — same thing source_location gave you.
Falls back to plain __func__ (C99) if somehow unavailable. */
#define applog(level, fmt, ...) \
log_write(level, __func__, fmt, ##__VA_ARGS__)
/* format attribute gives compile-time printf format checking —
better than wha*t std::format gave you at runtime */
void log_write(log_level_t level, const char *func, const char *fmt, ...)
__attribute__((format(printf, 3, 4)));
#else /* APPLOG_ENABLED not defined */
#define applog(...) ((void)0)
#endif /* APPLOG_ENABLED */
#endif /* LINUXSMARTIE_LOG_H */

96
include/log.hpp Normal file
View File

@@ -0,0 +1,96 @@
#ifndef LOG_HPP
#define LOG_HPP
// comment out to disable logging in general
#define applog(level, fmt, ...) utils::log(level, std::source_location::current(), fmt, ##__VA_ARGS__)
// comment out to disable logging to file
// #define LOG_FILE_LOCATION "linuxSmartie.log.gz"
#ifdef applog
#include <chrono>
#include <format>
#include <iostream>
#include <mutex>
#include <source_location>
#include <string_view>
#ifdef LOG_FILE_LOCATION
#include <zlib.h>
inline std::mutex fmtx; // file logging mutex
#endif
enum class LOG { INF, WRN, ERR, DBG, START };
namespace utils {
inline std::mutex cmtx; // console logging mutex
constexpr std::string pFname(std::string_view full) {
if (const auto pos = full.find(' '); pos != std::string_view::npos)
full.remove_prefix(pos + 1);
if (const auto pos = full.rfind('('); pos != std::string_view::npos)
full.remove_suffix(full.size() - pos);
return std::string(full);
}
template <typename... Args>
void log(const LOG level, const std::source_location& loc, std::format_string<Args...> str, Args&&... args) {
constexpr std::string_view reset = "\033[0m";
constexpr std::string_view pattern = "[{}{}{}] ";
std::string_view color;
switch (level) {
case LOG::INF:
color = "\033[1;32m";
break; // bold; green
case LOG::WRN:
color = "\033[1;33m"; // bold; yellow
break;
case LOG::ERR:
color = "\033[1;31m"; // bold; red
break;
case LOG::DBG:
break;
case LOG::START:
color = "\033[1;3;36m"; // bold; blinking; cyan
break;
}
/* log to console */
{
std::scoped_lock lock(cmtx);
std::cout << std::format(pattern, color, pFname(loc.function_name()), reset);
std::cout << std::format(str, std::forward<Args>(args)...) << std::endl;
}
#ifdef LOG_FILE_LOCATION
/* log to file */
{
auto now = std::chrono::system_clock::now();
auto local = std::chrono::current_zone()->to_local(now);
std::string msg = std::format("{:%x %H:%M:%S }", floor<std::chrono::milliseconds>(local)); // timestamp
std::format_to(std::back_inserter(msg), pattern, color, pFname(loc.function_name()), reset); // tag
std::format_to(std::back_inserter(msg), str, std::forward<Args>(args)...); // actual log message
std::scoped_lock lock(fmtx);
gzFile file = gzopen(LOG_FILE_LOCATION, "ab");
if (file) {
gzprintf(file, "%s\n", msg.c_str());
gzclose(file);
}
}
#endif
}
} // namespace utils
#else
#define applog(...)
#endif
#endif

View File

@@ -1,56 +0,0 @@
#ifndef LINUXSMARTIE_SYS_H
#define LINUXSMARTIE_SYS_H
#include "utils.h"
#include <stdint.h>
#define SYS_GET_TIME_STR(t) sys_get_time_string((char[16]){0}, 16, t)
#define SYS_GET_DATE_STR(t) sys_get_date_string((char[16]){0}, 16, t)
struct memory_t {
uint64_t free;
uint64_t used;
uint64_t total;
};
struct disk_t {
struct memory_t usage;
char mountpoint[64];
};
struct network_t {
uint32_t download;
uint32_t upload;
sensor_file_t f_rx;
sensor_file_t f_tx;
char device[16];
};
struct syssensors_t {
char hwmon[64];
// uint16_t fan_speed[1]; // just one fan (pump) we monitor
int16_t t_sensor; // ntc temp sensor
};
char *sys_get_time_string(char *buf, size_t bufsz, time_t t);
char *sys_get_date_string(char *buf, size_t bufsz, time_t t);
void sys_get_load_avg(double *load_avg);
void sys_get_distro(char* name, char* ver_str);
void sys_get_ram(struct memory_t *ram);
void sys_get_disk(struct disk_t *disk);
void sys_get_network(struct network_t *network);
void sys_netdev_detect(struct network_t *network);
void sys_detect(struct syssensors_t *s, const char *platform);
void sys_update_sensors(const struct syssensors_t *s);
#endif // LINUXSMARTIE_SYS_H

49
include/sys.hpp Normal file
View File

@@ -0,0 +1,49 @@
#ifndef SYS_HPP
#define SYS_HPP
#include <cstdint>
#include <string>
namespace sys {
constexpr char months[][4] = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
constexpr char wdays[][4] = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
struct sysSensors {
std::string hwmon;
uint16_t fan_speed[2] = {0}; // left and right cpu fans
int16_t t_sensor = 0; // ntc temp sensor
std::byte _pad[2];
};
struct Memory {
uint64_t free = 0;
uint64_t used = 0;
uint64_t total = 0;
};
struct Disk {
std::string mountpoint;
Memory usage;
};
struct TimeDate {
std::string time;
std::string date;
};
struct Network {
uint32_t download = 0;
uint32_t upload = 0;
};
void getTimeDate(TimeDate* td);
void getDisk(Disk* disk);
void getRam(Memory* mem);
void getNetwork(Network* net, const std::string& device);
int detect(sysSensors* s, const std::string& platform);
void updateSensors(sysSensors* s);
} // namespace sys
#endif

View File

@@ -1,60 +0,0 @@
#ifndef LINUXSMARTIE_UTILS_H
#define LINUXSMARTIE_UTILS_H
#include <stdint.h>
#include <sys/types.h> // NOLINT yes its, needed for ssize_t
#if __STDC_VERSION__ >= 202000L
#define C_STD "C23"
#elif __STDC_VERSION__ >= 201710L
#define C_STD "C17"
#elif __STDC_VERSION__ >= 201112L
#define C_STD "C11"
#elif __STDC_VERSION__ >= 199901L
#define C_STD "C99"
#else
#define C_STD "C89"
#endif
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define P_BYTES(bytes, start) pretty_bytes((bytes), (start), (char[16]){0}, 16)
#define P_NUM(num) pretty_num((num), (char[16]){0}, 16)
typedef enum : uint8_t {
SENSOR_I16,
SENSOR_U8,
SENSOR_U16,
SENSOR_U64,
SENSOR_FLOAT,
} sensor_type_t;
typedef struct {
sensor_type_t type;
int fd;
void* value;
uint32_t divider;
} sensor_file_t;
int get_instance_lock();
void timespec_add_ms(struct timespec *ts, uint16_t ms);
int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b);
char *pretty_num(double num, char *buf, size_t bufsz);
char *pretty_bytes(uint64_t bytes, int8_t start_suffix, char *buf, size_t bufsz);
int get_hwmon_dir(char *path, size_t bufsz);
void open_sensor(sensor_file_t* s, const char* dir, const char *file, void* value, uint32_t divider, sensor_type_t type);
void fast_read_sensor_into_mem(const sensor_file_t *s, void* val);
void fast_read_sensor(const sensor_file_t *s);
void close_sensor(const sensor_file_t* s);
#endif

80
include/utils.hpp Normal file
View File

@@ -0,0 +1,80 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include <fcntl.h>
#include <unistd.h>
#include <array>
#include <charconv>
#include <cstdint>
#include <filesystem>
#include <log.hpp>
#include <string>
namespace utils {
constexpr char fill_char = ' ';
enum just : uint8_t { right, left };
std::string pad(std::string s, uint8_t len, just j = right);
std::string center(std::string str, uint8_t len);
std::string getFirstDir(const std::string_view& path);
inline std::string pNum(const auto num, const uint8_t decs = 0) {
static std::array<char, 20> buf;
auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), static_cast<double>(num), std::chars_format::fixed, decs);
return {buf.data(), ptr};
}
/* converts large number in (k)bytes to human-readable numer with suffix and sane decimal */
std::string pBytes(const auto bytes, const bool isKbytes = false) {
double bytes_d = bytes;
for (constexpr std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'}; const auto& suffix : suffix_list) {
if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes
bytes_d /= 1024.0;
if (bytes_d < 1024) {
if (bytes_d > 100) return pNum(bytes_d) + suffix;
if (bytes_d > 10) return pNum(bytes_d, 1) + suffix;
return pNum(bytes_d, 2) + suffix;
}
}
return "++++";
}
/* reads file into a std::array buffer */
template <size_t N>
inline ssize_t readFile(const std::string& path, std::array<char, N>& buffer) {
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
applog(LOG::ERR, "Error reading {}", path);
return 0;
}
const ssize_t len = read(fd, buffer.data(), buffer.size());
if (len >= 0) buffer[len] = '\0';
close(fd);
return len;
}
/* reads a number fast and divides it if needed */
template <uint32_t Divider = 1, typename T, size_t N>
inline void fastReadNum(const std::string& path, std::array<char, N>& buf, T& result) {
readFile(path, buf);
if constexpr (Divider == 1) {
std::from_chars(buf.data(), buf.data() + N, result);
return;
}
uint64_t temp{};
std::from_chars(buf.data(), buf.data() + N, temp);
if constexpr (std::is_floating_point_v<T>)
result = temp / static_cast<T>(Divider);
else
result = static_cast<T>(temp / Divider);
}
inline bool fileExists(const std::string_view& path) {
return std::filesystem::exists(path);
}
} // namespace utils
#endif

View File

@@ -1,2 +0,0 @@
#pragma once
#define GIT_HASH "@VCS_TAG@"

View File

@@ -5,7 +5,6 @@ Description=HD44780 display service (linuxSmartie)
Type=simple
ExecStart=%h/.local/bin/linuxSmartie
Restart=on-failure
RestartSec=5
[Install]
WantedBy=graphical-session.target
WantedBy=default.target

20
main.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include <app.hpp>
int main() {
Application::Config config;
config.lcd.port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00"; // tis a symlink, can also be just /dev/ttyACM0)
config.lcd.refresh_interval_ms = 500;
config.lcd.contrast = 215;
config.cpu_pciaddr = "0000:00:18.3";
config.gpu_pciaddr = "0000:0a:00.0";
config.sys_sensor = "nct6775.656";
config.sys_ifname = "enp7s0";
config.home_dir = "/home/drath";
config.hass.url = "home.drath.win";
config.hass.entity = "sensor.gniazdko_komputer_moc";
config.hass.token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI0MzUxMTZiY2VjNTg0MTI3OGI4YmU0NTY4ZGQ5MzIyYiIsImlhdCI6MTc1NzQzMDI0MCwiZXhwIjoyMDcyNzkwMjQwfQ.6wKTlxEA0FLWfv-qW35vewUSvTxdVrZWqFwHT5Khu6E";
Application App(config);
return App.Run();
}

155
src/LCDDisplay.cpp Normal file
View File

@@ -0,0 +1,155 @@
#include "LCDDisplay.hpp"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <array>
#include <cstdint>
#include <cstring>
#include <log.hpp>
#include <string>
/* lcd size hardcoded to 4x40 idgaf */
LCDDisplay::LCDDisplay(const std::string& dev, const uint32_t baud) {
this->dev = dev;
this->baud = baud;
}
LCDDisplay::~LCDDisplay() {
this->close();
}
int LCDDisplay::Init() {
if (const int ret = this->open(); ret < 0) {
this->close();
return -1;
}
struct termios term{};
// Setup the serial port 8 data bits, no parity, 1 stopbit, no flow control
term.c_cflag = baud | CS8 | CSTOPB | CLOCAL | CREAD;
term.c_iflag = 0;
term.c_oflag = 0;
term.c_lflag = 0;
tcflush(lcd, TCIFLUSH);
tcsetattr(lcd, TCSANOW, &term);
// init linebuf to spaces
this->clearBuf();
// disable underline and block cursor
constexpr uint8_t cmdbuf[] = {254, 75, 254, 84};
const ssize_t ret = ::write(lcd, &cmdbuf, sizeof(cmdbuf));
(void)ret;
// clear display and set cursor home
this->clear();
return 0;
}
int LCDDisplay::open(const bool reopen) {
applog(LOG::INF, "Opening serial port at \"{}\"", dev);
if (lcd < 0 || reopen)
lcd = ::open(dev.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // ::open calls the global open() instead of LCDDisplay::open()
if (lcd < 0) {
applog(LOG::ERR, "Error opening serial port \"{}\"", dev); // err
return -1;
}
return 0;
}
void LCDDisplay::close() {
if (lcd >= 0) {
::close(lcd);
applog(LOG::INF, "LCD port closed");
}
lcd = -1;
}
void LCDDisplay::write(const uint8_t* cmdbuf, const uint8_t len) {
if (::write(lcd, cmdbuf, len) != len) {
applog(LOG::ERR, "failed to execute ::write, lost access to port?");
this->open(true);
}
}
void LCDDisplay::clear() {
constexpr uint8_t cmdbuf[] = {254, 88};
this->write(cmdbuf, sizeof(cmdbuf));
}
void LCDDisplay::setContrast(const uint8_t contrast) {
applog(LOG::INF, "Set contrast to {}", contrast);
const uint8_t cmdbuf[] = {254, 80, contrast};
this->write(cmdbuf, sizeof(cmdbuf));
}
void LCDDisplay::setBacklight(const bool enabled) {
applog(LOG::INF, "Set backlight to {}", enabled);
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(enabled ? 66 : 70), 255};
this->write(cmdbuf, enabled ? 3 : 2);
}
/* still doesnt work? */
void LCDDisplay::setBacklight(const uint8_t brightness) {
applog(LOG::INF, "Set backlight brightness to {}", brightness);
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(brightness ? 66 : 70), 255, brightness}; // 3rd char is timer, 4th is brightness
this->write(cmdbuf, brightness ? 4 : 2);
}
void LCDDisplay::setCursor(const uint8_t row, const uint8_t col) {
const uint8_t cmdbuf[] = {254, 71, static_cast<uint8_t>(col + 1), static_cast<uint8_t>(row + 1)};
this->write(cmdbuf, sizeof(cmdbuf));
}
/* reconsider so im not writing every time. prepend custom chars to the write buffer? */
void LCDDisplay::setCustomChar(const uint8_t index, const std::array<uint8_t, 8> cchar) {
uint8_t cmdbuf[] = {254, 78, index, 0, 0, 0, 0, 0, 0, 0, 0};
std::memcpy(&cmdbuf[3], &cchar, sizeof(cchar));
this->write(cmdbuf, sizeof(cmdbuf));
}
/* immediately writes string to the lcd at current cursor pos */
void LCDDisplay::writeStr(const std::string& str) {
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
}
/* immediately writes string to the lcd at given cursor pos */
void LCDDisplay::writeStr(const uint8_t row, const uint8_t col, const std::string& str) {
this->setCursor(row, col);
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
}
/* writes string to write buffer at calculated pos */
void LCDDisplay::writeBuf(const uint8_t row, const uint8_t col, const std::string& str) {
const size_t len = std::min(str.size(), writebuf.size() - col);
uint8_t pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
if (row >= 2) pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
std::memcpy(&writebuf[pos], str.c_str(), len);
}
/* initializes the write buffer to spaces */
void LCDDisplay::clearBuf() {
std::memset(writebuf.data(), ' ', writebuf.size());
}
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
void LCDDisplay::flushBuf() {
constexpr uint8_t setDisp[] = {254, 71, 1, 1}; // set cursor to char 1, line 1
std::memcpy(&writebuf[0], setDisp, 4);
std::memcpy(&writebuf[2 * LCD_COLS + 4], setDisp, 4);
writebuf[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
this->write(reinterpret_cast<uint8_t*>(writebuf.data()), writebuf.size());
}
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
char LCDDisplay::bar(const uint8_t char_id, const double value) {
std::array<uint8_t, 8> b{};
b.fill(0b01110);
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
b[i] = 0;
this->setCustomChar(char_id, b);
return cc(char_id);
}

255
src/aio.c
View File

@@ -1,255 +0,0 @@
#include "aio.h"
#include "log.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
static constexpr uint16_t usb_timeout = 250;
static constexpr uint8_t buf_size = 32; // needs 32 bc of overflow issue on these aios?
static constexpr uint8_t max_curve_points = 7;
static uint8_t req[buf_size], resp[buf_size];
static const struct curve_point_t fan_curve[] = {
{ .temp = 35, .duty = 25 }, // ~350rpm
{ .temp = 40, .duty = 40 },
{ .temp = 46, .duty = 60 },
{ .temp = 50, .duty = 100 }
};
static void aio_usb_claim(libusb_device_handle *dev) {
int ret = libusb_claim_interface(dev, 0);
if (ret != 0) {
applog(LOG_ERR, "failed to claim AIO device: %s", libusb_error_name(ret));
return;
}
ret = libusb_control_transfer(dev, (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE), 0x02, 0x02, 0, nullptr, 0, usb_timeout);
if (ret < 0)
applog(LOG_ERR, "AIO clear-to-send failed: %s", libusb_error_name(ret));
}
static void aio_usb_release(libusb_device_handle *dev) {
libusb_release_interface(dev, 0);
}
static int aio_post(libusb_device_handle *dev, const int req_len, const int resp_len) {
// applog(LOG_DBG, "writing to aio reg 0x%02x", req[0]);
int ret = 0;
ret += libusb_bulk_transfer(dev, 0x01, req, req_len, nullptr, usb_timeout);
ret += libusb_bulk_transfer(dev, 0x81, resp, resp_len, nullptr, usb_timeout);
return ret;
}
static float aio_get_water_temp(libusb_device_handle *dev) {
req[0] = 0xa9;
aio_post(dev, 1, 6);
return (float)resp[3] + (float)resp[4] / 10.0f;
}
static uint16_t aio_get_pump_speed(libusb_device_handle *dev) {
req[0] = 0x31;
if (aio_post(dev, 1, 5) != 0)
return UINT16_MAX;
return (uint16_t)(resp[3] << 8 | resp[4]);
}
static enum aio_pump_mode_t aio_get_pump_mode(libusb_device_handle *dev) {
req[0] = 0x33;
if (aio_post(dev, 1, 4) != 0)
return AIO_PUMP_MODE_UNKNOWN;
return (enum aio_pump_mode_t)resp[3];
}
static void aio_get_fan_speed(libusb_device_handle *dev, uint16_t *speeds) {
for (uint8_t i = 0; i < 2; i++) {
req[0] = 0x41;
req[1] = i;
if (aio_post(dev, 2, 6) != 0)
speeds[i] = UINT16_MAX;
speeds[i] = (uint16_t)(resp[4] << 8 | resp[5]);
}
}
static void aio_set_fan_duty(libusb_device_handle *dev, const uint8_t *duty) {
if (duty == nullptr)
return;
static uint8_t last_duty = 255;
if (duty[0] == last_duty)
return;
req[0] = 0x42;
for (uint8_t i = 0; i < 2; i++) {
req[1] = i;
req[2] = MIN(MAX(duty[i], 0), 100);
if (aio_post(dev, 3, buf_size) == 0)
last_duty = req[2];
}
}
static char *aio_get_firmware_version(libusb_device_handle *dev, char *buf) {
req[0] = 0xaa;
aio_post(dev, 1, buf_size);
sprintf(buf, "%d.%d.%d.%d", resp[3], resp[4], resp[5], resp[6]);
return buf;
}
static void aio_set_pump_mode(libusb_device_handle *dev, const enum aio_pump_mode_t mode) {
static enum aio_pump_mode_t last_mode = AIO_PUMP_MODE_UNKNOWN;
if (mode == last_mode)
return;
applog(LOG_DBG, "setting pump to %u", mode);
req[0] = 0x32;
req[1] = (uint8_t)mode;
if (aio_post(dev, 2, 6) == 0)
last_mode = mode;
}
static void aio_set_color(libusb_device_handle *dev, const enum aio_color_mode_t mode, const uint8_t speed, const struct rgb_t *colors, const uint8_t color_count) {
if (colors == nullptr) // NOLINT no its not
return;
constexpr uint8_t modes[] = {0x52, 0x55, 0x55, 0x58};
req[0] = 0x56;
req[1] = color_count;
// sort out and send color table
memcpy(&req[2], colors, color_count * 3);
if (color_count < 2 || mode == AIO_COLOR_FIXED) {
memcpy(&req[5], colors, 3); // copy the same color to the 2nd pos in the table
req[1] = 2;
}
aio_post(dev, 2 + req[1] * 3, buf_size);
// send speed, skip if fixed color
if (mode != AIO_COLOR_FIXED) {
constexpr uint8_t speed_shift[] = {0x46, 0x28, 0x0F};
constexpr uint8_t speed_pulse[] = {0x50, 0x37, 0x1E};
req[0] = 0x53;
req[1] = mode == AIO_COLOR_SHIFT ? speed_shift[speed] : speed_pulse[speed];
aio_post(dev,2, buf_size);
}
// actually set the aio to desired mode
req[0] = modes[mode];
req[1] = 0x01;
aio_post(dev, 2, buf_size);
}
static void aio_set_fan_curve(libusb_device_handle *dev, const struct curve_point_t *curve, const uint8_t count) {
if (curve == nullptr)
return;
if (count < 1 || count > max_curve_points) {
applog(LOG_ERR, "aio: curve must have 1-%d points", max_curve_points);
return;
}
struct curve_point_t padded[max_curve_points];
for (int i = 0; i < max_curve_points; i++)
padded[i] = (i < count) ? curve[i] : curve[count - 1];
req[0] = 0x40;
// setup request
for (int i = 0; i < max_curve_points; i++)
req[2 + i] = padded[i].temp;
for (int i = 0; i < max_curve_points; i++)
req[9 + i] = padded[i].duty;
// set curve for both fans
for (uint8_t i = 0; i < 2; i++) {
req[1] = i;
aio_post(dev, 16, sizeof(resp));
}
applog(LOG_INF, "set up AIO fan curve");
}
void aio_detect(struct aio_t *aio) {
libusb_init(nullptr);
aio->handle = libusb_open_device_with_vid_pid(nullptr, AIO_USB_VID, AIO_USB_PID);
if (aio->handle == nullptr) {
applog(LOG_ERR, "Can't open AIO USB device %x:%x", AIO_USB_VID, AIO_USB_PID);
return;
}
/* detaches other software that might take the device? */
if (libusb_kernel_driver_active(aio->handle, 0))
libusb_detach_kernel_driver(aio->handle, 0);
aio_usb_claim(aio->handle);
applog(LOG_INF, "found Corsair AIO device at %04x:%04x, fw ver: %s", AIO_USB_VID, AIO_USB_PID, aio_get_firmware_version(aio->handle, (char[16]){0}));
aio_set_fan_duty(aio->handle, nullptr); // so it doenst complain the func is unused
aio_set_fan_curve(aio->handle, fan_curve, sizeof(fan_curve) / sizeof(fan_curve[0]));
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_QUIET); // needed for init ig?
aio_set_color(aio->handle, AIO_COLOR_FIXED, 0, nullptr, 0); // so it doenst complain the func is unused
aio_usb_release(aio->handle);
}
void aio_update(struct aio_t *aio) {
if (aio->handle == nullptr)
return;
aio_usb_claim(aio->handle);
aio->water_temp = aio_get_water_temp(aio->handle);
aio->pump_speed = aio_get_pump_speed(aio->handle);
aio->pump_mode = aio_get_pump_mode(aio->handle);
aio_get_fan_speed(aio->handle, aio->fan_speed);
const uint16_t avg_rpm = (uint16_t)((aio->fan_speed[0] + aio->fan_speed[1]) / 2);
switch (aio->pump_mode) {
case AIO_PUMP_MODE_QUIET:
if (avg_rpm > 400)
aio->pump_counter++;
else
aio->pump_counter = 0;
if (aio->pump_counter >= 300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
}
break;
case AIO_PUMP_MODE_BALANCED:
if (avg_rpm >= 700)
aio->pump_counter++;
else if (avg_rpm <= 375)
aio->pump_counter--;
else
aio->pump_counter = 0;
if (aio->pump_counter >= 300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_EXTREME);
}
if (aio->pump_counter <= -300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_QUIET);
}
break;
case AIO_PUMP_MODE_EXTREME:
if (avg_rpm <= 500)
aio->pump_counter--;
else
aio->pump_counter = 0;
if (aio->pump_counter <= -300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
}
break;
case AIO_PUMP_MODE_UNKNOWN:
break;
}
aio_usb_release(aio->handle);
}
void aio_close(const struct aio_t *aio) {
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
libusb_attach_kernel_driver(aio->handle, 0); // reattach so liquidctl still works
libusb_close(aio->handle);
libusb_exit(nullptr);
applog(LOG_INF, "AIO device closed");
}

209
src/app.cpp Normal file
View File

@@ -0,0 +1,209 @@
#include <LCDDisplay.hpp>
#include <app.hpp>
#include <chrono>
#include <cpu.hpp>
#include <csignal>
#include <functional>
#include <gpu.hpp>
#include <hass.hpp>
#include <log.hpp>
#include <mutex>
#include <stop_token>
#include <string>
#include <sys.hpp>
#include <thread>
#include <utility>
#include <utils.hpp>
#include <vector>
Application::Application(Config config) {
m_config = std::move(config);
// set up signals
sigemptyset(&m_control.sigset);
sigaddset(&m_control.sigset, SIGINT);
sigaddset(&m_control.sigset, SIGTERM);
applog(LOG::START, "Application initialized");
}
Application::~Application() {
auto level = LOG::START;
if (m_ret < 0) level = LOG::WRN;
applog(level, "Application destroyed");
}
int Application::Run() {
int sig;
m_mainPid = getpid();
pthread_sigmask(SIG_BLOCK, &m_control.sigset, nullptr);
{
std::vector<std::jthread> threads;
std::function<void(std::stop_token)> funcs[] = {
[this](const std::stop_token& st) { displayThread(st); },
[this](const std::stop_token& st) { sensorsThread(st); },
[this](const std::stop_token& st) { hassThread(st); },
};
// start threads in order
for (auto& f : funcs) {
threads.emplace_back(f);
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // start threads with delay so displayThread can quit the app if theres no lcd
}
// waits for signal, the threads are stopped when we go out of scope
sigwait(&m_control.sigset, &sig);
applog(LOG::WRN, "got stop signal, shutting down...");
}
return m_ret;
}
void Application::Crash(const int ret) {
m_ret = ret;
kill(m_mainPid, SIGINT);
}
void Application::hassThread(const std::stop_token& st) { //NOLINT
applog(LOG::START, "Thread started");
#ifdef ENABLE_HOME_ASSISTANT
static double power;
HomeAssistant::Config config;
config.address = m_config.hass.url;
config.entity = m_config.hass.entity;
config.token = m_config.hass.token;
HomeAssistant pc_power(config);
pc_power.connect();
while (!st.stop_requested()) {
pc_power.service();
if (power != pc_power.state) {
power = pc_power.state;
{
std::lock_guard lock(m_control.sensors);
m_sensors.hass_state = power;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
#else
(void)st;
applog(LOG::WRN, "feature disabled at compile time, probably no libwebsockets on host. exiting thread");
{
std::lock_guard lock(m_control.sensors);
m_sensors.hass_state = -1.0;
}
#endif
}
void Application::sensorsThread(const std::stop_token& st) {
applog(LOG::START, "Thread started, poll interval {} ms", m_config.lcd.refresh_interval_ms);
int ret = cpu::detect(&m_sensors.cpu, m_config.cpu_pciaddr);
ret += gpu::detect(&m_sensors.gpu, m_config.gpu_pciaddr);
ret += sys::detect(&m_sensors.sys, m_config.sys_sensor);
m_sensors.home.mountpoint = m_config.home_dir;
if (ret < 0)
applog(LOG::ERR, "Failed to detect some sensors");
while (!st.stop_requested()) {
auto start = std::chrono::steady_clock::now();
{
std::lock_guard lock(m_control.sensors);
sys::getTimeDate(&m_sensors.td);
getloadavg(m_sensors.loadavg, 3);
sys::getRam(&m_sensors.ram);
sys::getDisk(&m_sensors.home);
sys::getNetwork(&m_sensors.net, m_config.sys_ifname);
sys::updateSensors(&m_sensors.sys);
cpu::update(&m_sensors.cpu);
gpu::update(&m_sensors.gpu);
}
// log(LOG::DBG, "took {} microseconds", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
m_control.display.release(); // unlocks display loop
std::this_thread::sleep_until(start + std::chrono::milliseconds(m_config.lcd.refresh_interval_ms));
}
applog(LOG::INF, "Thread stopped");
}
void Application::displayThread(const std::stop_token& st) {
std::stop_callback cb(st, [&] { m_control.display.release(); });
applog(LOG::START, "Thread started");
// init internal variables of LCDDisplay class
LCDDisplay lcd(m_config.lcd.port);
if (const int ret = lcd.Init(); ret < 0) {
applog(LOG::ERR, "Failed to initialize LCDDisplay");
this->Crash(ret);
return;
}
// basic lcd setup
lcd.setContrast(m_config.lcd.contrast);
lcd.setBacklight(true);
lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius
lcd.setCustomChar(1, {0, 0, 4, 10, 10, 17, 31, 0}); // delta
lcd.setCustomChar(2, {0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
lcd.setCustomChar(3, {4, 14, 21, 4, 4, 4, 0}); // up arrow
lcd.setCustomChar(4, {4, 4, 4, 21, 14, 4, 0}); // down arrow
// welcome msg
lcd.writeBuf(2, 0, utils::center("Waiting for sensors...", 40));
lcd.flushBuf();
lcd.clearBuf();
// actual main display loop
while (!st.stop_requested()) {
m_control.display.try_acquire_for(std::chrono::milliseconds(static_cast<int>(m_config.lcd.refresh_interval_ms * 1.25))); // holds the loop until sensor data is ready, max 1.25x of the loop time
if (st.stop_requested()) break; // needed, otherwise it wont quit correctly
{
std::lock_guard lock(m_control.sensors);
// prepare some tricky sensors
std::string gpu_temps = std::to_string(m_sensors.gpu.temp_edge);
if (m_sensors.gpu.temp_hotspot) {
const auto delta = m_sensors.gpu.temp_hotspot - m_sensors.gpu.temp_edge;
gpu_temps.append("/" + std::to_string(m_sensors.gpu.temp_hotspot));
gpu_temps.insert(0, LCDDisplay::cc(1) + std::to_string(delta) + LCDDisplay::cc(0) + " ");
}
uint16_t cpu_fan = 0;
for (const auto& fan : m_sensors.sys.fan_speed)
cpu_fan += fan;
cpu_fan /= 2;
// write to display buffer
lcd.writeBuf(0, 0, utils::pad(m_sensors.td.date, 11, utils::just::left) + " " + m_sensors.td.time + lcd.bar(7, m_sensors.gpu.usage_core / 100.0));
lcd.writeBuf(0, 26, utils::pNum(m_sensors.loadavg[0], m_sensors.loadavg[0] > 10 ? 1 : 2));
lcd.writeBuf(0, 31, utils::pNum(m_sensors.loadavg[1], m_sensors.loadavg[1] > 10 ? 1 : 2));
lcd.writeBuf(0, 36, utils::pNum(m_sensors.loadavg[2], m_sensors.loadavg[2] > 10 ? 1 : 2));
lcd.writeBuf(1, 0, utils::pad(std::to_string(m_sensors.cpu.tctl) + "/" + std::to_string(m_sensors.cpu.tccd1) + LCDDisplay::cc(0), 8, utils::just::left));
lcd.writeBuf(1, 7, utils::pad(utils::pBytes(m_sensors.ram.used, true), 6, utils::just::left));
if (m_sensors.hass_state >= 0)
lcd.writeBuf(1, 13, utils::pad(utils::pNum(m_sensors.hass_state) + "W", 4));
lcd.writeBuf(1, 19, std::to_string(m_sensors.sys.t_sensor) + LCDDisplay::cc(0)); // t_sensor temp
lcd.writeBuf(1, 32 - gpu_temps.size(), utils::pad(utils::pBytes(m_sensors.gpu.vram_used), 6));
lcd.writeBuf(1, 39 - gpu_temps.size(), gpu_temps + LCDDisplay::cc(0));
lcd.writeBuf(2, 0, utils::pad(std::to_string(static_cast<int>(m_sensors.cpu.ptotal)) + "W", 8, utils::just::left));
lcd.writeBuf(2, m_sensors.cpu.ptotal < 100 ? 4 : 5, std::to_string(cpu_fan) + "R"); // avg fan speed
lcd.writeBuf(2, 12, LCDDisplay::cc(3) + utils::pBytes(m_sensors.net.upload));
lcd.writeBuf(2, 19, LCDDisplay::cc(4) + utils::pBytes(m_sensors.net.download));
lcd.writeBuf(2, 26, utils::pad(std::to_string(m_sensors.gpu.fan_speed) + "R", 5));
lcd.writeBuf(2, 31, utils::pad(std::to_string(m_sensors.gpu.power_avg) + "/" + std::to_string(m_sensors.gpu.power_cap) + "W", 9));
lcd.writeBuf(3, 0, std::to_string(m_sensors.cpu.avg_freq) + "MHz");
lcd.writeBuf(3, 14, utils::pad(LCDDisplay::cc(2) + utils::pBytes(m_sensors.home.usage.free), 6));
lcd.writeBuf(3, 21, utils::pad(std::to_string(m_sensors.gpu.clock_core) + "/" + std::to_string(m_sensors.gpu.clock_mem) + "MHz ", 13));
lcd.writeBuf(3, 34, utils::pad(utils::pNum(m_sensors.gpu.vcore, 3) + "V", 5));
}
lcd.flushBuf();
lcd.clearBuf();
}
// goodbye msg
lcd.writeBuf(2, 0, utils::center("Goodbye", 40));
lcd.flushBuf();
}

105
src/cpu.c
View File

@@ -1,105 +0,0 @@
#include "cpu.h"
#include "log.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static sensor_file_t* sensors;
static sensor_file_t* cpu_freqs;
static constexpr uint64_t max_energy_range_uj = 65532610987;
void cpu_detect(struct cpu_sensors_t *t, const char *pci_dev) {
t->module = unknown;
t->num_cores = (uint8_t)sysconf(_SC_NPROCESSORS_ONLN);
snprintf(t->hwmon, sizeof(t->hwmon), "%s/hwmon", pci_dev);
const int ret = get_hwmon_dir(t->hwmon, sizeof(t->hwmon));
if (ret != 0) {
t->hwmon[0] = '\0';
applog(LOG_WRN, "Detected %d cores, module enum #%d. Can't get hwmon path, skipping", t->num_cores, t->module);
return;
}
// fast_read_num_u64("/sys/class/powercap/intel-rapl:0/", "max_energy_range_uj", buf, &max_energy_range_uj, 1);
// t->module = read_file(t->hwmon, "/in1_input", nullptr, 0) ? zenpower : k10temp; // if we can read zenpower file then zenpower. idk if this is reliable, we're trying to avoid just reading the name
t->module = k10temp; // just assume its k10temp until we figure out a better way to detect it
applog(LOG_INF, "Detected %d cores, module enum #%d in path %s", t->num_cores, t->module, t->hwmon);
cpu_freqs = malloc(sizeof(sensor_file_t) * t->num_cores);
sensors = malloc(sizeof(sensor_file_t) * (t->module == zenpower ? 4 : 3));
for (uint8_t i = 0; i < t->num_cores; i++) {
char p[128];
sprintf(p, "/sys/devices/system/cpu/cpu%d/cpufreq/", i);
open_sensor(&cpu_freqs[i], p, "cpuinfo_avg_freq", nullptr, 1000, SENSOR_U64);
}
if (max_energy_range_uj != 0)
open_sensor(&sensors[0], "/sys/class/powercap/intel-rapl:0/", "energy_uj", nullptr, 1, SENSOR_U64);
if (t->module == k10temp) {
open_sensor(&sensors[1], t->hwmon, "/temp1_input", &t->tctl, 1000, SENSOR_I16);
open_sensor(&sensors[2], t->hwmon, "/temp3_input", &t->tccd1, 1000, SENSOR_I16);
} else if (t->module == zenpower) {
open_sensor(&sensors[1], t->hwmon, "/temp1_input", &t->tdie, 1000, SENSOR_I16);
open_sensor(&sensors[2], t->hwmon, "/temp2_input", &t->tctl, 1000, SENSOR_I16);
open_sensor(&sensors[3], t->hwmon, "/temp3_input", &t->tccd1, 1000, SENSOR_I16);
}
}
void cpu_update(struct cpu_sensors_t *c) {
struct energy_t {
struct timespec ts;
uint64_t energy;
};
static struct energy_t prev;
struct energy_t curr = {};
uint64_t energy_delta;
// read power from intel-rapl? works on amd too, btop uses this trick, seems accurate
// it tracks with "socket power" in hwinfo on windows, its actually the same thing i had in lcdsmartie
clock_gettime(CLOCK_MONOTONIC, &curr.ts);
fast_read_sensor_into_mem(&sensors[0], &curr.energy);
if (curr.energy >= prev.energy)
energy_delta = curr.energy - prev.energy;
else
energy_delta = (max_energy_range_uj - prev.energy) + curr.energy;
c->ptotal = (float)energy_delta / (float)timespec_diff_us(&curr.ts, &prev.ts);
if (c->ptotal > 500.0f || c->ptotal < 0.0f)
c->ptotal = 0.0f;
else
prev = curr;
// lots of reads but still faster than reading /proc/cpuinfo
c->avg_freq = 0;
for (uint8_t i = 0; i < c->num_cores; i++) {
uint64_t t = 0;
fast_read_sensor_into_mem(&cpu_freqs[i], &t);
c->avg_freq += (uint32_t)t;
}
c->avg_freq /= (c->num_cores > 1) ? c->num_cores : 1;
if (c->module == unknown)
return;
if (c->module == k10temp) {
fast_read_sensor(&sensors[1]);
fast_read_sensor(&sensors[2]);
}
// commented out unused values so its faster
if (c->module == zenpower) {
fast_read_sensor(&sensors[1]);
fast_read_sensor(&sensors[2]);
fast_read_sensor(&sensors[3]);
// utils::fastReadNum<1000>(c->hwmon + "/in1_input", buf, c->vcore);
// utils::fastReadNum<1000>(c->hwmon + "/in2_input", buf, c->vsoc);
}
}

72
src/cpu.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include <array>
#include <chrono>
#include <cpu.hpp>
#include <cstdint>
#include <log.hpp>
#include <string>
#include <thread>
#include <utils.hpp>
namespace cpu {
int detect(cpuSensors* target, const std::string& pci) {
const std::string path = "/sys/bus/pci/devices/" + pci + "/hwmon";
try {
target->hwmon = utils::getFirstDir(path);
} catch (...) {
applog(LOG::ERR, "NO CPU HWMON KERNEL MODULE DETECTED");
return -1;
}
target->module = utils::fileExists(target->hwmon + "/in1_input") ? zenpower : k10temp; // idk if this is reliable, we're trying to avoid just reading the name
target->num_cores = std::thread::hardware_concurrency();
applog(LOG::INF, "Detected {} cores, module enum #{} in path \"{}\"", target->num_cores, static_cast<uint8_t>(target->module), target->hwmon);
return 0;
}
void update(cpuSensors* c) {
static std::array<char, 32> buf;
struct EnergyTs {
uint64_t energy = 0;
std::chrono::time_point<std::chrono::steady_clock> ts;
};
static EnergyTs prev;
EnergyTs curr;
// read power from intel-rapl? works on amd too, btop uses this trick, seems accurate
// it tracks with "socket power" in hwinfo on windows, its actually the same thing i had in lcdsmartie
curr.ts = std::chrono::steady_clock::now();
utils::fastReadNum("/sys/class/powercap/intel-rapl:0/energy_uj", buf, curr.energy);
c->ptotal = static_cast<float>(curr.energy - prev.energy) / static_cast<float>(std::chrono::duration_cast<std::chrono::microseconds>(curr.ts - prev.ts).count());
prev = curr;
// lots of reads but still faster than reading /proc/cpuinfo
c->avg_freq = 0;
for (uint8_t i = 0; i < c->num_cores; i++) {
uint32_t t = 0;
utils::fastReadNum<1000>("/sys/devices/system/cpu/cpu" + std::to_string(i) + "/cpufreq/cpuinfo_avg_freq", buf, t);
c->avg_freq += t;
}
c->avg_freq /= (c->num_cores > 1) ? c->num_cores : 1;
if (c->hwmon.empty())
return;
// commented out unused values so its faster
if (c->module == zenpower) {
utils::fastReadNum<1000>(c->hwmon + "/temp1_input", buf, c->tdie);
utils::fastReadNum<1000>(c->hwmon + "/temp2_input", buf, c->tctl);
utils::fastReadNum<1000>(c->hwmon + "/temp3_input", buf, c->tccd1);
// utils::fastReadNum<1000>(c->hwmon + "/in1_input", buf, c->vcore);
// utils::fastReadNum<1000>(c->hwmon + "/in2_input", buf, c->vsoc);
}
if (c->module == k10temp) {
utils::fastReadNum<1000>(c->hwmon + "/temp1_input", buf, c->tctl);
utils::fastReadNum<1000>(c->hwmon + "/temp3_input", buf, c->tccd1);
}
}
} // namespace cpu

136
src/gpu.c
View File

@@ -1,136 +0,0 @@
#include "gpu.h"
#include "gpu_metrics.h"
#include <stdio.h>
#include "log.h"
#include "utils.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static constexpr char rel_metrics_path[] = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path
static constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer
static sensor_file_t* sensors;
static int gpu_metrics_fd;
void gpu_detect(struct gpu_sensors_t *t, const char *pci) {
snprintf(t->hwmon, sizeof(t->hwmon), "%s/hwmon", pci);
const int ret = get_hwmon_dir(t->hwmon, sizeof(t->hwmon));
if (ret != 0) {
t->hwmon[0] = '\0';
applog(LOG_WRN, "Detected GPU hwmon");
return;
}
t->has_gpu_metrics = false;
char p[160];
char *nt = stpcpy(p, t->hwmon);
strcpy(nt, rel_metrics_path);
gpu_metrics_fd = open(p, O_RDONLY | O_CLOEXEC);
if (gpu_metrics_fd > 0) {
t->has_gpu_metrics = true;
char raw_metrics[max_metrics_length];
read(gpu_metrics_fd, raw_metrics, sizeof(raw_metrics));
const struct metrics_table_header *header = (struct metrics_table_header *)raw_metrics;
applog(LOG_INF, "Found gpu_metrics_v%d_%d at %s", header->format_revision, header->content_revision, p);
}
sensors = malloc(sizeof(sensor_file_t) * (t->has_gpu_metrics ? 10 : 5));
// we always open these
open_sensor(&sensors[0], t->hwmon, "/in0_input", &t->vcore, 1000, SENSOR_FLOAT);
open_sensor(&sensors[1], t->hwmon, "/fan1_input", &t->fan_speed, 1, SENSOR_U16);
open_sensor(&sensors[2], t->hwmon, "/power1_cap", &t->power_cap, 1000000, SENSOR_U16);
open_sensor(&sensors[3], t->hwmon, "/power1_average", &t->power_avg, 1000000, SENSOR_U16);
open_sensor(&sensors[4], t->hwmon, "/../../mem_info_vram_used", &t->vram_used, 1, SENSOR_U64);
if (t->has_gpu_metrics == false) {
open_sensor(&sensors[5], t->hwmon, "/freq1_input", &t->clock_core, 1000000, SENSOR_U16);
open_sensor(&sensors[6], t->hwmon, "/freq2_input", &t->clock_mem, 1000000, SENSOR_U16);
open_sensor(&sensors[7], t->hwmon, "/power1_input",&t->power_avg, 1000000, SENSOR_U16);
open_sensor(&sensors[8], t->hwmon, "/temp1_input", &t->temp_edge, 1000, SENSOR_U8);
open_sensor(&sensors[9], t->hwmon, "/../../gpu_busy_percent", &t->usage_core, 1, SENSOR_U8);
}
}
/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */
static void fill_missing_metrics() {
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
// utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
// utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
for (uint8_t i = 0; i < 5; i++)
fast_read_sensor(&sensors[i]);
}
/* get sensors for pre-vega cards (rx480/580) */
static void update_from_fs() {
for (uint8_t i = 5; i < 10; i++)
fast_read_sensor(&sensors[i]);
}
static void update_from_metrics(struct gpu_sensors_t *g) {
char raw_metrics[max_metrics_length];
lseek(gpu_metrics_fd, 0, SEEK_SET);
read(gpu_metrics_fd, raw_metrics, sizeof(raw_metrics));
const struct metrics_table_header *header = (struct metrics_table_header *)raw_metrics;
switch (header->format_revision * 10 + header->content_revision) {
/* rx5700xt, rx7800xt, rx9070xt */
case 13: {
const struct gpu_metrics_v1_3 *metrics = (struct gpu_metrics_v1_3 *)raw_metrics;
g->usage_core = (uint8_t)metrics->average_gfx_activity;
g->usage_umc = (uint8_t)metrics->average_umc_activity;
g->usage_media = (uint8_t)metrics->average_mm_activity;
g->temp_edge = (uint8_t)metrics->temperature_edge;
g->temp_hotspot = (uint8_t)metrics->temperature_hotspot;
g->temp_mem = (uint8_t)metrics->temperature_mem;
g->temp_vrm_gfx = (uint8_t)metrics->temperature_vrgfx;
g->temp_vrm_soc = (uint8_t)metrics->temperature_vrsoc;
g->temp_vrm_mem = (uint8_t)metrics->temperature_vrmem;
// g->power_avg = metrics->average_socket_power; // jumpy on 9070xt
g->fan_speed = metrics->current_fan_speed;
g->clock_core = metrics->average_gfxclk_frequency;
g->clock_mem = metrics->average_uclk_frequency; // mem controller clock, needs to be 2x on 5700xt for some reason
// hacks for rx7800xt
// g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock?
// g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping
break;
}
/* ryzen 5 5600g */
case 22: {
const struct gpu_metrics_v2_2 *metrics = (struct gpu_metrics_v2_2 *)raw_metrics;
g->temp_edge = (uint8_t)(metrics->temperature_gfx / 100);
break;
}
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
default: {
applog(LOG_WRN, "Unsupported gpu_metrics_v%d_%d", header->format_revision, header->content_revision);
break;
}
}
}
/* reads values from system and updates the target gpu_sensors_t struct */
void gpu_update(struct gpu_sensors_t *g) {
const bool hwmon_valid = g->hwmon[0] != '\0';
if (hwmon_valid)
fill_missing_metrics();
if (g->has_gpu_metrics)
update_from_metrics(g);
else if (hwmon_valid)
update_from_fs();
if (g->fan_speed > 9999)
g->fan_speed = 9999; // sanity check fan speed
}

123
src/gpu.cpp Normal file
View File

@@ -0,0 +1,123 @@
#include <array>
#include <gpu.hpp>
#include <gpu_metrics.hpp>
#include <log.hpp>
#include <string>
#include <utils.hpp>
/* maybe have a read pp_table function */
/* would be neat to wrap it in a class but eh namespace is good enough */
namespace gpu {
const std::string rel_metrics_path = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path
constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer
/* takes pointer to a target gpu::sensors struct and updates its hwmon path and detects gpu_metrics */
int detect(gpuSensors* target, const std::string& pci) {
try {
target->hwmon = utils::getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon");
} catch (...) {
applog(LOG::ERR, "Couldn't find GPU hwmon");
return -1;
}
applog(LOG::INF, "Detected path \"{}\"", target->hwmon);
if (utils::fileExists(target->hwmon + rel_metrics_path)) {
std::array<char, max_metrics_length> raw_metrics{};
if (!utils::readFile(target->hwmon + rel_metrics_path, raw_metrics))
return -1;
const auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics.data());
applog(LOG::INF, "Found gpu_metrics_v{}_{}", header->format_revision, header->content_revision);
target->hasGpuMetrics = true;
}
return 0;
}
/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */
void fillMissingMetrics(gpuSensors* g) {
static std::array<char, 32> buf;
utils::fastReadNum<1000>(g->hwmon + "/in0_input", buf, g->vcore);
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
utils::fastReadNum(g->hwmon + "/fan1_input", buf, g->fan_speed);
utils::fastReadNum<1000000>(g->hwmon + "/power1_cap", buf, g->power_cap);
// utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
utils::fastReadNum(g->hwmon + "/../../mem_info_vram_used", buf, g->vram_used);
// utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
}
/* get sensors for pre-vega cards (rx480/580) */
void updateFromFs(gpuSensors* g) {
static std::array<char, 32> buf;
utils::fastReadNum<1000000>(g->hwmon + "/freq1_input", buf, g->clock_core);
utils::fastReadNum<1000000>(g->hwmon + "/freq2_input", buf, g->clock_mem);
utils::fastReadNum<1000000>(g->hwmon + "/power1_input", buf, g->power_avg);
utils::fastReadNum<1000>(g->hwmon + "/temp1_input", buf, g->temp_edge);
utils::fastReadNum(g->hwmon + "/../../gpu_busy_percent", buf, g->usage_core);
}
/* get sensors for vega and newer (rx5700xt, rx7800xt [*]) */
void updateFromMetrics(gpuSensors* g) {
std::array<char, max_metrics_length> raw_metrics{};
if (!utils::readFile(g->hwmon + rel_metrics_path, raw_metrics)) return;
switch (const auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics.data()); header->format_revision * 10 + header->content_revision) {
/* rx5700xt, rx7800xtx rx9070xt */
case 13: {
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v1_3*>(raw_metrics.data());
g->usage_core = metrics->average_gfx_activity;
g->usage_umc = metrics->average_umc_activity;
g->usage_media = metrics->average_mm_activity;
g->temp_edge = metrics->temperature_edge;
g->temp_hotspot = metrics->temperature_hotspot;
g->temp_mem = metrics->temperature_mem;
g->temp_vrm_gfx = metrics->temperature_vrgfx;
g->temp_vrm_soc = metrics->temperature_vrsoc;
g->temp_vrm_mem = metrics->temperature_vrmem;
g->power_avg = metrics->average_socket_power;
g->fan_speed = metrics->current_fan_speed;
// hacks for rx7800xt
// g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock?
// g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping
// hacks for rx5700xt
g->clock_core = metrics->average_gfxclk_frequency;
g->clock_mem = metrics->average_uclk_frequency; // mem controller clock, needs to be 2x on 5700xt for some reason
break;
}
/* ryzen 5 5600g */
case 22: {
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v2_2*>(raw_metrics.data());
g->temp_edge = metrics->temperature_gfx / 100;
break;
}
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
default: {
applog(LOG::WRN, "Unsupported gpu_metrics_v{}_{}", header->format_revision, header->content_revision);
break;
}
}
}
/* reads values from system and updates the target gpu::sensors struct */
void update(gpuSensors* g) {
const bool hwmon_valid = !g->hwmon.empty();
if (hwmon_valid)
fillMissingMetrics(g);
if (g->hasGpuMetrics)
updateFromMetrics(g);
else
if (hwmon_valid)
updateFromFs(g);
if (g->fan_speed > 9999) g->fan_speed = 9999; // sanity check fan speed
}
} // namespace gpu

163
src/hass.cpp Normal file
View File

@@ -0,0 +1,163 @@
#ifdef ENABLE_HOME_ASSISTANT
#include <libwebsockets.h>
#include <cstring>
#include <hass.hpp>
#include <log.hpp>
#include <nlohmann/json.hpp>
#include <string>
HomeAssistant::HomeAssistant(const Config& config) {
m_config = config;
}
HomeAssistant::~HomeAssistant() {
disconnect();
}
void HomeAssistant::event_handler(lws* wsi, const lws_callback_reasons reason, char* in) {
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
applog(LOG::ERR, "websocket conn error: {}", in ? in : "(null)");
return;
}
case LWS_CALLBACK_CLIENT_RECEIVE: {
nlohmann::json json = nlohmann::json::parse(in);
const std::string ha_type = json["type"].get<std::string>();
if (ha_type == "auth_required") {
json.clear();
json["type"] = "auth";
json["access_token"] = m_config.token;
tx = json.dump();
break;
}
if (ha_type == "auth_ok") {
applog(LOG::INF, "subscribing to entity \"{}\"", m_config.entity);
json.clear();
json["id"] = id++;
json["type"] = "subscribe_entities";
json["entity_ids"][0] = m_config.entity.c_str();
tx = json.dump();
break;
}
if (ha_type == "result") {
if (json["success"].get<bool>() != true)
applog(LOG::ERR, "api said success was \"false\" on message id {}", id - 1);
break;
}
if (ha_type == "event") {
json = json["event"];
if (json.contains("a"))
state = std::stof(json["a"][m_config.entity]["s"].get<std::string>());
if (json.contains("c"))
state = std::stof(json["c"][m_config.entity]["+"]["s"].get<std::string>());
break;
}
applog(LOG::WRN, "unhandled event: {}", static_cast<char*>(in));
break;
}
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
applog(LOG::INF, "Websocket connected");
break;
}
case LWS_CALLBACK_CLIENT_CLOSED: {
applog(LOG::INF, "Websocket closed");
return;
}
case LWS_CALLBACK_CLIENT_WRITEABLE: {
/* skip whole callback if there's no tx to send */
if (!tx.empty()) {
const size_t msgLen = LWS_PRE + tx.size();
auto* buf = static_cast<unsigned char*>(malloc(msgLen));
if (buf) {
memcpy(&buf[LWS_PRE], tx.c_str(), tx.size());
lws_write(wsi, &buf[LWS_PRE], tx.size(), LWS_WRITE_TEXT);
tx = "";
free(buf);
}
}
break;
}
default:
break;
}
}
int HomeAssistant::callback(lws* wsi, const lws_callback_reasons reason, void* user, void* in, size_t) {
auto* self = static_cast<HomeAssistant*>(user);
self->event_handler(wsi, reason, static_cast<char*>(in));
return 0;
}
int HomeAssistant::connect() {
lws_client_connect_info i{};
lws_context_creation_info info{};
lws_set_log_level(LLL_ERR | LLL_WARN, lwsl_emit_syslog);
std::memset(&i, 0, sizeof(i));
std::memset(&info, 0, sizeof info);
static constexpr lws_protocols protocols[] = {
{"lws-minimal-client", callback, sizeof(void*), 0, 0, nullptr, 0},
LWS_PROTOCOL_LIST_TERM};
static constexpr lws_extension extensions[] = {
{"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
"; client_no_context_takeover"
"; client_max_window_bits"},
{nullptr, nullptr, nullptr}};
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
info.fd_limit_per_thread = 1 + 1 + 1;
info.extensions = extensions;
context = lws_create_context(&info);
if (!context) {
applog(LOG::ERR, "error creating context");
return -1;
}
i.context = context;
i.port = 443;
i.address = m_config.address.c_str();
i.path = "/api/websocket";
i.host = i.address;
i.origin = i.address;
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_PRIORITIZE_READS;
i.protocol = nullptr;
i.userdata = this;
i.local_protocol_name = "lws-minimal-client";
websock = lws_client_connect_via_info(&i);
if (!websock) {
applog(LOG::ERR, "error creating lws client");
return -1;
}
return 0;
}
void HomeAssistant::service(const int timeout_ms) const {
lws_callback_on_writable(websock);
lws_service(context, timeout_ms);
}
void HomeAssistant::disconnect() const {
lws_context_destroy(context);
}
#endif

154
src/lcd.c
View File

@@ -1,154 +0,0 @@
#include "lcd.h"
#include "log.h"
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int lcd_open(struct lcd_t *lcd, const bool reopen) {
applog(LOG_INF, "Opening serial port at %s", lcd->port);
if (lcd->fd <= 0 || reopen)
lcd->fd = open(lcd->port, O_WRONLY | O_NOCTTY);
if (lcd->fd <= 0) {
applog(LOG_ERR, "Error opening serial port %s", lcd->port); // err
return -1;
}
struct termios term = {};
term.c_cflag = lcd->baud | CS8 | CLOCAL | CREAD;
tcflush(lcd->fd, TCIFLUSH);
tcsetattr(lcd->fd, TCSANOW, &term);
return 0;
}
void lcd_close(struct lcd_t *lcd) {
if (lcd->fd >= 0) {
close(lcd->fd);
applog(LOG_INF, "LCD port closed");
}
lcd->fd = -1;
}
static void lcd_write(struct lcd_t *lcd, const uint8_t *cmdbuf, const uint16_t len) {
if (write(lcd->fd, cmdbuf, len) <= 0) {
applog(LOG_ERR, "failed to execute write(), lost access to port?");
lcd_close(lcd);
lcd_open(lcd, true);
}
}
void lcd_reinit(struct lcd_t *lcd) {
constexpr uint8_t cmdbuf[] = {0xFE, 63};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_splash(struct lcd_t *lcd) {
constexpr uint8_t cmdbuf[] = {0xFE, 65};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_clear(struct lcd_t *lcd) {
constexpr uint8_t cmdbuf[] = {0xFE, 88};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_contrast(struct lcd_t *lcd, const uint8_t contrast) {
applog(LOG_INF, "Set contrast to %d", contrast);
const uint8_t cmdbuf[] = {0xFE, 80, contrast};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_backlight(struct lcd_t *lcd, const bool enabled) {
applog(LOG_INF, "Set backlight to %s", enabled ? "true" : "false");
const uint8_t cmdbuf[] = {0xFE, (enabled ? 66 : 70), 255};
lcd_write(lcd, cmdbuf, enabled ? 3 : 2);
}
void lcd_set_brightness(struct lcd_t *lcd, const uint8_t brightness) {
applog(LOG_INF, "Set backlight brightness to %d", brightness);
const uint8_t cmdbuf[] = {0xFE, 152, brightness};
lcd_write(lcd, cmdbuf, 3);
}
void lcd_set_cursor(struct lcd_t *lcd, const uint8_t row, const uint8_t col) {
const uint8_t cmdbuf[] = {0xFE, 71, col + 1, row + 1};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_custom_char(struct lcd_t *lcd, const uint8_t index, const lcd_glyph_t *glyph) {
memcpy(lcd->writebuf.cchar[index] + 3, glyph, sizeof(lcd_glyph_t)); // we prepare the command in init, so we can just copy the glyph after the command
}
/* writes string to write buffer at calculated pos, we trust it to be null terminated */
void lcd_write_buf(struct lcd_t *lcd, const uint8_t row, const uint8_t col, const char *str) {
size_t len = 0;
while (str[len] != '\0')
len++;
len = MIN(len, sizeof(lcd->writebuf) - col);
int pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
if (row >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf.text + pos, str, len);
}
/* hyperoptimized function to write a formatted string to lcd buffer. */
/* we cant just snprintf to the buffer directly bc it null terminates */
void lcd_writef(struct lcd_t *lcd, const uint8_t x, const uint8_t y, const char *fmt, ...) {
char tmp[LCD_COLS + 1];
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(tmp, sizeof(tmp), fmt, ap); // print fmt and ap to a temporary buffer
va_end(ap);
len = MIN(len, LCD_COLS - x);
int pos = 4 + x + (LCD_COLS * (y % 2)); // skip 4 bytes to leave room for set cursor command
if (y >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf.text + pos, tmp, (size_t)len); // copy to lcd writebuffer address + whatever pos is
}
inline void lcd_clear_buf(struct lcd_t *lcd) {
memset(lcd->writebuf.text, ' ', sizeof(lcd->writebuf.text));
// prepare commands for custom chars
for (uint8_t i = 0; i < 8; i++) {
const uint8_t charbuf[] = {0xFE, 78, i};
memcpy(lcd->writebuf.cchar[i], charbuf, sizeof(charbuf));
}
}
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
void lcd_flush_buf(struct lcd_t *lcd) {
constexpr uint8_t set_disp[] = {0xFE, 71, 1, 1}; // set cursor to char 1, line 1
memcpy(&lcd->writebuf.text[0], set_disp, 4);
memcpy(&lcd->writebuf.text[2 * LCD_COLS + 4], set_disp, 4);
lcd->writebuf.text[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
lcd_write(lcd, (const uint8_t *)&lcd->writebuf, sizeof(lcd->writebuf));
}
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
inline char lcd_bar(struct lcd_t *lcd, const uint8_t char_id, const double value) {
uint8_t b[8];
memset(b, 0b01110, sizeof(b));
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
b[i] = 0;
lcd_set_custom_char(lcd, char_id, &b);
return (char)char_id;
}
int lcd_init(struct lcd_t *lcd) {
const int ret = lcd_open(lcd, false);
if (ret < 0) {
lcd_close(lcd);
return -1;
}
lcd_clear_buf(lcd);
lcd_reinit(lcd);
return 0;
}

View File

@@ -1,73 +0,0 @@
#include "log.h"
#ifdef APPLOG_ENABLED
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
static pthread_mutex_t cmtx = PTHREAD_MUTEX_INITIALIZER;
/* Replicates pFname: strips return type and parameters from __PRETTY_FUNCTION__
* "void myFunc(int x)" → "myFunc"
* "int other_func(void)" → "other_func"
*/
static void fmt_funcname(const char *pretty, char *out, size_t outsz) {
const char *start = pretty;
const char *space = strchr(pretty, ' ');
if (space)
start = space + 1;
const char *paren = strrchr(start, '(');
size_t len = paren ? (size_t)(paren - start) : strlen(start);
if (len >= outsz)
len = outsz - 1;
memcpy(out, start, len);
out[len] = '\0';
}
void log_write(const log_level_t level, const char *func, const char *fmt, ...) {
static const char *reset = "\033[0m";
const char *color;
switch (level) {
case LOG_INF:
color = "\033[1;32m";
break; /* bold green */
case LOG_WRN:
color = "\033[1;33m";
break; /* bold yellow */
case LOG_ERR:
color = "\033[1;31m";
break; /* bold red */
case LOG_DBG:
color = "";
break;
case LOG_START:
color = "\033[1;3;36m";
break; /* bold italic cyan */
default:
color = "";
break;
}
char funcname[128];
fmt_funcname(func, funcname, sizeof(funcname));
/* ── console ─────────────────────────────────────────────────────── */
{
va_list ap;
va_start(ap, fmt);
pthread_mutex_lock(&cmtx);
fprintf(stderr, "[%s%s%s] ", color, funcname, reset);
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
pthread_mutex_unlock(&cmtx);
va_end(ap);
}
}
#endif /* APPLOG_ENABLED */

View File

@@ -1,237 +0,0 @@
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/signalfd.h>
#include <unistd.h>
#include "aio.h"
#include "cpu.h"
#include "gpu.h"
#include "lcd.h"
#include "log.h"
#include "sys.h"
#include "utils.h"
#include "version.h"
static constexpr uint8_t lcd_contrast = 250;
static constexpr uint16_t refresh_interval_ms = 1000;
static constexpr char cpu_pcidev[] = "/sys/bus/pci/devices/0000:00:18.3";
static constexpr char gpu_pcidev[] = "/sys/class/drm/renderD128/device";
static struct {
pthread_mutex_t sensors_mtx;
pthread_cond_t sensors_ready;
atomic_bool running;
} control;
struct sensors_t { // NOLINT stfu
time_t ts;
struct memory_t ram;
struct disk_t home;
struct disk_t gry;
struct network_t net;
struct syssensors_t sys;
struct cpu_sensors_t cpu;
struct gpu_sensors_t gpu;
struct aio_t aio;
double loadavg[3];
};
static void *sensors_thread(void *arg) {
applog(LOG_START, "sensors thread started, refresh interval is %dms", refresh_interval_ms);
struct sensors_t *sensors = arg;
struct timespec next;
clock_gettime(CLOCK_REALTIME, &next);
/* set up paths for the sensors */
strcpy(sensors->home.mountpoint, "/home/drath");
strcpy(sensors->gry.mountpoint, "/var/mnt/gry");
// strcpy(sensors->net.device, netif);
cpu_detect(&sensors->cpu, cpu_pcidev);
gpu_detect(&sensors->gpu, gpu_pcidev);
sys_detect(&sensors->sys, "nct6775.656");
sys_netdev_detect(&sensors->net);
aio_detect(&sensors->aio);
/* main sensor loop */
applog(LOG_START, "reached sensors main loop");
while (control.running) { // NOLINT
pthread_mutex_lock(&control.sensors_mtx);
{
sensors->ts = time(nullptr);
sys_get_load_avg(sensors->loadavg);
sys_get_ram(&sensors->ram);
sys_get_disk(&sensors->home);
sys_get_disk(&sensors->gry);
sys_get_network(&sensors->net);
sys_update_sensors(&sensors->sys);
cpu_update(&sensors->cpu);
gpu_update(&sensors->gpu);
aio_update(&sensors->aio);
}
pthread_cond_signal(&control.sensors_ready);
pthread_mutex_unlock(&control.sensors_mtx);
timespec_add_ms(&next, refresh_interval_ms);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
}
aio_close(&sensors->aio);
applog(LOG_WRN, "sensors thread stopped");
return nullptr;
}
static void *display_thread(void *arg) {
applog(LOG_START, "display thread started");
const struct sensors_t *sensors = arg;
/* lcd setup */
struct lcd_t lcd = {
.port = "/dev/serial/by-id/usb-Drath_Technologies_Serial_Display_Controller_00002-if00",
.baud = 0010002, // from termios-baud.h, B115200
};
lcd_init(&lcd);
lcd_set_brightness(&lcd, 255);
lcd_set_backlight(&lcd, true);
lcd_set_contrast(&lcd, lcd_contrast);
char name[64], ver[64];
sys_get_distro(name, ver);
lcd_writef(&lcd, 0, 0, "%s %s", name, ver);
lcd_writef(&lcd, 0, 3, "linuxSmartie %s", GIT_HASH);
lcd_flush_buf(&lcd);
lcd_clear_buf(&lcd);
struct timespec next;
clock_gettime(CLOCK_REALTIME, &next);
timespec_add_ms(&next, refresh_interval_ms);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
/* setup custom chars */
lcd_set_custom_char(&lcd, 0, &(lcd_glyph_t){16, 6, 9, 8, 8, 9, 6, 0}); // celcius
lcd_set_custom_char(&lcd, 1, &(lcd_glyph_t){0, 0, 4, 10, 10, 17, 31, 0}); // delta
lcd_set_custom_char(&lcd, 2, &(lcd_glyph_t){0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
lcd_set_custom_char(&lcd, 3, &(lcd_glyph_t){4, 14, 21, 4, 4, 4, 0}); // up arrow
lcd_set_custom_char(&lcd, 4, &(lcd_glyph_t){4, 4, 4, 21, 14, 4, 0}); // down arrow
lcd_set_custom_char(&lcd, 5, &(lcd_glyph_t){0, 0, 15, 21, 21, 17, 15, 0}); // games (steam?)
/* main display loop */
applog(LOG_START, "reached display main loop");
while (control.running) { // NOLINT its updated globally bud
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
timespec_add_ms(&ts, (uint16_t)(refresh_interval_ms * 1.25));
pthread_mutex_lock(&control.sensors_mtx); // lock sensor struct
pthread_cond_timedwait(&control.sensors_ready, &control.sensors_mtx, &ts); // wait until data ready
{
// prepare some tricky sensors
constexpr char modes[] = "QBE";
const uint16_t avg_fan_speed = (uint16_t)((sensors->aio.fan_speed[0] + sensors->aio.fan_speed[1]) / 2);
char gpudelta[5];
const uint8_t delta = sensors->gpu.temp_hotspot - sensors->gpu.temp_edge;
snprintf(gpudelta, sizeof(gpudelta), "%c%d", 1, delta);
const uint8_t gpu_pos = delta >= 10 ? 3 : 2;
char gpuclock[16];
snprintf(gpuclock, sizeof(gpuclock), "%d/%dMHz", sensors->gpu.clock_core, sensors->gpu.clock_mem);
lcd_writef(&lcd, 0, 0, "%c%s", 2, P_BYTES(sensors->home.usage.free, 1));
lcd_writef(&lcd, 7, 0, "%c%s", 5, P_BYTES(sensors->gry.usage.free, 1));
lcd_writef(&lcd, 15, 0, "%s%c", SYS_GET_TIME_STR(sensors->ts), lcd_bar(&lcd, 6, sensors->gpu.usage_core / 100.0));
lcd_writef(&lcd, 26, 0, "%s %s %s", P_NUM(sensors->loadavg[0]), P_NUM(sensors->loadavg[1]), P_NUM(sensors->loadavg[2]));
lcd_writef(&lcd, 0, 1, "%d/%d%c", sensors->cpu.tctl, sensors->cpu.tccd1, 0);
lcd_writef(&lcd, 7, 1, "%s", P_BYTES(sensors->ram.used, 0));
lcd_writef(&lcd, 13, 1, "%.1f%c", (double)sensors->aio.water_temp, 0);
lcd_writef(&lcd, 18, 1, "%3d%c", sensors->sys.t_sensor, 0);
lcd_writef(&lcd, 28, 1, "%4s%c", gpudelta, 0);
lcd_writef(&lcd, 25 - gpu_pos, 1, "%6s", P_BYTES(sensors->gpu.vram_used, 1));
lcd_writef(&lcd, 34, 1, "%d/%d%c", sensors->gpu.temp_edge, sensors->gpu.temp_hotspot, 0);
lcd_writef(&lcd, 0, 2, "%.0fW", (double)sensors->cpu.ptotal);
lcd_writef(&lcd, 5, 2, "%dR", avg_fan_speed);
lcd_writef(&lcd, 12, 2, "%5s%c", P_BYTES(sensors->net.upload, 1), 3);
lcd_writef(&lcd, 19, 2, "%c%s", 4, P_BYTES(sensors->net.download, 1));
lcd_writef(&lcd, 26, 2, "%4dR", sensors->gpu.fan_speed);
lcd_writef(&lcd, 32, 2, "%3d/%3dW", sensors->gpu.power_avg, sensors->gpu.power_cap);
lcd_writef(&lcd, 0, 3, "%uMHz", sensors->cpu.avg_freq);
lcd_writef(&lcd, 8, 3, "%4dR-%c %d", sensors->aio.pump_speed, modes[sensors->aio.pump_mode], sensors->aio.pump_counter);
lcd_writef(&lcd, 21, 3, "%12s", gpuclock);
lcd_writef(&lcd, 34, 3, "%.3fV", (double)sensors->gpu.vcore);
}
pthread_mutex_unlock(&control.sensors_mtx); // unlock sensor struct
lcd_flush_buf(&lcd);
lcd_clear_buf(&lcd);
}
lcd_splash(&lcd);
lcd_close(&lcd);
applog(LOG_WRN, "display thread stopped");
return nullptr;
}
int main() {
applog(LOG_START, "linuxSmartie %s starting", GIT_HASH);
applog(LOG_DBG, "Compiled with %s", C_STD);
/* see if another instance is up, quit if it is */
if (get_instance_lock() < 0) {
applog(LOG_ERR, "Another instance is already running. Exiting.");
return 0;
}
/* malloc memory for the struct */
struct sensors_t *sensors = malloc(sizeof(struct sensors_t)); // no actual point in mallocing but its cool
if (sensors == nullptr) {
applog(LOG_ERR, "can't allocate mem for the sensor struct. You don't got %lu bytes bro?", sizeof(*sensors));
free(sensors);
return 1;
}
applog(LOG_INF, "sensor struct allocated, size is %lu bytes", sizeof(*sensors));
/* signal setup */
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
struct signalfd_siginfo info;
const int sfd = signalfd(-1, &sigset, SFD_CLOEXEC);
/* control vars setup */
pthread_mutex_init(&control.sensors_mtx, nullptr);
pthread_cond_init(&control.sensors_ready, nullptr);
control.running = true;
/* thread setup */
pthread_t sensors_pthread;
pthread_t display_pthread;
/* actually start the threads */
pthread_create(&sensors_pthread, nullptr, sensors_thread, sensors);
pthread_create(&display_pthread, nullptr, display_thread, sensors);
/* halts here until signal to stop */
const ssize_t ret = read(sfd, &info, sizeof(info));
(void)ret;
/* cleanup on exit */
applog(LOG_WRN, "got stop signal, shutting down...");
close(sfd);
control.running = false;
pthread_join(sensors_pthread, nullptr);
pthread_join(display_pthread, nullptr);
free(sensors);
return 0;
}

142
src/sys.c
View File

@@ -1,142 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <time.h>
#include "log.h"
#include "sys.h"
#include "utils.h"
static sensor_file_t sensors;
char *sys_get_time_string(char *buf, const size_t bufsz, const time_t t) {
const struct tm *tm = localtime(&t);
snprintf(buf, bufsz, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
return buf;
}
char *sys_get_date_string(char *buf, const size_t bufsz, const time_t t) {
const struct tm *tm = localtime(&t);
constexpr char months[][4] = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
constexpr char wdays[][4] = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
snprintf(buf, bufsz, "%s, %02d %s", wdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon]);
return buf;
}
void sys_get_load_avg(double *load_avg) {
getloadavg(load_avg, 3);
}
void sys_get_distro(char* name, char* ver_str) {
char buf[128];
FILE *f = fopen("/etc/os-release", "r");
if (f == nullptr)
return;
while (fgets(buf, sizeof(buf), f)) {
sscanf(buf, "NAME=\"%s\"", name); // NOLINT we live dangerously
sscanf(buf, "VERSION=\"%s\"", ver_str); // NOLINT (its not really that dangerous)
}
fclose(f);
}
void sys_get_ram(struct memory_t *ram) {
char buf[64];
memset(ram, 0, sizeof(struct memory_t)); // zero out all values so we dont break out of the loop early
FILE *f = fopen("/proc/meminfo", "r");
if (f == nullptr)
return;
while (fgets(buf, sizeof(buf), f)) {
sscanf(buf, "MemTotal: %lu kB", &ram->total); // NOLINT we live dangerously
sscanf(buf, "MemAvailable: %lu kB", &ram->free); // NOLINT (its not really that dangerous)
if (ram->total && ram->free)
break;
}
fclose(f);
ram->used = ram->total - ram->free;
}
void sys_get_disk(struct disk_t *disk) {
struct statvfs diskData;
statvfs(disk->mountpoint, &diskData);
disk->usage.free = diskData.f_bfree * diskData.f_frsize;
disk->usage.used = (diskData.f_blocks - diskData.f_bfree) * diskData.f_frsize;
disk->usage.total = diskData.f_blocks * diskData.f_frsize;
}
void sys_get_network(struct network_t *network) {
struct _net_t {
struct timespec ts;
uint64_t rx;
uint64_t tx;
};
static struct _net_t prev;
struct _net_t current = {};
clock_gettime(CLOCK_MONOTONIC, &current.ts);
fast_read_sensor_into_mem(&network->f_rx, &current.rx);
fast_read_sensor_into_mem(&network->f_tx, &current.tx);
const int64_t diff_us = timespec_diff_us(&current.ts, &prev.ts);
const uint64_t rx_delta = current.rx >= prev.rx ? current.rx - prev.rx : 0;
const uint64_t tx_delta = current.tx >= prev.tx ? current.tx - prev.tx : 0;
const double diff_s = (double)diff_us / 1000000.0;
network->download = (uint32_t)((double)rx_delta / diff_s);
network->upload = (uint32_t)((double)tx_delta / diff_s);
prev = current;
// still fucky?
}
void sys_netdev_detect(struct network_t *network) {
struct ifaddrs *ifaddr;
getifaddrs(&ifaddr);
char addr[INET_ADDRSTRLEN];
for (const struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
// ugliest fucking if
if (!ifa->ifa_addr ||
ifa->ifa_addr->sa_family != AF_INET ||
!(ifa->ifa_flags & IFF_UP) ||
!(ifa->ifa_flags & IFF_RUNNING) ||
ifa->ifa_flags & IFF_LOOPBACK)
continue;
const struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
inet_ntop(AF_INET, &sin->sin_addr, addr, sizeof(addr));
if (strncmp("10.0.1.", addr, 7) == 0) {
strcpy(network->device, ifa->ifa_name);
break;
}
}
freeifaddrs(ifaddr);
applog(LOG_INF, "Detected %s with addr %s", network->device, addr);
static char path[128];
snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/", network->device);
open_sensor(&network->f_rx, path, "rx_bytes", nullptr, 1, SENSOR_U64);
open_sensor(&network->f_tx, path, "tx_bytes", nullptr, 1, SENSOR_U64);
}
void sys_detect(struct syssensors_t *s, const char *platform) {
snprintf(s->hwmon, sizeof(s->hwmon), "/sys/devices/platform/%s/hwmon", platform);
const int ret = get_hwmon_dir(s->hwmon, sizeof(s->hwmon));
if (ret != 0) {
s->hwmon[0] = '\0';
applog(LOG_WRN, "Can't open system hwmon. Kernel params added? Module loaded? See README");
return;
}
applog(LOG_INF, "Detected system sensors in %s", s->hwmon);
open_sensor(&sensors, s->hwmon, "/temp5_input", &s->t_sensor, 1000, SENSOR_I16);
}
void sys_update_sensors(const struct syssensors_t *s) {
if (!s->hwmon[0])
return;
fast_read_sensor(&sensors);
}

87
src/sys.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include <sys/statvfs.h>
#include <charconv>
#include <chrono>
#include <format>
#include <log.hpp>
#include <string>
#include <sys.hpp>
#include <utils.hpp>
int getVal(const std::string_view target, std::string_view content) {
int result = -1;
if (const std::size_t start = content.find(target); start != std::string::npos) {
content.remove_prefix(start + target.length());
const std::size_t end = content.find(" kB");
while (std::isspace(content.front()))
content.remove_prefix(1);
std::from_chars(content.data(), content.data() + end, result);
}
return result;
}
void sys::getTimeDate(TimeDate* td) {
const auto now = std::chrono::system_clock::now();
const auto local = std::chrono::current_zone()->to_local(now);
const std::chrono::year_month_day ymd{floor<std::chrono::days>(local)};
const std::chrono::weekday wd{floor<std::chrono::days>(local)};
td->time = std::format("{:%H:%M:%S}", floor<std::chrono::seconds>(local));
td->date = std::format("{}, {} {}", wdays[wd.c_encoding()], ymd.day(), months[static_cast<unsigned>(ymd.month()) - 1]);
}
void sys::getRam(Memory* mem) {
static std::array<char, 128> buf; // file is longer but we only need first 128 chars
utils::readFile("/proc/meminfo", buf);
mem->free = getVal("MemAvailable:", buf.data());
mem->total = getVal("MemTotal:", buf.data()); // shouldn't really update this on each loop
mem->used = mem->total - mem->free;
}
void sys::getDisk(Disk* disk) {
struct statvfs diskData{};
statvfs(disk->mountpoint.c_str(), &diskData);
disk->usage.free = diskData.f_bfree * diskData.f_frsize;
disk->usage.used = (diskData.f_blocks - diskData.f_bfree) * diskData.f_frsize;
disk->usage.total = diskData.f_blocks * diskData.f_frsize;
}
void sys::getNetwork(Network* net, const std::string& device) {
struct Network {
std::chrono::time_point<std::chrono::steady_clock> ts;
uint64_t rx = 0;
uint64_t tx = 0;
};
static std::array<char, 32> buf;
static Network prev;
Network current;
current.ts = std::chrono::steady_clock::now();
utils::fastReadNum("/sys/class/net/" + device + "/statistics/rx_bytes", buf, current.rx);
utils::fastReadNum("/sys/class/net/" + device + "/statistics/tx_bytes", buf, current.tx);
const double ts_diff = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(current.ts - prev.ts).count()) / 1000000.0f;
net->download = static_cast<uint32_t>(static_cast<double>(current.rx - prev.rx) / ts_diff);
net->upload = static_cast<uint32_t>(static_cast<double>(current.tx - prev.tx) / ts_diff);
prev = current;
}
int sys::detect(sysSensors* s, const std::string& platform) {
// we dont really need getFirstDir since its a static folder, but its useful for try/catch
try {
s->hwmon = utils::getFirstDir("/sys/devices/platform/" + platform + "/hwmon");
} catch (...) {
applog(LOG::ERR, "Can't open \"/sys/devices/platform/{}/hwmon\". Kernel params added? Module loaded?", platform);
return -1;
}
applog(LOG::INF, "Found system sensors");
return 0;
}
void sys::updateSensors(sysSensors* s) {
if (s->hwmon.empty())
return;
static std::array<char, 8> buf;
utils::fastReadNum(s->hwmon + "/fan2_input", buf, s->fan_speed[0]);
utils::fastReadNum(s->hwmon + "/fan7_input", buf, s->fan_speed[1]);
utils::fastReadNum<1000>(s->hwmon + "/temp5_input", buf, s->t_sensor);
}

View File

@@ -1,151 +0,0 @@
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "utils.h"
int get_instance_lock() {
const char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
char tmp_dir[256];
snprintf(tmp_dir, sizeof(tmp_dir), "%s/linuxSmartie.lock", xdg_runtime_dir);
const int file = open(tmp_dir, O_CREAT | O_RDWR | O_CLOEXEC, 0600);
if (file < 0) {
applog(LOG_ERR, "Couldn't create a lock file at %s", tmp_dir);
return -1;
}
if (flock(file, LOCK_EX | LOCK_NB) < 0) {
if (errno == EWOULDBLOCK)
applog(LOG_ERR, "another instance already running, this one will exit.");
else
applog(LOG_ERR, "flock failed: %s", strerror(errno));
close(file);
return -1;
}
return 0;
}
inline void timespec_add_ms(struct timespec *ts, const uint16_t ms) {
ts->tv_nsec += ms * 1000000L;
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000L;
}
}
inline int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b) {
const int64_t sec = a->tv_sec - b->tv_sec;
const int64_t nsec = a->tv_nsec - b->tv_nsec;
return sec * 1000000LL + nsec / 1000LL;
}
inline char *pretty_num(const double num, char *buf, const size_t bufsz) {
const uint8_t decimals = (num <= 10.0) ? 2 : (num <= 100.0) ? 1 : 0;
snprintf(buf, bufsz, "%.*f", decimals, num);
return buf;
}
inline char *pretty_bytes(const uint64_t bytes, int8_t start_suffix, char *buf, const size_t bufsz) {
constexpr char suffix_list[] = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'};
start_suffix *= -1;
double bytes_d = (double)bytes;
while (bytes_d >= 1024.0) {
bytes_d /= 1024.0;
start_suffix++;
}
snprintf(buf, bufsz, "%s%c", P_NUM(bytes_d), suffix_list[MAX(0, start_suffix)]);
return buf;
}
/* path has to have at least 8 free chars at the end for glob */
int get_hwmon_dir(char *path, const size_t bufsz) {
glob_t g;
strcat(path, "/hwmon*");
if (glob(path, 0, nullptr, &g) == 0) {
strlcpy(path, g.gl_pathv[0], bufsz); // same size as hwmon length, careful
globfree(&g);
return 0;
}
return -1;
}
void open_sensor(sensor_file_t* s, const char* dir, const char *file, void* value, const uint32_t divider, const sensor_type_t type) {
char path[256];
char *nt = stpcpy(path, dir); // nt is a pointer to the null terminator of the copied string
strcpy(nt, file); // copy to the null terminator of prev copy, append basically
s->divider = divider;
s->value = value;
s->type = type;
s->fd = open(path, O_RDONLY | O_CLOEXEC);
if (s->fd < 0)
applog(LOG_ERR, "couldn't open %s", path);
}
inline void fast_read_sensor_into_mem(const sensor_file_t *s, void* val) {
if (s->fd < 0) {
applog(LOG_ERR, "invalid sensor fd %d", s->fd);
return;
}
char buf[32];
lseek(s->fd, 0, SEEK_SET);
const ssize_t len = read(s->fd, buf, sizeof(buf));
if (len <= 0) {
applog(LOG_ERR, "error reading sensor");
return;
}
buf[len] = '\0';
switch (s->type) {
case SENSOR_I16:
*(int16_t*)val = (int16_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U8:
*(uint8_t*)val = (uint8_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U16:
*(uint16_t*)val = (uint16_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U64:
*(uint64_t*)val = strtoull(buf, nullptr, 10) / s->divider;
break;
case SENSOR_FLOAT:
const uint64_t t = strtoull(buf, nullptr, 10);
*(float*)val = (float)t / (float)s->divider;
break;
default:
applog(LOG_ERR, "unknown sensor type %d", s->type);
break;
}
}
inline void fast_read_sensor(const sensor_file_t *s) {
if (s->value != nullptr) {
fast_read_sensor_into_mem(s, s->value);
return;
}
applog(LOG_ERR, "attempted to read into nullptr");
}
void close_sensor(const sensor_file_t* s) {
close(s->fd);
}

32
src/utils.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include <utils.hpp>
#include <cstdint>
#include <filesystem>
#include <string>
#include <string_view>
namespace utils {
/* pads given string to length */
std::string pad(std::string s, const uint8_t len, const just j) {
if (len > s.size())
s.insert(j == left ? s.end() : s.begin(), len - s.size(), fill_char);
return s;
}
/* returns the string centered within given length */
std::string center(std::string str, const uint8_t len) {
const uint8_t padding = len - str.size();
str.insert(str.begin(), padding / 2, fill_char);
str.insert(str.end(), padding / 2, fill_char);
return str;
}
/* finds first dir in path and returns it. don't refactor, you don't understand it, its fast enough, and you only use it on init */
std::string getFirstDir(const std::string_view& path) {
for (const auto& entry : std::filesystem::directory_iterator(path))
return entry.path();
return "";
}
} // namespace utils