Files

56 lines
2.3 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package atspi
import (
"math"
"git.stormux.org/storm/cthulhu/internal/platform/xorg"
)
// Epoch changes with the foreground client, including departure to an empty
// workspace. Moving/resizing the same client does not reset reading ownership.
type ForegroundState struct {
Client xorg.Foreground
Epoch uint64
}
func foregroundWindowMatches(client xorg.Foreground, pid int, bounds Rectangle) bool {
if client.PID > 0 && pid > 0 && client.PID != pid {
return false
}
if !bounds.Valid() || client.Width <= 0 || client.Height <= 0 {
return false
}
inner := Rectangle{X: int32(client.X), Y: int32(client.Y), Width: int32(client.Width), Height: int32(client.Height)}
outer := Rectangle{X: int32(client.OuterX), Y: int32(client.OuterY), Width: int32(client.OuterWidth), Height: int32(client.OuterHeight)}
return bounds == inner || bounds == outer ||
scaledWindowMatches(bounds, inner) || scaledWindowMatches(bounds, outer)
}
// Some Chromium windows report AT-SPI extents in logical pixels while X11
// reports physical pixels. Require the same origin and proportional dimensions
// so a smaller dialog or unrelated sibling cannot claim the foreground client.
func scaledWindowMatches(accessible, xWindow Rectangle) bool {
if !accessible.Valid() || !xWindow.Valid() || accessible.X != xWindow.X || accessible.Y != xWindow.Y {
return false
}
scaleX := float64(xWindow.Width) / float64(accessible.Width)
scaleY := float64(xWindow.Height) / float64(accessible.Height)
if scaleX < 0.5 || scaleX > 3 || scaleY < 0.5 || scaleY > 3 {
return false
}
return math.Abs(scaleX-scaleY) <= 0.01*math.Max(scaleX, scaleY)
}
// A menu popup remains within the foreground application's AT-SPI bus even
// when its own accessible frame does not match the X11 foreground client's
// bounds. The caller must confirm an active menu window or a focused menu item.
func foregroundMenuPopupMatches(client xorg.Foreground, foregroundWindow, popupWindow ObjectID, pid int, menuContext bool) bool {
return menuContext && client.Window != 0 && foregroundWindow.Valid() && popupWindow.Valid() &&
foregroundWindow != popupWindow && foregroundWindow.BusName == popupWindow.BusName &&
(client.PID <= 0 || pid <= 0 || client.PID == pid)
}