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