kde-lockscreen-suspend-display/afterlock/afterlock.go

126 lines
2.5 KiB
Go

package afterlock
import(
"time"
"sync"
"errors"
)
// Display controls and monitors the physical display
type Display interface {
Suspend() error
IsOn() (bool, error)
}
// Lockscreen can determine whether the screensaver is active or not
type Lockscreen interface {
IsActive() (bool, error)
}
// KeypressDetector can wait for keypresses
type KeypressDetector interface {
BlockUntilKeypress(string) error
}
// AfterLock suspends the display when the lockscreen is active
type AfterLock struct {
InitialDelay uint
LoopDelay uint
keyboardDeviceNode string
wg sync.WaitGroup
stopFlag *AtomicFlag
keypressFlag *AtomicFlag
Display Display
LockScreen Lockscreen
KeypressDetector KeypressDetector
}
// New constructs a new AfterLock instance
func New(keyboardDeviceNode string) *AfterLock {
return &AfterLock{
keyboardDeviceNode: keyboardDeviceNode,
stopFlag: NewAtomicFlag(false),
keypressFlag: NewAtomicFlag(false),
}
}
// Start starts monitoring the lockscreen/keyboard to determine
// when to suspend the display.
// Exits after the computer is unlocked
// Requires InitialDelay, LoopDelay, Display and LockScreen to be set
func (af *AfterLock) Start() {
af.checkStructConfigured()
af.wg.Add(1)
go af.detectKeypresses()
time.Sleep(time.Duration(af.InitialDelay) * time.Second)
af.hybernateDisplayLoop()
}
func (af *AfterLock) checkStructConfigured() {
if af.InitialDelay == 0 {
panic(errors.New("InitialDelay not configured"))
}
if af.LoopDelay == 0 {
panic(errors.New("LoopDelay not configured"))
}
if af.Display == nil {
panic(errors.New("Display not configured"))
}
if af.LockScreen == nil {
panic(errors.New("LockScreen not configured"))
}
if af.KeypressDetector == nil {
panic(errors.New("KeypressDetector not configured"))
}
}
func (af *AfterLock) detectKeypresses() {
for {
err := af.KeypressDetector.BlockUntilKeypress(af.keyboardDeviceNode)
if err != nil {
panic(err)
}
if af.stopFlag.Get() {
af.wg.Done()
return
}
af.keypressFlag.Set(true)
}
}
func (af *AfterLock) hybernateDisplayLoop() {
for {
screenLocked, err := af.LockScreen.IsActive()
if err != nil {
panic(err)
}
if !screenLocked {
af.stopFlag.Set(true)
af.wg.Wait()
return
}
displayOn, err := af.Display.IsOn()
if err != nil {
panic(err)
}
if displayOn {
err := af.Display.Suspend()
if err != nil {
panic(err)
}
}
for {
time.Sleep(time.Duration(af.LoopDelay) * time.Second)
if af.keypressFlag.Get() {
af.keypressFlag.Set(false)
continue
}
break
}
}
}