feat: Inhibit screensaver during backup operations

Adds logic to prevent the screensaver or screen lock from activating while a backup is in progress.

- Uses a D-Bus call to `org.freedesktop.ScreenSaver.Inhibit` to request a lock.
- The lock is activated when a backup starts.
- The lock is reliably released when the backup finishes, fails, or is cancelled.
This commit is contained in:
2025-09-06 12:56:18 +02:00
parent 452a56b813
commit abc0f4e8cf

View File

@@ -28,6 +28,59 @@ class BackupManager:
self.is_system_process = False
self.app = app
self.encryption_manager = EncryptionManager(logger, app)
self.inhibit_cookie = None
def _inhibit_screensaver(self):
"""Prevents the screensaver and auto-suspend during a backup."""
if not shutil.which("gdbus"):
self.logger.log("gdbus command not found, cannot inhibit screensaver.")
return
try:
self.logger.log("Attempting to inhibit screensaver and power management.")
command = [
"gdbus", "call", "--session", "--dest", "org.freedesktop.ScreenSaver",
"--object-path", "/org/freedesktop/ScreenSaver",
"--method", "org.freedesktop.ScreenSaver.Inhibit",
"Py-Backup", "Backup in progress"
]
result = subprocess.run(command, capture_output=True, text=True, check=True)
# Output is like "(uint32 12345,)", we need to extract the number.
match = re.search(r'uint32\s+(\d+)', result.stdout)
if match:
self.inhibit_cookie = int(match.group(1))
self.logger.log(f"Successfully inhibited screensaver with cookie {self.inhibit_cookie}")
else:
self.logger.log(f"Could not parse inhibit cookie from gdbus output: {result.stdout}")
except FileNotFoundError:
self.logger.log("gdbus command not found, cannot inhibit screensaver.")
except subprocess.CalledProcessError as e:
self.logger.log(f"Failed to inhibit screensaver. D-Bus call failed: {e.stderr}")
except Exception as e:
self.logger.log(f"An unexpected error occurred while inhibiting screensaver: {e}")
def _uninhibit_screensaver(self):
"""Releases the screensaver and auto-suspend lock."""
if self.inhibit_cookie is None:
return
if not shutil.which("gdbus"):
self.logger.log("gdbus command not found, cannot uninhibit screensaver.")
return
try:
self.logger.log(f"Attempting to uninhibit screensaver with cookie {self.inhibit_cookie}")
command = [
"gdbus", "call", "--session", "--dest", "org.freedesktop.ScreenSaver",
"--object-path", "/org/freedesktop/ScreenSaver",
"--method", "org.freedesktop.ScreenSaver.UnInhibit",
str(self.inhibit_cookie)
]
subprocess.run(command, capture_output=True, text=True, check=True)
self.logger.log("Successfully uninhibited screensaver.")
except Exception as e:
self.logger.log(f"Failed to uninhibit screensaver: {e}")
finally:
self.inhibit_cookie = None
def cancel_and_delete_privileged_backup(self, delete_path: str):
"""Cancels a running system backup and deletes the target directory in one atomic pkexec call."""
@@ -169,6 +222,7 @@ rm -f '{info_file_path}'
def start_backup(self, queue, source_path: str, dest_path: str, is_system: bool, is_dry_run: bool = False, exclude_files: Optional[List[Path]] = None, source_size: int = 0, is_compressed: bool = False, is_encrypted: bool = False, mode: str = "incremental", password: Optional[str] = None, key_file: Optional[str] = None):
self.is_system_process = is_system
self._inhibit_screensaver()
thread = threading.Thread(target=self._run_backup_path, args=(
queue, source_path, dest_path, is_system, is_dry_run, exclude_files, source_size, is_compressed, is_encrypted, mode, password, key_file))
thread.daemon = True
@@ -370,6 +424,7 @@ set -e
finally:
if is_encrypted and mount_point:
self.encryption_manager.cleanup_encrypted_backup(base_dest_path)
self._uninhibit_screensaver()
self.process = None
def _create_info_file(self, pybackup_dir: str, backup_name: str, source_size: int, is_encrypted: bool):