101 lines
2.8 KiB
Go
101 lines
2.8 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
type flatpakInstance struct {
|
|
application, instance string
|
|
}
|
|
|
|
// Flatpak gives its D-Bus proxy and application the same /.flatpak-info.
|
|
// Compare a running instance, never just an application ID: two instances of
|
|
// the same application must not acquire one another's foreground window.
|
|
func readFlatpakInstance(procRoot string, pid int) flatpakInstance {
|
|
if pid <= 0 {
|
|
return flatpakInstance{}
|
|
}
|
|
file, err := os.OpenFile(fmt.Sprintf("%s/%d/root/.flatpak-info", procRoot, pid), os.O_RDONLY|syscall.O_NONBLOCK, 0)
|
|
if err != nil {
|
|
return flatpakInstance{}
|
|
}
|
|
defer file.Close()
|
|
const limit = 64 * 1024
|
|
stat, err := file.Stat()
|
|
if err != nil || !stat.Mode().IsRegular() || stat.Size() > limit {
|
|
return flatpakInstance{}
|
|
}
|
|
data, err := io.ReadAll(io.LimitReader(file, limit+1))
|
|
if err != nil || len(data) > limit {
|
|
return flatpakInstance{}
|
|
}
|
|
var result flatpakInstance
|
|
var section string
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
|
section = line
|
|
continue
|
|
}
|
|
key, value, ok := strings.Cut(line, "=")
|
|
if !ok {
|
|
return flatpakInstance{}
|
|
}
|
|
key, value = strings.TrimSpace(key), strings.TrimSpace(value)
|
|
if section == "[Application]" && key == "name" {
|
|
if result.application != "" {
|
|
return flatpakInstance{}
|
|
}
|
|
result.application = value
|
|
}
|
|
if section == "[Instance]" && key == "instance-id" {
|
|
if result.instance != "" {
|
|
return flatpakInstance{}
|
|
}
|
|
result.instance = value
|
|
}
|
|
}
|
|
if result.application == "" || result.instance == "" {
|
|
return flatpakInstance{}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func sameFlatpakInstance(procRoot string, first, second int) bool {
|
|
identity := readFlatpakInstance(procRoot, first)
|
|
return identity != (flatpakInstance{}) && identity == readFlatpakInstance(procRoot, second)
|
|
}
|
|
|
|
// Owned by the GLib thread and cleared with the foreground client. This only
|
|
// resolves process identity; fresh window state and geometry still decide
|
|
// whether an accessible can own focus.
|
|
func (adapter *Adapter) foregroundProcessID(pid int) int {
|
|
client := adapter.Foreground().Client
|
|
if client.Window == 0 || client.PID <= 0 || pid <= 0 || pid == client.PID {
|
|
return pid
|
|
}
|
|
matched, known := adapter.foregroundProcesses[pid]
|
|
if !known {
|
|
matched = sameFlatpakInstance("/proc", client.PID, pid)
|
|
if adapter.foregroundProcesses == nil || len(adapter.foregroundProcesses) >= 128 {
|
|
adapter.foregroundProcesses = make(map[int]bool)
|
|
}
|
|
adapter.foregroundProcesses[pid] = matched
|
|
}
|
|
if matched {
|
|
return client.PID
|
|
}
|
|
return pid
|
|
}
|