frontengine 1.0.44__tar.gz → 1.0.46__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.
Files changed (256) hide show
  1. {frontengine-1.0.44 → frontengine-1.0.46}/PKG-INFO +23 -1
  2. {frontengine-1.0.44 → frontengine-1.0.46}/README.md +22 -0
  3. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/base_widget.py +6 -0
  4. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/focus_shield/focus_shield_widget.py +3 -0
  5. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/particle/particle_ui.py +30 -31
  6. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/pet/desktop_pet.py +93 -61
  7. frontengine-1.0.46/frontengine/ui/dialog/screen_privacy_dialog.py +98 -0
  8. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/main_ui.py +26 -0
  9. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/settings_menu.py +7 -0
  10. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/control_center/control_center_ui.py +45 -2
  11. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/user_setting/user_setting_file.py +3 -0
  12. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/english.py +9 -0
  13. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/traditional_chinese.py +9 -0
  14. frontengine-1.0.46/frontengine/utils/screen_privacy/capture_affinity.py +120 -0
  15. frontengine-1.0.46/frontengine/utils/screen_privacy/share_watch.py +128 -0
  16. frontengine-1.0.46/frontengine/worker/__init__.py +0 -0
  17. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/PKG-INFO +23 -1
  18. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/SOURCES.txt +4 -0
  19. {frontengine-1.0.44 → frontengine-1.0.46}/pyproject.toml +1 -1
  20. {frontengine-1.0.44 → frontengine-1.0.46}/LICENSE +0 -0
  21. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/__init__.py +0 -0
  22. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/__main__.py +0 -0
  23. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/__init__.py +0 -0
  24. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/camera/__init__.py +0 -0
  25. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/camera/camera_widget.py +0 -0
  26. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/capture/__init__.py +0 -0
  27. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/capture/region_capture.py +0 -0
  28. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/focus_shield/__init__.py +0 -0
  29. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/gaming/__init__.py +0 -0
  30. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/gaming/crosshair_widget.py +0 -0
  31. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/gif/__init__.py +0 -0
  32. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/gif/paint_gif.py +0 -0
  33. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/image/__init__.py +0 -0
  34. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/image/paint_image.py +0 -0
  35. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/load/__init__.py +0 -0
  36. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/load/load_someone_make_ui.py +0 -0
  37. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/measure/__init__.py +0 -0
  38. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/measure/measure_widget.py +0 -0
  39. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/monitor/__init__.py +0 -0
  40. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/monitor/monitor_widget.py +0 -0
  41. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/notes/__init__.py +0 -0
  42. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/notes/sticky_note_widget.py +0 -0
  43. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/overlay_factory.py +0 -0
  44. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/particle/__init__.py +0 -0
  45. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/pet/__init__.py +0 -0
  46. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/presentation/__init__.py +0 -0
  47. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/presentation/annotation_overlay.py +0 -0
  48. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/presentation/cursor_effects.py +0 -0
  49. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/presentation/keystroke_display.py +0 -0
  50. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/presentation/magnifier.py +0 -0
  51. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/scene/__init__.py +0 -0
  52. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  53. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/scene/extend_graphic_view.py +0 -0
  54. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/scene/scene.py +0 -0
  55. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/screen_care/__init__.py +0 -0
  56. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  57. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/screen_care/screen_filter.py +0 -0
  58. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/sound_player/__init__.py +0 -0
  59. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/sound_player/sound_effect.py +0 -0
  60. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/sound_player/sound_player.py +0 -0
  61. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/spectrum/__init__.py +0 -0
  62. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  63. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/teleprompter/__init__.py +0 -0
  64. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/teleprompter/teleprompter_widget.py +0 -0
  65. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/text/__init__.py +0 -0
  66. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/text/draw_text.py +0 -0
  67. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/toast/__init__.py +0 -0
  68. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/toast/toast_widget.py +0 -0
  69. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/video/__init__.py +0 -0
  70. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/video/video_player.py +0 -0
  71. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/wallpaper/__init__.py +0 -0
  72. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  73. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/web/__init__.py +0 -0
  74. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/web/webview.py +0 -0
  75. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/show/window_helpers.py +0 -0
  76. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/system_tray/__init__.py +0 -0
  77. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/system_tray/extend_system_tray.py +0 -0
  78. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/__init__.py +0 -0
  79. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/color/__init__.py +0 -0
  80. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/color/global_color.py +0 -0
  81. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/__init__.py +0 -0
  82. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  83. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  84. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  85. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  86. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  87. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  88. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  89. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  90. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  91. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  92. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/__init__.py +0 -0
  93. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/help_menu.py +0 -0
  94. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/how_to_menu.py +0 -0
  95. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/language_menu.py +0 -0
  96. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/menu/preset_menu.py +0 -0
  97. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/__init__.py +0 -0
  98. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/control_center/__init__.py +0 -0
  99. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/focus/__init__.py +0 -0
  100. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  101. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/gif/__init__.py +0 -0
  102. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  103. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/image/__init__.py +0 -0
  104. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  105. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/particle/__init__.py +0 -0
  106. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  107. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/pet/__init__.py +0 -0
  108. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  109. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/presentation/__init__.py +0 -0
  110. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  111. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  112. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  113. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  114. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  115. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  116. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  117. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  118. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  119. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  120. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  121. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  122. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  123. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/screen_care/__init__.py +0 -0
  124. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
  125. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/sound_player/__init__.py +0 -0
  126. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  127. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/stream/__init__.py +0 -0
  128. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/stream/stream_setting_ui.py +0 -0
  129. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/text/__init__.py +0 -0
  130. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  131. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/tools/__init__.py +0 -0
  132. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/tools/tools_setting_ui.py +0 -0
  133. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/utils.py +0 -0
  134. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/video/__init__.py +0 -0
  135. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  136. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/wallpaper/__init__.py +0 -0
  137. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  138. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/web/__init__.py +0 -0
  139. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  140. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/widgets/__init__.py +0 -0
  141. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  142. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/user_setting/__init__.py +0 -0
  143. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/user_setting/preset_repository.py +0 -0
  144. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/user_setting/scene_setting.py +0 -0
  145. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/__init__.py +0 -0
  146. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/app_profile/__init__.py +0 -0
  147. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  148. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/__init__.py +0 -0
  149. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  150. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  151. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  152. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  153. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  154. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/autostart/__init__.py +0 -0
  155. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/autostart/autostart_service.py +0 -0
  156. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/break_reminder/__init__.py +0 -0
  157. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  158. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/browser/__init__.py +0 -0
  159. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/browser/browser.py +0 -0
  160. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/clipboard/__init__.py +0 -0
  161. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  162. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  163. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/color_vision/__init__.py +0 -0
  164. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/color_vision/color_vision.py +0 -0
  165. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/critical_exit/__init__.py +0 -0
  166. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  167. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  168. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  169. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/exception/__init__.py +0 -0
  170. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/exception/exception_tags.py +0 -0
  171. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/exception/exceptions.py +0 -0
  172. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/file/__init__.py +0 -0
  173. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/file/open/__init__.py +0 -0
  174. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/file/open/open_file.py +0 -0
  175. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/file/save/__init__.py +0 -0
  176. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/file/save/save_file.py +0 -0
  177. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/focus_shield/__init__.py +0 -0
  178. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  179. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/focus_timer/__init__.py +0 -0
  180. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  181. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/hotkey/__init__.py +0 -0
  182. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  183. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/input_watch/__init__.py +0 -0
  184. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  185. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/json/__init__.py +0 -0
  186. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/json/json_file.py +0 -0
  187. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/json/json_repository.py +0 -0
  188. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/json_format/__init__.py +0 -0
  189. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/json_format/json_process.py +0 -0
  190. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/logging/__init__.py +0 -0
  191. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/logging/loggin_instance.py +0 -0
  192. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/measure/__init__.py +0 -0
  193. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/measure/measure.py +0 -0
  194. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/__init__.py +0 -0
  195. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/france.py +0 -0
  196. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/germany.py +0 -0
  197. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/italy.py +0 -0
  198. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  199. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/russian.py +0 -0
  200. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  201. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/now_playing/__init__.py +0 -0
  202. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/now_playing/now_playing.py +0 -0
  203. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/obs/__init__.py +0 -0
  204. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/obs/obs_client.py +0 -0
  205. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/obs/obs_protocol.py +0 -0
  206. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/obs/websocket_frames.py +0 -0
  207. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/pet_chat/__init__.py +0 -0
  208. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  209. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/platform_info/__init__.py +0 -0
  210. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/platform_info/platform_info.py +0 -0
  211. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/playlist/__init__.py +0 -0
  212. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/playlist/playlist.py +0 -0
  213. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/plugins/__init__.py +0 -0
  214. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/plugins/plugin_loader.py +0 -0
  215. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/power_mode/__init__.py +0 -0
  216. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/power_mode/power_mode.py +0 -0
  217. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/preset_schedule/__init__.py +0 -0
  218. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  219. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/recording/__init__.py +0 -0
  220. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/recording/frame_recorder.py +0 -0
  221. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/recording/gif_writer.py +0 -0
  222. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/redirect_manager/__init__.py +0 -0
  223. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  224. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/reminder/__init__.py +0 -0
  225. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/reminder/reminder_service.py +0 -0
  226. {frontengine-1.0.44/frontengine/utils/screen_text → frontengine-1.0.46/frontengine/utils/screen_privacy}/__init__.py +0 -0
  227. {frontengine-1.0.44/frontengine/utils/smart_pause → frontengine-1.0.46/frontengine/utils/screen_text}/__init__.py +0 -0
  228. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/screen_text/screen_text_service.py +0 -0
  229. {frontengine-1.0.44/frontengine/utils/soundboard → frontengine-1.0.46/frontengine/utils/smart_pause}/__init__.py +0 -0
  230. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  231. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  232. {frontengine-1.0.44/frontengine/utils/system_stats → frontengine-1.0.46/frontengine/utils/soundboard}/__init__.py +0 -0
  233. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/soundboard/soundboard.py +0 -0
  234. {frontengine-1.0.44/frontengine/utils/text_source → frontengine-1.0.46/frontengine/utils/system_stats}/__init__.py +0 -0
  235. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/system_stats/system_stats.py +0 -0
  236. {frontengine-1.0.44/frontengine/utils/theme_schedule → frontengine-1.0.46/frontengine/utils/text_source}/__init__.py +0 -0
  237. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/text_source/text_source.py +0 -0
  238. {frontengine-1.0.44/frontengine/utils/usage_tracking → frontengine-1.0.46/frontengine/utils/theme_schedule}/__init__.py +0 -0
  239. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  240. {frontengine-1.0.44/frontengine/utils/weather → frontengine-1.0.46/frontengine/utils/usage_tracking}/__init__.py +0 -0
  241. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  242. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  243. {frontengine-1.0.44/frontengine/utils/window_pin → frontengine-1.0.46/frontengine/utils/weather}/__init__.py +0 -0
  244. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/weather/weather_service.py +0 -0
  245. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/web_url.py +0 -0
  246. {frontengine-1.0.44/frontengine/utils/workshop → frontengine-1.0.46/frontengine/utils/window_pin}/__init__.py +0 -0
  247. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/window_pin/window_layout.py +0 -0
  248. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/window_pin/window_pin.py +0 -0
  249. {frontengine-1.0.44/frontengine/worker → frontengine-1.0.46/frontengine/utils/workshop}/__init__.py +0 -0
  250. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/utils/workshop/workshop_content.py +0 -0
  251. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine/worker/qthread_worker.py +0 -0
  252. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/dependency_links.txt +0 -0
  253. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/entry_points.txt +0 -0
  254. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/requires.txt +0 -0
  255. {frontengine-1.0.44 → frontengine-1.0.46}/frontengine.egg-info/top_level.txt +0 -0
  256. {frontengine-1.0.44 → frontengine-1.0.46}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.44
3
+ Version: 1.0.46
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
@@ -196,6 +196,28 @@ Four things on the **Stream** tab, for playing and for going live:
196
196
 
197
197
  ---
198
198
 
199
+ ## Screen-sharing privacy
200
+
201
+ Your overlays are for you, not for the people you are sharing with. From
202
+ **Settings → Screen-sharing privacy**, FrontEngine can take them out of the
203
+ capture while a meeting app is open:
204
+
205
+ - **They stay on your own screen.** Only the captured copy is blank — this uses
206
+ Windows' `WDA_EXCLUDEFROMCAPTURE`, an OS-level flag that conferencing apps and
207
+ recorders honour.
208
+ - **Masks are the exception.** A distraction mask exists to cover something, so
209
+ it deliberately stays visible in the capture.
210
+ - **The trigger is your list.** Windows has no dependable "am I being captured"
211
+ API, so it watches for window titles you name — which also catches a meeting
212
+ held in a browser tab, where the executable is just the browser.
213
+
214
+ There is a manual **Hide from capture** button in the control center too.
215
+
216
+ > This is privacy, not security: it defeats the ordinary capture path, and it
217
+ > never hides anything from the person sitting at the desk. Windows only.
218
+
219
+ ---
220
+
199
221
  ## Screen time, clipboard and layouts
200
222
 
201
223
  Three things that remember, all of them **off until you switch them on** and
@@ -172,6 +172,28 @@ Four things on the **Stream** tab, for playing and for going live:
172
172
 
173
173
  ---
174
174
 
175
+ ## Screen-sharing privacy
176
+
177
+ Your overlays are for you, not for the people you are sharing with. From
178
+ **Settings → Screen-sharing privacy**, FrontEngine can take them out of the
179
+ capture while a meeting app is open:
180
+
181
+ - **They stay on your own screen.** Only the captured copy is blank — this uses
182
+ Windows' `WDA_EXCLUDEFROMCAPTURE`, an OS-level flag that conferencing apps and
183
+ recorders honour.
184
+ - **Masks are the exception.** A distraction mask exists to cover something, so
185
+ it deliberately stays visible in the capture.
186
+ - **The trigger is your list.** Windows has no dependable "am I being captured"
187
+ API, so it watches for window titles you name — which also catches a meeting
188
+ held in a browser tab, where the executable is just the browser.
189
+
190
+ There is a manual **Hide from capture** button in the control center too.
191
+
192
+ > This is privacy, not security: it defeats the ordinary capture path, and it
193
+ > never hides anything from the person sitting at the desk. Windows only.
194
+
195
+ ---
196
+
175
197
  ## Screen time, clipboard and layouts
176
198
 
177
199
  Three things that remember, all of them **off until you switch them on** and
@@ -33,6 +33,12 @@ class BaseWidget(QWidget):
33
33
  # 畫質檔位:影響更新頻率上限與算圖縮放(見 utils/power_mode)
34
34
  # Quality tier: caps the refresh rate and scales rendering (see utils/power_mode).
35
35
  self.quality_tier: str = DEFAULT_TIER
36
+ # 分享畫面時要不要一起藏起來。遮蔽用的覆蓋層要留在畫面上——
37
+ # 那正是它存在的理由,藏了就等於沒遮。
38
+ # Whether this hides along with the others while sharing. A masking
39
+ # overlay must stay visible in the capture: hiding it would defeat
40
+ # the only thing it is for.
41
+ self.keep_in_capture: bool = False
36
42
  self._drag_origin = None
37
43
  self._geometry_restored = False
38
44
 
@@ -118,6 +118,9 @@ class DistractionMaskWidget(BaseWidget):
118
118
  f"[DistractionMaskWidget] Init | region={region}, percent={percent}")
119
119
  super().__init__()
120
120
  self.opacity = 1.0
121
+ # 這一層是拿來遮住東西的,分享畫面時必須留在擷取結果裡
122
+ # This layer exists to cover something, so it stays in the capture.
123
+ self.keep_in_capture = True
121
124
  self.region = region
122
125
  self.percent = clamp_percent(percent)
123
126
  self.mask_color = QColor(color) if QColor(color).isValid() else QColor("#101010")
@@ -107,40 +107,39 @@ class ParticleOpenGLWidget(QOpenGLWidget):
107
107
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
108
108
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
109
109
 
110
- def update_particles(self):
111
- for p in self.particles:
112
- x, y = p
113
- if self.particle_direction == "down":
114
- y -= self.particle_speed
115
- if y < -1:
116
- y = 1
117
- x = random.uniform(-1, 1)
118
-
119
- elif self.particle_direction == "up":
120
- y += self.particle_speed
121
- if y > 1:
122
- y = -1
123
- x = random.uniform(-1, 1)
124
-
125
- elif self.particle_direction == "left":
126
- x -= self.particle_speed
127
- if x < -1:
128
- x = 1
129
- y = random.uniform(-1, 1)
130
-
131
- elif self.particle_direction == "right":
132
- x += self.particle_speed
133
- if x > 1:
134
- x = -1
135
- y = random.uniform(-1, 1)
136
-
137
- # 你可以自行擴充其他方向
138
-
139
- p[0] = x
140
- p[1] = y
110
+ # 每個方向:(要移動的軸, 每步的位移正負, 越過哪一邊算離場)
111
+ # Per direction: which axis moves, the sign of each step, and which edge
112
+ # counts as leaving the screen.
113
+ _DIRECTIONS = {
114
+ "down": (1, -1, -1.0),
115
+ "up": (1, +1, +1.0),
116
+ "left": (0, -1, -1.0),
117
+ "right": (0, +1, +1.0),
118
+ }
141
119
 
120
+ def update_particles(self):
121
+ """把每個粒子往設定的方向推一步,離開畫面的就從對面重新進場。"""
122
+ movement = self._DIRECTIONS.get(self.particle_direction)
123
+ if movement is None:
124
+ self.update()
125
+ return
126
+ axis, sign, edge = movement
127
+ other = 1 - axis
128
+ for particle in self.particles:
129
+ particle[axis] += sign * self.particle_speed
130
+ if self._left_the_screen(particle[axis], sign, edge):
131
+ # 從對面邊緣回來,另一軸重新隨機,看起來才像源源不絕
132
+ # Re-enter from the opposite edge with a fresh position on the
133
+ # other axis, so the stream never looks like a repeating loop.
134
+ particle[axis] = -edge
135
+ particle[other] = random.uniform(-1, 1) # nosec B311 - visual only
142
136
  self.update()
143
137
 
138
+ @staticmethod
139
+ def _left_the_screen(value: float, sign: int, edge: float) -> bool:
140
+ """這個粒子是不是已經越過它前進方向的那一邊。"""
141
+ return value < edge if sign < 0 else value > edge
142
+
144
143
  def paintGL(self):
145
144
  glClearColor(0, 0, 0, 0)
146
145
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
@@ -806,7 +806,15 @@ class PetMotion:
806
806
  if self.surface != SURFACE_FLOOR:
807
807
  return self._step_climb(left, top, right, bottom, floor_y)
808
808
 
809
- # grounded on the screen floor: random walk / idle, with a chance to climb a wall
809
+ return self._walk_on_floor(left, right, floor_y)
810
+
811
+ def _walk_on_floor(self, left, right, floor_y) -> Tuple[int, int]:
812
+ """
813
+ 站在螢幕地板上:隨機在走路與發呆之間切換,走到左右邊緣時有機會改成
814
+ 爬牆,否則就轉頭。
815
+ Grounded on the screen floor: alternate between walking and idling, and
816
+ on reaching an edge either grab the wall or turn around.
817
+ """
810
818
  self.y = float(floor_y)
811
819
  if self._state_ticks <= 0:
812
820
  self._new_state()
@@ -816,20 +824,20 @@ class PetMotion:
816
824
  self.x += self.vx
817
825
  if self.x <= left:
818
826
  self.x = float(left)
819
- if self.climb and self._rng.random() < self.CLIMB_CHANCE:
820
- self.surface = SURFACE_LEFT
821
- self.vy = float(-self.speed)
822
- else:
823
- self.vx = abs(self.vx)
827
+ self._turn_or_climb(SURFACE_LEFT, turned_vx=abs(self.vx))
824
828
  elif self.x + self.width >= right:
825
829
  self.x = float(right - self.width)
826
- if self.climb and self._rng.random() < self.CLIMB_CHANCE:
827
- self.surface = SURFACE_RIGHT
828
- self.vy = float(-self.speed)
829
- else:
830
- self.vx = -abs(self.vx)
830
+ self._turn_or_climb(SURFACE_RIGHT, turned_vx=-abs(self.vx))
831
831
  return int(self.x), int(self.y)
832
832
 
833
+ def _turn_or_climb(self, wall: str, turned_vx: float) -> None:
834
+ """走到邊緣了:抓上牆去爬,或是掉頭往回走。"""
835
+ if self.climb and self._rng.random() < self.CLIMB_CHANCE:
836
+ self.surface = wall
837
+ self.vy = float(-self.speed)
838
+ else:
839
+ self.vx = turned_vx
840
+
833
841
  def _step_fall(self, left, top, right, bottom, _floor_y) -> Tuple[int, int]:
834
842
  """落下:撞牆/天花板反彈,並在越過視窗平台或螢幕地板時落地。"""
835
843
  prev_feet = self.y + self.height
@@ -948,42 +956,49 @@ class PetMotion:
948
956
  return int(self.x), int(self.y)
949
957
 
950
958
  if self.surface == SURFACE_RIGHT:
951
- self.x = float(right - self.width)
952
- self.y += self.vy
953
- if self.vy < 0 and self.y <= top: # reached the ceiling, crawl left
954
- self.y = float(top)
955
- self.surface = SURFACE_CEILING
956
- self.vx = float(-self.speed)
957
- elif self.vy > 0 and self.y >= floor_y: # climbed back down
958
- self.y = float(floor_y)
959
- self.surface = SURFACE_FLOOR
960
- self.vx = float(-self.speed)
961
- self._new_state(force_walk=True)
959
+ # 右牆:貼著右緣上下爬,離開時往左走
960
+ # Right wall: hug the right edge, and leave it heading left.
961
+ self._climb_wall(x=float(right - self.width), top=top, floor_y=floor_y,
962
+ leave_vx=float(-self.speed))
962
963
  elif self.surface == SURFACE_LEFT:
963
- self.x = float(left)
964
- self.y += self.vy
965
- if self.vy < 0 and self.y <= top:
966
- self.y = float(top)
967
- self.surface = SURFACE_CEILING
968
- self.vx = float(self.speed)
969
- elif self.vy > 0 and self.y >= floor_y:
970
- self.y = float(floor_y)
971
- self.surface = SURFACE_FLOOR
972
- self.vx = float(self.speed)
973
- self._new_state(force_walk=True)
964
+ self._climb_wall(x=float(left), top=top, floor_y=floor_y,
965
+ leave_vx=float(self.speed))
974
966
  elif self.surface == SURFACE_CEILING:
975
- self.y = float(top)
976
- self.x += self.vx
977
- if self.vx < 0 and self.x <= left: # corner -> climb down the left wall
978
- self.x = float(left)
979
- self.surface = SURFACE_LEFT
980
- self.vy = float(self.speed)
981
- elif self.vx > 0 and self.x + self.width >= right:
982
- self.x = float(right - self.width)
983
- self.surface = SURFACE_RIGHT
984
- self.vy = float(self.speed)
967
+ self._crawl_ceiling(left, top, right)
985
968
  return int(self.x), int(self.y)
986
969
 
970
+ def _climb_wall(self, x: float, top: float, floor_y: float, leave_vx: float) -> None:
971
+ """
972
+ 沿著一面牆上下爬。左右兩面牆的規則一模一樣,只差「離開時往哪走」,
973
+ 所以由呼叫端把那個方向帶進來。
974
+ Climb one wall. Both walls follow identical rules and differ only in
975
+ which way the pet leaves, so the caller passes that in.
976
+ """
977
+ self.x = x
978
+ self.y += self.vy
979
+ if self.vy < 0 and self.y <= top: # 到天花板,改成橫著爬
980
+ self.y = float(top)
981
+ self.surface = SURFACE_CEILING
982
+ self.vx = leave_vx
983
+ elif self.vy > 0 and self.y >= floor_y: # 爬回地面,恢復走路
984
+ self.y = float(floor_y)
985
+ self.surface = SURFACE_FLOOR
986
+ self.vx = leave_vx
987
+ self._new_state(force_walk=True)
988
+
989
+ def _crawl_ceiling(self, left, top, right) -> None:
990
+ """在天花板上橫著爬,到角落就轉成往下爬那面牆。"""
991
+ self.y = float(top)
992
+ self.x += self.vx
993
+ if self.vx < 0 and self.x <= left:
994
+ self.x = float(left)
995
+ self.surface = SURFACE_LEFT
996
+ self.vy = float(self.speed)
997
+ elif self.vx > 0 and self.x + self.width >= right:
998
+ self.x = float(right - self.width)
999
+ self.surface = SURFACE_RIGHT
1000
+ self.vy = float(self.speed)
1001
+
987
1002
  def _new_state(self, force_walk: bool = False) -> None:
988
1003
  if force_walk or self._rng.random() < 0.65:
989
1004
  self.state = "walk"
@@ -1245,25 +1260,42 @@ class DesktopPetWidget(BaseWidget):
1245
1260
  self._peer_greeted = False
1246
1261
  self.motion.clear_follow()
1247
1262
  return
1248
- # play: a floor pet may walk over to a near-ish peer
1249
- if self.motion.behaviour == BEHAVIOUR_FLOOR:
1250
- if self.MEET_DISTANCE < distance <= self.PLAY_RANGE:
1251
- if self.motion.follow_target_x is not None or self._chatter_rng.random() < self.PLAY_CHANCE:
1252
- self.motion.set_follow(peer[0])
1253
- elif distance > self.PLAY_RANGE:
1254
- self.motion.clear_follow()
1255
- # greet: face and say a meet line once per approach
1256
- if distance <= self.MEET_DISTANCE:
1263
+ self._approach_peer(peer, distance)
1264
+ self._greet_peer(peer, distance, my_center)
1265
+
1266
+ def _approach_peer(self, peer, distance: float) -> None:
1267
+ """在地板上的寵物看到不遠的同伴時,有機會走過去玩。"""
1268
+ if self.motion.behaviour != BEHAVIOUR_FLOOR:
1269
+ return
1270
+ if self.MEET_DISTANCE < distance <= self.PLAY_RANGE:
1271
+ already_walking = self.motion.follow_target_x is not None
1272
+ if already_walking or self._chatter_rng.random() < self.PLAY_CHANCE:
1273
+ self.motion.set_follow(peer[0])
1274
+ elif distance > self.PLAY_RANGE:
1257
1275
  self.motion.clear_follow()
1258
- if self._talk and not self._peer_greeted:
1259
- self._peer_greeted = True
1260
- self._gain_affection(3)
1261
- self.motion.vx = abs(self.motion.vx) if peer[0] >= my_center[0] else -abs(self.motion.vx)
1262
- pool = self._messages.get("meet") or []
1263
- if pool:
1264
- self.say(pool[self._chatter_rng.randrange(len(pool))])
1265
- elif distance > self.MEET_DISTANCE * 1.6:
1266
- self._peer_greeted = False
1276
+
1277
+ def _greet_peer(self, peer, distance: float, my_center) -> None:
1278
+ """
1279
+ 走到夠近就轉頭打個招呼,一次接近只打一次;離開夠遠之後才會重新算一次
1280
+ (這段遲滯是為了避免在邊界來回時一直重複講話)。
1281
+ Close enough: turn to face them and say hello, once per approach. The
1282
+ flag only resets once they are well clear again, which stops a pet
1283
+ hovering at the boundary from repeating itself.
1284
+ """
1285
+ if distance > self.MEET_DISTANCE:
1286
+ if distance > self.MEET_DISTANCE * 1.6:
1287
+ self._peer_greeted = False
1288
+ return
1289
+ self.motion.clear_follow()
1290
+ if not self._talk or self._peer_greeted:
1291
+ return
1292
+ self._peer_greeted = True
1293
+ self._gain_affection(3)
1294
+ self.motion.vx = (abs(self.motion.vx) if peer[0] >= my_center[0]
1295
+ else -abs(self.motion.vx))
1296
+ pool = self._messages.get("meet") or []
1297
+ if pool:
1298
+ self.say(pool[self._chatter_rng.randrange(len(pool))])
1267
1299
 
1268
1300
  def _load_sprite(self, path: str):
1269
1301
  """載入一張精靈;檔案損毀或格式不支援時回傳 None。"""
@@ -0,0 +1,98 @@
1
+ """
2
+ 螢幕分享隱私設定:偵測到會議程式時,自動把覆蓋層藏出擷取畫面。
3
+
4
+ Windows 沒有可靠的「我正在被擷取嗎」API,所以判斷依據是使用者自己列出的
5
+ 會議程式有沒有開著視窗。這是刻意的:與其猜錯,不如讓使用者說了算。
6
+
7
+ Screen-sharing privacy: hide the overlays from the capture when a meeting app
8
+ turns up.
9
+
10
+ Windows offers no dependable "am I being captured" API, so the trigger is
11
+ whether one of the apps the user listed has a window open. That is deliberate -
12
+ better their list than our guess.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Any, Dict, Optional
17
+
18
+ from PySide6.QtWidgets import (
19
+ QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QWidget,
20
+ )
21
+
22
+ from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
23
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
24
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
25
+ from frontengine.utils.screen_privacy import capture_affinity
26
+ from frontengine.utils.screen_privacy.share_watch import DEFAULT_SHARING_APPS
27
+ from frontengine.utils.smart_pause.pause_rules import parse_app_list
28
+
29
+ SETTING_KEY = "screen_share_privacy"
30
+ DEFAULT_SETTINGS: Dict[str, Any] = {"enabled": False, "apps": list(DEFAULT_SHARING_APPS)}
31
+
32
+
33
+ def _t(key: str, fallback: str) -> str:
34
+ return language_wrapper.language_word_dict.get(key, fallback)
35
+
36
+
37
+ def current_settings() -> Dict[str, Any]:
38
+ """目前的分享隱私設定(缺項目補預設值)。"""
39
+ stored = user_setting_dict.get(SETTING_KEY)
40
+ settings = dict(DEFAULT_SETTINGS)
41
+ if isinstance(stored, dict):
42
+ settings.update({key: stored[key] for key in DEFAULT_SETTINGS if key in stored})
43
+ settings["apps"] = parse_app_list(settings.get("apps")) or list(DEFAULT_SHARING_APPS)
44
+ return settings
45
+
46
+
47
+ class ScreenPrivacyDialog(QDialog):
48
+ """設定哪些程式算是「正在分享」,以及要不要自動隱藏。"""
49
+
50
+ def __init__(self, parent: Optional[QWidget] = None) -> None:
51
+ front_engine_logger.info("[ScreenPrivacyDialog] Init")
52
+ super().__init__(parent)
53
+ self.setWindowTitle(_t("screen_privacy_title", "Screen-sharing privacy"))
54
+ settings = current_settings()
55
+
56
+ self.enable_checkbox = QCheckBox(
57
+ _t("screen_privacy_enable", "Hide overlays while one of these apps is open"))
58
+ self.enable_checkbox.setChecked(bool(settings.get("enabled")))
59
+ self.apps_label = QLabel(_t("screen_privacy_apps_label", "Meeting and capture apps:"))
60
+ self.apps_edit = QLineEdit(", ".join(settings.get("apps", [])))
61
+ self.apps_edit.setPlaceholderText("zoom, teams, discord")
62
+ self.hint_label = QLabel(
63
+ _t("screen_privacy_hint",
64
+ "Matched against window titles, so a meeting held in a browser tab is caught "
65
+ "too. Your overlays stay on your own screen - only the capture is blank. "
66
+ "Masks stay visible, since covering something is what they are for.")
67
+ if capture_affinity.available() else
68
+ _t("screen_privacy_unsupported",
69
+ "Hiding overlays from screen capture is Windows only."))
70
+ self.hint_label.setWordWrap(True)
71
+
72
+ self.button_box = QDialogButtonBox(
73
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
74
+ self.button_box.accepted.connect(self.accept)
75
+ self.button_box.rejected.connect(self.reject)
76
+
77
+ layout = QGridLayout(self)
78
+ layout.addWidget(self.enable_checkbox, 0, 0, 1, 2)
79
+ layout.addWidget(self.apps_label, 1, 0, 1, 2)
80
+ layout.addWidget(self.apps_edit, 2, 0, 1, 2)
81
+ layout.addWidget(self.hint_label, 3, 0, 1, 2)
82
+ layout.addWidget(self.button_box, 4, 0, 1, 2)
83
+ for widget in (self.enable_checkbox, self.apps_edit):
84
+ widget.setEnabled(capture_affinity.available())
85
+
86
+ def settings(self) -> Dict[str, Any]:
87
+ """對話框上目前的設定。"""
88
+ return {
89
+ "enabled": self.enable_checkbox.isChecked(),
90
+ "apps": parse_app_list(self.apps_edit.text()) or list(DEFAULT_SHARING_APPS),
91
+ }
92
+
93
+ def accept(self) -> None:
94
+ settings = self.settings()
95
+ user_setting_dict[SETTING_KEY] = settings
96
+ write_user_setting()
97
+ front_engine_logger.info(f"[ScreenPrivacyDialog] saved | {settings}")
98
+ super().accept()
@@ -49,7 +49,9 @@ from frontengine.utils.logging.loggin_instance import front_engine_logger
49
49
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
50
50
  from frontengine.utils.plugins.plugin_loader import load_plugins
51
51
  from frontengine.utils.preset_schedule.preset_schedule_service import PresetScheduleService
52
+ from frontengine.ui.dialog.screen_privacy_dialog import current_settings as current_privacy_settings
52
53
  from frontengine.ui.dialog.smart_pause_dialog import current_rules
54
+ from frontengine.utils.screen_privacy.share_watch import ShareWatchService
53
55
  from frontengine.utils.app_profile.app_profile_service import AppProfileService
54
56
  from frontengine.utils.clipboard.clipboard_history import DEFAULT_LIMIT, ClipboardHistory
55
57
  from frontengine.utils.clipboard.clipboard_watcher import ClipboardWatcher
@@ -209,6 +211,12 @@ class FrontEngineMainUI(QMainWindow):
209
211
  )
210
212
  self.app_profile_service.start()
211
213
 
214
+ # 分享畫面時把覆蓋層藏出擷取結果(自己仍看得到)
215
+ # Hide the overlays from the capture while sharing; they stay on screen here.
216
+ self.share_watch_service = ShareWatchService(config_provider=current_privacy_settings)
217
+ self.share_watch_service.sharing_changed.connect(self._on_sharing_changed)
218
+ self.share_watch_service.start()
219
+
212
220
  # 使用時間統計:預設關閉,只寫本機檔案,使用者可隨時清除
213
221
  # Usage tracking: off by default, local file only, clearable at any time.
214
222
  self.usage_tracker = UsageTracker(JsonRepository(Path(getcwd()) / USAGE_FILE))
@@ -460,6 +468,22 @@ class FrontEngineMainUI(QMainWindow):
460
468
  self.control_center_ui.show_all()
461
469
  self._smart_pause_hid = False
462
470
 
471
+ def _on_sharing_changed(self, sharing: bool, match: str) -> None:
472
+ """
473
+ 偵測到會議程式開著/關掉時,自動把覆蓋層藏出擷取結果或放回去。
474
+ 遮蔽用的覆蓋層不受影響——它們本來就是要被看到的。
475
+ Hide the overlays from the capture when a meeting app appears, and put
476
+ them back when it goes. Masking overlays are left alone: being seen is
477
+ the whole point of them.
478
+ """
479
+ front_engine_logger.info(
480
+ f"[MainUI] screen sharing={sharing}" + (f" | {match}" if match else ""))
481
+ self.control_center_ui.set_hide_from_capture(sharing)
482
+ if sharing:
483
+ self._last_toast = show_toast(
484
+ language_wrapper.language_word_dict.get(
485
+ "screen_privacy_toast", "Overlays hidden from screen capture"))
486
+
463
487
  def _show_reminder(self, label: str) -> None:
464
488
  """提醒到期:在畫面上方顯示一張會自己消失的提示卡。"""
465
489
  front_engine_logger.info(f"[MainUI] reminder | {label}")
@@ -526,6 +550,8 @@ class FrontEngineMainUI(QMainWindow):
526
550
  self.tools_setting_ui.stop_camera()
527
551
  if getattr(self, "stream_setting_ui", None) is not None:
528
552
  self.stream_setting_ui.soundboard.stop_all()
553
+ if getattr(self, "share_watch_service", None) is not None:
554
+ self.share_watch_service.stop()
529
555
  if getattr(self, "reminder_service", None) is not None:
530
556
  self.reminder_service.stop()
531
557
  if getattr(self, "app_profile_service", None) is not None:
@@ -10,6 +10,7 @@ from frontengine.ui.dialog.app_profile_dialog import AppProfileDialog
10
10
  from frontengine.ui.dialog.clipboard_dialog import ClipboardDialog
11
11
  from frontengine.ui.dialog.hotkey_settings_dialog import HotkeySettingsDialog
12
12
  from frontengine.ui.dialog.reminder_dialog import ReminderDialog
13
+ from frontengine.ui.dialog.screen_privacy_dialog import ScreenPrivacyDialog
13
14
  from frontengine.ui.dialog.smart_pause_dialog import SmartPauseDialog
14
15
  from frontengine.ui.dialog.usage_report_dialog import UsageReportDialog
15
16
  from frontengine.user_setting.user_setting_file import (
@@ -87,6 +88,12 @@ def build_settings_menu(ui: "FrontEngineMainUI") -> None:
87
88
  menu.addAction(reminder_action)
88
89
  ui.reminder_action = reminder_action
89
90
 
91
+ privacy_action = QAction(
92
+ _t("settings_menu_screen_privacy", "Screen-sharing privacy..."), menu)
93
+ privacy_action.triggered.connect(lambda: _open_dialog(ui, ScreenPrivacyDialog))
94
+ menu.addAction(privacy_action)
95
+ ui.screen_privacy_action = privacy_action
96
+
90
97
  usage_action = QAction(_t("settings_menu_usage", "Screen time..."), menu)
91
98
  usage_action.triggered.connect(lambda: _open_usage_dialog(ui))
92
99
  menu.addAction(usage_action)
@@ -16,6 +16,7 @@ from frontengine.ui.page.web.web_setting_ui import WEBSettingUI
16
16
  from frontengine.user_setting.user_setting_file import clear_overlay_geometry
17
17
  from frontengine.utils.logging.loggin_instance import front_engine_logger
18
18
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
19
+ from frontengine.utils.screen_privacy import capture_affinity
19
20
  from frontengine.utils.power_mode.power_mode import (
20
21
  TIER_BALANCED, TIER_HIGH, TIER_SAVER, normalize_tier,
21
22
  )
@@ -94,6 +95,10 @@ class ControlCenterUI(QWidget):
94
95
  self._extra_overlay_sources: list = []
95
96
  self.low_power_button = self._create_button(
96
97
  "control_center_low_power_on", self.toggle_low_power)
98
+ self._hidden_from_capture = False
99
+ self.capture_button = self._create_button(
100
+ "control_center_hide_from_capture", self.toggle_hide_from_capture)
101
+ self.capture_button.setEnabled(capture_affinity.available())
97
102
  self.clear_all_button = self._create_button("control_center_close_all", self.clear_all)
98
103
 
99
104
  # 畫質檔位:比省電模式更細,一次套用到所有覆蓋層
@@ -135,8 +140,9 @@ class ControlCenterUI(QWidget):
135
140
  self.grid_layout.addWidget(self.low_power_button, 13, 0)
136
141
  self.grid_layout.addWidget(self.quality_label, 14, 0)
137
142
  self.grid_layout.addWidget(self.quality_combobox, 15, 0)
138
- self.grid_layout.addWidget(self.clear_all_button, 16, 0)
139
- self.grid_layout.addWidget(self.log_panel_scroll_area, 0, 1, 17, 1) # rowSpan covers every button
143
+ self.grid_layout.addWidget(self.capture_button, 16, 0)
144
+ self.grid_layout.addWidget(self.clear_all_button, 17, 0)
145
+ self.grid_layout.addWidget(self.log_panel_scroll_area, 0, 1, 18, 1) # rowSpan covers every button
140
146
  self.setLayout(self.grid_layout)
141
147
 
142
148
  # Redirect
@@ -353,6 +359,43 @@ class ControlCenterUI(QWidget):
353
359
  )
354
360
  )
355
361
 
362
+ def set_hide_from_capture(self, hidden: bool) -> int:
363
+ """
364
+ 分享畫面時把覆蓋層藏起來(自己看得到、對方看不到)。標了
365
+ keep_in_capture 的覆蓋層不會被藏——遮蔽層本來就是要被看到的。
366
+ Hide the overlays from screen capture: still visible here, blank for the
367
+ people you are sharing with. Overlays marked keep_in_capture are left
368
+ alone, since a mask is meant to be seen.
369
+ """
370
+ self._hidden_from_capture = bool(hidden)
371
+ applied = 0
372
+
373
+ def apply(widget) -> None:
374
+ nonlocal applied
375
+ if getattr(widget, "keep_in_capture", False):
376
+ capture_affinity.apply_to_widget(widget, False)
377
+ return
378
+ if capture_affinity.apply_to_widget(widget, self._hidden_from_capture):
379
+ applied += 1
380
+
381
+ self._for_each_overlay(apply)
382
+ front_engine_logger.info(
383
+ f"ControlCenterUI set_hide_from_capture | hidden={hidden}, applied={applied}")
384
+ self.capture_button.setText(
385
+ language_wrapper.language_word_dict.get(
386
+ "control_center_show_in_capture" if self._hidden_from_capture
387
+ else "control_center_hide_from_capture",
388
+ "Show in capture" if self._hidden_from_capture else "Hide from capture"))
389
+ return applied
390
+
391
+ def toggle_hide_from_capture(self) -> None:
392
+ """切換分享時是否隱藏覆蓋層。"""
393
+ self.set_hide_from_capture(not self._hidden_from_capture)
394
+
395
+ @property
396
+ def hidden_from_capture(self) -> bool:
397
+ return self._hidden_from_capture
398
+
356
399
  def toggle_low_power(self) -> None:
357
400
  """切換省電模式 / Toggle low-power mode."""
358
401
  self.set_low_power(not getattr(self, "_low_power", False))
@@ -54,6 +54,9 @@ user_setting_dict: Dict[str, Any] = {
54
54
  # Sticky note contents and positions, and whether to reopen them on launch.
55
55
  "sticky_notes": [],
56
56
  "sticky_notes_restore": False,
57
+ # 分享畫面時自動把覆蓋層藏出擷取結果(預設關閉)
58
+ # Hide overlays from the capture while sharing; off by default.
59
+ "screen_share_privacy": {"enabled": False, "apps": []},
57
60
  # 使用時間統計(只寫本機檔案、預設關閉)
58
61
  # Usage tracking: local file only, off by default.
59
62
  "usage_tracking": False,
@@ -542,4 +542,13 @@ english_word_dict = {
542
542
  "stream_obs_record": 'Toggle recording',
543
543
  "stream_obs_stream": 'Toggle streaming',
544
544
  "stream_hint": 'The crosshair is only drawn on top - no game is modified. OBS control uses its own WebSocket; the password is kept in your local settings.',
545
+ "control_center_hide_from_capture": 'Hide from capture',
546
+ "control_center_show_in_capture": 'Show in capture',
547
+ "settings_menu_screen_privacy": 'Screen-sharing privacy...',
548
+ "screen_privacy_title": 'Screen-sharing privacy',
549
+ "screen_privacy_enable": 'Hide overlays while one of these apps is open',
550
+ "screen_privacy_apps_label": 'Meeting and capture apps:',
551
+ "screen_privacy_hint": 'Matched against window titles, so a meeting held in a browser tab is caught too. Your overlays stay on your own screen - only the capture is blank. Masks stay visible, since covering something is what they are for.',
552
+ "screen_privacy_unsupported": 'Hiding overlays from screen capture is Windows only.',
553
+ "screen_privacy_toast": 'Overlays hidden from screen capture',
545
554
  }
@@ -541,4 +541,13 @@ traditional_chinese_word_dict = {
541
541
  "stream_obs_record": '切換錄影',
542
542
  "stream_obs_stream": '切換直播',
543
543
  "stream_hint": '準星只是疊在畫面上的一層,不會更動任何遊戲。OBS 控制走它自己的 WebSocket,密碼只留在本機設定檔裡。',
544
+ "control_center_hide_from_capture": '分享時隱藏',
545
+ "control_center_show_in_capture": '分享時顯示',
546
+ "settings_menu_screen_privacy": '螢幕分享隱私…',
547
+ "screen_privacy_title": '螢幕分享隱私',
548
+ "screen_privacy_enable": '這些程式開著時,把覆蓋層藏出擷取畫面',
549
+ "screen_privacy_apps_label": '會議與擷取程式:',
550
+ "screen_privacy_hint": '比對的是視窗標題,所以在瀏覽器分頁裡開的會議也抓得到。覆蓋層仍會顯示在你自己的螢幕上,只有擷取畫面那一份是空的。遮蔽層不會被藏起來——遮住東西正是它存在的理由。',
551
+ "screen_privacy_unsupported": '把覆蓋層藏出螢幕擷取僅支援 Windows。',
552
+ "screen_privacy_toast": '已把覆蓋層藏出螢幕擷取',
544
553
  }