@@ -3,81 +3,134 @@ package winpowrprof
33// modify from https://github.com/golang/go/blob/b634f6fdcbebee23b7da709a243f3db217b64776/src/runtime/os_windows.go#L257
44
55import (
6+ "sync"
67 "syscall"
78 "unsafe"
89
10+ "github.com/sagernet/sing/common/x/list"
11+
912 "golang.org/x/sys/windows"
1013)
1114
15+ type powerEventListener struct {
16+ element * list.Element [EventCallback ]
17+ }
18+
19+ func NewEventListener (callback EventCallback ) (EventListener , error ) {
20+ err := initCallback ()
21+ if err != nil {
22+ return nil , err
23+ }
24+ access .Lock ()
25+ defer access .Unlock ()
26+ return & powerEventListener {
27+ element : callbackList .PushBack (callback ),
28+ }, nil
29+ }
30+
31+ func (l * powerEventListener ) Start () error {
32+ access .Lock ()
33+ defer access .Unlock ()
34+ if handle != 0 {
35+ return nil
36+ }
37+ return startListener ()
38+ }
39+
40+ func (l * powerEventListener ) Close () error {
41+ access .Lock ()
42+ defer access .Unlock ()
43+ if l .element != nil {
44+ callbackList .Remove (l .element )
45+ }
46+ if callbackList .Len () > 0 {
47+ return nil
48+ }
49+ return closeListener ()
50+ }
51+
1252var (
1353 modpowerprof = windows .NewLazySystemDLL ("powrprof.dll" )
1454 procPowerRegisterSuspendResumeNotification = modpowerprof .NewProc ("PowerRegisterSuspendResumeNotification" )
1555 procPowerUnregisterSuspendResumeNotification = modpowerprof .NewProc ("PowerUnregisterSuspendResumeNotification" )
1656)
1757
18- const (
19- PBT_APMSUSPEND uint32 = 4
20- PBT_APMRESUMESUSPEND uint32 = 7
21- PBT_APMRESUMEAUTOMATIC uint32 = 18
22- )
23-
24- const (
25- _DEVICE_NOTIFY_CALLBACK = 2
58+ var (
59+ access sync.Mutex
60+ callbackList list.List [EventCallback ]
61+ initCallbackOnce sync.Once
62+ rawCallback uintptr
63+ handle uintptr
2664)
2765
28- type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {
29- callback uintptr
30- context uintptr
31- }
32-
33- type eventListener struct {
34- params _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
35- handle uintptr
36- }
37-
38- func NewEventListener (callback func (event int )) (EventListener , error ) {
39- if err := procPowerRegisterSuspendResumeNotification .Find (); err != nil {
40- return nil , err // Running on Windows 7, where we don't need it anyway.
66+ func initCallback () error {
67+ err := procPowerRegisterSuspendResumeNotification .Find ()
68+ if err != nil {
69+ return err // Running on Windows 7, where we don't need it anyway.
4170 }
42- if err := procPowerUnregisterSuspendResumeNotification .Find (); err != nil {
43- return nil , err // Running on Windows 7, where we don't need it anyway.
71+ err = procPowerUnregisterSuspendResumeNotification .Find ()
72+ if err != nil {
73+ return err // Running on Windows 7, where we don't need it anyway.
4474 }
45-
46- var fn interface {} = func (context uintptr , changeType uint32 , setting uintptr ) uintptr {
47- switch changeType {
48- case PBT_APMSUSPEND :
49- callback (EVENT_SUSPEND )
50- case PBT_APMRESUMESUSPEND :
51- callback (EVENT_RESUME )
52- case PBT_APMRESUMEAUTOMATIC :
53- callback (EVENT_RESUME_AUTOMATIC )
54- }
55- return 0
56- }
57- return & eventListener {
58- params : _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
59- callback : windows .NewCallback (fn ),
60- },
61- }, nil
75+ initCallbackOnce .Do (func () {
76+ rawCallback = windows .NewCallback (func (context uintptr , changeType uint32 , setting uintptr ) uintptr {
77+ const (
78+ PBT_APMSUSPEND uint32 = 4
79+ PBT_APMRESUMESUSPEND uint32 = 7
80+ PBT_APMRESUMEAUTOMATIC uint32 = 18
81+ )
82+ var event int
83+ switch changeType {
84+ case PBT_APMSUSPEND :
85+ event = EVENT_SUSPEND
86+ case PBT_APMRESUMESUSPEND :
87+ event = EVENT_RESUME
88+ case PBT_APMRESUMEAUTOMATIC :
89+ event = EVENT_RESUME_AUTOMATIC
90+ default :
91+ return 0
92+ }
93+ access .Lock ()
94+ callbacks := callbackList .Array ()
95+ access .Unlock ()
96+ for _ , callback := range callbacks {
97+ callback (event )
98+ }
99+ return 0
100+ })
101+ })
102+ return nil
62103}
63104
64- func (l * eventListener ) Start () error {
105+ func startListener () error {
106+ type DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {
107+ callback uintptr
108+ context uintptr
109+ }
110+ const DEVICE_NOTIFY_CALLBACK = 2
111+ params := DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
112+ callback : rawCallback ,
113+ }
65114 _ , _ , errno := syscall .SyscallN (
66115 procPowerRegisterSuspendResumeNotification .Addr (),
67- _DEVICE_NOTIFY_CALLBACK ,
68- uintptr (unsafe .Pointer (& l . params )),
69- uintptr (unsafe .Pointer (& l . handle )),
116+ DEVICE_NOTIFY_CALLBACK ,
117+ uintptr (unsafe .Pointer (& params )),
118+ uintptr (unsafe .Pointer (& handle )),
70119 )
71120 if errno != 0 {
72121 return errno
73122 }
74123 return nil
75124}
76125
77- func (l * eventListener ) Close () error {
78- _ , _ , errno := syscall .SyscallN (procPowerUnregisterSuspendResumeNotification .Addr (), uintptr (unsafe .Pointer (& l .handle )))
126+ func closeListener () error {
127+ if handle == 0 {
128+ return nil
129+ }
130+ _ , _ , errno := syscall .SyscallN (procPowerUnregisterSuspendResumeNotification .Addr (), uintptr (unsafe .Pointer (& handle )))
79131 if errno != 0 {
80132 return errno
81133 }
134+ handle = 0
82135 return nil
83136}
0 commit comments