frontengine 1.0.32__tar.gz → 1.0.33__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.
- {frontengine-1.0.32 → frontengine-1.0.33}/PKG-INFO +1 -1
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/pet/desktop_pet.py +24 -93
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/text/draw_text.py +12 -1
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/main_ui.py +10 -3
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/preset_menu.py +21 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/settings_menu.py +37 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/control_center/control_center_ui.py +27 -2
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/user_setting/user_setting_file.py +3 -0
- frontengine-1.0.33/frontengine/utils/autostart/autostart_service.py +172 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/english.py +5 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/traditional_chinese.py +5 -0
- frontengine-1.0.33/frontengine/utils/platform_info/platform_info.py +303 -0
- frontengine-1.0.33/frontengine/utils/power_mode/power_mode.py +29 -0
- frontengine-1.0.33/frontengine/utils/theme_schedule/__init__.py +0 -0
- frontengine-1.0.33/frontengine/utils/weather/__init__.py +0 -0
- frontengine-1.0.33/frontengine/worker/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/PKG-INFO +1 -1
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/SOURCES.txt +6 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/pyproject.toml +1 -1
- {frontengine-1.0.32 → frontengine-1.0.33}/LICENSE +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/README.md +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/__main__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/presentation/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/presentation/annotation_overlay.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/presentation/cursor_effects.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/presentation/keystroke_display.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/presentation/magnifier.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/scene/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/screen_care/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/screen_care/screen_filter.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/sound_player/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/text/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/video/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/web/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/system_tray/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/color/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/dialog/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/dialog/save_file_dialog.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/control_center/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/gif/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/image/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/particle/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/pet/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/presentation/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/screen_care/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/sound_player/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/text/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/video/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/web/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/user_setting/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/audio_meter/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/audio_meter/screen_audio.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
- {frontengine-1.0.32/frontengine/utils/break_reminder → frontengine-1.0.33/frontengine/utils/autostart}/__init__.py +0 -0
- {frontengine-1.0.32/frontengine/utils/browser → frontengine-1.0.33/frontengine/utils/break_reminder}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/break_reminder/break_reminder.py +0 -0
- {frontengine-1.0.32/frontengine/utils/critical_exit → frontengine-1.0.33/frontengine/utils/browser}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.32/frontengine/utils/file → frontengine-1.0.33/frontengine/utils/critical_exit}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.32/frontengine/utils/focus_timer → frontengine-1.0.33/frontengine/utils/file}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/file/open/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/file/open/open_file.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/file/save/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/file/save/save_file.py +0 -0
- {frontengine-1.0.32/frontengine/utils/hotkey → frontengine-1.0.33/frontengine/utils/focus_timer}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/focus_timer/focus_timer.py +0 -0
- {frontengine-1.0.32/frontengine/utils/input_watch → frontengine-1.0.33/frontengine/utils/hotkey}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.32/frontengine/utils/json → frontengine-1.0.33/frontengine/utils/input_watch}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/input_watch/input_watch_service.py +0 -0
- {frontengine-1.0.32/frontengine/utils/logging → frontengine-1.0.33/frontengine/utils/json}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/json_format/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/json_format/json_process.py +0 -0
- {frontengine-1.0.32/frontengine/utils/multi_language → frontengine-1.0.33/frontengine/utils/logging}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.32/frontengine/utils/pet_chat → frontengine-1.0.33/frontengine/utils/multi_language}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/france.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/germany.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/italy.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/russian.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
- {frontengine-1.0.32/frontengine/utils/preset_schedule → frontengine-1.0.33/frontengine/utils/pet_chat}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
- {frontengine-1.0.32/frontengine/utils/redirect_manager → frontengine-1.0.33/frontengine/utils/platform_info}/__init__.py +0 -0
- {frontengine-1.0.32/frontengine/utils/smart_pause → frontengine-1.0.33/frontengine/utils/power_mode}/__init__.py +0 -0
- {frontengine-1.0.32/frontengine/utils/system_stats → frontengine-1.0.33/frontengine/utils/preset_schedule}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.32/frontengine/utils/text_source → frontengine-1.0.33/frontengine/utils/redirect_manager}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.32/frontengine/utils/theme_schedule → frontengine-1.0.33/frontengine/utils/smart_pause}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.32/frontengine/utils/weather → frontengine-1.0.33/frontengine/utils/system_stats}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/system_stats/system_stats.py +0 -0
- {frontengine-1.0.32/frontengine/worker → frontengine-1.0.33/frontengine/utils/text_source}/__init__.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/text_source/text_source.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/weather/weather_service.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine/worker/qthread_worker.py +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.32 → frontengine-1.0.33}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.33
|
|
4
4
|
Summary: FrontEngine is use-define frontview or only use like screen saver
|
|
5
5
|
Author-email: JE-Chen <jechenmailman@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/Intergration-Automation-Testing/FrontEngine
|
|
@@ -16,6 +16,12 @@ from frontengine.show.window_helpers import apply_overlay_window_flags
|
|
|
16
16
|
from frontengine.user_setting.user_setting_file import user_setting_dict
|
|
17
17
|
from frontengine.utils.audio_meter.audio_envelope import AudioEnvelope
|
|
18
18
|
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
19
|
+
from frontengine.utils.power_mode.power_mode import scaled_interval
|
|
20
|
+
from frontengine.utils.platform_info.platform_info import (
|
|
21
|
+
idle_seconds as platform_idle_seconds,
|
|
22
|
+
read_battery as platform_read_battery,
|
|
23
|
+
standable_windows,
|
|
24
|
+
)
|
|
19
25
|
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
20
26
|
|
|
21
27
|
# 行為模式 / Behaviour modes
|
|
@@ -212,75 +218,17 @@ def derive_visual_state(dragging: bool, airborne: bool, surface: str, motion_sta
|
|
|
212
218
|
return STATE_WALK
|
|
213
219
|
|
|
214
220
|
|
|
215
|
-
# 列舉可站立視窗時要略過的外殼視窗類別
|
|
216
|
-
# Shell window classes skipped when enumerating standable windows.
|
|
217
|
-
_PLATFORM_SKIP_CLASSES = {"WorkerW", "Progman", "Shell_TrayWnd", "Button"}
|
|
218
|
-
|
|
219
|
-
|
|
220
221
|
def windows_platforms(exclude_hwnds=()) -> list:
|
|
221
222
|
"""
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
Return the top edges of visible top-level windows as standable platforms
|
|
225
|
-
(left, right, top_y). Windows only; returns [] elsewhere or on error.
|
|
223
|
+
回傳目前可站立的視窗上緣 (left, right, top_y);各平台實作見 platform_info。
|
|
224
|
+
Standable window top edges; per-platform implementations live in platform_info.
|
|
226
225
|
"""
|
|
227
|
-
|
|
228
|
-
return []
|
|
229
|
-
try:
|
|
230
|
-
import ctypes
|
|
231
|
-
from ctypes import wintypes
|
|
232
|
-
|
|
233
|
-
user32 = ctypes.windll.user32
|
|
234
|
-
exclude = {int(h) for h in exclude_hwnds}
|
|
235
|
-
platforms: list = []
|
|
236
|
-
|
|
237
|
-
@ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
|
|
238
|
-
def _collect(hwnd, _lparam):
|
|
239
|
-
if int(hwnd) in exclude or not user32.IsWindowVisible(hwnd):
|
|
240
|
-
return True
|
|
241
|
-
rect = wintypes.RECT()
|
|
242
|
-
if not user32.GetWindowRect(hwnd, ctypes.byref(rect)):
|
|
243
|
-
return True
|
|
244
|
-
width = rect.right - rect.left
|
|
245
|
-
height = rect.bottom - rect.top
|
|
246
|
-
if width < 80 or height < 40:
|
|
247
|
-
return True
|
|
248
|
-
class_buffer = ctypes.create_unicode_buffer(64)
|
|
249
|
-
user32.GetClassNameW(hwnd, class_buffer, 64)
|
|
250
|
-
if class_buffer.value in _PLATFORM_SKIP_CLASSES:
|
|
251
|
-
return True
|
|
252
|
-
platforms.append((rect.left, rect.right, rect.top))
|
|
253
|
-
return True
|
|
254
|
-
|
|
255
|
-
user32.EnumWindows(_collect, 0)
|
|
256
|
-
return platforms
|
|
257
|
-
except Exception as error: # pragma: no cover - Win32 boundary
|
|
258
|
-
front_engine_logger.warning(f"[windows_platforms] error: {error!r}")
|
|
259
|
-
return []
|
|
226
|
+
return standable_windows(exclude_hwnds)
|
|
260
227
|
|
|
261
228
|
|
|
262
229
|
def idle_seconds():
|
|
263
|
-
"""
|
|
264
|
-
|
|
265
|
-
Seconds since the last user input, or None when unavailable (Windows only).
|
|
266
|
-
"""
|
|
267
|
-
if sys.platform != "win32":
|
|
268
|
-
return None
|
|
269
|
-
try:
|
|
270
|
-
import ctypes
|
|
271
|
-
|
|
272
|
-
class _LASTINPUTINFO(ctypes.Structure):
|
|
273
|
-
_fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]
|
|
274
|
-
|
|
275
|
-
info = _LASTINPUTINFO()
|
|
276
|
-
info.cbSize = ctypes.sizeof(info)
|
|
277
|
-
if not ctypes.windll.user32.GetLastInputInfo(ctypes.byref(info)):
|
|
278
|
-
return None
|
|
279
|
-
elapsed_ms = ctypes.windll.kernel32.GetTickCount() - info.dwTime
|
|
280
|
-
return max(0.0, elapsed_ms / 1000.0)
|
|
281
|
-
except Exception as error: # pragma: no cover - Win32 boundary
|
|
282
|
-
front_engine_logger.warning(f"[idle_seconds] error: {error!r}")
|
|
283
|
-
return None
|
|
230
|
+
"""使用者未操作的秒數;取不到回傳 None(跨平台,見 platform_info)。"""
|
|
231
|
+
return platform_idle_seconds()
|
|
284
232
|
|
|
285
233
|
|
|
286
234
|
class PetTimeline:
|
|
@@ -335,35 +283,8 @@ def pop_due_reminders(now, reminders):
|
|
|
335
283
|
|
|
336
284
|
|
|
337
285
|
def read_battery():
|
|
338
|
-
"""
|
|
339
|
-
|
|
340
|
-
Return (percent, charging) or None when unavailable. Windows only.
|
|
341
|
-
"""
|
|
342
|
-
if sys.platform != "win32":
|
|
343
|
-
return None
|
|
344
|
-
try:
|
|
345
|
-
import ctypes
|
|
346
|
-
|
|
347
|
-
class _SPS(ctypes.Structure):
|
|
348
|
-
_fields_ = [
|
|
349
|
-
("ACLineStatus", ctypes.c_byte),
|
|
350
|
-
("BatteryFlag", ctypes.c_byte),
|
|
351
|
-
("BatteryLifePercent", ctypes.c_byte),
|
|
352
|
-
("SystemStatusFlag", ctypes.c_byte),
|
|
353
|
-
("BatteryLifeTime", ctypes.c_ulong),
|
|
354
|
-
("BatteryFullLifeTime", ctypes.c_ulong),
|
|
355
|
-
]
|
|
356
|
-
|
|
357
|
-
status = _SPS()
|
|
358
|
-
if not ctypes.windll.kernel32.GetSystemPowerStatus(ctypes.byref(status)):
|
|
359
|
-
return None
|
|
360
|
-
percent = status.BatteryLifePercent & 0xFF
|
|
361
|
-
if percent == 255: # unknown / no battery
|
|
362
|
-
return None
|
|
363
|
-
return (int(percent), status.ACLineStatus == 1)
|
|
364
|
-
except Exception as error: # pragma: no cover - Win32 boundary
|
|
365
|
-
front_engine_logger.warning(f"[read_battery] error: {error!r}")
|
|
366
|
-
return None
|
|
286
|
+
"""回傳 (電量百分比, 是否充電中);沒有電池或取不到回傳 None(跨平台)。"""
|
|
287
|
+
return platform_read_battery()
|
|
367
288
|
|
|
368
289
|
|
|
369
290
|
def nearest_peer(center, peers):
|
|
@@ -1147,6 +1068,9 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1147
1068
|
|
|
1148
1069
|
self._timer = QTimer(self)
|
|
1149
1070
|
self._timer.timeout.connect(self._on_tick)
|
|
1071
|
+
# 省電模式與原始更新間隔 / Low-power state and the base tick interval
|
|
1072
|
+
self._low_power = False
|
|
1073
|
+
self._tick_interval_ms = 33
|
|
1150
1074
|
|
|
1151
1075
|
# Speech bubbles / chatter / mood
|
|
1152
1076
|
self._talk = bool(talk)
|
|
@@ -1417,6 +1341,12 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1417
1341
|
painter.drawPixmap(
|
|
1418
1342
|
QRect((self.width() - width) // 2, self.height() - height, width, height), pixmap)
|
|
1419
1343
|
|
|
1344
|
+
def set_low_power(self, enabled: bool) -> None:
|
|
1345
|
+
"""省電模式:把移動更新拉慢(畫面較不順,但省電)。"""
|
|
1346
|
+
self._low_power = bool(enabled)
|
|
1347
|
+
if self._timer.isActive():
|
|
1348
|
+
self._timer.start(scaled_interval(self._tick_interval_ms, self._low_power))
|
|
1349
|
+
|
|
1420
1350
|
def start_moving(self, bounds: Tuple[int, int, int, int], interval_ms: int = 33) -> None:
|
|
1421
1351
|
front_engine_logger.info(f"[DesktopPetWidget] start_moving | bounds={bounds}")
|
|
1422
1352
|
left, top, right, bottom = bounds
|
|
@@ -1424,7 +1354,8 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1424
1354
|
self.motion.x = float(left)
|
|
1425
1355
|
self.motion.y = float(bottom - self.pet_size)
|
|
1426
1356
|
self.move(int(self.motion.x), int(self.motion.y))
|
|
1427
|
-
self.
|
|
1357
|
+
self._tick_interval_ms = max(10, int(interval_ms))
|
|
1358
|
+
self._timer.start(scaled_interval(self._tick_interval_ms, self._low_power))
|
|
1428
1359
|
self._schedule_chatter()
|
|
1429
1360
|
self._reminder_timer.start(10000)
|
|
1430
1361
|
if self._timeline_start is None:
|
|
@@ -3,6 +3,7 @@ from PySide6.QtGui import QPainter, QFont, QColor, QFontMetrics
|
|
|
3
3
|
|
|
4
4
|
from frontengine.show.base_widget import BaseWidget
|
|
5
5
|
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
6
|
+
from frontengine.utils.power_mode.power_mode import scaled_interval
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_ALIGNMENT_MAP = {
|
|
@@ -35,6 +36,7 @@ class TextWidget(BaseWidget):
|
|
|
35
36
|
self.draw_font = QFont(self.font().family(), self.font_size)
|
|
36
37
|
|
|
37
38
|
# Marquee (horizontal scroll) state
|
|
39
|
+
self.low_power: bool = False
|
|
38
40
|
self.marquee: bool = False
|
|
39
41
|
self.marquee_speed: int = 4
|
|
40
42
|
self.marquee_offset: int = 0
|
|
@@ -83,7 +85,7 @@ class TextWidget(BaseWidget):
|
|
|
83
85
|
self.marquee_speed = 4
|
|
84
86
|
if self.marquee:
|
|
85
87
|
self.marquee_offset = self.width()
|
|
86
|
-
self._marquee_timer.start(30)
|
|
88
|
+
self._marquee_timer.start(scaled_interval(30, self.low_power))
|
|
87
89
|
else:
|
|
88
90
|
self._marquee_timer.stop()
|
|
89
91
|
|
|
@@ -116,6 +118,15 @@ class TextWidget(BaseWidget):
|
|
|
116
118
|
self.text = text
|
|
117
119
|
self.update()
|
|
118
120
|
|
|
121
|
+
def set_low_power(self, enabled: bool) -> None:
|
|
122
|
+
"""省電模式:拉慢文字來源與跑馬燈的更新,降低重繪次數。"""
|
|
123
|
+
self.low_power = bool(enabled)
|
|
124
|
+
if self._source_timer.isActive() and self.text_source is not None:
|
|
125
|
+
self._source_timer.start(scaled_interval(
|
|
126
|
+
getattr(self.text_source, "refresh_interval_ms", 1000), self.low_power))
|
|
127
|
+
if self._marquee_timer.isActive():
|
|
128
|
+
self._marquee_timer.start(scaled_interval(30, self.low_power))
|
|
129
|
+
|
|
119
130
|
def _tick_marquee(self) -> None:
|
|
120
131
|
text_width = QFontMetrics(self.draw_font).horizontalAdvance(self.text)
|
|
121
132
|
self.marquee_offset -= self.marquee_speed
|
|
@@ -12,7 +12,10 @@ from frontengine.system_tray.extend_system_tray import ExtendSystemTray
|
|
|
12
12
|
from frontengine.ui.menu.help_menu import build_help_menu
|
|
13
13
|
from frontengine.ui.menu.how_to_menu import build_how_to_menu
|
|
14
14
|
from frontengine.ui.menu.language_menu import build_language_menu
|
|
15
|
-
from frontengine.ui.menu.preset_menu import
|
|
15
|
+
from frontengine.ui.menu.preset_menu import (
|
|
16
|
+
apply_named_preset, apply_startup_preset, build_preset_menu, restore_last_session,
|
|
17
|
+
save_last_session,
|
|
18
|
+
)
|
|
16
19
|
from frontengine.ui.menu.settings_menu import build_settings_menu
|
|
17
20
|
from frontengine.ui.page.control_center.control_center_ui import ControlCenterUI
|
|
18
21
|
from frontengine.ui.page.gif.gif_setting_ui import GIFSettingUI
|
|
@@ -184,8 +187,10 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
184
187
|
)
|
|
185
188
|
self.preset_schedule_service.start()
|
|
186
189
|
|
|
187
|
-
#
|
|
188
|
-
#
|
|
190
|
+
# 還原上次工作階段(若有開啟),再套用啟動預設集
|
|
191
|
+
# Restore the last session when enabled, then apply the startup preset.
|
|
192
|
+
if user_setting_dict.get("restore_last_session"):
|
|
193
|
+
restore_last_session(self)
|
|
189
194
|
apply_startup_preset(self)
|
|
190
195
|
|
|
191
196
|
# Debug 模式下自動關閉
|
|
@@ -348,6 +353,8 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
348
353
|
|
|
349
354
|
def close(self) -> None:
|
|
350
355
|
"""關閉程式並清理資源 / Close application and clear resources"""
|
|
356
|
+
if user_setting_dict.get("restore_last_session"):
|
|
357
|
+
save_last_session(self)
|
|
351
358
|
if getattr(self, "preset_schedule_service", None) is not None:
|
|
352
359
|
self.preset_schedule_service.stop()
|
|
353
360
|
if getattr(self, "theme_schedule_service", None) is not None:
|
|
@@ -225,6 +225,27 @@ def apply_named_preset(ui: "FrontEngineMainUI", name: str) -> bool:
|
|
|
225
225
|
return True
|
|
226
226
|
|
|
227
227
|
|
|
228
|
+
LAST_SESSION_PRESET = "__last_session__"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def save_last_session(ui: "FrontEngineMainUI") -> bool:
|
|
232
|
+
"""
|
|
233
|
+
把目前各頁面的設定存成「上次工作階段」預設集(關閉程式時呼叫)。
|
|
234
|
+
Store the current page settings as the last-session preset on exit.
|
|
235
|
+
"""
|
|
236
|
+
try:
|
|
237
|
+
PresetRepository().save(LAST_SESSION_PRESET, _collect_state(ui))
|
|
238
|
+
return True
|
|
239
|
+
except (OSError, ValueError) as error:
|
|
240
|
+
front_engine_logger.warning(f"[PresetMenu] save_last_session failed: {error!r}")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def restore_last_session(ui: "FrontEngineMainUI") -> bool:
|
|
245
|
+
"""啟動時還原上次的設定;沒存過或讀不到就跳過。"""
|
|
246
|
+
return apply_named_preset(ui, LAST_SESSION_PRESET)
|
|
247
|
+
|
|
248
|
+
|
|
228
249
|
def apply_startup_preset(ui: "FrontEngineMainUI") -> None:
|
|
229
250
|
"""
|
|
230
251
|
啟動時套用設定的預設集;缺失或損毀時僅記錄警告,不影響啟動。
|
|
@@ -13,6 +13,7 @@ from frontengine.user_setting.user_setting_file import (
|
|
|
13
13
|
user_setting_dict,
|
|
14
14
|
write_user_setting,
|
|
15
15
|
)
|
|
16
|
+
from frontengine.utils.autostart import autostart_service
|
|
16
17
|
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
17
18
|
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
18
19
|
|
|
@@ -44,6 +45,20 @@ def build_settings_menu(ui: "FrontEngineMainUI") -> None:
|
|
|
44
45
|
menu.addAction(theme_schedule_action)
|
|
45
46
|
ui.theme_schedule_action = theme_schedule_action
|
|
46
47
|
|
|
48
|
+
autostart_action = QAction(_t("settings_menu_autostart", "Start with the system"), menu)
|
|
49
|
+
autostart_action.setCheckable(True)
|
|
50
|
+
autostart_action.setChecked(autostart_service.is_enabled())
|
|
51
|
+
autostart_action.toggled.connect(lambda checked: _toggle_autostart(ui, checked))
|
|
52
|
+
menu.addAction(autostart_action)
|
|
53
|
+
ui.autostart_action = autostart_action
|
|
54
|
+
|
|
55
|
+
restore_action = QAction(_t("settings_menu_restore_session", "Restore last session"), menu)
|
|
56
|
+
restore_action.setCheckable(True)
|
|
57
|
+
restore_action.setChecked(bool(user_setting_dict.get("restore_last_session")))
|
|
58
|
+
restore_action.toggled.connect(lambda checked: _toggle_restore_session(checked))
|
|
59
|
+
menu.addAction(restore_action)
|
|
60
|
+
ui.restore_session_action = restore_action
|
|
61
|
+
|
|
47
62
|
menu.addSeparator()
|
|
48
63
|
export_action = QAction(_t("settings_menu_export", "Export settings..."), menu)
|
|
49
64
|
export_action.triggered.connect(lambda: _export_settings(ui))
|
|
@@ -54,6 +69,28 @@ def build_settings_menu(ui: "FrontEngineMainUI") -> None:
|
|
|
54
69
|
menu.addAction(import_action)
|
|
55
70
|
|
|
56
71
|
|
|
72
|
+
def _toggle_autostart(ui: "FrontEngineMainUI", enabled: bool) -> None:
|
|
73
|
+
"""開關開機自動啟動;設定失敗時把勾選狀態改回去並告知使用者。"""
|
|
74
|
+
if autostart_service.set_enabled(enabled):
|
|
75
|
+
return
|
|
76
|
+
action = getattr(ui, "autostart_action", None)
|
|
77
|
+
if action is not None:
|
|
78
|
+
action.blockSignals(True)
|
|
79
|
+
action.setChecked(not enabled)
|
|
80
|
+
action.blockSignals(False)
|
|
81
|
+
QMessageBox.warning(
|
|
82
|
+
ui,
|
|
83
|
+
_t("settings_menu_autostart", "Start with the system"),
|
|
84
|
+
_t("settings_autostart_failed", "Could not change the autostart setting."),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _toggle_restore_session(enabled: bool) -> None:
|
|
89
|
+
"""開關「還原上次工作階段」。"""
|
|
90
|
+
user_setting_dict["restore_last_session"] = bool(enabled)
|
|
91
|
+
write_user_setting()
|
|
92
|
+
|
|
93
|
+
|
|
57
94
|
def _toggle_theme_schedule(ui: "FrontEngineMainUI", enabled: bool) -> None:
|
|
58
95
|
"""啟用/停用排程主題並立即套用 / Enable/disable scheduled theme and apply now."""
|
|
59
96
|
front_engine_logger.info(f"[SettingsMenu] toggle theme schedule | enabled={enabled}")
|
{frontengine-1.0.32 → frontengine-1.0.33}/frontengine/ui/page/control_center/control_center_ui.py
RENAMED
|
@@ -83,6 +83,9 @@ class ControlCenterUI(QWidget):
|
|
|
83
83
|
self.chroma_key_button = self._create_button("control_center_chroma_key_on", self.toggle_chroma_key)
|
|
84
84
|
self.reset_positions_button = self._create_button(
|
|
85
85
|
"control_center_reset_positions", self.reset_overlay_positions)
|
|
86
|
+
self._low_power = False
|
|
87
|
+
self.low_power_button = self._create_button(
|
|
88
|
+
"control_center_low_power_on", self.toggle_low_power)
|
|
86
89
|
self.clear_all_button = self._create_button("control_center_close_all", self.clear_all)
|
|
87
90
|
|
|
88
91
|
# Log panel
|
|
@@ -108,8 +111,9 @@ class ControlCenterUI(QWidget):
|
|
|
108
111
|
self.grid_layout.addWidget(self.lock_all_button, 10, 0)
|
|
109
112
|
self.grid_layout.addWidget(self.chroma_key_button, 11, 0)
|
|
110
113
|
self.grid_layout.addWidget(self.reset_positions_button, 12, 0)
|
|
111
|
-
self.grid_layout.addWidget(self.
|
|
112
|
-
self.grid_layout.addWidget(self.
|
|
114
|
+
self.grid_layout.addWidget(self.low_power_button, 13, 0)
|
|
115
|
+
self.grid_layout.addWidget(self.clear_all_button, 14, 0)
|
|
116
|
+
self.grid_layout.addWidget(self.log_panel_scroll_area, 0, 1, 15, 1) # rowSpan covers every button
|
|
113
117
|
self.setLayout(self.grid_layout)
|
|
114
118
|
|
|
115
119
|
# Redirect
|
|
@@ -289,6 +293,27 @@ class ControlCenterUI(QWidget):
|
|
|
289
293
|
"""忘掉記住的覆蓋層位置,之後開的覆蓋層回到各自的預設顯示方式。"""
|
|
290
294
|
front_engine_logger.info("ControlCenterUI reset_overlay_positions")
|
|
291
295
|
clear_overlay_geometry()
|
|
296
|
+
def set_low_power(self, enabled: bool) -> None:
|
|
297
|
+
"""省電模式:讓支援的覆蓋層把更新頻率調慢。"""
|
|
298
|
+
front_engine_logger.info(f"ControlCenterUI set_low_power | enabled={enabled}")
|
|
299
|
+
self._low_power = bool(enabled)
|
|
300
|
+
|
|
301
|
+
def apply(widget) -> None:
|
|
302
|
+
setter = getattr(widget, "set_low_power", None)
|
|
303
|
+
if setter is not None:
|
|
304
|
+
setter(self._low_power)
|
|
305
|
+
|
|
306
|
+
self._for_each_overlay(apply)
|
|
307
|
+
self.low_power_button.setText(
|
|
308
|
+
language_wrapper.language_word_dict.get(
|
|
309
|
+
"control_center_low_power_off" if self._low_power else "control_center_low_power_on",
|
|
310
|
+
"Low power off" if self._low_power else "Low power on",
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def toggle_low_power(self) -> None:
|
|
315
|
+
"""切換省電模式 / Toggle low-power mode."""
|
|
316
|
+
self.set_low_power(not getattr(self, "_low_power", False))
|
|
292
317
|
|
|
293
318
|
def _sample_overlay_opacity(self) -> float:
|
|
294
319
|
"""取一個現有覆蓋層的不透明度作為起始值 / Seed from an existing overlay's opacity."""
|
|
@@ -38,6 +38,9 @@ user_setting_dict: Dict[str, Any] = {
|
|
|
38
38
|
# 使用者拖曳過的覆蓋層位置:類別名稱 -> [x, y, 寬, 高]
|
|
39
39
|
# Overlay positions the user dragged into place: class name -> [x, y, w, h]
|
|
40
40
|
"overlay_geometry": {},
|
|
41
|
+
# 啟動時還原上次的頁面設定
|
|
42
|
+
# Restore the previous session's page settings on launch
|
|
43
|
+
"restore_last_session": False,
|
|
41
44
|
# 桌面寵物心情值(0~100),跨工作階段保存
|
|
42
45
|
# Desktop pet mood (0..100), persisted across sessions
|
|
43
46
|
"pet_mood": 60,
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
開機自動啟動:Windows 寫 HKCU 的 Run 登錄機碼,Linux 放 XDG autostart 的 .desktop,
|
|
3
|
+
macOS 放 LaunchAgents 的 plist。全部只碰目前使用者的設定,不需要管理員權限,
|
|
4
|
+
也不新增相依套件;產生內容的部分是純函式,方便測試。
|
|
5
|
+
|
|
6
|
+
Start FrontEngine with the session: an HKCU Run value on Windows, an XDG
|
|
7
|
+
autostart .desktop on Linux, a LaunchAgent plist on macOS. Everything stays in
|
|
8
|
+
the current user's own configuration — no elevation, no new dependencies — and
|
|
9
|
+
the content builders are pure so they can be tested on any platform.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
18
|
+
|
|
19
|
+
APP_NAME = "FrontEngine"
|
|
20
|
+
LAUNCH_AGENT_LABEL = "com.frontengine.autostart"
|
|
21
|
+
_DESKTOP_FILE = "frontengine.desktop"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def escape_xml(text) -> str:
|
|
25
|
+
"""
|
|
26
|
+
跳脫 plist 需要的三個字元。這裡只產生輸出、不解析任何 XML,所以不引入
|
|
27
|
+
XML 解析器(也就沒有 XXE 那類疑慮)。
|
|
28
|
+
Escape the three characters a plist needs. This only writes XML — nothing
|
|
29
|
+
here parses it — so no XML parser is pulled in.
|
|
30
|
+
"""
|
|
31
|
+
return str(text).replace("&", "&").replace("<", "<").replace(">", ">")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def launch_command() -> List[str]:
|
|
35
|
+
"""
|
|
36
|
+
回傳能重新啟動本程式的命令:打包成執行檔時就是執行檔本身,
|
|
37
|
+
否則以目前的直譯器執行 `-m frontengine`。
|
|
38
|
+
The command that relaunches the app: the frozen executable when packaged,
|
|
39
|
+
otherwise the current interpreter running ``-m frontengine``.
|
|
40
|
+
"""
|
|
41
|
+
if getattr(sys, "frozen", False):
|
|
42
|
+
return [sys.executable]
|
|
43
|
+
return [sys.executable, "-m", "frontengine"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def quote_command(command: List[str]) -> str:
|
|
47
|
+
"""把命令組成一行字串,含空白的項目加上引號。"""
|
|
48
|
+
parts = []
|
|
49
|
+
for part in command:
|
|
50
|
+
text = str(part)
|
|
51
|
+
parts.append(f'"{text}"' if " " in text and not text.startswith('"') else text)
|
|
52
|
+
return " ".join(parts)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def desktop_entry_text(command: List[str], app_name: str = APP_NAME) -> str:
|
|
56
|
+
"""產生 XDG autostart 用的 .desktop 內容。"""
|
|
57
|
+
return (
|
|
58
|
+
"[Desktop Entry]\n"
|
|
59
|
+
"Type=Application\n"
|
|
60
|
+
f"Name={app_name}\n"
|
|
61
|
+
f"Exec={quote_command(command)}\n"
|
|
62
|
+
"X-GNOME-Autostart-enabled=true\n"
|
|
63
|
+
"Terminal=false\n"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def launch_agent_plist(command: List[str], label: str = LAUNCH_AGENT_LABEL) -> str:
|
|
68
|
+
"""產生 macOS LaunchAgent 用的 plist 內容。"""
|
|
69
|
+
arguments = "".join(f" <string>{escape_xml(part)}</string>\n" for part in command)
|
|
70
|
+
return (
|
|
71
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n'
|
|
72
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" '
|
|
73
|
+
'"http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
|
|
74
|
+
'<plist version="1.0">\n'
|
|
75
|
+
"<dict>\n"
|
|
76
|
+
" <key>Label</key>\n"
|
|
77
|
+
f" <string>{escape_xml(label)}</string>\n"
|
|
78
|
+
" <key>ProgramArguments</key>\n"
|
|
79
|
+
" <array>\n"
|
|
80
|
+
f"{arguments}"
|
|
81
|
+
" </array>\n"
|
|
82
|
+
" <key>RunAtLoad</key>\n"
|
|
83
|
+
" <true/>\n"
|
|
84
|
+
"</dict>\n"
|
|
85
|
+
"</plist>\n"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def autostart_path() -> Optional[Path]:
|
|
90
|
+
"""回傳本平台放置自動啟動設定的檔案路徑;Windows 用登錄檔,回傳 None。"""
|
|
91
|
+
if sys.platform == "win32":
|
|
92
|
+
return None
|
|
93
|
+
if sys.platform == "darwin":
|
|
94
|
+
return Path.home() / "Library" / "LaunchAgents" / f"{LAUNCH_AGENT_LABEL}.plist"
|
|
95
|
+
return Path.home() / ".config" / "autostart" / _DESKTOP_FILE
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def is_enabled() -> bool:
|
|
99
|
+
"""目前是否已設定開機自動啟動。"""
|
|
100
|
+
if sys.platform == "win32":
|
|
101
|
+
return _read_windows_run_value() is not None
|
|
102
|
+
path = autostart_path()
|
|
103
|
+
return bool(path and path.is_file())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def set_enabled(enabled: bool) -> bool:
|
|
107
|
+
"""
|
|
108
|
+
開啟或關閉開機自動啟動;成功回傳 True,失敗記錄後回傳 False(不丟例外)。
|
|
109
|
+
Enable or disable autostart. Returns False (logged) instead of raising.
|
|
110
|
+
"""
|
|
111
|
+
front_engine_logger.info(f"[autostart] set_enabled | enabled={enabled}")
|
|
112
|
+
try:
|
|
113
|
+
if sys.platform == "win32":
|
|
114
|
+
return _set_windows(enabled)
|
|
115
|
+
return _set_file(enabled)
|
|
116
|
+
except (OSError, ValueError) as error:
|
|
117
|
+
front_engine_logger.warning(f"[autostart] failed: {error!r}")
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _set_file(enabled: bool) -> bool:
|
|
122
|
+
path = autostart_path()
|
|
123
|
+
if path is None:
|
|
124
|
+
return False
|
|
125
|
+
if not enabled:
|
|
126
|
+
if path.is_file():
|
|
127
|
+
path.unlink()
|
|
128
|
+
return True
|
|
129
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
command = launch_command()
|
|
131
|
+
content = launch_agent_plist(command) if sys.platform == "darwin" else desktop_entry_text(command)
|
|
132
|
+
path.write_text(content, encoding="utf-8")
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _run_key():
|
|
137
|
+
import winreg
|
|
138
|
+
|
|
139
|
+
return winreg.OpenKey(
|
|
140
|
+
winreg.HKEY_CURRENT_USER,
|
|
141
|
+
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
|
142
|
+
0,
|
|
143
|
+
winreg.KEY_READ | winreg.KEY_WRITE,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _read_windows_run_value() -> Optional[str]:
|
|
148
|
+
try:
|
|
149
|
+
import winreg
|
|
150
|
+
|
|
151
|
+
with _run_key() as key:
|
|
152
|
+
value, _kind = winreg.QueryValueEx(key, APP_NAME)
|
|
153
|
+
return str(value)
|
|
154
|
+
except FileNotFoundError:
|
|
155
|
+
return None
|
|
156
|
+
except Exception as error: # pragma: no cover - registry boundary
|
|
157
|
+
front_engine_logger.debug(f"[autostart] registry read failed: {error!r}")
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _set_windows(enabled: bool) -> bool:
|
|
162
|
+
import winreg
|
|
163
|
+
|
|
164
|
+
with _run_key() as key:
|
|
165
|
+
if enabled:
|
|
166
|
+
winreg.SetValueEx(key, APP_NAME, 0, winreg.REG_SZ, quote_command(launch_command()))
|
|
167
|
+
else:
|
|
168
|
+
try:
|
|
169
|
+
winreg.DeleteValue(key, APP_NAME)
|
|
170
|
+
except FileNotFoundError:
|
|
171
|
+
pass
|
|
172
|
+
return True
|
|
@@ -186,6 +186,11 @@ english_word_dict = {
|
|
|
186
186
|
"control_center_chroma_key_off": "Chroma key off",
|
|
187
187
|
"control_center_reset_positions": "Reset overlay positions",
|
|
188
188
|
"hotkey_toggle_lock": "Lock / unlock overlays",
|
|
189
|
+
"settings_menu_autostart": "Start with the system",
|
|
190
|
+
"settings_menu_restore_session": "Restore last session",
|
|
191
|
+
"settings_autostart_failed": "Could not change the autostart setting.",
|
|
192
|
+
"control_center_low_power_on": "Low power on",
|
|
193
|
+
"control_center_low_power_off": "Low power off",
|
|
189
194
|
"control_center_mute_all": "Mute all",
|
|
190
195
|
"control_center_unmute_all": "Unmute all",
|
|
191
196
|
# Recent files
|
{frontengine-1.0.32 → frontengine-1.0.33}/frontengine/utils/multi_language/traditional_chinese.py
RENAMED
|
@@ -187,6 +187,11 @@ traditional_chinese_word_dict = {
|
|
|
187
187
|
"control_center_chroma_key_off": "關閉綠幕背景",
|
|
188
188
|
"control_center_reset_positions": "重設覆蓋層位置",
|
|
189
189
|
"hotkey_toggle_lock": "鎖定/解鎖覆蓋層",
|
|
190
|
+
"settings_menu_autostart": "開機時自動啟動",
|
|
191
|
+
"settings_menu_restore_session": "還原上次工作階段",
|
|
192
|
+
"settings_autostart_failed": "無法變更自動啟動設定。",
|
|
193
|
+
"control_center_low_power_on": "開啟省電模式",
|
|
194
|
+
"control_center_low_power_off": "關閉省電模式",
|
|
190
195
|
"control_center_mute_all": "全部靜音",
|
|
191
196
|
"control_center_unmute_all": "取消全部靜音",
|
|
192
197
|
# Recent files
|