77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
package injection
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// clipboardPasteBackend copies text to the primary selection and pastes it
|
|
// with ydotool's layout-independent Shift+Insert key codes.
|
|
//
|
|
// This preserves the regular clipboard, is independent of the active keyboard
|
|
// layout, and delivers the whole string at once.
|
|
type clipboardPasteBackend struct {
|
|
clipboard *clipboardBackend
|
|
ydotool *ydotoolBackend
|
|
}
|
|
|
|
func NewClipboardPasteBackend() Backend {
|
|
return &clipboardPasteBackend{
|
|
clipboard: NewClipboardBackend().(*clipboardBackend),
|
|
ydotool: NewYdotoolBackend().(*ydotoolBackend),
|
|
}
|
|
}
|
|
|
|
func (c *clipboardPasteBackend) Name() string {
|
|
return "clipboard-paste"
|
|
}
|
|
|
|
func (c *clipboardPasteBackend) Available() error {
|
|
if err := c.clipboard.Available(); err != nil {
|
|
return err
|
|
}
|
|
if err := c.ydotool.Available(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *clipboardPasteBackend) Inject(ctx context.Context, text string, timeout time.Duration) error {
|
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
|
|
if err := c.Available(); err != nil {
|
|
return err
|
|
}
|
|
// Shift+Insert pastes the primary selection, leaving the user's regular
|
|
// clipboard untouched.
|
|
primaryCopy := exec.CommandContext(ctx, "wl-copy", "--primary")
|
|
primaryCopy.Stdin = strings.NewReader(text)
|
|
if err := primaryCopy.Run(); err != nil {
|
|
return fmt.Errorf("copy transcription to primary selection: %w", err)
|
|
}
|
|
// wl-copy returns before every client has observed the new selection. Give
|
|
// the compositor a short chance to publish it; this is still perceived as
|
|
// an immediate paste and avoids inserting the previous clipboard contents.
|
|
select {
|
|
case <-time.After(150 * time.Millisecond):
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
|
|
// Linux input event codes: KEY_LEFTSHIFT=42, KEY_INSERT=110. These keys do
|
|
// not depend on the alphabetic layout, and Shift+Insert is widely supported.
|
|
cmd := exec.CommandContext(ctx, "ydotool", "key", "42:1", "110:1", "110:0", "42:0")
|
|
if socketPath := c.ydotool.getSocketPath(); socketPath != "" {
|
|
cmd.Env = append(os.Environ(), "YDOTOOL_SOCKET="+socketPath)
|
|
}
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("paste primary selection with ydotool: %w", err)
|
|
}
|
|
return nil
|
|
}
|