feat(sysauth-dialog): add thread-safe dismiss and service wiring

run_auth_dialog now returns a thread-safe dismiss callable so polkitd
cancellations can close the dialog from any thread. connect_sysauth_service
wires the agent signals to the dialog: show on begin, respond on submit,
re-prompt on failure, dismiss on cancel.
This commit is contained in:
sankalp committed 2026-08-02 12:10:06 +05:45
1 parent 1059833576
commit e36e0c1a7d
1 file changed
+89
+89
View File
@@ -22,6 +22,7 @@ class AuthDialog(Window):
def __init__(self, action_id: str, message: str, icon_name: str):
self._result_accepted = False
self._main_loop = None
self._dismissed = False
super().__init__(
title="modus-dialog",
@@ -172,6 +173,9 @@ class AuthDialog(Window):
self._error_label.set_visible(False)
self._entry_password.grab_focus()
if self._dismissed:
return self._result_accepted
self._main_loop.run()
return self._result_accepted
@@ -214,6 +218,11 @@ class AuthDialog(Window):
self.hide()
GLib.timeout_add(100, self._quit_main_loop)
def dismiss(self):
"""Dismiss the dialog from any thread."""
self._dismissed = True
GLib.idle_add(self._dismiss)
def _quit_main_loop(self):
"""Quit the nested main loop."""
if self._main_loop is not None and self._main_loop.is_running():
@@ -230,6 +239,7 @@ class AuthDialog(Window):
def reset(self):
"""Reset the dialog for reuse."""
self._result_accepted = False
self._dismissed = False
self._error_label.set_visible(False)
self._entry_password.set_text("")
@@ -254,10 +264,25 @@ def run_auth_dialog(
icon_name: Icon name for the action.
on_result: Callback(password: str | None) when dialog closes.
error_message: Optional error message to show on first display.
Returns:
A callable that dismisses the dialog (thread-safe). Calling it
before the dialog appears prevents the dialog from being shown.
"""
state = {"dialog": None}
def _dismiss():
dialog = state.get("dialog")
if dialog is not None:
dialog.dismiss()
else:
state["cancelled"] = True
def _run():
dialog = AuthDialog(action_id, message, icon_name)
state["dialog"] = dialog
if state.get("cancelled"):
return
if error_message:
GLib.idle_add(lambda: dialog.show_error(error_message))
@@ -275,3 +300,67 @@ def run_auth_dialog(
thread = threading.Thread(target=_run, daemon=True)
thread.start()
return _dismiss
def connect_sysauth_service(service):
"""Connect a SysauthService to the authentication dialog.
Shows the dialog on ``begin-authentication``, responds on submit,
re-prompts with an error on failed authentication, and dismisses
the open dialog when polkitd cancels the request.
Args:
service: The SysauthService instance to wire up.
"""
pending = {}
open_dialogs = {}
def _respond(cookie, password):
threading.Thread(
target=lambda: service.respond(cookie, password), daemon=True
).start()
def _prompt(cookie, error_message=None):
info = pending.get(cookie)
if info is None:
return
action_id, message, icon_name = info
def on_result(password):
open_dialogs.pop(cookie, None)
if password is None:
pending.pop(cookie, None)
service.cancel(cookie)
return
_respond(cookie, password)
open_dialogs[cookie] = run_auth_dialog(
action_id,
message,
icon_name,
on_result=on_result,
error_message=error_message,
)
def on_begin(_service, action_id, message, icon_name, cookie, _uid):
pending[cookie] = (action_id, message, icon_name)
_prompt(cookie)
def on_cancelled(_service, cookie):
pending.pop(cookie, None)
dismiss = open_dialogs.pop(cookie, None)
if dismiss is not None:
dismiss()
def on_completed(_service, cookie, success):
if success:
pending.pop(cookie, None)
open_dialogs.pop(cookie, None)
else:
_prompt(cookie, error_message="Incorrect password. Please try again.")
service.connect("begin-authentication", on_begin)
service.connect("authentication-cancelled", on_cancelled)
service.connect("authentication-completed", on_completed)