Added support for remote0
This commit is contained in:
163
internal/hwinfo/hwinfo.go
Normal file
163
internal/hwinfo/hwinfo.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package hwinfo
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
#include "hwisenssm2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/shmem"
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/util"
|
||||
)
|
||||
|
||||
// SharedMemory provides access to the HWiNFO shared memory
|
||||
type SharedMemory struct {
|
||||
data []byte
|
||||
shmem C.PHWiNFO_SENSORS_SHARED_MEM2
|
||||
}
|
||||
|
||||
// ReadSharedMem reads data from HWiNFO shared memory
|
||||
// creating a copy of the data
|
||||
func ReadSharedMem() (*SharedMemory, error) {
|
||||
data, err := shmem.ReadBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SharedMemory{
|
||||
data: append([]byte(nil), data...),
|
||||
shmem: C.PHWiNFO_SENSORS_SHARED_MEM2(unsafe.Pointer(&data[0])),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Result for streamed shared memory updates
|
||||
type Result struct {
|
||||
Shmem *SharedMemory
|
||||
Err error
|
||||
}
|
||||
|
||||
func readAndSend(ch chan<- Result) {
|
||||
shmem, err := ReadSharedMem()
|
||||
ch <- Result{Shmem: shmem, Err: err}
|
||||
}
|
||||
|
||||
// StreamSharedMem delivers shared memory hardware sensors updates
|
||||
// over a channel
|
||||
func StreamSharedMem() <-chan Result {
|
||||
ch := make(chan Result)
|
||||
go func() {
|
||||
readAndSend(ch)
|
||||
// TODO: don't use time.Tick, cancellable?
|
||||
for range time.Tick(1 * time.Second) {
|
||||
readAndSend(ch)
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Signature "HWiS" if active, 'DEAD' when inactive
|
||||
func (s *SharedMemory) Signature() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&s.shmem.dwSignature), C.sizeof_DWORD)
|
||||
}
|
||||
|
||||
// Version v1 is latest
|
||||
func (s *SharedMemory) Version() int {
|
||||
return int(s.shmem.dwVersion)
|
||||
}
|
||||
|
||||
// Revision revision of version
|
||||
func (s *SharedMemory) Revision() int {
|
||||
return int(s.shmem.dwRevision)
|
||||
}
|
||||
|
||||
// PollTime last polling time
|
||||
func (s *SharedMemory) PollTime() uint64 {
|
||||
addr := unsafe.Pointer(uintptr(unsafe.Pointer(&s.shmem.dwRevision)) + C.sizeof_DWORD)
|
||||
return uint64(*(*C.__time64_t)(addr))
|
||||
}
|
||||
|
||||
// OffsetOfSensorSection offset of the Sensor section from beginning of HWiNFO_SENSORS_SHARED_MEM2
|
||||
func (s *SharedMemory) OffsetOfSensorSection() int {
|
||||
return int(s.shmem.dwOffsetOfSensorSection)
|
||||
}
|
||||
|
||||
// SizeOfSensorElement size of each sensor element = sizeof( HWiNFO_SENSORS_SENSOR_ELEMENT )
|
||||
func (s *SharedMemory) SizeOfSensorElement() int {
|
||||
return int(s.shmem.dwSizeOfSensorElement)
|
||||
}
|
||||
|
||||
// NumSensorElements number of sensor elements
|
||||
func (s *SharedMemory) NumSensorElements() int {
|
||||
return int(s.shmem.dwNumSensorElements)
|
||||
}
|
||||
|
||||
// OffsetOfReadingSection offset of the Reading section from beginning of HWiNFO_SENSORS_SHARED_MEM2
|
||||
func (s *SharedMemory) OffsetOfReadingSection() int {
|
||||
return int(s.shmem.dwOffsetOfReadingSection)
|
||||
}
|
||||
|
||||
// SizeOfReadingElement size of each Reading element = sizeof( HWiNFO_SENSORS_READING_ELEMENT )
|
||||
func (s *SharedMemory) SizeOfReadingElement() int {
|
||||
return int(s.shmem.dwSizeOfReadingElement)
|
||||
}
|
||||
|
||||
// NumReadingElements number of Reading elements
|
||||
func (s *SharedMemory) NumReadingElements() int {
|
||||
return int(s.shmem.dwNumReadingElements)
|
||||
}
|
||||
|
||||
func (s *SharedMemory) dataForSensor(pos int) ([]byte, error) {
|
||||
if pos >= s.NumSensorElements() {
|
||||
return nil, fmt.Errorf("dataForSensor pos out of range, %d for size %d", pos, s.NumSensorElements())
|
||||
}
|
||||
start := s.OffsetOfSensorSection() + (pos * s.SizeOfSensorElement())
|
||||
end := start + s.SizeOfSensorElement()
|
||||
return s.data[start:end], nil
|
||||
}
|
||||
|
||||
// IterSensors iterate over each sensor
|
||||
func (s *SharedMemory) IterSensors() <-chan Sensor {
|
||||
ch := make(chan Sensor)
|
||||
go func() {
|
||||
for i := 0; i < s.NumSensorElements(); i++ {
|
||||
data, err := s.dataForSensor(i)
|
||||
if err != nil {
|
||||
log.Fatalf("TODO: failed to read dataForSensor: %v", err)
|
||||
}
|
||||
ch <- NewSensor(data)
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *SharedMemory) dataForReading(pos int) ([]byte, error) {
|
||||
if pos >= s.NumReadingElements() {
|
||||
return nil, fmt.Errorf("dataForReading pos out of range, %d for size %d", pos, s.NumSensorElements())
|
||||
}
|
||||
start := s.OffsetOfReadingSection() + (pos * s.SizeOfReadingElement())
|
||||
end := start + s.SizeOfReadingElement()
|
||||
return s.data[start:end], nil
|
||||
}
|
||||
|
||||
// IterReadings iterate over each sensor
|
||||
func (s *SharedMemory) IterReadings() <-chan Reading {
|
||||
ch := make(chan Reading)
|
||||
go func() {
|
||||
for i := 0; i < s.NumReadingElements(); i++ {
|
||||
data, err := s.dataForReading(i)
|
||||
if err != nil {
|
||||
log.Fatalf("TODO: failed to read dataForReading: %v", err)
|
||||
}
|
||||
ch <- NewReading(data)
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
126
internal/hwinfo/hwisenssm2.h
Normal file
126
internal/hwinfo/hwisenssm2.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef _HWISENSSM2_H_INCLUDED_
|
||||
#define _HWISENSSM2_H_INCLUDED_
|
||||
|
||||
// Name of the file mapping object that needs to be opened using OpenFileMapping Function:
|
||||
#define HWiNFO_SENSORS_MAP_FILE_NAME2 "Global\\HWiNFO_SENS_SM2"
|
||||
|
||||
// Name of the global mutex which is acquired when accessing the Shared Memory space. Release as quick as possible !
|
||||
#define HWiNFO_SENSORS_SM2_MUTEX "Global\\HWiNFO_SM2_MUTEX"
|
||||
|
||||
#define HWiNFO_SENSORS_STRING_LEN2 128
|
||||
#define HWiNFO_UNIT_STRING_LEN 16
|
||||
|
||||
enum SENSOR_READING_TYPE
|
||||
{
|
||||
SENSOR_TYPE_NONE = 0,
|
||||
SENSOR_TYPE_TEMP,
|
||||
SENSOR_TYPE_VOLT,
|
||||
SENSOR_TYPE_FAN,
|
||||
SENSOR_TYPE_CURRENT,
|
||||
SENSOR_TYPE_POWER,
|
||||
SENSOR_TYPE_CLOCK,
|
||||
SENSOR_TYPE_USAGE,
|
||||
SENSOR_TYPE_OTHER
|
||||
};
|
||||
typedef enum SENSOR_READING_TYPE SENSOR_READING_TYPE;
|
||||
|
||||
// No alignment of structure members
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct _HWiNFO_SENSORS_READING_ELEMENT
|
||||
{
|
||||
|
||||
SENSOR_READING_TYPE tReading; // Type of sensor reading
|
||||
DWORD dwSensorIndex; // This is the index of sensor in the Sensors[] array to which this reading belongs to
|
||||
DWORD dwReadingID; // A unique ID of the reading within a particular sensor
|
||||
char szLabelOrig[HWiNFO_SENSORS_STRING_LEN2]; // Original label (e.g. "Chassis2 Fan")
|
||||
char szLabelUser[HWiNFO_SENSORS_STRING_LEN2]; // Label displayed, which might have been renamed by user
|
||||
char szUnit[HWiNFO_UNIT_STRING_LEN]; // e.g. "RPM"
|
||||
double Value;
|
||||
double ValueMin;
|
||||
double ValueMax;
|
||||
double ValueAvg;
|
||||
|
||||
} HWiNFO_SENSORS_READING_ELEMENT, *PHWiNFO_SENSORS_READING_ELEMENT;
|
||||
|
||||
typedef struct _HWiNFO_SENSORS_SENSOR_ELEMENT
|
||||
{
|
||||
|
||||
DWORD dwSensorID; // A unique Sensor ID
|
||||
DWORD dwSensorInst; // The instance of the sensor (together with dwSensorID forms a unique ID)
|
||||
char szSensorNameOrig[HWiNFO_SENSORS_STRING_LEN2]; // Original sensor name
|
||||
char szSensorNameUser[HWiNFO_SENSORS_STRING_LEN2]; // Sensor name displayed, which might have been renamed by user
|
||||
|
||||
} HWiNFO_SENSORS_SENSOR_ELEMENT, *PHWiNFO_SENSORS_SENSOR_ELEMENT;
|
||||
|
||||
typedef struct _HWiNFO_SENSORS_SHARED_MEM2
|
||||
{
|
||||
|
||||
DWORD dwSignature; // "HWiS" if active, 'DEAD' when inactive
|
||||
DWORD dwVersion; // v1 is latest
|
||||
DWORD dwRevision; //
|
||||
__time64_t poll_time; // last polling time
|
||||
|
||||
// descriptors for the Sensors section
|
||||
DWORD dwOffsetOfSensorSection; // Offset of the Sensor section from beginning of HWiNFO_SENSORS_SHARED_MEM2
|
||||
DWORD dwSizeOfSensorElement; // Size of each sensor element = sizeof( HWiNFO_SENSORS_SENSOR_ELEMENT )
|
||||
DWORD dwNumSensorElements; // Number of sensor elements
|
||||
|
||||
// descriptors for the Readings section
|
||||
DWORD dwOffsetOfReadingSection; // Offset of the Reading section from beginning of HWiNFO_SENSORS_SHARED_MEM2
|
||||
DWORD dwSizeOfReadingElement; // Size of each Reading element = sizeof( HWiNFO_SENSORS_READING_ELEMENT )
|
||||
DWORD dwNumReadingElements; // Number of Reading elements
|
||||
|
||||
} HWiNFO_SENSORS_SHARED_MEM2, *PHWiNFO_SENSORS_SHARED_MEM2;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
// ***************************************************************************************************************
|
||||
// HWiNFO Shared Memory Footprint
|
||||
// ***************************************************************************************************************
|
||||
//
|
||||
// |-----------------------------|-----------------------------------|-----------------------------------|
|
||||
// Content | HWiNFO_SENSORS_SHARED_MEM2 | HWiNFO_SENSORS_SENSOR_ELEMENT[] | HWiNFO_SENSORS_READING_ELEMENT[] |
|
||||
// |-----------------------------|-----------------------------------|-----------------------------------|
|
||||
// Pointer |<--0 |<--dwOffsetOfSensorSection |<--dwOffsetOfReadingSection |
|
||||
// |-----------------------------|-----------------------------------|-----------------------------------|
|
||||
// Size | dwOffsetOfSensorSection | dwSizeOfSensorElement | dwSizeOfReadingElement |
|
||||
// | | * dwNumSensorElement | * dwNumReadingElement |
|
||||
// |-----------------------------|-----------------------------------|-----------------------------------|
|
||||
//
|
||||
// ***************************************************************************************************************
|
||||
// Code Example
|
||||
// ***************************************************************************************************************
|
||||
/*
|
||||
|
||||
HANDLE hHWiNFOMemory = OpenFileMapping( FILE_MAP_READ, FALSE, HWiNFO_SENSORS_MAP_FILE_NAME2 );
|
||||
if (hHWiNFOMemory)
|
||||
PHWiNFO_SENSORS_SHARED_MEM2 pHWiNFOMemory =
|
||||
(PHWiNFO_SENSORS_SHARED_MEM2) MapViewOfFile( hHWiNFOMemory, FILE_MAP_READ, 0, 0, 0 );
|
||||
|
||||
// TODO: process signature, version, revision and poll time
|
||||
|
||||
// loop through all available sensors
|
||||
for (DWORD dwSensor = 0; dwSensor < pHWiNFOMemory->dwNumSensorElements; dwSensor++)
|
||||
{
|
||||
PHWiNFO_SENSORS_SENSOR_ELEMENT sensor = (PHWiNFO_SENSORS_SENSOR_ELEMENT) ((BYTE*)pHWiNFOMemory +
|
||||
pHWiNFOMemory->dwOffsetOfSensorSection +
|
||||
(pHWiNFOMemory->dwSizeOfSensorElement * dwSensor));
|
||||
|
||||
// TODO: process sensor
|
||||
}
|
||||
|
||||
// loop through all available readings
|
||||
for (DWORD dwReading = 0; dwReading < pHWiNFOMemory->dwNumReadingElements; dwReading++)
|
||||
{
|
||||
PHWiNFO_SENSORS_READING_ELEMENT reading = (PHWiNFO_SENSORS_READING_ELEMENT) ((BYTE*)pHWiNFOMemory +
|
||||
pHWiNFOMemory->dwOffsetOfReadingSection +
|
||||
(pHWiNFOMemory->dwSizeOfReadingElement * dwReading));
|
||||
|
||||
// TODO: process reading
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
38
internal/hwinfo/mutex/mutex.go
Normal file
38
internal/hwinfo/mutex/mutex.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package mutex
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
#include "../hwisenssm2.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/util"
|
||||
)
|
||||
|
||||
var ghnd C.HANDLE
|
||||
var imut = sync.Mutex{}
|
||||
|
||||
// Lock the global mutex
|
||||
func Lock() error {
|
||||
imut.Lock()
|
||||
lpName := C.CString(C.HWiNFO_SENSORS_SM2_MUTEX)
|
||||
defer C.free(unsafe.Pointer(lpName))
|
||||
|
||||
ghnd = C.OpenMutex(C.READ_CONTROL, C.FALSE, lpName)
|
||||
if ghnd == C.HANDLE(C.NULL) {
|
||||
errstr := util.HandleLastError(uint64(C.GetLastError()))
|
||||
return fmt.Errorf("failed to lock global mutex: %w", errstr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock the global mutex
|
||||
func Unlock() {
|
||||
defer imut.Unlock()
|
||||
C.CloseHandle(ghnd)
|
||||
}
|
||||
70
internal/hwinfo/plugin/plugin.go
Normal file
70
internal/hwinfo/plugin/plugin.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo"
|
||||
hwsensorsservice "github.com/shayne/hwinfo-streamdeck/pkg/service"
|
||||
)
|
||||
|
||||
// Plugin implementation
|
||||
type Plugin struct {
|
||||
Service *Service
|
||||
}
|
||||
|
||||
// PollTime implementation for plugin
|
||||
func (p *Plugin) PollTime() (uint64, error) {
|
||||
shmem, err := p.Service.Shmem()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return shmem.PollTime(), nil
|
||||
}
|
||||
|
||||
// Sensors implementation for plugin
|
||||
func (p *Plugin) Sensors() ([]hwsensorsservice.Sensor, error) {
|
||||
shmem, err := p.Service.Shmem()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sensors []hwsensorsservice.Sensor
|
||||
for s := range shmem.IterSensors() {
|
||||
sensors = append(sensors, &sensor{s})
|
||||
}
|
||||
return sensors, nil
|
||||
}
|
||||
|
||||
// ReadingsForSensorID implementation for plugin
|
||||
func (p *Plugin) ReadingsForSensorID(id string) ([]hwsensorsservice.Reading, error) {
|
||||
res, err := p.Service.ReadingsBySensorID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var readings []hwsensorsservice.Reading
|
||||
for _, r := range res {
|
||||
readings = append(readings, &reading{r})
|
||||
}
|
||||
return readings, nil
|
||||
}
|
||||
|
||||
type sensor struct {
|
||||
hwinfo.Sensor
|
||||
}
|
||||
|
||||
func (s sensor) Name() string {
|
||||
return s.NameOrig()
|
||||
}
|
||||
|
||||
type reading struct {
|
||||
hwinfo.Reading
|
||||
}
|
||||
|
||||
func (r reading) Label() string {
|
||||
return r.LabelOrig()
|
||||
}
|
||||
|
||||
func (r reading) Type() string {
|
||||
return r.Reading.Type().String()
|
||||
}
|
||||
|
||||
func (r reading) TypeI() int32 {
|
||||
return int32(r.Reading.Type())
|
||||
}
|
||||
128
internal/hwinfo/plugin/service.go
Normal file
128
internal/hwinfo/plugin/service.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo"
|
||||
)
|
||||
|
||||
// Service wraps hwinfo shared mem streaming
|
||||
// and provides convenient methods for data access
|
||||
type Service struct {
|
||||
streamch <-chan hwinfo.Result
|
||||
mu sync.RWMutex
|
||||
sensorIDByIdx []string
|
||||
readingsBySensorID map[string][]hwinfo.Reading
|
||||
shmem *hwinfo.SharedMemory
|
||||
readingsBuilt bool
|
||||
}
|
||||
|
||||
// Start starts the service providing updating hardware info
|
||||
func StartService() *Service {
|
||||
return &Service{
|
||||
streamch: hwinfo.StreamSharedMem(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) recvShmem(shmem *hwinfo.SharedMemory) error {
|
||||
if shmem == nil {
|
||||
return fmt.Errorf("shmem nil")
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.shmem = shmem
|
||||
|
||||
s.sensorIDByIdx = s.sensorIDByIdx[:0]
|
||||
for k, v := range s.readingsBySensorID {
|
||||
s.readingsBySensorID[k] = v[:0]
|
||||
}
|
||||
s.readingsBuilt = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recv receives new hardware sensor updates
|
||||
func (s *Service) Recv() error {
|
||||
select {
|
||||
case r := <-s.streamch:
|
||||
if r.Err != nil {
|
||||
return r.Err
|
||||
}
|
||||
return s.recvShmem(r.Shmem)
|
||||
}
|
||||
}
|
||||
|
||||
// Shmem provides access to underlying hwinfo shared memory
|
||||
func (s *Service) Shmem() (*hwinfo.SharedMemory, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if s.shmem != nil {
|
||||
return s.shmem, nil
|
||||
}
|
||||
return nil, fmt.Errorf("shmem nil")
|
||||
}
|
||||
|
||||
// SensorIDByIdx returns ordered slice of sensor IDs
|
||||
func (s *Service) SensorIDByIdx() ([]string, error) {
|
||||
s.mu.RLock()
|
||||
if len(s.sensorIDByIdx) > 0 {
|
||||
defer s.mu.RUnlock()
|
||||
return s.sensorIDByIdx, nil
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for sens := range s.shmem.IterSensors() {
|
||||
s.sensorIDByIdx = append(s.sensorIDByIdx, sens.ID())
|
||||
}
|
||||
|
||||
return s.sensorIDByIdx, nil
|
||||
}
|
||||
|
||||
// ReadingsBySensorID returns slice of hwinfoReading for a given sensor ID
|
||||
func (s *Service) ReadingsBySensorID(id string) ([]hwinfo.Reading, error) {
|
||||
s.mu.RLock()
|
||||
if s.readingsBySensorID != nil && s.readingsBuilt {
|
||||
defer s.mu.RUnlock()
|
||||
readings, ok := s.readingsBySensorID[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("readings for sensor id %s do not exist", id)
|
||||
}
|
||||
return readings, nil
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
|
||||
sids, err := s.SensorIDByIdx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.readingsBySensorID == nil {
|
||||
s.readingsBySensorID = make(map[string][]hwinfo.Reading)
|
||||
}
|
||||
|
||||
for r := range s.shmem.IterReadings() {
|
||||
sidx := int(r.SensorIndex())
|
||||
if sidx < len(sids) {
|
||||
sid := sids[sidx]
|
||||
s.readingsBySensorID[sid] = append(s.readingsBySensorID[sid], r)
|
||||
} else {
|
||||
return nil, fmt.Errorf("sensor at index %d out of range ", sidx)
|
||||
}
|
||||
}
|
||||
s.readingsBuilt = true
|
||||
|
||||
readings, ok := s.readingsBySensorID[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("readings for sensor id %s do not exist", id)
|
||||
}
|
||||
return readings, nil
|
||||
}
|
||||
124
internal/hwinfo/reading.go
Normal file
124
internal/hwinfo/reading.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package hwinfo
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
#include "hwisenssm2.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/util"
|
||||
)
|
||||
|
||||
// ReadingType enum of value/unit type for reading
|
||||
type ReadingType int
|
||||
|
||||
const (
|
||||
// ReadingTypeNone no type
|
||||
ReadingTypeNone ReadingType = iota
|
||||
// ReadingTypeTemp temperature in celsius
|
||||
ReadingTypeTemp
|
||||
// ReadingTypeVolt voltage
|
||||
ReadingTypeVolt
|
||||
// ReadingTypeFan RPM
|
||||
ReadingTypeFan
|
||||
// ReadingTypeCurrent amps
|
||||
ReadingTypeCurrent
|
||||
// ReadingTypePower watts
|
||||
ReadingTypePower
|
||||
// ReadingTypeClock Mhz
|
||||
ReadingTypeClock
|
||||
// ReadingTypeUsage e.g. MBs
|
||||
ReadingTypeUsage
|
||||
// ReadingTypeOther other
|
||||
ReadingTypeOther
|
||||
)
|
||||
|
||||
func (t ReadingType) String() string {
|
||||
return [...]string{"None", "Temp", "Volt", "Fan", "Current", "Power", "Clock", "Usage", "Other"}[t]
|
||||
}
|
||||
|
||||
// Reading element (e.g. usage, power, mhz...)
|
||||
type Reading struct {
|
||||
cr C.PHWiNFO_SENSORS_READING_ELEMENT
|
||||
}
|
||||
|
||||
// NewReading contructs a Reading
|
||||
func NewReading(data []byte) Reading {
|
||||
return Reading{
|
||||
cr: C.PHWiNFO_SENSORS_READING_ELEMENT(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
|
||||
// ID unique ID of the reading within a particular sensor
|
||||
func (r *Reading) ID() int32 {
|
||||
return int32(r.cr.dwReadingID)
|
||||
}
|
||||
|
||||
// Type of sensor reading
|
||||
func (r *Reading) Type() ReadingType {
|
||||
return ReadingType(r.cr.tReading)
|
||||
}
|
||||
|
||||
// SensorIndex this is the index of sensor in the Sensors[] array to
|
||||
// which this reading belongs to
|
||||
func (r *Reading) SensorIndex() uint64 {
|
||||
return uint64(r.cr.dwSensorIndex)
|
||||
}
|
||||
|
||||
// ReadingID a unique ID of the reading within a particular sensor
|
||||
func (r *Reading) ReadingID() uint64 {
|
||||
return uint64(r.cr.dwReadingID)
|
||||
}
|
||||
|
||||
// LabelOrig original label (e.g. "Chassis2 Fan")
|
||||
func (r *Reading) LabelOrig() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&r.cr.szLabelOrig), C.HWiNFO_SENSORS_STRING_LEN2)
|
||||
}
|
||||
|
||||
// LabelUser label displayed, which might have been renamed by user
|
||||
func (r *Reading) LabelUser() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&r.cr.szLabelUser), C.HWiNFO_SENSORS_STRING_LEN2)
|
||||
}
|
||||
|
||||
// Unit e.g. "RPM"
|
||||
func (r *Reading) Unit() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&r.cr.szUnit), C.HWiNFO_UNIT_STRING_LEN)
|
||||
}
|
||||
|
||||
func (r *Reading) valuePtr() unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(unsafe.Pointer(&r.cr.szUnit)) + C.HWiNFO_UNIT_STRING_LEN)
|
||||
}
|
||||
|
||||
// Value current value
|
||||
func (r *Reading) Value() float64 {
|
||||
return float64(*(*C.double)(r.valuePtr()))
|
||||
}
|
||||
|
||||
func (r *Reading) valueMinPtr() unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(r.valuePtr()) + C.sizeof_double)
|
||||
}
|
||||
|
||||
// ValueMin current value
|
||||
func (r *Reading) ValueMin() float64 {
|
||||
return float64(*(*C.double)(r.valueMinPtr()))
|
||||
}
|
||||
|
||||
func (r *Reading) valueMaxPtr() unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(r.valueMinPtr()) + C.sizeof_double)
|
||||
}
|
||||
|
||||
// ValueMax current value
|
||||
func (r *Reading) ValueMax() float64 {
|
||||
return float64(*(*C.double)(r.valueMaxPtr()))
|
||||
}
|
||||
|
||||
func (r *Reading) valueAvgPtr() unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(r.valueMaxPtr()) + C.sizeof_double)
|
||||
}
|
||||
|
||||
// ValueAvg current value
|
||||
func (r *Reading) ValueAvg() float64 {
|
||||
return float64(*(*C.double)(r.valueAvgPtr()))
|
||||
}
|
||||
52
internal/hwinfo/sensor.go
Normal file
52
internal/hwinfo/sensor.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package hwinfo
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
#include "hwisenssm2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/util"
|
||||
)
|
||||
|
||||
// Sensor element (e.g. motherboard, cpu, gpu...)
|
||||
type Sensor struct {
|
||||
cs C.PHWiNFO_SENSORS_SENSOR_ELEMENT
|
||||
}
|
||||
|
||||
// NewSensor constructs a Sensor
|
||||
func NewSensor(data []byte) Sensor {
|
||||
return Sensor{
|
||||
cs: C.PHWiNFO_SENSORS_SENSOR_ELEMENT(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
|
||||
// SensorID a unique Sensor ID
|
||||
func (s *Sensor) SensorID() uint64 {
|
||||
return uint64(s.cs.dwSensorID)
|
||||
}
|
||||
|
||||
// SensorInst the instance of the sensor (together with SensorID forms a unique ID)
|
||||
func (s *Sensor) SensorInst() uint64 {
|
||||
return uint64(s.cs.dwSensorInst)
|
||||
}
|
||||
|
||||
// ID a unique ID combining SensorID and SensorInst
|
||||
func (s *Sensor) ID() string {
|
||||
// keeping old method used in legacy steam deck plugin
|
||||
return strconv.FormatUint(s.SensorID()*100+s.SensorInst(), 10)
|
||||
}
|
||||
|
||||
// NameOrig original name of sensor
|
||||
func (s *Sensor) NameOrig() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&s.cs.szSensorNameOrig), C.HWiNFO_SENSORS_STRING_LEN2)
|
||||
}
|
||||
|
||||
// NameUser sensor name displayed, which might have been renamed by user
|
||||
func (s *Sensor) NameUser() string {
|
||||
return util.DecodeCharPtr(unsafe.Pointer(&s.cs.szSensorNameUser), C.HWiNFO_SENSORS_STRING_LEN2)
|
||||
}
|
||||
95
internal/hwinfo/shmem/shmem.go
Normal file
95
internal/hwinfo/shmem/shmem.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package shmem
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
#include "../hwisenssm2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/mutex"
|
||||
"github.com/shayne/hwinfo-streamdeck/internal/hwinfo/util"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var buf = make([]byte, 200000)
|
||||
|
||||
func copyBytes(addr uintptr) []byte {
|
||||
headerLen := C.sizeof_HWiNFO_SENSORS_SHARED_MEM2
|
||||
|
||||
var d []byte
|
||||
dh := (*reflect.SliceHeader)(unsafe.Pointer(&d))
|
||||
|
||||
dh.Data = addr
|
||||
dh.Len, dh.Cap = headerLen, headerLen
|
||||
|
||||
cheader := C.PHWiNFO_SENSORS_SHARED_MEM2(unsafe.Pointer(&d[0]))
|
||||
fullLen := int(cheader.dwOffsetOfReadingSection + (cheader.dwSizeOfReadingElement * cheader.dwNumReadingElements))
|
||||
|
||||
if fullLen > cap(buf) {
|
||||
buf = append(buf, make([]byte, fullLen-cap(buf))...)
|
||||
}
|
||||
|
||||
dh.Len, dh.Cap = fullLen, fullLen
|
||||
|
||||
copy(buf, d)
|
||||
|
||||
return buf[:fullLen]
|
||||
}
|
||||
|
||||
// ReadBytes copies bytes from global shared memory
|
||||
func ReadBytes() ([]byte, error) {
|
||||
err := mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hnd, err := openFileMapping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := mapViewOfFile(hnd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer unmapViewOfFile(addr)
|
||||
defer windows.CloseHandle(windows.Handle(unsafe.Pointer(hnd)))
|
||||
|
||||
return copyBytes(addr), nil
|
||||
}
|
||||
|
||||
func openFileMapping() (C.HANDLE, error) {
|
||||
lpName := C.CString("Global\\HWiNFO_SENS_SM2_REMOTE_0")
|
||||
defer C.free(unsafe.Pointer(lpName))
|
||||
|
||||
hnd := C.OpenFileMapping(syscall.FILE_MAP_READ, 0, lpName)
|
||||
if hnd == C.HANDLE(C.NULL) {
|
||||
errstr := util.HandleLastError(uint64(C.GetLastError()))
|
||||
return nil, fmt.Errorf("OpenFileMapping: %w", errstr)
|
||||
}
|
||||
|
||||
return hnd, nil
|
||||
}
|
||||
|
||||
func mapViewOfFile(hnd C.HANDLE) (uintptr, error) {
|
||||
addr, err := windows.MapViewOfFile(windows.Handle(unsafe.Pointer(hnd)), C.FILE_MAP_READ, 0, 0, 0)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("MapViewOfFile: %w", err)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func unmapViewOfFile(ptr uintptr) error {
|
||||
err := windows.UnmapViewOfFile(ptr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UnmapViewOfFile: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
61
internal/hwinfo/util/util.go
Normal file
61
internal/hwinfo/util/util.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package util
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
)
|
||||
|
||||
// ErrFileNotFound Windows error
|
||||
var ErrFileNotFound = errors.New("file not found")
|
||||
|
||||
// ErrInvalidHandle Windows error
|
||||
var ErrInvalidHandle = errors.New("invalid handle")
|
||||
|
||||
// UnknownError unhandled Windows error
|
||||
type UnknownError struct {
|
||||
Code uint64
|
||||
}
|
||||
|
||||
func (e UnknownError) Error() string {
|
||||
return fmt.Sprintf("unknown error code: %d", e.Code)
|
||||
}
|
||||
|
||||
// HandleLastError converts C.GetLastError() to golang error
|
||||
func HandleLastError(code uint64) error {
|
||||
switch code {
|
||||
case 2: // ERROR_FILE_NOT_FOUND
|
||||
return ErrFileNotFound
|
||||
case 6: // ERROR_INVALID_HANDLE
|
||||
return ErrInvalidHandle
|
||||
default:
|
||||
return UnknownError{Code: code}
|
||||
}
|
||||
}
|
||||
|
||||
func goStringFromPtr(ptr unsafe.Pointer, len int) string {
|
||||
s := C.GoStringN((*C.char)(ptr), C.int(len))
|
||||
return s[:strings.IndexByte(s, 0)]
|
||||
}
|
||||
|
||||
// DecodeCharPtr decodes ISO8859_1 string to UTF-8
|
||||
func DecodeCharPtr(ptr unsafe.Pointer, len int) string {
|
||||
s := goStringFromPtr(ptr, len)
|
||||
ds, err := decodeISO8859_1(s)
|
||||
if err != nil {
|
||||
log.Fatalf("TODO: failed to decode: %v", err)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
var isodecoder = charmap.ISO8859_1.NewDecoder()
|
||||
|
||||
func decodeISO8859_1(in string) (string, error) {
|
||||
return isodecoder.String(in)
|
||||
}
|
||||
Reference in New Issue
Block a user