105 lines
3.0 KiB
Go
105 lines
3.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package wine
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
)
|
|
|
|
func program_name(commandLine string) string {
|
|
name := strings.SplitN(commandLine, "\x00", 2)[0]
|
|
return filepath.Base(strings.ReplaceAll(strings.ToLower(strings.TrimSpace(name)), "\\", "/"))
|
|
}
|
|
|
|
func wine_loader(exe, configured string) string {
|
|
if executable(configured) {
|
|
// Protontricks' proxy requires its own runtime environment. Join the
|
|
// existing server with the actual Proton loader instead.
|
|
file, err := os.Open(configured)
|
|
if err == nil {
|
|
data := make([]byte, 8192)
|
|
count, _ := file.Read(data)
|
|
file.Close()
|
|
if !bytes.Contains(data[:count], []byte("PROTONTRICKS_STEAM_RUNTIME")) {
|
|
return configured
|
|
}
|
|
}
|
|
}
|
|
directory := filepath.Dir(exe)
|
|
for _, name := range []string{strings.TrimSuffix(filepath.Base(exe), "-preloader"), "wine"} {
|
|
candidate := filepath.Join(directory, name)
|
|
if executable(candidate) {
|
|
return candidate
|
|
}
|
|
}
|
|
for range 5 {
|
|
parent := filepath.Dir(directory)
|
|
if parent == directory || parent == string(filepath.Separator) {
|
|
break
|
|
}
|
|
directory = parent
|
|
for _, name := range []string{"wine", "wine64"} {
|
|
candidate := filepath.Join(directory, "bin", name)
|
|
if executable(candidate) {
|
|
return candidate
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// wholesale_controller_only reports whether the whole helper must run
|
|
// without dialog reading. Only the global controller-only switch and the
|
|
// environment override select it here; a helper binary remembered as
|
|
// unconfirmed (see Manager.helper_unconfirmed) is the third trigger. A
|
|
// listed program by itself never selects wholesale mode: the helper gates
|
|
// it natively while other programs in the prefix keep dialog reading.
|
|
func wholesale_controller_only(settings Settings) bool {
|
|
if settings.ControllerOnly {
|
|
return true
|
|
}
|
|
override := os.Getenv("WINE_ACCESS_NO_DIALOG_READER")
|
|
return override != "" && override != "0"
|
|
}
|
|
|
|
// exclusion_names returns the hook-exclusion program names exactly as the
|
|
// helper will store them: validated at the process boundary, lowercased,
|
|
// deduplicated case-insensitively, and sorted. The validation mirrors the
|
|
// helper's own rules so the count sent here matches the count its ready
|
|
// record reports.
|
|
func exclusion_names(settings Settings) []string {
|
|
seen := make(map[string]bool, len(settings.ExcludedPrograms))
|
|
var names []string
|
|
for _, name := range settings.ExcludedPrograms {
|
|
if !config.ValidWineProgramName(name) {
|
|
continue
|
|
}
|
|
lower := strings.ToLower(name)
|
|
if seen[lower] {
|
|
continue
|
|
}
|
|
seen[lower] = true
|
|
names = append(names, lower)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|
|
|
|
func helper_arguments(controllerOnly bool, settings Settings) []string {
|
|
var arguments []string
|
|
for _, name := range exclusion_names(settings) {
|
|
arguments = append(arguments, "--exclude-hook-program", name)
|
|
}
|
|
if controllerOnly {
|
|
arguments = append(arguments, "--no-dialog-reader")
|
|
}
|
|
return arguments
|
|
}
|