From 1b5c86a1f4dafac1c866ba28a40533e95125be5e Mon Sep 17 00:00:00 2001 From: AmN <16545063+amnweb@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:53:52 +0100 Subject: [PATCH 1/2] feat(fullscreen-detection): add fullscreen detection to prevent false triggers - Implemented `is_window_fullscreen` function to check if a window covers the entire monitor. --- src/core/bar_helper.py | 4 +++- src/core/utils/win32/utilities.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/bar_helper.py b/src/core/bar_helper.py index 37f882e9c..2f820fa9f 100644 --- a/src/core/bar_helper.py +++ b/src/core/bar_helper.py @@ -37,7 +37,7 @@ from core.utils.win32.bindings import SetWindowPos from core.utils.win32.bindings.user32 import KillTimer, RegisterWindowMessage, SetTimer, user32 from core.utils.win32.structs import MSG -from core.utils.win32.utilities import apply_qmenu_style +from core.utils.win32.utilities import apply_qmenu_style, is_window_fullscreen # Register TaskbarCreated message to detect Explorer restarts WM_TASKBARCREATED = RegisterWindowMessage("TaskbarCreated") @@ -551,6 +551,8 @@ def _is_foreground_excluded(self) -> bool: self.EXCLUDED_WINDOW_CLASS_SUFFIXES ): return True + if not is_window_fullscreen(hwnd): + return True except Exception: pass return False diff --git a/src/core/utils/win32/utilities.py b/src/core/utils/win32/utilities.py index e01abbe40..c04d4bda3 100644 --- a/src/core/utils/win32/utilities.py +++ b/src/core/utils/win32/utilities.py @@ -281,6 +281,14 @@ def is_window_maximized(hwnd: int) -> bool: return window_placement[1] == SW_MAXIMIZE +def is_window_fullscreen(hwnd: int) -> bool: + """Check if a window covers the entire monitor it is on.""" + rect = GetWindowRect(hwnd) + monitor_info = GetMonitorInfo(int(MonitorFromWindow(hwnd))) + mon = monitor_info["Monitor"] # (left, top, right, bottom) + return rect[0] <= mon[0] and rect[1] <= mon[1] and rect[2] >= mon[2] and rect[3] >= mon[3] + + def get_hwnd_info(hwnd: int) -> dict: with suppress(Exception): monitor_hwnd = get_monitor_hwnd(hwnd) From 5b8253a29cfaff18973a33c954520bbb55e7e1a3 Mon Sep 17 00:00:00 2001 From: AmN <16545063+amnweb@users.noreply.github.com> Date: Mon, 9 Feb 2026 06:32:24 +0100 Subject: [PATCH 2/2] feat(fullscreen-detection): enhance fullscreen detection to prevent false triggers - Added a new function to check if any visible window from the same process is fullscreen. - Updated the logic in AppBarManager to differentiate between maximized and true fullscreen windows. - Improved handling of fullscreen detection for applications like Firefox using separate child windows for HTML5 video. --- src/core/bar_helper.py | 20 +++++++++++-- src/core/utils/win32/utilities.py | 50 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/core/bar_helper.py b/src/core/bar_helper.py index 2f820fa9f..d4a587d31 100644 --- a/src/core/bar_helper.py +++ b/src/core/bar_helper.py @@ -37,7 +37,12 @@ from core.utils.win32.bindings import SetWindowPos from core.utils.win32.bindings.user32 import KillTimer, RegisterWindowMessage, SetTimer, user32 from core.utils.win32.structs import MSG -from core.utils.win32.utilities import apply_qmenu_style, is_window_fullscreen +from core.utils.win32.utilities import ( + apply_qmenu_style, + is_process_fullscreen_on_monitor, + is_window_fullscreen, + is_window_maximized, +) # Register TaskbarCreated message to detect Explorer restarts WM_TASKBARCREATED = RegisterWindowMessage("TaskbarCreated") @@ -551,7 +556,18 @@ def _is_foreground_excluded(self) -> bool: self.EXCLUDED_WINDOW_CLASS_SUFFIXES ): return True - if not is_window_fullscreen(hwnd): + if is_window_fullscreen(hwnd): + if is_window_maximized(hwnd): + # Maximized windows cover the screen but are not fullscreen apps + return True + # True fullscreen (not maximized) allow hiding the bar + return False + # Foreground not fullscreen check if a sibling window from the + # same process is fullscreen (e.g. Firefox HTML5 fullscreen video) + process_fs = is_process_fullscreen_on_monitor( + hwnd, self.EXCLUDED_WINDOW_CLASSES, self.EXCLUDED_WINDOW_CLASS_SUFFIXES + ) + if not process_fs: return True except Exception: pass diff --git a/src/core/utils/win32/utilities.py b/src/core/utils/win32/utilities.py index c04d4bda3..c4cb00a35 100644 --- a/src/core/utils/win32/utilities.py +++ b/src/core/utils/win32/utilities.py @@ -7,6 +7,7 @@ import win32api import win32gui +import win32process from PyQt6.QtGui import QCursor from PyQt6.QtWidgets import QApplication, QWidget from win32api import GetMonitorInfo, MonitorFromWindow @@ -283,12 +284,61 @@ def is_window_maximized(hwnd: int) -> bool: def is_window_fullscreen(hwnd: int) -> bool: """Check if a window covers the entire monitor it is on.""" + if win32gui.IsIconic(hwnd): + return False rect = GetWindowRect(hwnd) monitor_info = GetMonitorInfo(int(MonitorFromWindow(hwnd))) mon = monitor_info["Monitor"] # (left, top, right, bottom) return rect[0] <= mon[0] and rect[1] <= mon[1] and rect[2] >= mon[2] and rect[3] >= mon[3] +def is_process_fullscreen_on_monitor( + hwnd: int, + excluded_classes: set[str] | None = None, + excluded_suffixes: tuple[str, ...] | None = None, +) -> bool: + """Check if any visible window belonging to the same process as hwnd + is fullscreen on the same monitor. This should handles apps like Firefox + that use a separate child window for HTML5 fullscreen video.""" + try: + _, target_pid = win32process.GetWindowThreadProcessId(hwnd) + if target_pid == 0: + return False + + target_monitor = int(MonitorFromWindow(hwnd)) + _excluded_classes = excluded_classes or set() + _excluded_suffixes = excluded_suffixes or () + found = False + + def _enum_cb(candidate_hwnd, _): + nonlocal found + try: + if not win32gui.IsWindowVisible(candidate_hwnd): + return True + _, c_pid = win32process.GetWindowThreadProcessId(candidate_hwnd) + if c_pid != target_pid: + return True + cls = GetClassName(candidate_hwnd) + if cls in _excluded_classes or cls.endswith(_excluded_suffixes): + return True + if int(MonitorFromWindow(candidate_hwnd)) != target_monitor: + return True + if is_window_fullscreen(candidate_hwnd): + found = True + return False + except Exception: + pass + return True + + try: + win32gui.EnumWindows(_enum_cb, 0) + except Exception: + pass + return found + except Exception: + return False + + def get_hwnd_info(hwnd: int) -> dict: with suppress(Exception): monitor_hwnd = get_monitor_hwnd(hwnd)