single windowed overlay

parent 11b0ada8
...@@ -223,6 +223,12 @@ Examples: ...@@ -223,6 +223,12 @@ Examples:
help='RustDesk ID for periodic whoami API calls (enables /api/whoami endpoint)' help='RustDesk ID for periodic whoami API calls (enables /api/whoami endpoint)'
) )
parser.add_argument(
'--overlay-single',
action='store_true',
help='Enable single window overlay mode (overlay stacks on top of player widget instead of separate window)'
)
return parser.parse_args() return parser.parse_args()
def validate_arguments(args): def validate_arguments(args):
...@@ -292,6 +298,9 @@ def main(): ...@@ -292,6 +298,9 @@ def main():
# SSL settings # SSL settings
settings.web.enable_ssl = args.ssl settings.web.enable_ssl = args.ssl
# Overlay settings
settings.qt.overlay_single_window = args.overlay_single
if args.db_path: if args.db_path:
settings.database_path = args.db_path settings.database_path = args.db_path
......
...@@ -228,6 +228,7 @@ class QtConfig: ...@@ -228,6 +228,7 @@ class QtConfig:
overlay_enabled: bool = True overlay_enabled: bool = True
default_template: str = "news_template" default_template: str = "news_template"
overlay_opacity: float = 0.9 overlay_opacity: float = 0.9
overlay_single_window: bool = False
# Performance settings # Performance settings
hardware_acceleration: bool = True hardware_acceleration: bool = True
......
...@@ -138,6 +138,9 @@ class MbetterClientApplication: ...@@ -138,6 +138,9 @@ class MbetterClientApplication:
stored_settings.debug_overlay = self.settings.debug_overlay stored_settings.debug_overlay = self.settings.debug_overlay
stored_settings.debug_player = self.settings.debug_player stored_settings.debug_player = self.settings.debug_player
# Preserve command line overlay settings
stored_settings.qt.overlay_single_window = self.settings.qt.overlay_single_window
# Preserve command line SSL settings # Preserve command line SSL settings
stored_settings.web.enable_ssl = self.settings.web.enable_ssl stored_settings.web.enable_ssl = self.settings.web.enable_ssl
stored_settings.web.ssl_cert_path = self.settings.web.ssl_cert_path stored_settings.web.ssl_cert_path = self.settings.web.ssl_cert_path
......
...@@ -2026,9 +2026,45 @@ class PlayerWindow(QMainWindow): ...@@ -2026,9 +2026,45 @@ class PlayerWindow(QMainWindow):
self.video_widget = VideoWidget(parent=central_widget) self.video_widget = VideoWidget(parent=central_widget)
layout.addWidget(self.video_widget, 1) layout.addWidget(self.video_widget, 1)
# THREADING FIXED: Re-enable overlay system with proper Qt main thread architecture # Check if single window overlay mode is enabled
# Create overlay as SEPARATE TOP-LEVEL WINDOW if self.settings.overlay_single_window:
logger.info("=== CREATING OVERLAY WINDOW ===") # SINGLE WINDOW MODE: Overlay stacks on top of player widget using Qt widget transparency
logger.info("=== CREATING SINGLE WINDOW OVERLAY ===")
logger.info("Single window overlay mode enabled - overlay will stack on top of player widget")
# Create overlay widget directly in central layout (no separate window)
debug_overlay = getattr(self, 'debug_overlay', False)
web_server_url = self._get_web_server_base_url()
db_manager = self._get_database_manager()
self.window_overlay = OverlayWebView(central_widget, debug_overlay=debug_overlay, web_server_url=web_server_url, db_manager=db_manager)
logger.debug("PlayerWindow: Created QWebEngineView overlay as widget in central layout")
# Configure widget-level transparency for single window mode
import os
is_mesa = os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or \
os.environ.get('MESA_GL_VERSION_OVERRIDE') is not None
if is_mesa:
# Mesa compatibility - use composition-based transparency
self.window_overlay.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
self.window_overlay.setStyleSheet("background: rgba(0, 0, 0, 0.01);")
logger.info("Mesa single window overlay configured with composition transparency")
else:
# Standard hardware transparency
self.window_overlay.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
self.window_overlay.setStyleSheet("background: transparent;")
logger.info("Hardware single window overlay configured with native transparency")
# Add overlay widget to central layout on top of video widget
layout.addWidget(self.window_overlay, 1) # Give it stretch to fill available space
# No separate overlay window needed
self.overlay_window = None
logger.info("SINGLE WINDOW OVERLAY SYSTEM ENABLED: Overlay stacked on top of player widget")
else:
# MULTI-WINDOW MODE: Create overlay as SEPARATE TOP-LEVEL WINDOW (original behavior)
logger.info("=== CREATING MULTI-WINDOW OVERLAY ===")
self.overlay_window = QWidget() self.overlay_window = QWidget()
self.overlay_window.setWindowFlags( self.overlay_window.setWindowFlags(
Qt.WindowType.FramelessWindowHint | Qt.WindowType.FramelessWindowHint |
...@@ -2066,7 +2102,7 @@ class PlayerWindow(QMainWindow): ...@@ -2066,7 +2102,7 @@ class PlayerWindow(QMainWindow):
overlay_layout.setContentsMargins(0, 0, 0, 0) overlay_layout.setContentsMargins(0, 0, 0, 0)
overlay_layout.addWidget(self.window_overlay) overlay_layout.addWidget(self.window_overlay)
logger.info("OVERLAY SYSTEM RE-ENABLED: Threading conflicts resolved with Qt main thread architecture") logger.info("MULTI-WINDOW OVERLAY SYSTEM ENABLED: Separate overlay window created")
# Controls removed per user request - clean overlay-only interface # Controls removed per user request - clean overlay-only interface
self.controls = None self.controls = None
...@@ -2101,7 +2137,8 @@ class PlayerWindow(QMainWindow): ...@@ -2101,7 +2137,8 @@ class PlayerWindow(QMainWindow):
def _sync_overlay_position(self): def _sync_overlay_position(self):
"""Synchronize overlay window position with main player window""" """Synchronize overlay window position with main player window"""
try: try:
if hasattr(self, 'overlay_window') and self.overlay_window: # Only sync position if we have a separate overlay window (multi-window mode)
if hasattr(self, 'overlay_window') and self.overlay_window and not self.settings.overlay_single_window:
# Check video state during overlay positioning # Check video state during overlay positioning
if hasattr(self, 'media_player'): if hasattr(self, 'media_player'):
video_state = self.media_player.playbackState() video_state = self.media_player.playbackState()
...@@ -2137,6 +2174,15 @@ class PlayerWindow(QMainWindow): ...@@ -2137,6 +2174,15 @@ class PlayerWindow(QMainWindow):
logger.debug(f"Overlay window positioned at: {self.overlay_window.geometry()}") logger.debug(f"Overlay window positioned at: {self.overlay_window.geometry()}")
logger.debug(f"GREEN SCREEN DEBUG: Overlay window after sync - geometry: {self.overlay_window.geometry()}") logger.debug(f"GREEN SCREEN DEBUG: Overlay window after sync - geometry: {self.overlay_window.geometry()}")
elif self.settings.overlay_single_window:
# Single window mode - overlay is already part of the central widget, no positioning needed
logger.debug("Single window overlay mode - no position sync needed")
# Ensure overlay widget visibility in single window mode
if hasattr(self, 'window_overlay') and hasattr(self.window_overlay, 'isVisible'):
if not self.window_overlay.isVisible():
logger.debug("Ensuring single window overlay visibility")
self.window_overlay.show()
except Exception as e: except Exception as e:
if self.debug_overlay: if self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Overlay sync failed: {e}") logger.debug(f"GREEN SCREEN DEBUG: Overlay sync failed: {e}")
...@@ -2145,14 +2191,16 @@ class PlayerWindow(QMainWindow): ...@@ -2145,14 +2191,16 @@ class PlayerWindow(QMainWindow):
def resizeEvent(self, event): def resizeEvent(self, event):
"""Handle window resize events - sync overlay position""" """Handle window resize events - sync overlay position"""
super().resizeEvent(event) super().resizeEvent(event)
if hasattr(self, 'overlay_window') and self.overlay_window: # Only sync overlay position in multi-window mode
if hasattr(self, 'overlay_window') and self.overlay_window and not self.settings.overlay_single_window:
# Delay sync to allow window to finish resizing # Delay sync to allow window to finish resizing
QTimer.singleShot(10, self._sync_overlay_position) QTimer.singleShot(10, self._sync_overlay_position)
def moveEvent(self, event): def moveEvent(self, event):
"""Handle window move events - sync overlay position""" """Handle window move events - sync overlay position"""
super().moveEvent(event) super().moveEvent(event)
if hasattr(self, 'overlay_window') and self.overlay_window: # Only sync overlay position in multi-window mode
if hasattr(self, 'overlay_window') and self.overlay_window and not self.settings.overlay_single_window:
# Delay sync to allow window to finish moving # Delay sync to allow window to finish moving
QTimer.singleShot(10, self._sync_overlay_position) QTimer.singleShot(10, self._sync_overlay_position)
...@@ -2320,8 +2368,8 @@ class PlayerWindow(QMainWindow): ...@@ -2320,8 +2368,8 @@ class PlayerWindow(QMainWindow):
logger.info(f"Video widget size: {self.video_widget.get_video_widget().size()}") logger.info(f"Video widget size: {self.video_widget.get_video_widget().size()}")
logger.info(f"Window visible: {self.isVisible()}") logger.info(f"Window visible: {self.isVisible()}")
logger.info(f"Window size: {self.size()}") logger.info(f"Window size: {self.size()}")
logger.info(f"Overlay window visible: {hasattr(self, 'overlay_window') and self.overlay_window.isVisible()}") logger.info(f"Overlay window visible: {hasattr(self, 'overlay_window') and self.overlay_window and self.overlay_window.isVisible() if not self.settings.overlay_single_window else 'N/A (single window mode)'}")
logger.info(f"Overlay window size: {hasattr(self, 'overlay_window') and self.overlay_window.size()}") logger.info(f"Overlay window size: {hasattr(self, 'overlay_window') and self.overlay_window and self.overlay_window.size() if not self.settings.overlay_single_window else 'N/A (single window mode)'}")
# Process loop control parameters # Process loop control parameters
if loop_data: if loop_data:
...@@ -2843,10 +2891,12 @@ class PlayerWindow(QMainWindow): ...@@ -2843,10 +2891,12 @@ class PlayerWindow(QMainWindow):
self.template_rotation_timer.stop() self.template_rotation_timer.stop()
logger.debug("Template rotation timer stopped on window close") logger.debug("Template rotation timer stopped on window close")
# Close overlay window # Close overlay window (only in multi-window mode)
if hasattr(self, 'overlay_window') and self.overlay_window: if hasattr(self, 'overlay_window') and self.overlay_window and not self.settings.overlay_single_window:
self.overlay_window.close() self.overlay_window.close()
logger.debug("Overlay window closed") logger.debug("Overlay window closed")
elif self.settings.overlay_single_window:
logger.debug("Single window overlay mode - no separate overlay window to close")
logger.info("Player window closing - Qt will handle application exit") logger.info("Player window closing - Qt will handle application exit")
event.accept() event.accept()
...@@ -2979,8 +3029,8 @@ class PlayerWindow(QMainWindow): ...@@ -2979,8 +3029,8 @@ class PlayerWindow(QMainWindow):
if self.debug_overlay: if self.debug_overlay:
logger.debug(f"GREEN SCREEN FIX: WebEngine overlay visibility forced during update") logger.debug(f"GREEN SCREEN FIX: WebEngine overlay visibility forced during update")
# Also ensure parent overlay window is visible # Also ensure parent overlay window is visible (only in multi-window mode)
if hasattr(self, 'overlay_window') and self.overlay_window and not self.overlay_window.isVisible(): if hasattr(self, 'overlay_window') and self.overlay_window and not self.settings.overlay_single_window and not self.overlay_window.isVisible():
if self.debug_overlay: if self.debug_overlay:
logger.debug(f"GREEN SCREEN FIX: Parent overlay window not visible, forcing visibility") logger.debug(f"GREEN SCREEN FIX: Parent overlay window not visible, forcing visibility")
self.overlay_window.show() self.overlay_window.show()
...@@ -3808,10 +3858,12 @@ class QtVideoPlayer(QObject): ...@@ -3808,10 +3858,12 @@ class QtVideoPlayer(QObject):
logger.debug(f"GREEN SCREEN DEBUG: Video position: {self.window.media_player.position()}") logger.debug(f"GREEN SCREEN DEBUG: Video position: {self.window.media_player.position()}")
logger.debug(f"GREEN SCREEN DEBUG: Video duration: {self.window.media_player.duration()}") logger.debug(f"GREEN SCREEN DEBUG: Video duration: {self.window.media_player.duration()}")
# Check overlay window transparency state # Check overlay window transparency state (only in multi-window mode)
if hasattr(self.window, 'overlay_window') and self.debug_overlay: if hasattr(self.window, 'overlay_window') and self.window.overlay_window and not self.window.settings.overlay_single_window and self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Overlay window geometry: {self.window.overlay_window.geometry()}") logger.debug(f"GREEN SCREEN DEBUG: Overlay window geometry: {self.window.overlay_window.geometry()}")
logger.debug(f"GREEN SCREEN DEBUG: Overlay window visible: {self.window.overlay_window.isVisible()}") logger.debug(f"GREEN SCREEN DEBUG: Overlay window visible: {self.window.overlay_window.isVisible()}")
elif self.window.settings.overlay_single_window and self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Single window overlay mode - no separate overlay window")
# CRITICAL FIX: Protect video context during template changes # CRITICAL FIX: Protect video context during template changes
video_widget = None video_widget = None
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment