mirror of
https://github.com/jahruz67/wisp-open.git
synced 2026-08-08 18:14:08 +00:00
- Add platform notes for tray startup, shortcut saving, and Linux-only settings - Handle stat error in processRecording (missing file) - Select correct binary name based on OS in updater - Increase tray icon canvas size from 64 to 128 pixels - Update frontend dist asset hash
187 lines
4.6 KiB
Go
187 lines
4.6 KiB
Go
//go:build linux
|
|
|
|
// ============================================================
|
|
// LINUX-ONLY FILE — Linux startup (autostart) management via
|
|
// XDG Desktop Entry files (.desktop). Creates/removes autostart
|
|
// entries and manages the application's .desktop file.
|
|
// The Windows equivalent is in internal/windows/
|
|
// ============================================================
|
|
|
|
package linux
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
const appName = "wis-free-v3"
|
|
|
|
// desktopFileTemplate is filled with execLine built from the absolute binary path.
|
|
// Paths with spaces must be quoted per the Desktop Entry spec.
|
|
const baseDesktopFileTemplate = `[Desktop Entry]
|
|
Type=Application
|
|
Name=WIS Free V3
|
|
Comment=Voice Recording and Transcription
|
|
%s
|
|
%s
|
|
Icon=%s
|
|
StartupWMClass=%s
|
|
Terminal=false
|
|
Categories=Utility;
|
|
Keywords=voice;recorder;transcription;
|
|
`
|
|
|
|
const autostartDesktopFileTemplate = `[Desktop Entry]
|
|
Type=Application
|
|
Name=WIS Free V3
|
|
Comment=Voice Recording and Transcription
|
|
%s
|
|
%s
|
|
Icon=%s
|
|
StartupWMClass=%s
|
|
Terminal=false
|
|
Categories=Utility;
|
|
X-GNOME-Autostart-enabled=true
|
|
`
|
|
|
|
func EnsureDesktopFile(iconBytes []byte) error {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
appsDir := filepath.Join(home, ".local", "share", "applications")
|
|
if err := os.MkdirAll(appsDir, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
desktopPath := filepath.Join(appsDir, appName+".desktop")
|
|
|
|
exePath, err := getExecutablePath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write icon to local pixmaps so the desktop file Icon= field resolves
|
|
pixmapsDir := filepath.Join(home, ".local", "share", "pixmaps")
|
|
if err := os.MkdirAll(pixmapsDir, 0755); err != nil {
|
|
return err
|
|
}
|
|
iconPath := filepath.Join(pixmapsDir, appName+".png")
|
|
if err := os.WriteFile(iconPath, iconBytes, 0644); err != nil {
|
|
return fmt.Errorf("failed to write icon to pixmaps: %w", err)
|
|
}
|
|
|
|
content := fmt.Sprintf(baseDesktopFileTemplate, desktopExecField(exePath), desktopTryExecField(exePath), iconPath, appName)
|
|
return os.WriteFile(desktopPath, []byte(content), 0644)
|
|
}
|
|
|
|
func getAutostartPath() (string, error) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
configDir := os.Getenv("XDG_CONFIG_HOME")
|
|
if configDir == "" {
|
|
configDir = filepath.Join(home, ".config")
|
|
}
|
|
autostartDir := filepath.Join(configDir, "autostart")
|
|
|
|
if err := os.MkdirAll(autostartDir, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return filepath.Join(autostartDir, appName+".desktop"), nil
|
|
}
|
|
|
|
func getExecutablePath() (string, error) {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Abs(exe)
|
|
}
|
|
|
|
// desktopExecField returns one line: Exec=/path or Exec="/path with spaces"
|
|
func desktopExecField(exePath string) string {
|
|
needsQuote := false
|
|
for _, r := range exePath {
|
|
if r == ' ' || r == '\t' || r == '"' || r == '\'' || r == '\\' {
|
|
needsQuote = true
|
|
break
|
|
}
|
|
}
|
|
if !needsQuote {
|
|
return "Exec=" + exePath
|
|
}
|
|
escaped := ""
|
|
for _, r := range exePath {
|
|
if r == '"' || r == '`' || r == '$' || r == '\\' {
|
|
escaped += `\`
|
|
}
|
|
escaped += string(r)
|
|
}
|
|
return `Exec="` + escaped + `"`
|
|
}
|
|
|
|
// desktopTryExecField returns one line: TryExec=/path (desktop entries do not accept quoting for TryExec)
|
|
func desktopTryExecField(exePath string) string {
|
|
return "TryExec=" + exePath
|
|
}
|
|
|
|
func AddToStartup() error {
|
|
autostartPath, err := getAutostartPath()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to determine autostart path: %w", err)
|
|
}
|
|
|
|
exePath, err := getExecutablePath()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get executable path: %w", err)
|
|
}
|
|
|
|
// Prefer a concrete icon file path if we already created one in EnsureDesktopFile.
|
|
// Fall back to the icon name (theme lookup) if it doesn't exist.
|
|
iconValue := appName
|
|
if home, err := os.UserHomeDir(); err == nil {
|
|
iconPath := filepath.Join(home, ".local", "share", "pixmaps", appName+".png")
|
|
if _, statErr := os.Stat(iconPath); statErr == nil {
|
|
iconValue = iconPath
|
|
}
|
|
}
|
|
|
|
content := fmt.Sprintf(autostartDesktopFileTemplate, desktopExecField(exePath), desktopTryExecField(exePath), iconValue, appName)
|
|
if err := os.WriteFile(autostartPath, []byte(content), 0644); err != nil {
|
|
return fmt.Errorf("failed to write autostart file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func RemoveFromStartup() error {
|
|
autostartPath, err := getAutostartPath()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to determine autostart path: %w", err)
|
|
}
|
|
|
|
if err := os.Remove(autostartPath); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return fmt.Errorf("failed to remove autostart file: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func IsInStartup() bool {
|
|
autostartPath, err := getAutostartPath()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
_, err = os.Stat(autostartPath)
|
|
return err == nil
|
|
}
|