diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 3b8872448..02b70af1f 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -285,6 +285,22 @@ def setupLangAndBackend( ########################################################################## +class NativeEventFilter(QAbstractNativeEventFilter): + def nativeEventFilter( + self, eventType: Any, message: Any + ) -> tuple[bool, Optional[sip.voidptr]]: + if eventType == "windows_generic_MSG": + import ctypes + + msg = ctypes.wintypes.MSG.from_address(int(message)) + if msg.message == 17: # WM_QUERYENDSESSION + if mw.can_auto_sync(): + mw.app._set_windows_shutdown_block_reason(tr.sync_syncing()) + mw.progress.single_shot(100, mw.unloadProfileAndExit) + return (True, 0) + return (False, 0) + + class AnkiApp(QApplication): # Single instance support on Win32/Linux ################################################## @@ -298,6 +314,27 @@ class AnkiApp(QApplication): QApplication.__init__(self, argv) self.installEventFilter(self) self._argv = argv + self._native_event_filter = NativeEventFilter() + if is_win: + self.installNativeEventFilter(self._native_event_filter) + + def _set_windows_shutdown_block_reason(self, reason: str) -> None: + if is_win: + import ctypes + from ctypes import windll, wintypes # type: ignore + + windll.user32.ShutdownBlockReasonCreate( + wintypes.HWND.from_param(int(mw.effectiveWinId())), + ctypes.c_wchar_p(reason), + ) + + def _unset_windows_shutdown_block_reason(self) -> None: + if is_win: + from ctypes import windll, wintypes # type: ignore + + windll.user32.ShutdownBlockReasonDestroy( + wintypes.HWND.from_param(int(mw.effectiveWinId())), + ) def secondInstance(self) -> bool: # we accept only one command line argument. if it's missing, send diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 08f74d447..61f2363fc 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -573,6 +573,7 @@ class AnkiQt(QMainWindow): self.backend.await_backup_completion() self.deleteLater() app = self.app + app._unset_windows_shutdown_block_reason() def exit(): # try to ensure Qt objects are deleted in a logical order,