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) } }