Skip to content

Commit

Permalink
Add winpowrprof package
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx authored and nekohasekai committed Mar 5, 2024
1 parent c4c4f45 commit b4d5d57
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
12 changes: 12 additions & 0 deletions common/winpowrprof/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package winpowrprof

const (
EVENT_SUSPEND = iota
EVENT_RESUME
EVENT_RESUME_AUTOMATIC // Because the user is not present, most applications should do nothing.
)

type EventListener interface {
Start() error
Close() error
}
11 changes: 11 additions & 0 deletions common/winpowrprof/event_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build !windows

package winpowrprof

import (
"os"
)

func NewEventListener(callback func(event int)) (EventListener, error) {
return nil, os.ErrInvalid
}
19 changes: 19 additions & 0 deletions common/winpowrprof/event_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package winpowrprof

import (
"runtime"
"testing"

"github.com/stretchr/testify/require"
)

func TestPowerEvents(t *testing.T) {
if runtime.GOOS != "windows" {
t.SkipNow()
}
listener, err := NewEventListener(func(event int) {})
require.NoError(t, err)
require.NotNil(t, listener)
require.NoError(t, listener.Start())
require.NoError(t, listener.Close())
}
83 changes: 83 additions & 0 deletions common/winpowrprof/event_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package winpowrprof

// modify from https://github.com/golang/go/blob/b634f6fdcbebee23b7da709a243f3db217b64776/src/runtime/os_windows.go#L257

import (
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

var (
modpowerprof = windows.NewLazySystemDLL("powrprof.dll")
procPowerRegisterSuspendResumeNotification = modpowerprof.NewProc("PowerRegisterSuspendResumeNotification")
procPowerUnregisterSuspendResumeNotification = modpowerprof.NewProc("PowerUnregisterSuspendResumeNotification")
)

const (
PBT_APMSUSPEND uint32 = 4
PBT_APMRESUMESUSPEND uint32 = 7
PBT_APMRESUMEAUTOMATIC uint32 = 18
)

const (
_DEVICE_NOTIFY_CALLBACK = 2
)

type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {
callback uintptr
context uintptr
}

type eventListener struct {
params _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
handle uintptr
}

func NewEventListener(callback func(event int)) (EventListener, error) {
if err := procPowerRegisterSuspendResumeNotification.Find(); err != nil {
return nil, err // Running on Windows 7, where we don't need it anyway.
}
if err := procPowerUnregisterSuspendResumeNotification.Find(); err != nil {
return nil, err // Running on Windows 7, where we don't need it anyway.
}

var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr {
switch changeType {
case PBT_APMSUSPEND:
callback(EVENT_SUSPEND)
case PBT_APMRESUMESUSPEND:
callback(EVENT_RESUME)
case PBT_APMRESUMEAUTOMATIC:
callback(EVENT_RESUME_AUTOMATIC)
}
return 0
}
return &eventListener{
params: _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS{
callback: windows.NewCallback(fn),
},
}, nil
}

func (l *eventListener) Start() error {
_, _, errno := syscall.SyscallN(
procPowerRegisterSuspendResumeNotification.Addr(),
_DEVICE_NOTIFY_CALLBACK,
uintptr(unsafe.Pointer(&l.params)),
uintptr(unsafe.Pointer(&l.handle)),
)
if errno != 0 {
return errno
}
return nil
}

func (l *eventListener) Close() error {
_, _, errno := syscall.SyscallN(procPowerUnregisterSuspendResumeNotification.Addr(), uintptr(unsafe.Pointer(&l.handle)))
if errno != 0 {
return errno
}
return nil
}

0 comments on commit b4d5d57

Please sign in to comment.