Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions src/player/video_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def __init__(self, *args, **kwargs):
# Handler should be created after everything initialized
self.active_handler, self.window_handler = None, None
self.is_any_maximized, self.is_any_fullscreen = False, False
self.monitor_states = {} # Per-monitor maximized/fullscreen state
self.is_paused_by_user = False

def new_window(self, gdk_monitor):
Expand All @@ -337,14 +338,25 @@ def _on_active_changed(self, active):
self.pause_playback()

def _on_window_state_changed(self, state):
self.is_any_maximized, self.is_any_fullscreen = state["is_any_maximized"], state["is_any_fullscreen"]
logger.info(f"is_any_maximized: {self.is_any_maximized}, is_any_fullscreen: {self.is_any_fullscreen}")
self.is_any_maximized = state["is_any_maximized"]
self.is_any_fullscreen = state["is_any_fullscreen"]
self.monitor_states = state.get("monitor_states", {})
logger.info(f"Window state changed: {state}")

if self.config[CONFIG_KEY_PAUSE_WHEN_MAXIMIZED]:
if self._should_playback_start():
self.start_playback()
else:
self.pause_playback()
# Per-monitor pause/play logic
for monitor, window in self.windows.items():
monitor_name = monitor.get_model()
mon_state = self.monitor_states.get(monitor_name, {})

if mon_state.get("maximized") or mon_state.get("fullscreen"):
window.pause_fade(fade_duration_sec=self.config[CONFIG_KEY_FADE_DURATION_SEC],
fade_interval=self.config[CONFIG_KEY_FADE_INTERVAL])
else:
if not self.is_paused_by_user:
window.play_fade(target=self.volume if monitor.is_primary() else 0,
fade_duration_sec=self.config[CONFIG_KEY_FADE_DURATION_SEC],
fade_interval=self.config[CONFIG_KEY_FADE_INTERVAL])
elif self.config[CONFIG_KEY_MUTE_WHEN_MAXIMIZED]:
for monitor, window in self.windows.items():
if not monitor.is_primary():
Expand All @@ -356,11 +368,16 @@ def _on_window_state_changed(self, state):
window.volume_fade(target=self.volume, fade_duration_sec=self.config[CONFIG_KEY_FADE_DURATION_SEC],
fade_interval=self.config[CONFIG_KEY_FADE_INTERVAL])

def _should_playback_start(self):
if self.config[CONFIG_KEY_PAUSE_WHEN_MAXIMIZED] and (self.is_any_maximized or self.is_any_fullscreen):
return False
def _should_playback_start(self, monitor_name=None):
"""Check if playback should start. If monitor_name given, check that specific monitor."""
if self.is_paused_by_user:
return False
if self.config[CONFIG_KEY_PAUSE_WHEN_MAXIMIZED]:
if monitor_name and hasattr(self, 'monitor_states'):
mon_state = self.monitor_states.get(monitor_name, {})
return not (mon_state.get("maximized") or mon_state.get("fullscreen"))
# Fallback to global check
return not (self.is_any_maximized or self.is_any_fullscreen)
return True

@property
Expand Down
68 changes: 53 additions & 15 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import gi
gi.require_version("Wnck", "3.0")
from gi.repository import Gio, GLib, Wnck # , Gdk
from gi.repository import Gio, GLib, Wnck, Gdk

import pydbus

Expand Down Expand Up @@ -287,6 +287,9 @@ def __init__(self, on_window_state_changed: callable):
self.signal_handlers = []
self.window_signal_handlers = {}

# Cache monitor geometries for window-to-monitor matching
self.monitor_geometries = self._get_monitor_geometries()

# Connect screen signals and store handler IDs
handler_id = self.screen.connect("window-opened", self.window_opened, None)
self.signal_handlers.append((self.screen, handler_id))
Expand All @@ -305,6 +308,30 @@ def __init__(self, on_window_state_changed: callable):
# Initial check
self.eval()

def _get_monitor_geometries(self):
"""Get all monitor geometries as dict {name: (x, y, width, height)}"""
display = Gdk.Display.get_default()
geometries = {}
for i in range(display.get_n_monitors()):
monitor = display.get_monitor(i)
rect = monitor.get_geometry()
geometries[monitor.get_model()] = (rect.x, rect.y, rect.width, rect.height)
return geometries

def _get_window_monitor(self, window):
"""Determine which monitor a window is on based on its center point"""
geo = window.get_geometry()
if geo is None:
return None

center_x = geo[0] + geo[2] // 2
center_y = geo[1] + geo[3] // 2

for name, (mx, my, mw, mh) in self.monitor_geometries.items():
if mx <= center_x < mx + mw and my <= center_y < my + mh:
return name
return None

def _connect_window(self, window):
"""Connect to a window and store the handler ID"""
if window not in self.window_signal_handlers:
Expand All @@ -315,31 +342,42 @@ def window_opened(self, screen, window, _):
self._connect_window(window)

def eval(self, *args):
# TODO: #28 (Wallpaper stops animating on other monitor when app maximized on other)
is_changed = False

is_any_maximized, is_any_fullscreen = False, False
monitor_states = {name: {"maximized": False, "fullscreen": False}
for name in self.monitor_geometries}

for window in self.screen.get_windows():
base_state = not Wnck.Window.is_minimized(window) and \
Wnck.Window.is_on_workspace(
window, self.screen.get_active_workspace())
window_name, is_maximized, is_fullscreen = window.get_name(), \
Wnck.Window.is_maximized(window) and base_state, \
Wnck.Window.is_fullscreen(window) and base_state
if is_maximized is True:
is_any_maximized = True
if is_fullscreen is True:
is_any_fullscreen = True

cur_state = {"is_any_maximized": is_any_maximized,
"is_any_fullscreen": is_any_fullscreen}

is_maximized = Wnck.Window.is_maximized(window) and base_state
is_fullscreen = Wnck.Window.is_fullscreen(window) and base_state

if is_maximized or is_fullscreen:
# Find which monitor this window is on
monitor_name = self._get_window_monitor(window)
if monitor_name and monitor_name in monitor_states:
if is_maximized:
monitor_states[monitor_name]["maximized"] = True
if is_fullscreen:
monitor_states[monitor_name]["fullscreen"] = True

is_any_maximized = any(s["maximized"] for s in monitor_states.values())
is_any_fullscreen = any(s["fullscreen"] for s in monitor_states.values())

cur_state = {
"is_any_maximized": is_any_maximized,
"is_any_fullscreen": is_any_fullscreen,
"monitor_states": monitor_states
}
if self.prev_state is None or self.prev_state != cur_state:
is_changed = True
self.prev_state = cur_state

if is_changed:
self.on_window_state_changed(
{"is_any_maximized": is_any_maximized, "is_any_fullscreen": is_any_fullscreen})
self.on_window_state_changed(cur_state)
logger.debug(f"[WindowHandler] {cur_state}")

def cleanup(self):
Expand Down