Files
hyprvoice/internal/injection/ydotool.go
T
burakgizlice b694e58d90 fix: add timeout to unixgram dial
Use net.Dialer with a 500ms timeout for the unixgram dial to guard
against edge cases, as suggested in review.
2026-02-14 00:16:30 +03:00

97 lines
2.3 KiB
Go

package injection
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"time"
)
type ydotoolBackend struct{}
func NewYdotoolBackend() Backend {
return &ydotoolBackend{}
}
func (y *ydotoolBackend) Name() string {
return "ydotool"
}
func (y *ydotoolBackend) Available() error {
if _, err := exec.LookPath("ydotool"); err != nil {
return fmt.Errorf("ydotool not found: %w (install ydotool package)", err)
}
// Only check socket if ydotoold exists
if _, err := exec.LookPath("ydotoold"); err == nil {
socketPath := y.getSocketPath()
if socketPath == "" {
return fmt.Errorf("ydotoold socket not found - ensure ydotoold is running")
}
// ydotoold v1.0.4+ uses SOCK_DGRAM (unixgram) sockets.
// Try unixgram first, then fall back to stream for older versions.
// Note: unixgram dials are effectively instant (no handshake), but we
// use a dialer with a deadline to stay consistent and guard against edge cases.
dialer := net.Dialer{Timeout: 500 * time.Millisecond}
conn, err := dialer.Dial("unixgram", socketPath)
if err != nil {
conn, err = net.DialTimeout("unix", socketPath, 500*time.Millisecond)
}
if err != nil {
return fmt.Errorf("ydotoold not responding at %s: %w", socketPath, err)
}
conn.Close()
}
return nil
}
func (y *ydotoolBackend) getSocketPath() string {
// Check YDOTOOL_SOCKET env var first
if sock := os.Getenv("YDOTOOL_SOCKET"); sock != "" {
if _, err := os.Stat(sock); err == nil {
return sock
}
}
// Check common locations
paths := []string{
"/run/user/" + fmt.Sprint(os.Getuid()) + "/.ydotool_socket",
"/tmp/.ydotool_socket",
}
// Also check XDG_RUNTIME_DIR
if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" {
paths = append([]string{filepath.Join(xdg, ".ydotool_socket")}, paths...)
}
for _, p := range paths {
if _, err := os.Stat(p); err == nil {
return p
}
}
return ""
}
func (y *ydotoolBackend) Inject(ctx context.Context, text string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
if err := y.Available(); err != nil {
return err
}
// ydotool type -- "text"
cmd := exec.CommandContext(ctx, "ydotool", "type", "--", text)
if err := cmd.Run(); err != nil {
return fmt.Errorf("ydotool failed: %w", err)
}
return nil
}