Skip to content

Instantly share code, notes, and snippets.

@DarwinAwardWinner
Last active November 12, 2021 02:51
Show Gist options
  • Save DarwinAwardWinner/77e8acea2f14ed9ea66d7222d7ace500 to your computer and use it in GitHub Desktop.
Save DarwinAwardWinner/77e8acea2f14ed9ea66d7222d7ace500 to your computer and use it in GitHub Desktop.
Script to auto-unlock GNOME after short sleeps
#!/usr/bin/env python3
# shortsleep-unlock.py - Keep GNOME unlocked on short lid closes
# Copyright (C) 2021 Ryan C. Thompson <[email protected]>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details:
# https://www.gnu.org/licenses/.
import typing
import time
from attr import attrs, attrib
from os import getpid
from pydbus import SystemBus, SessionBus
from gi.repository import GLib
loop = GLib.MainLoop()
sysbus = SystemBus()
sesbus = SessionBus()
gss = sesbus.get(
"org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver"
)
def unlock_session():
try:
gss.SetActive(False)
print("Unlocked session!")
except Exception:
# If we fail, the session just doesn't unlock. That's fine.
print("Failed to unlock session!")
pass
upower = sysbus.get(
"org.freedesktop.UPower"
)
@attrs(auto_attribs=True)
class ShortSleepUnlockManager:
unlock_window: float = 300
require_lid: bool = True
lock_suspend_window: float = 10
last_lock_time: typing.Optional[float] = attrib(default = None, init = False)
last_suspend_time: typing.Optional[float] = attrib(default = None, init = False)
suspended_on_lid_close: typing.Optional[bool] = attrib(default = None, init = False)
def lid_ok(self) -> bool:
return bool(not self.require_lid or self.suspended_on_lid_close)
def record_lock(self):
if self.last_lock_time is None:
self.last_lock_time = time.time()
def record_suspend(self):
if self.last_suspend_time is None:
self.last_suspend_time = time.time()
self.suspended_on_lid_close = upower.LidIsClosed
def reset(self):
self.last_lock_time = None
self.last_suspend_time = None
self.suspended_on_lid_close = None
def locked_at_suspend_time(self) -> bool:
try:
lock_suspend_delta = abs(self.last_suspend_time - self.last_lock_time)
return lock_suspend_delta <= self.lock_suspend_window
except TypeError:
return False
def time_since_last_suspend(self) -> typing.Optional[float]:
if self.locked_at_suspend_time():
return time.time() - self.last_suspend_time
def inside_unlock_window(self) -> bool:
try:
return 0 <= self.time_since_last_suspend() <= self.unlock_window
except TypeError:
return False
def should_unlock(self) -> bool:
return self.lid_ok() and self.inside_unlock_window()
def handle_PrepareForSleep(self, before_sleep: bool):
if before_sleep:
self.record_suspend()
print("Detected suspend!")
# print(self)
else:
if self.should_unlock():
print("Unlocking session!")
unlock_session()
self.reset()
else:
print("Not unlocking session")
# print(self)
def handle_ActiveChanged(self, ss_active: bool):
if ss_active:
self.record_lock()
print("Detected lock!")
else:
print("Detected unlock!")
self.reset()
# print(self)
# Connect to signals
manager = ShortSleepUnlockManager(
# Only activate when suspend was triggered by lid closing
require_lid = True,
# Unlock if resume happens within 5 minutes of suspend
unlock_window = 300,
)
login1_main = sysbus.get("org.freedesktop.login1", "/org/freedesktop/login1")
login1_main.PrepareForSleep.connect(manager.handle_PrepareForSleep)
gss.ActiveChanged.connect(manager.handle_ActiveChanged)
ss = sesbus.get("org.freedesktop.ScreenSaver")
ss.ActiveChanged.connect(manager.handle_ActiveChanged)
ssp = sesbus.get("org.gnome.SettingsDaemon.ScreensaverProxy", "/org/freedesktop/ScreenSaver")
ssp.ActiveChanged.connect(manager.handle_ActiveChanged)
print("Starting!")
loop.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment