ok-script 2.0.7b1__tar.gz → 2.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ok_script-2.0.7b1/ok_script.egg-info → ok_script-2.0.8}/PKG-INFO +1 -1
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/__init__.py +36 -9
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/start_controller.py +6 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/windows_graphics.py +16 -1
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/TaskExecutor.py +13 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/logger.py +3 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/windows_schedule.py +1 -1
- {ok_script-2.0.7b1 → ok_script-2.0.8/ok_script.egg-info}/PKG-INFO +1 -1
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/SOURCES.txt +3 -1
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_logger.py +21 -0
- ok_script-2.0.8/tests/test_overlay_lifecycle.py +105 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_start_controller.py +87 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_task_executor_queue.py +54 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_windows_graphics_capture.py +103 -1
- ok_script-2.0.8/tests/test_windows_schedule_xml.py +53 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/LICENSE.txt +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/MANIFEST.in +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/README.md +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/diagnostics/qq_trace.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/__main__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/alas/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/alas/emulator_base.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/alas/emulator_windows.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/alas/platform_windows.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/alas/utils.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/deep.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/minitouch.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/nemu_ipc.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/nemu_utils.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/pool.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/timer.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/adb/util.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/windows/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/windows/d3d11.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/capture/windows/dump.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/cli.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/config_schema.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/downloads.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/events.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/icons.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/notifications.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/screenshot.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/script_editing.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/script_packager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/script_templates.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/task_manager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/template_store.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/translation.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/core/ui_config.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/DeviceManager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/adb.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/base.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/bitblt.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/bitblt_utils.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/browser.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/desktop_duplication.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/hwnd_window.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/image.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/nemu_ipc.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/types.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/capture_methods/update.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/adb.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/base.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/browser.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/do_nothing.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/foreground_post_message.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/genshin.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/keys.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/post_message.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/pydirect.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/pynput.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/interaction_methods/swipe.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/device/intercation.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/feature/Box.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/feature/Feature.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/feature/FeatureSet.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/feature/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/gui/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/gui/util/pyappify_startup.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/manager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/messenger_images.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/pipeline.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/ppocr.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/providers.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/system.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/notification/windows_messenger.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ocr/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ocr/download_paddle_model.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Media/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Security/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/Storage/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/UI/ViewManagement/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/UI/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/Windows/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/delegate.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/export.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/idldsl.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/inspectable.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/roapi.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/types.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/rotypes/winstring.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/DiagnosisTask.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/exceptions.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/task.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/task/web.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/test/OKTestRunner.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/test/RunTests.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/test/TaskTestCase.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/test/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/third_party/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/third_party/paperclip.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/overlay/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/overlay/win32_gdi.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/Communicate.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/MainWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/MessageWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/StartController.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/TabTitles.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/AboutTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/HttpTextEdit.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/LinksBar.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/ProjectCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/UpdateCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/VersionCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/about/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/OKIcon.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/accent_color.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/config.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/design_system.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/icons.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/common/style_sheet.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/debug/DebugTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/debug/OverlayWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/debug/RunCodeTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/debug/Screenshot.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/debug/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/events.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/GettextTranslator.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/en_US.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/en_US.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/es_ES.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/es_ES.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/ja_JP.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/ja_JP.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/ko_KR.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/ko_KR.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/path.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/update.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/zh_CN.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/zh_CN.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/zh_TW.qm +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/i18n/zh_TW.ts +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/overlay/OverlayWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/overlay/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/card.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/discord.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/heart.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/message_window.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/setting_interface.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/status_bar.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/stop.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/dark/tab.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/card.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/discord.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/heart.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/message_window.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/setting_interface.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/status_bar.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/stop.svg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qss/light/tab.qss +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/qt.qrc +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/resources.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/settings/GlobalConfigCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/settings/GlobalConfigTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/settings/SettingTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/settings/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/LogWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/SelectCaptureListView.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/SelectInteractionListView.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/StartCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/StartTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/start/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ConfigCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ConfigItemFactory.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ConfigLabelAndWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/EditTaskTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndButtons.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndDoubleSpinBox.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndDropDown.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndFileSelector.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndGlobal.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndLineEdit.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndList.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndMultiSelection.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndSpinBox.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndSwitchButton.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndTextEdit.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/LabelAndWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/MarkUpWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ModifyListDialog.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ModifyListItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/OneTimeTaskTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/PythonHighlighter.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/RecordScript.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ScheduleTaskTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/ScriptPackager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/StartButton.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TaskCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TaskManger.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TaskOpButton.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TaskTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TemplateFactory.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TemplateTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TooltipTableWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/TriggerTaskTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/tasks/schedule_index_sync.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/Alert.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/InitWorker.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/app.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/download.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/pip_util.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/pyappify_startup.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/touch_scroll.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/util/windows_thumbnail.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/BaseLoading.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/BaseWindow.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/Card.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/CustomTab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/EmptyCard.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/ExpandCardLayout.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/FlowLayout.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/FramelessDialog.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/ImageWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/ListTableWidgetItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/NumericTableWidgetItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/OkCompactSpinBox.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/SortingTableWidgetItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/StartLoadingDialog.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/StatusBar.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/Tab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/TabWidget.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/UpdateConfigWidgetItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/WidgetWithVLabel.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/YesNonWidgetItem.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/qt/widget/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/app.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/requirements.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/server.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/static/app.js +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/static/chunk-fluent.js +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/static/chunk-react.js +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/static/index.css +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/ui/web/static/index.html +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/update/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/update/copy_ok_folder.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/update/inline_ok_requirements.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/Analytics.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/GlobalConfig.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/__init__.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/blur.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/clazz.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/collection.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/color.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/config.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/explorer.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/file.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/gpu_driver_settings.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/handler.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/i18n.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/partial_sync_repo.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/print_hwnd.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/process.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok/util/window.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/dependency_links.txt +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/entry_points.txt +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/not-zip-safe +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/requires.txt +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/ok_script.egg-info/top_level.txt +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/pyproject.toml +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/scripts/generate_web_i18n.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/setup.cfg +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/setup.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_accent_color.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_app.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_base_interaction.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_blur.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_box.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_capture_update.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_cli.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_config_card.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_core_events.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_core_ui_services.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_device_manager.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_expand_card_layout.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_explorer.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_global_config.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_gpu_driver_settings.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_handler.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_headless_imports.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_inline_ok_requirements.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_main_window_notifications.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_main_window_startup_tab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_main_window_system_theme.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_missing_frame.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_navigation_state.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_nemu_ipc_capture.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_notifications.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_ok_mutex.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_overlay_custom_draw.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_package_metadata.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_process.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_process_mutex.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_pyappify_startup_update.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_qt_events.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_qt_legacy_imports.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_qt_page_layout.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_runtime_startup.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_schedule_startup.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_schedule_task_index_sync.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_start_tab_overlay.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_task_ui.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_template_tab.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_touch_scroll.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_translation_fallback.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_ui_config.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_app.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_i18n.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_server.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_task_tabs.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_ui_pages.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_web_ui_tasks.py +0 -0
- {ok_script-2.0.7b1 → ok_script-2.0.8}/tests/test_windows_schedule_target_parse.py +0 -0
|
@@ -231,15 +231,35 @@ def _create_ok_config(config):
|
|
|
231
231
|
|
|
232
232
|
|
|
233
233
|
class _OverlayConfigMixin:
|
|
234
|
+
def _blur_enabled(self):
|
|
235
|
+
executor = getattr(og, 'executor', None)
|
|
236
|
+
return (callable(self.config.get('blur_area'))
|
|
237
|
+
and bool(getattr(executor, 'basic_options', {}).get('Enable Blur', False)))
|
|
238
|
+
|
|
239
|
+
def _overlay_needed(self):
|
|
240
|
+
return self.ok_config.get('use_overlay', False) or self._blur_enabled()
|
|
241
|
+
|
|
234
242
|
def overlay_state(self):
|
|
235
243
|
return {'boxes': bool(self.ok_config.get('use_overlay', False))}
|
|
236
244
|
|
|
237
245
|
def initialize_overlay(self):
|
|
238
246
|
"""Apply persisted overlay state independently of the active UI."""
|
|
239
|
-
if self.
|
|
247
|
+
if self._overlay_needed():
|
|
240
248
|
overlay = self.get_overlay_view()
|
|
241
249
|
if overlay is not None:
|
|
242
|
-
overlay.set_boxes_enabled(
|
|
250
|
+
overlay.set_boxes_enabled(self.ok_config.get('use_overlay', False))
|
|
251
|
+
|
|
252
|
+
def _on_blur_patches(self, patches):
|
|
253
|
+
# A live setting change can enable blur without a new window geometry event.
|
|
254
|
+
# The new overlay does not receive the signal currently being dispatched.
|
|
255
|
+
if self._blur_enabled() and self.overlay_window is None:
|
|
256
|
+
overlay = self.get_overlay_view()
|
|
257
|
+
if overlay is not None:
|
|
258
|
+
overlay.update_blur_patches(patches)
|
|
259
|
+
|
|
260
|
+
def _on_clear_blur_patches(self):
|
|
261
|
+
if not self._overlay_needed() and self.overlay_window is not None:
|
|
262
|
+
self._close_overlay(wait=False)
|
|
243
263
|
|
|
244
264
|
def _close_overlay(self, wait=True):
|
|
245
265
|
overlay = self.overlay_window
|
|
@@ -269,7 +289,7 @@ class _OverlayConfigMixin:
|
|
|
269
289
|
overlay = self.overlay_window
|
|
270
290
|
if name == 'boxes' and value:
|
|
271
291
|
overlay = self.get_overlay_view()
|
|
272
|
-
elif name == 'boxes' and not value and overlay is not None:
|
|
292
|
+
elif name == 'boxes' and not value and overlay is not None and not self._blur_enabled():
|
|
273
293
|
self._close_overlay()
|
|
274
294
|
return self.overlay_state()
|
|
275
295
|
if overlay is not None:
|
|
@@ -301,6 +321,8 @@ class App(_OverlayConfigMixin):
|
|
|
301
321
|
task_executor.load_tr()
|
|
302
322
|
self.ok_config = _create_ok_config(config)
|
|
303
323
|
communicate.quit.connect(self.quit)
|
|
324
|
+
communicate.blur_overlay.connect(self._on_blur_patches)
|
|
325
|
+
communicate.clear_blur_overlay.connect(self._on_clear_blur_patches)
|
|
304
326
|
|
|
305
327
|
self.about = self.config.get('about')
|
|
306
328
|
self.title = self.config.get('gui_title')
|
|
@@ -341,6 +363,9 @@ class App(_OverlayConfigMixin):
|
|
|
341
363
|
logger.debug('init app end')
|
|
342
364
|
|
|
343
365
|
def quit(self):
|
|
366
|
+
from ok.core.events import communicate
|
|
367
|
+
communicate.blur_overlay.disconnect(self._on_blur_patches)
|
|
368
|
+
communicate.clear_blur_overlay.disconnect(self._on_clear_blur_patches)
|
|
344
369
|
self.exit_event.set()
|
|
345
370
|
self._close_overlay(wait=False)
|
|
346
371
|
self._close_device_manager()
|
|
@@ -412,10 +437,8 @@ class App(_OverlayConfigMixin):
|
|
|
412
437
|
self.show_message_window(title, content)
|
|
413
438
|
|
|
414
439
|
def update_overlay(self, visible, x, y, window_width, window_height, width, height, scaling):
|
|
415
|
-
if not self.
|
|
416
|
-
#
|
|
417
|
-
# a configured blur callback must not resurrect a disabled native
|
|
418
|
-
# window (and its input/expiry workers).
|
|
440
|
+
if not self._overlay_needed():
|
|
441
|
+
# Keep the native window only while debug boxes or UID blur need it.
|
|
419
442
|
if self.overlay_window is not None:
|
|
420
443
|
self._close_overlay(wait=False)
|
|
421
444
|
return
|
|
@@ -427,7 +450,7 @@ class App(_OverlayConfigMixin):
|
|
|
427
450
|
|
|
428
451
|
def get_overlay_view(self):
|
|
429
452
|
"""Return the overlay widget exposed to tasks, custom tabs, and my_app."""
|
|
430
|
-
if not self.
|
|
453
|
+
if not self._overlay_needed():
|
|
431
454
|
return None
|
|
432
455
|
if self.overlay_window is None:
|
|
433
456
|
from ok.core.events import communicate
|
|
@@ -515,6 +538,8 @@ class HeadlessApp(_OverlayConfigMixin):
|
|
|
515
538
|
self.to_translate = None
|
|
516
539
|
communicate.quit.connect(self.quit)
|
|
517
540
|
communicate.adb_devices.connect(self.sync_overlay_source)
|
|
541
|
+
communicate.blur_overlay.connect(self._on_blur_patches)
|
|
542
|
+
communicate.clear_blur_overlay.connect(self._on_clear_blur_patches)
|
|
518
543
|
|
|
519
544
|
self.locale = LocaleName(config.get("locale", "en_US"))
|
|
520
545
|
from ok.core.start_controller import StartController
|
|
@@ -562,6 +587,8 @@ class HeadlessApp(_OverlayConfigMixin):
|
|
|
562
587
|
from ok.core.events import communicate
|
|
563
588
|
communicate.adb_devices.disconnect(self.sync_overlay_source)
|
|
564
589
|
communicate.notification.disconnect(self.show_notification)
|
|
590
|
+
communicate.blur_overlay.disconnect(self._on_blur_patches)
|
|
591
|
+
communicate.clear_blur_overlay.disconnect(self._on_clear_blur_patches)
|
|
565
592
|
if self.notification_manager is not None:
|
|
566
593
|
self.notification_manager.stop()
|
|
567
594
|
if self.exit_event:
|
|
@@ -581,7 +608,7 @@ class HeadlessApp(_OverlayConfigMixin):
|
|
|
581
608
|
self.notification_manager.submit(translated_title, translated_message, images)
|
|
582
609
|
|
|
583
610
|
def get_overlay_view(self):
|
|
584
|
-
if not self.
|
|
611
|
+
if not self._overlay_needed():
|
|
585
612
|
return None
|
|
586
613
|
if self.overlay_window is None:
|
|
587
614
|
from ok.core.events import communicate
|
|
@@ -193,6 +193,12 @@ class StartController:
|
|
|
193
193
|
dx11_config = og.global_config.get_config('Launch with DX11')
|
|
194
194
|
if dx11_config and dx11_config.get('Launch with DX11'):
|
|
195
195
|
args = "-dx11 -d3d11 -force-d3d11"
|
|
196
|
+
if device['device'] == "windows":
|
|
197
|
+
launch_arguments = (self.config.get('windows') or {}).get('launch_arguments')
|
|
198
|
+
if callable(launch_arguments):
|
|
199
|
+
launch_arguments = launch_arguments()
|
|
200
|
+
if launch_arguments:
|
|
201
|
+
args = f'{args or ""} {launch_arguments}'.strip()
|
|
196
202
|
if not execute(path, arguments=args, start_method=self.start_method):
|
|
197
203
|
communicate.starting_emulator.emit(True, self.tr("Start game failed, please start game first"), 0)
|
|
198
204
|
return False
|
|
@@ -157,6 +157,17 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
157
157
|
return 0
|
|
158
158
|
|
|
159
159
|
def start_or_stop(self, capture_cursor=False):
|
|
160
|
+
# Lifecycle changes may call close(), which needs get_frame_lock.
|
|
161
|
+
# Always acquire it before lock, just as do_get_frame() does, so a
|
|
162
|
+
# concurrent startup refresh cannot deadlock a frame request.
|
|
163
|
+
if not self.get_frame_lock.acquire(timeout=WGC_FRAME_WAIT_TIMEOUT):
|
|
164
|
+
raise TimeoutError('Windows Graphics Capture is busy, please retry starting the game')
|
|
165
|
+
try:
|
|
166
|
+
return self._start_or_stop(capture_cursor)
|
|
167
|
+
finally:
|
|
168
|
+
self.get_frame_lock.release()
|
|
169
|
+
|
|
170
|
+
def _start_or_stop(self, capture_cursor=False):
|
|
160
171
|
with self.lock:
|
|
161
172
|
if self.exit_event.is_set():
|
|
162
173
|
logger.warning('start_or_stop exit_event.is_set() return')
|
|
@@ -297,8 +308,12 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
297
308
|
def do_get_frame(self):
|
|
298
309
|
# frame_requested and last_frame represent one in-flight request. Keep
|
|
299
310
|
# concurrent task/UI callers from consuming each other's response.
|
|
300
|
-
|
|
311
|
+
if not self.get_frame_lock.acquire(timeout=WGC_FRAME_WAIT_TIMEOUT):
|
|
312
|
+
raise TimeoutError('Windows Graphics Capture is busy, please retry starting the game')
|
|
313
|
+
try:
|
|
301
314
|
return self._do_get_frame()
|
|
315
|
+
finally:
|
|
316
|
+
self.get_frame_lock.release()
|
|
302
317
|
|
|
303
318
|
def _do_get_frame(self):
|
|
304
319
|
if self.exit_event.is_set():
|
|
@@ -532,6 +532,18 @@ class TaskExecutor:
|
|
|
532
532
|
]
|
|
533
533
|
return min(delays, default=default)
|
|
534
534
|
|
|
535
|
+
def _prepare_task_for_run(self, is_trigger_task):
|
|
536
|
+
"""Prepare a task's input backend before the first frame is requested.
|
|
537
|
+
|
|
538
|
+
Foreground interaction backends such as Pynput only report a capture
|
|
539
|
+
frame while the target window is active. One-time tasks used to
|
|
540
|
+
request that frame first and only bring the target window forward from
|
|
541
|
+
inside ``task.run()``, which is too late and can leave the task waiting
|
|
542
|
+
indefinitely.
|
|
543
|
+
"""
|
|
544
|
+
if not is_trigger_task and self.interaction is not None:
|
|
545
|
+
self.interaction.on_run()
|
|
546
|
+
|
|
535
547
|
def execute(self):
|
|
536
548
|
logger.info(f"start execute")
|
|
537
549
|
while not self.exit_event.is_set():
|
|
@@ -553,6 +565,7 @@ class TaskExecutor:
|
|
|
553
565
|
self.current_task = task
|
|
554
566
|
if not is_trigger_task:
|
|
555
567
|
communicate.task.emit(task)
|
|
568
|
+
self._prepare_task_for_run(is_trigger_task)
|
|
556
569
|
if cycled or self._frame is None:
|
|
557
570
|
if self.next_frame(time_out=4) is None and is_trigger_task:
|
|
558
571
|
logger.info("no frame available, skip remaining trigger tasks")
|
|
@@ -49,6 +49,9 @@ class Logger:
|
|
|
49
49
|
stack_trace_str = self.exception_to_str(exception)
|
|
50
50
|
self.logger.error(f"{self.name}:{message} {stack_trace_str}")
|
|
51
51
|
|
|
52
|
+
def exception(self, message, *args, **kwargs):
|
|
53
|
+
self.logger.exception(f"{self.name}:{message}", *args, **kwargs)
|
|
54
|
+
|
|
52
55
|
def critical(self, message):
|
|
53
56
|
self.logger.critical(f"{self.name}:{message}")
|
|
54
57
|
|
|
@@ -1252,7 +1252,7 @@ class WindowsScheduleManager:
|
|
|
1252
1252
|
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
1253
1253
|
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
|
|
1254
1254
|
<AllowHardTerminate>true</AllowHardTerminate>
|
|
1255
|
-
<StartWhenAvailable>
|
|
1255
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
1256
1256
|
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
1257
1257
|
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
1258
1258
|
<Enabled>true</Enabled>
|
|
@@ -330,6 +330,7 @@ tests/test_nemu_ipc_capture.py
|
|
|
330
330
|
tests/test_notifications.py
|
|
331
331
|
tests/test_ok_mutex.py
|
|
332
332
|
tests/test_overlay_custom_draw.py
|
|
333
|
+
tests/test_overlay_lifecycle.py
|
|
333
334
|
tests/test_package_metadata.py
|
|
334
335
|
tests/test_process.py
|
|
335
336
|
tests/test_process_mutex.py
|
|
@@ -355,4 +356,5 @@ tests/test_web_task_tabs.py
|
|
|
355
356
|
tests/test_web_ui_pages.py
|
|
356
357
|
tests/test_web_ui_tasks.py
|
|
357
358
|
tests/test_windows_graphics_capture.py
|
|
358
|
-
tests/test_windows_schedule_target_parse.py
|
|
359
|
+
tests/test_windows_schedule_target_parse.py
|
|
360
|
+
tests/test_windows_schedule_xml.py
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from unittest.mock import Mock
|
|
3
3
|
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
4
6
|
import ok.util.logger as logger_module
|
|
5
7
|
from ok.util.logger import SafeFileHandler, config_logger
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
@pytest.mark.parametrize("use_exception_message", [False, True])
|
|
11
|
+
def test_logger_exception_preserves_original_traceback(caplog, use_exception_message):
|
|
12
|
+
logger = logger_module.Logger.get_logger("ok.ui.qt.MainWindow")
|
|
13
|
+
logger.logger = logging.getLogger("test.logger_exception")
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
raise RuntimeError("schedule resolution failed")
|
|
17
|
+
except RuntimeError as error:
|
|
18
|
+
logger.exception(error if use_exception_message else "schedule task index sync failed in __init__")
|
|
19
|
+
|
|
20
|
+
record = caplog.records[-1]
|
|
21
|
+
assert record.levelno == logging.ERROR
|
|
22
|
+
assert record.getMessage().startswith("MainWindow:")
|
|
23
|
+
assert record.exc_info[0] is RuntimeError
|
|
24
|
+
assert str(record.exc_info[1]) == "schedule resolution failed"
|
|
25
|
+
assert "Traceback (most recent call last)" in caplog.text
|
|
26
|
+
assert "RuntimeError: schedule resolution failed" in caplog.text
|
|
27
|
+
|
|
28
|
+
|
|
8
29
|
def test_config_logger_does_not_create_file_log_during_pytest(tmp_path, monkeypatch):
|
|
9
30
|
monkeypatch.chdir(tmp_path)
|
|
10
31
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
from unittest.mock import Mock
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from ok import App, HeadlessApp, og
|
|
7
|
+
from ok.core.events import communicate
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize("app_type", [App, HeadlessApp])
|
|
11
|
+
def test_blur_initializes_overlay_without_debug_boxes(app_type, monkeypatch):
|
|
12
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options={"Enable Blur": True}), raising=False)
|
|
13
|
+
app = object.__new__(app_type)
|
|
14
|
+
app.config = {"blur_area": lambda *_: None}
|
|
15
|
+
app.ok_config = {"use_overlay": False}
|
|
16
|
+
overlay = Mock()
|
|
17
|
+
app.get_overlay_view = Mock(return_value=overlay)
|
|
18
|
+
|
|
19
|
+
app.initialize_overlay()
|
|
20
|
+
|
|
21
|
+
app.get_overlay_view.assert_called_once_with()
|
|
22
|
+
overlay.set_boxes_enabled.assert_called_once_with(False)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize("app_type", [App, HeadlessApp])
|
|
26
|
+
def test_blur_only_getter_creates_overlay_with_boxes_disabled(app_type, monkeypatch):
|
|
27
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options={"Enable Blur": True}), raising=False)
|
|
28
|
+
monkeypatch.setattr(og, "device_manager", SimpleNamespace(hwnd_window=None), raising=False)
|
|
29
|
+
overlay = Mock()
|
|
30
|
+
monkeypatch.setattr("ok.ui.overlay.Win32GdiOverlay", Mock(return_value=overlay))
|
|
31
|
+
app = object.__new__(app_type)
|
|
32
|
+
app.config = {"blur_area": lambda *_: None}
|
|
33
|
+
app.ok_config = {"use_overlay": False}
|
|
34
|
+
app.overlay_window = None
|
|
35
|
+
app.exit_event = None
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
assert app.get_overlay_view() is overlay
|
|
39
|
+
overlay.set_boxes_enabled.assert_called_once_with(False)
|
|
40
|
+
finally:
|
|
41
|
+
communicate.window.disconnect(overlay.update_overlay)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.parametrize("app_type", [App, HeadlessApp])
|
|
45
|
+
def test_blur_event_creates_overlay_after_live_setting_change(app_type, monkeypatch):
|
|
46
|
+
options = {"Enable Blur": False}
|
|
47
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options=options), raising=False)
|
|
48
|
+
app = object.__new__(app_type)
|
|
49
|
+
app.config = {"blur_area": lambda *_: None}
|
|
50
|
+
app.ok_config = {"use_overlay": False}
|
|
51
|
+
app.overlay_window = None
|
|
52
|
+
overlay = Mock()
|
|
53
|
+
app.get_overlay_view = Mock(return_value=overlay)
|
|
54
|
+
patches = [object()]
|
|
55
|
+
|
|
56
|
+
app._on_blur_patches(patches)
|
|
57
|
+
app.get_overlay_view.assert_not_called()
|
|
58
|
+
options["Enable Blur"] = True
|
|
59
|
+
app._on_blur_patches(patches)
|
|
60
|
+
|
|
61
|
+
app.get_overlay_view.assert_called_once_with()
|
|
62
|
+
overlay.update_blur_patches.assert_called_once_with(patches)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_window_update_keeps_blur_only_overlay(monkeypatch):
|
|
66
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options={"Enable Blur": True}), raising=False)
|
|
67
|
+
app = object.__new__(App)
|
|
68
|
+
app.config = {"blur_area": lambda *_: None}
|
|
69
|
+
app.ok_config = {"use_overlay": False}
|
|
70
|
+
overlay = Mock()
|
|
71
|
+
app.overlay_window = overlay
|
|
72
|
+
|
|
73
|
+
app.update_overlay(True, 1, 2, 100, 80, 100, 80, 1)
|
|
74
|
+
|
|
75
|
+
overlay.update_overlay.assert_called_once_with(True, 1, 2, 100, 80, 100, 80, 1)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_disabling_debug_boxes_preserves_active_blur(monkeypatch):
|
|
79
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options={"Enable Blur": True}), raising=False)
|
|
80
|
+
app = object.__new__(HeadlessApp)
|
|
81
|
+
app.config = {"blur_area": lambda *_: None}
|
|
82
|
+
app.ok_config = {"use_overlay": True}
|
|
83
|
+
overlay = Mock()
|
|
84
|
+
app.overlay_window = overlay
|
|
85
|
+
app._close_overlay = Mock()
|
|
86
|
+
|
|
87
|
+
app.set_overlay_setting("boxes", False)
|
|
88
|
+
|
|
89
|
+
app._close_overlay.assert_not_called()
|
|
90
|
+
overlay.set_boxes_enabled.assert_called_once_with(False)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_disabling_last_overlay_feature_releases_window(monkeypatch):
|
|
94
|
+
options = {"Enable Blur": True}
|
|
95
|
+
monkeypatch.setattr(og, "executor", SimpleNamespace(basic_options=options), raising=False)
|
|
96
|
+
app = object.__new__(HeadlessApp)
|
|
97
|
+
app.config = {"blur_area": lambda *_: None}
|
|
98
|
+
app.ok_config = {"use_overlay": False}
|
|
99
|
+
app.overlay_window = Mock()
|
|
100
|
+
app._close_overlay = Mock()
|
|
101
|
+
|
|
102
|
+
options["Enable Blur"] = False
|
|
103
|
+
app._on_clear_blur_patches()
|
|
104
|
+
|
|
105
|
+
app._close_overlay.assert_called_once_with(wait=False)
|
|
@@ -38,6 +38,7 @@ class TestStartController(unittest.TestCase):
|
|
|
38
38
|
controller = StartController.__new__(StartController)
|
|
39
39
|
controller.exit_event = threading.Event()
|
|
40
40
|
controller.start_timeout = 20
|
|
41
|
+
controller.config = {}
|
|
41
42
|
controller.start_method = 'start'
|
|
42
43
|
controller.STARTED_WINDOW_STABLE_SECONDS = 2
|
|
43
44
|
controller.STARTED_WINDOW_POLL_INTERVAL = 1
|
|
@@ -61,6 +62,23 @@ class TestStartController(unittest.TestCase):
|
|
|
61
62
|
|
|
62
63
|
self.assertFalse(controller.starting)
|
|
63
64
|
|
|
65
|
+
def test_capture_refresh_timeout_closes_loading_and_reports_error(self):
|
|
66
|
+
controller = self.make_controller()
|
|
67
|
+
controller.start_exe = True
|
|
68
|
+
controller.tr = lambda message: message
|
|
69
|
+
message = 'Windows Graphics Capture is busy, please retry starting the game'
|
|
70
|
+
device_manager = SimpleNamespace(do_refresh=Mock(side_effect=TimeoutError(message)))
|
|
71
|
+
emit = Mock()
|
|
72
|
+
fake_communicate = SimpleNamespace(starting_emulator=SimpleNamespace(emit=emit))
|
|
73
|
+
|
|
74
|
+
with patch.object(start_controller_module, 'og', SimpleNamespace(device_manager=device_manager)), \
|
|
75
|
+
patch.object(start_controller_module, 'communicate', fake_communicate):
|
|
76
|
+
self.assertFalse(controller.do_start())
|
|
77
|
+
|
|
78
|
+
self.assertFalse(controller.starting)
|
|
79
|
+
self.assertEqual((False, None, controller.start_timeout), emit.call_args_list[0].args)
|
|
80
|
+
self.assertEqual((True, message, 0), emit.call_args_list[-1].args)
|
|
81
|
+
|
|
64
82
|
def test_started_window_must_be_usable_and_stable_before_continuing(self):
|
|
65
83
|
controller = self.make_controller()
|
|
66
84
|
window = FakeWindow([(80, 80), (120, 120), (140, 120), (140, 120), (140, 120)])
|
|
@@ -122,6 +140,75 @@ class TestStartController(unittest.TestCase):
|
|
|
122
140
|
self.assertEqual(['execute', 'stable', 'ready'], call_order)
|
|
123
141
|
execute.assert_called_once_with(r'C:\game.exe', arguments=None, start_method='os.startfile')
|
|
124
142
|
|
|
143
|
+
def make_launch_context(self, device_type='windows', connected=False, dx11=False):
|
|
144
|
+
controller = self.make_controller()
|
|
145
|
+
controller._wait_until_started_window_stable = Mock(return_value=True)
|
|
146
|
+
controller._wait_until_device_ready = Mock(return_value=True)
|
|
147
|
+
device_manager = Mock()
|
|
148
|
+
device_manager.get_preferred_device.return_value = {
|
|
149
|
+
'connected': connected,
|
|
150
|
+
'device': device_type,
|
|
151
|
+
}
|
|
152
|
+
device_manager.get_exe_path.return_value = r'C:\game.exe'
|
|
153
|
+
fake_og = SimpleNamespace(device_manager=device_manager, global_config=Mock())
|
|
154
|
+
fake_og.global_config.get_config.return_value = {'Launch with DX11': dx11}
|
|
155
|
+
return controller, fake_og
|
|
156
|
+
|
|
157
|
+
def test_launch_arguments_preserve_dx11_and_launch_method(self):
|
|
158
|
+
for start_method in ('start', 'os.startfile'):
|
|
159
|
+
for dx11 in (False, True):
|
|
160
|
+
for extra_args in ('-custom="value with spaces"', None, '', lambda: '-custom', lambda: None):
|
|
161
|
+
with self.subTest(start_method=start_method, dx11=dx11, extra_args=extra_args):
|
|
162
|
+
controller, fake_og = self.make_launch_context(dx11=dx11)
|
|
163
|
+
controller.start_method = start_method
|
|
164
|
+
controller.config = {'windows': {'launch_arguments': extra_args}}
|
|
165
|
+
expected_args = '-dx11 -d3d11 -force-d3d11' if dx11 else None
|
|
166
|
+
extra = extra_args() if callable(extra_args) else extra_args
|
|
167
|
+
if extra:
|
|
168
|
+
expected_args = f'{expected_args or ""} {extra}'.strip()
|
|
169
|
+
|
|
170
|
+
with patch.object(start_controller_module, 'og', fake_og), \
|
|
171
|
+
patch.object(start_controller_module, 'is_admin', return_value=True), \
|
|
172
|
+
patch.object(start_controller_module, 'execute', return_value=True) as execute:
|
|
173
|
+
self.assertTrue(controller.start_device())
|
|
174
|
+
|
|
175
|
+
execute.assert_called_once_with(
|
|
176
|
+
r'C:\game.exe', arguments=expected_args, start_method=start_method)
|
|
177
|
+
|
|
178
|
+
def test_launch_arguments_callback_reads_current_settings_on_each_launch(self):
|
|
179
|
+
controller, fake_og = self.make_launch_context()
|
|
180
|
+
settings = {'package': 'hd'}
|
|
181
|
+
callback = Mock(side_effect=lambda: f'-krqlv={settings["package"]}')
|
|
182
|
+
controller.config = {'windows': {'launch_arguments': callback}}
|
|
183
|
+
|
|
184
|
+
with patch.object(start_controller_module, 'og', fake_og), \
|
|
185
|
+
patch.object(start_controller_module, 'is_admin', return_value=True), \
|
|
186
|
+
patch.object(start_controller_module, 'execute', return_value=True) as execute:
|
|
187
|
+
self.assertTrue(controller.start_device())
|
|
188
|
+
self.assertEqual('-krqlv=hd', execute.call_args.kwargs['arguments'])
|
|
189
|
+
settings['package'] = 'uhd'
|
|
190
|
+
self.assertTrue(controller.start_device())
|
|
191
|
+
self.assertEqual('-krqlv=uhd', execute.call_args.kwargs['arguments'])
|
|
192
|
+
|
|
193
|
+
self.assertEqual([unittest.mock.call(), unittest.mock.call()], callback.call_args_list)
|
|
194
|
+
|
|
195
|
+
def test_launch_arguments_callback_is_skipped_for_connected_games_and_adb(self):
|
|
196
|
+
for device_type, connected in (('windows', True), ('adb', False)):
|
|
197
|
+
with self.subTest(device_type=device_type, connected=connected):
|
|
198
|
+
controller, fake_og = self.make_launch_context(device_type, connected)
|
|
199
|
+
callback = Mock(return_value='-windows-only')
|
|
200
|
+
controller.config = {'windows': {'launch_arguments': callback}}
|
|
201
|
+
|
|
202
|
+
with patch.object(start_controller_module, 'og', fake_og), \
|
|
203
|
+
patch.object(start_controller_module, 'execute', return_value=True) as execute:
|
|
204
|
+
self.assertTrue(controller.start_device())
|
|
205
|
+
|
|
206
|
+
callback.assert_not_called()
|
|
207
|
+
if connected:
|
|
208
|
+
execute.assert_not_called()
|
|
209
|
+
else:
|
|
210
|
+
execute.assert_called_once_with(r'C:\game.exe', arguments=None, start_method='start')
|
|
211
|
+
|
|
125
212
|
def test_gpu_driver_warning_identifies_each_enabled_vendor_feature(self):
|
|
126
213
|
controller = self.make_controller()
|
|
127
214
|
emit = Mock()
|
|
@@ -131,6 +131,60 @@ class TestTaskExecutorQueue(unittest.TestCase):
|
|
|
131
131
|
task.on_destroy.assert_called_once_with()
|
|
132
132
|
interaction.on_destroy.assert_called_once_with()
|
|
133
133
|
|
|
134
|
+
def test_prepare_one_time_task_before_first_frame(self):
|
|
135
|
+
executor = self.make_executor([])
|
|
136
|
+
interaction = SimpleNamespace(on_run=Mock(), on_destroy=Mock())
|
|
137
|
+
executor.device_manager = SimpleNamespace(interaction=interaction)
|
|
138
|
+
|
|
139
|
+
executor._prepare_task_for_run(is_trigger_task=False)
|
|
140
|
+
|
|
141
|
+
interaction.on_run.assert_called_once_with()
|
|
142
|
+
|
|
143
|
+
def test_prepare_trigger_task_does_not_force_foreground(self):
|
|
144
|
+
executor = self.make_executor([])
|
|
145
|
+
interaction = SimpleNamespace(on_run=Mock())
|
|
146
|
+
executor.device_manager = SimpleNamespace(interaction=interaction)
|
|
147
|
+
|
|
148
|
+
executor._prepare_task_for_run(is_trigger_task=True)
|
|
149
|
+
|
|
150
|
+
interaction.on_run.assert_not_called()
|
|
151
|
+
|
|
152
|
+
def test_one_time_task_prepares_interaction_before_first_frame(self):
|
|
153
|
+
executor = self.make_executor([])
|
|
154
|
+
executor.paused = False
|
|
155
|
+
executor._frame = None
|
|
156
|
+
executor._last_frame_time = time.time()
|
|
157
|
+
executor.reset_scene = lambda check_enabled=True: None
|
|
158
|
+
interaction = SimpleNamespace(on_run=Mock())
|
|
159
|
+
executor.device_manager = SimpleNamespace(interaction=interaction)
|
|
160
|
+
|
|
161
|
+
task = SimpleNamespace(
|
|
162
|
+
name="FishingOnce",
|
|
163
|
+
start_time=None,
|
|
164
|
+
running=False,
|
|
165
|
+
exit_after_task=False,
|
|
166
|
+
config={},
|
|
167
|
+
run=Mock(),
|
|
168
|
+
disable=Mock(),
|
|
169
|
+
on_destroy=Mock(),
|
|
170
|
+
)
|
|
171
|
+
events = []
|
|
172
|
+
interaction.on_run.side_effect = lambda: events.append("on_run")
|
|
173
|
+
executor.next_frame = Mock(
|
|
174
|
+
side_effect=lambda time_out=6: events.append("next_frame"))
|
|
175
|
+
task.run.side_effect = lambda: (
|
|
176
|
+
events.append("run"),
|
|
177
|
+
executor.exit_event.set(),
|
|
178
|
+
)
|
|
179
|
+
executor.next_task = lambda: (task, False, False)
|
|
180
|
+
|
|
181
|
+
with patch.object(task_executor_module.communicate.task, "emit"), \
|
|
182
|
+
patch.object(task_executor_module.communicate.task_done, "emit"), \
|
|
183
|
+
patch.object(task_executor_module, "prevent_sleeping"):
|
|
184
|
+
executor.execute()
|
|
185
|
+
|
|
186
|
+
self.assertEqual(["on_run", "next_frame", "run"], events)
|
|
187
|
+
|
|
134
188
|
|
|
135
189
|
if __name__ == '__main__':
|
|
136
190
|
unittest.main()
|
|
@@ -2,7 +2,7 @@ import unittest
|
|
|
2
2
|
import threading
|
|
3
3
|
import time
|
|
4
4
|
from types import SimpleNamespace
|
|
5
|
-
from unittest.mock import patch
|
|
5
|
+
from unittest.mock import Mock, patch
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
@@ -28,6 +28,108 @@ class _FakeFramePool:
|
|
|
28
28
|
return self.frame
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class _ObservedRLock:
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self.lock = threading.RLock()
|
|
34
|
+
self.attempted = threading.Event()
|
|
35
|
+
|
|
36
|
+
def acquire(self, **kwargs):
|
|
37
|
+
self.attempted.set()
|
|
38
|
+
return self.lock.acquire(**kwargs)
|
|
39
|
+
|
|
40
|
+
def release(self):
|
|
41
|
+
self.lock.release()
|
|
42
|
+
|
|
43
|
+
def __enter__(self):
|
|
44
|
+
self.acquire()
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def __exit__(self, *_args):
|
|
48
|
+
self.release()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestWindowsGraphicsCaptureLifecycle(unittest.TestCase):
|
|
52
|
+
def _method(self):
|
|
53
|
+
method = object.__new__(WindowsGraphicsCaptureMethod)
|
|
54
|
+
method.lock = threading.RLock()
|
|
55
|
+
method.get_frame_lock = _ObservedRLock()
|
|
56
|
+
method.exit_event = threading.Event()
|
|
57
|
+
method.frame_requested = threading.Event()
|
|
58
|
+
method.frame_event = threading.Event()
|
|
59
|
+
method._frame_cancel_generation = 0
|
|
60
|
+
method._hwnd_window = SimpleNamespace(exists=True, capture_target_signature='new')
|
|
61
|
+
method.get_capture_hwnd = lambda: 123
|
|
62
|
+
method.capture_target_signature = 'old'
|
|
63
|
+
method.frame_pool = Mock()
|
|
64
|
+
method.session = None
|
|
65
|
+
method.rtdevice = method.dxdevice = method.immediatedc = method.cputex = None
|
|
66
|
+
method.contexts = {}
|
|
67
|
+
method.last_start_failure_key = 123
|
|
68
|
+
method.last_start_failure_time = time.time()
|
|
69
|
+
return method
|
|
70
|
+
|
|
71
|
+
def test_target_change_does_not_hold_capture_lock_while_waiting_for_frame_request(self):
|
|
72
|
+
method = self._method()
|
|
73
|
+
pool = method.frame_pool
|
|
74
|
+
results = []
|
|
75
|
+
errors = []
|
|
76
|
+
|
|
77
|
+
def refresh():
|
|
78
|
+
try:
|
|
79
|
+
results.append(method.start_or_stop())
|
|
80
|
+
except Exception as error:
|
|
81
|
+
errors.append(error)
|
|
82
|
+
|
|
83
|
+
# Simulate a frame consumer already owning the request lock while the
|
|
84
|
+
# startup thread discovers a changed window target.
|
|
85
|
+
with method.get_frame_lock:
|
|
86
|
+
method.get_frame_lock.attempted.clear()
|
|
87
|
+
worker = threading.Thread(target=refresh, daemon=True)
|
|
88
|
+
worker.start()
|
|
89
|
+
self.assertTrue(method.get_frame_lock.attempted.wait(1))
|
|
90
|
+
acquired = method.lock.acquire(timeout=.2)
|
|
91
|
+
if acquired:
|
|
92
|
+
method.lock.release()
|
|
93
|
+
worker.join(1)
|
|
94
|
+
|
|
95
|
+
self.assertTrue(acquired, 'startup held the capture lock while waiting for a frame request')
|
|
96
|
+
self.assertFalse(worker.is_alive())
|
|
97
|
+
self.assertEqual([], errors)
|
|
98
|
+
self.assertEqual([False], results)
|
|
99
|
+
pool.Close.assert_called_once()
|
|
100
|
+
self.assertIsNone(method.frame_pool)
|
|
101
|
+
|
|
102
|
+
def test_busy_capture_reports_error_instead_of_waiting_indefinitely(self):
|
|
103
|
+
for operation in ('start_or_stop', 'do_get_frame'):
|
|
104
|
+
with self.subTest(operation=operation):
|
|
105
|
+
method = self._method()
|
|
106
|
+
errors = []
|
|
107
|
+
|
|
108
|
+
def call():
|
|
109
|
+
try:
|
|
110
|
+
getattr(method, operation)()
|
|
111
|
+
except Exception as error:
|
|
112
|
+
errors.append(error)
|
|
113
|
+
|
|
114
|
+
with method.get_frame_lock, patch.object(windows_graphics_module, 'WGC_FRAME_WAIT_TIMEOUT', .05):
|
|
115
|
+
worker = threading.Thread(target=call, daemon=True)
|
|
116
|
+
worker.start()
|
|
117
|
+
worker.join(.5)
|
|
118
|
+
self.assertFalse(worker.is_alive())
|
|
119
|
+
self.assertEqual(1, len(errors))
|
|
120
|
+
self.assertIsInstance(errors[0], TimeoutError)
|
|
121
|
+
self.assertIn('Windows Graphics Capture is busy', str(errors[0]))
|
|
122
|
+
|
|
123
|
+
def test_frame_request_can_restart_changed_target_without_deadlocking_itself(self):
|
|
124
|
+
method = self._method()
|
|
125
|
+
pool = method.frame_pool
|
|
126
|
+
|
|
127
|
+
self.assertIsNone(method.do_get_frame())
|
|
128
|
+
|
|
129
|
+
pool.Close.assert_called_once()
|
|
130
|
+
self.assertIsNone(method.frame_pool)
|
|
131
|
+
|
|
132
|
+
|
|
31
133
|
class TestCaptureTargetSignature(unittest.TestCase):
|
|
32
134
|
def test_capture_origin_uses_fallback_crop_for_embedded_title_bar(self):
|
|
33
135
|
window = object.__new__(HwndWindow)
|