from __future__ import annotations import ctypes from queue import Queue from r36s_dlna_browser.dlna.browser_state import BrowserState from r36s_dlna_browser.platform.controls import Action from r36s_dlna_browser.ui import theme from r36s_dlna_browser.ui import sdl_app as sdl_app_module from r36s_dlna_browser.ui.sdl_app import SDLApp, Screen class DummyPlayer: def __init__(self) -> None: self._new_frame = False self.viewport = None def attach_window(self, _window) -> None: pass def set_viewport(self, *args) -> None: self.viewport = args def has_new_frame(self) -> bool: return self._new_frame def render(self, _renderer) -> bool: self._new_frame = False return True def _make_app() -> SDLApp: return SDLApp(Queue(), Queue(), BrowserState(), DummyPlayer()) def test_non_playback_draws_only_when_dirty() -> None: app = _make_app() app._screen = Screen.SERVERS app._needs_redraw = False assert app._should_draw() is False app._mark_dirty() assert app._should_draw() is True def test_playback_snapshot_change_waits_for_refresh_interval(monkeypatch) -> None: app = _make_app() app._screen = Screen.PLAYBACK app._needs_redraw = False app._last_playback_snapshot = app._playback_snapshot() app._last_playback_draw_at = 10.0 app._state.playback_position = 1.0 monkeypatch.setattr(sdl_app_module.time, "monotonic", lambda: 10.1) assert app._should_draw() is False def test_playback_snapshot_change_redraws_after_refresh_interval(monkeypatch) -> None: app = _make_app() app._screen = Screen.PLAYBACK app._needs_redraw = False app._last_playback_snapshot = app._playback_snapshot() app._last_playback_draw_at = 10.0 app._state.playback_position = 1.0 monkeypatch.setattr(sdl_app_module.time, "monotonic", lambda: 10.3) assert app._should_draw() is True def test_playback_snapshot_without_changes_does_not_redraw(monkeypatch) -> None: app = _make_app() app._screen = Screen.PLAYBACK app._needs_redraw = False app._last_playback_snapshot = app._playback_snapshot() app._last_playback_draw_at = 10.0 monkeypatch.setattr(sdl_app_module.time, "monotonic", lambda: 10.5) assert app._should_draw() is False def test_playback_draws_immediately_for_new_video_frame() -> None: app = _make_app() app._screen = Screen.PLAYBACK app._needs_redraw = False app._player._new_frame = True assert app._should_draw() is True def test_hud_mode_cycles_auto_fixed_hidden(monkeypatch) -> None: app = _make_app() app._screen = Screen.PLAYBACK monkeypatch.setattr(sdl_app_module.time, "monotonic", lambda: 12.0) app._handle_action(Action.HUD_MODE) assert app._state.playback_hud_mode == theme.PLAYBACK_HUD_PINNED assert app._state.playback_hud_visible is True app._handle_action(Action.HUD_MODE) assert app._state.playback_hud_mode == theme.PLAYBACK_HUD_HIDDEN assert app._state.playback_hud_visible is False app._handle_action(Action.HUD_MODE) assert app._state.playback_hud_mode == theme.PLAYBACK_HUD_AUTO assert app._state.playback_hud_visible is True def test_paused_playback_keeps_hud_visible_in_auto_mode(monkeypatch) -> None: app = _make_app() app._screen = Screen.PLAYBACK app._state.playback_hud_mode = theme.PLAYBACK_HUD_AUTO app._state.playback_hud_visible = False app._state.playback_paused = True monkeypatch.setattr(sdl_app_module.time, "monotonic", lambda: 99.0) app._refresh_playback_hud_visibility() assert app._state.playback_hud_visible is True def test_layout_scales_for_square_display() -> None: compact = theme.get_layout(640, 480) square = theme.get_layout(720, 720) assert square.visible_items > compact.visible_items assert square.list_item_h > compact.list_item_h assert square.playback_hud_top > compact.playback_hud_top def test_sync_player_viewport_uses_runtime_layout(monkeypatch) -> None: app = _make_app() app._window = object() def fake_get_window_size(_window, width_ptr, height_ptr): width = ctypes.cast(width_ptr, ctypes.POINTER(ctypes.c_int)) height = ctypes.cast(height_ptr, ctypes.POINTER(ctypes.c_int)) width.contents.value = 720 height.contents.value = 720 monkeypatch.setattr(sdl_app_module.sdl2, "SDL_GetWindowSize", fake_get_window_size) app._sync_player_viewport() layout = app._layout assert layout.width == 720 assert layout.height == 720 assert app._player.viewport == ( 720, 720, layout.playback_hud_top, 720 - layout.playback_video_bottom, layout.playback_video_left, 720 - layout.playback_video_right, )