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

132 lines
2.7 KiB
Go

package afterlock
import(
"time"
"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
keypressFlag *AtomicFlag
Display Display
LockScreen Lockscreen
KeypressDetector KeypressDetector
}
// New constructs a new AfterLock instance
func New(keyboardDeviceNode string) *AfterLock {
return &AfterLock{
keyboardDeviceNode: keyboardDeviceNode,
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()
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)
}
af.keypressFlag.Set(true)
}
}
func (af *AfterLock) hybernateDisplayLoop() {
for {
if !af.screenLocked() {
return
}
if af.displayIsOn() {
af.suspendDisplay()
}
// avoid suspending the display while typing:
// sleep until no keys were pressed for LoopDelay seconds
for {
time.Sleep(time.Duration(af.LoopDelay) * time.Second)
if !af.screenLocked() {
return
}
if af.keypressFlag.Get() {
af.keypressFlag.Set(false)
continue
}
break
}
}
}
func (af *AfterLock) screenLocked() bool {
screenLocked, err := af.LockScreen.IsActive()
if err != nil {
panic(err)
}
return screenLocked
}
func (af *AfterLock) displayIsOn() bool {
displayOn, err := af.Display.IsOn()
if err != nil {
panic(err)
}
return displayOn
}
func (af *AfterLock) suspendDisplay() {
err := af.Display.Suspend()
if err != nil {
panic(err)
}
}