Allow clearing the clipboard instantly; improve code sharing between platforms; facilitate GUI/TUI client development regarding the clipboard

This commit is contained in:
2024-12-21 15:31:14 -05:00
parent 9ed7b8ad9b
commit 3b57d59ddc
10 changed files with 117 additions and 128 deletions
+36 -3
View File
@@ -59,18 +59,51 @@ func CopyArgument(targetLocation string, field int) {
}
}
// ClipClearArgument is called to clear the clipboard after 30 seconds if the contents have not been modified.
// ClipClearArgument reads the assigned clipboard contents from stdin and passes them to clipClearProcess.
func ClipClearArgument() {
// read previous clipboard contents from stdin
clipScanner := bufio.NewScanner(os.Stdin)
if clipScanner.Scan() {
oldContents := clipScanner.Text()
clipClear(oldContents)
assignedContents := clipScanner.Text()
clipClearProcess(assignedContents)
} else {
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
}
}
// clipClearProcess clears the clipboard after 30 seconds if the clipboard contents have not changed.
// assignedContents can be omitted to clear the clipboard immediately and unconditionally.
func clipClearProcess(assignedContents string) {
cmdPaste, cmdClear := getClipCommands()
clearClipboard := func() {
err := cmdClear.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
Exit(0)
}
// if assignedContents is empty, clear the clipboard immediately and unconditionally
if assignedContents == "" {
clearClipboard()
}
// wait 30 seconds before checking clipboard contents
time.Sleep(30 * time.Second)
newContents, err := cmdPaste.Output()
if err != nil {
fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
if assignedContents == strings.TrimRight(string(newContents), "\r\n") {
clearClipboard()
}
}
// GenTOTP generates a TOTP token from a secret (supports standard and Steam TOTP).
func GenTOTP(secret string, time time.Time, forSteam bool) string {
var totpToken string
+6 -24
View File
@@ -6,8 +6,6 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"time"
)
// copyString copies a string to the clipboard.
@@ -21,29 +19,13 @@ func copyString(continuous bool, copySubject string) {
}
if !continuous {
launchClipClear(copySubject)
launchClipClearProcess(copySubject)
}
}
// clipClear is called in a separate process to clear the clipboard after 30 seconds.
func clipClear(oldContents string) {
time.Sleep(30 * time.Second)
cmd := exec.Command("pbpaste")
newContents, err := cmd.Output()
if err != nil {
fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
cmd = exec.Command("pbcopy")
writeToStdin(cmd, "")
err = cmd.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
}
Exit(0)
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
cmdClear := exec.Command("pbcopy")
writeToStdin(cmdClear, "")
return exec.Command("pbpaste"), cmdClear
}
+7 -25
View File
@@ -1,4 +1,4 @@
//go:build linux && termux
//go:build android && termux
package core
@@ -6,8 +6,6 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"time"
)
// copyString copies a string to the clipboard.
@@ -21,29 +19,13 @@ func copyString(continuous bool, copySubject string) {
}
if !continuous {
launchClipClear(copySubject)
launchClipClearProcess(copySubject)
}
}
// clipClear is called in a separate process to clear the clipboard after 30 seconds.
func clipClear(oldContents string) {
time.Sleep(30 * time.Second)
cmd := exec.Command("termux-clipboard-get")
newContents, err := cmd.Output()
if err != nil {
fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
cmd = exec.Command("termux-clipboard-set")
writeToStdin(cmd, "")
err = cmd.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
}
Exit(0)
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
cmdClear := exec.Command("termux-clipboard-set")
writeToStdin(cmdClear, "")
return exec.Command("termux-clipboard-get"), cmdClear
}
+13 -42
View File
@@ -6,68 +6,39 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"time"
)
// copyString copies a string to the clipboard.
func copyString(continuous bool, copySubject string) {
var envSet bool // track whether environment variables are set
var cmd *exec.Cmd
var envSet, isWayland bool // track whether environment variables are set
var cmdCopy *exec.Cmd
// determine whether to use wl-copy (Wayland) or xclip (X11)
if _, envSet = os.LookupEnv("WAYLAND_DISPLAY"); envSet {
cmd = exec.Command("wl-copy", "-t", "text/plain")
cmdCopy = exec.Command("wl-copy", "-t", "text/plain")
isWayland = true
} else if _, envSet = os.LookupEnv("DISPLAY"); envSet {
cmd = exec.Command("xclip", "-sel", "c", "-t", "text/plain")
cmdCopy = exec.Command("xclip", "-sel", "c", "-t", "text/plain")
} else {
fmt.Println(AnsiError + "Clipboard platform could not be determined - Note that the clipboard does not function in a raw TTY" + AnsiReset)
os.Exit(ErrorClipboard)
}
writeToStdin(cmd, copySubject)
err := cmd.Run()
writeToStdin(cmdCopy, copySubject)
err := cmdCopy.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to copy to clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
if !continuous {
launchClipClear(copySubject)
launchClipClearProcess(copySubject, isWayland)
}
}
// clipClear is called in a separate process to clear the clipboard after 30 seconds.
func clipClear(oldContents string) {
time.Sleep(30 * time.Second)
// determine clipboard tool to use (wl-clipboard VS xclip)
var envSet bool
var cmdClear, cmdPaste *exec.Cmd
if _, envSet = os.LookupEnv("WAYLAND_DISPLAY"); envSet {
cmdClear = exec.Command("wl-copy", "-c")
cmdPaste = exec.Command("wl-paste")
} else if _, envSet = os.LookupEnv("DISPLAY"); envSet {
cmdClear = exec.Command("xclip", "-i", "/dev/null", "-sel", "c")
cmdPaste = exec.Command("xclip", "-o", "-sel", "c")
} else {
fmt.Println(AnsiError + "Clipboard platform could not be determined - Neither $WAYLAND_DISPLAY nor $DISPLAY are set" + AnsiReset)
os.Exit(ErrorClipboard)
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
if os.Args[2] == "true" { // wayland
return exec.Command("wl-paste"), exec.Command("wl-copy", "-c")
}
// read current clipboard contents
newContents, err := cmdPaste.Output()
if err != nil {
fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
// clear clipboard if contents have not been modified
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
err = cmdClear.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
}
Exit(0)
return exec.Command("xclip", "-o", "-sel", "c"), exec.Command("xclip", "-i", "/dev/null", "-sel", "c")
}
+4 -22
View File
@@ -7,7 +7,6 @@ import (
"os"
"os/exec"
"strings"
"time"
)
// copyString copies a string to the clipboard.
@@ -20,28 +19,11 @@ func copyString(continuous bool, copySubject string) {
}
if !continuous {
launchClipClear(copySubject)
launchClipClearProcess(copySubject)
}
}
// clipClear is called in a separate process to clear the clipboard after 30 seconds.
func clipClear(oldContents string) {
time.Sleep(30 * time.Second)
cmd := exec.Command("powershell.exe", "-c", "Get-Clipboard")
newContents, err := cmd.Output()
if err != nil {
fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
cmd = exec.Command("powershell.exe", "-c", "Set-Clipboard")
err = cmd.Run()
if err != nil {
fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset)
os.Exit(ErrorClipboard)
}
}
Exit(0)
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
return exec.Command("powershell.exe", "-c", "Get-Clipboard"), exec.Command("powershell.exe", "-c", "Set-Clipboard")
}
@@ -1,4 +1,4 @@
//go:build !returnOnExit
//go:build (windows || darwin || android || termux || wsl) && !returnOnExit
package core
@@ -8,9 +8,9 @@ import (
"os/exec"
)
// launchClipClear launches the automated clipboard clearing process.
// launchClipClearProcess launches the automated clipboard clearing process.
// For non-interactive CLI implementations, an entirely separate process is created for this purpose.
func launchClipClear(copySubject string) {
func launchClipClearProcess(copySubject string) {
executableName := os.Args[0]
cmd := exec.Command(executableName, "clipclear")
writeToStdin(cmd, copySubject)
+24
View File
@@ -0,0 +1,24 @@
//go:build !windows && !darwin && !android && !termux && !wsl && !returnOnExit
package core
import (
"fmt"
"os"
"os/exec"
"strconv"
)
// launchClipClearProcess launches the automated clipboard clearing process.
// For non-interactive CLI implementations, an entirely separate process is created for this purpose.
func launchClipClearProcess(copySubject string, isWayland bool) {
executableName := os.Args[0]
cmd := exec.Command(executableName, "clipclear", strconv.FormatBool(isWayland))
writeToStdin(cmd, copySubject)
err := cmd.Start()
if err != nil {
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
os.Exit(ErrorClipboard)
}
os.Exit(0) // use os.Exit directly since this version of this function is only meant for non-interactive CLI implementations
}
-9
View File
@@ -1,9 +0,0 @@
//go:build returnOnExit
package core
// launchClipClear launches the automated clipboard clearing process.
// For interactive GUI/TUI implementations, the clipboard clearing process is launched as a goroutine.
func launchClipClear(copySubject string) {
go clipClear(copySubject)
}
+9
View File
@@ -0,0 +1,9 @@
//go:build (windows || darwin || android || termux || wsl) && returnOnExit
package core
// launchClipClearProcess launches the automated clipboard clearing process.
// For interactive GUI/TUI implementations, the clipboard clearing process is launched as a goroutine.
func launchClipClearProcess(copySubject string) {
go clipClearProcess(copySubject)
}
+15
View File
@@ -0,0 +1,15 @@
//go:build !windows && !darwin && !android && !termux && !wsl && returnOnExit
package core
import (
"os"
"strconv"
)
// launchClipClearProcess launches the automated clipboard clearing process.
// For interactive GUI/TUI implementations, the clipboard clearing process is launched as a goroutine.
func launchClipClearProcess(copySubject string, isWayland bool) {
os.Args[2] = strconv.FormatBool(isWayland)
go clipClearProcess(copySubject)
}