This commit is contained in:
PÉAU Clément 2025-11-01 21:34:26 +02:00 committed by GitHub
commit d03334e893
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 640 additions and 18 deletions

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Container
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: entfernen
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Containers
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: remove
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Contenedores
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: borrar
<kbd>e</kbd>: esconder/mostrar contenedores parados
<kbd>p</kbd>: pausa

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Conteneurs
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: supprimer
<kbd>e</kbd>: cacher/montrer les conteneurs arrêtés
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Containers
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: verwijder
<kbd>e</kbd>: verberg gestopte containers
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Kontenery
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: usuń
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Contêineres
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: remover
<kbd>e</kbd>: ocultar/mostrar contêineres parados
<kbd>p</kbd>: pausar

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Konteynerler
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: kaldır
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,6 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## 容器
<pre>
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: 移除
<kbd>e</kbd>: 隐藏/显示已停止的容器
<kbd>p</kbd>: 暂停

1
go.mod
View file

@ -31,6 +31,7 @@ require (
)
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect

2
go.sum
View file

@ -4,6 +4,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=

View file

@ -13,6 +13,7 @@ import (
"github.com/go-errors/errors"
"github.com/atotto/clipboard"
"github.com/jesseduffield/kill"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils"
@ -373,3 +374,20 @@ func (c *OSCommand) Kill(cmd *exec.Cmd) error {
func (c *OSCommand) PrepareForChildren(cmd *exec.Cmd) {
kill.PrepareForChildren(cmd)
}
func (c *OSCommand) CopyToClipboard(str string) error {
escaped := strings.Replace(str, "\n", "\\n", -1)
truncated := utils.TruncateWithEllipsis(escaped, 40)
c.Log.Debug(utils.ResolvePlaceholderString("Copying '{{str}}' to clipboard", map[string]string{"str": truncated}))
// Not needed yet
//if c.UserConfig().OS.CopyToClipboardCmd != "" {
// cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
// "text": c.Cmd.Quote(str),
// })
// return c.Cmd.NewShell(cmdStr).Run()
//}
return clipboard.WriteAll(str)
}

View file

@ -17,6 +17,10 @@ type statusManager struct {
statuses []appStatus
}
const (
TickIntervalMs = 50
)
func (m *statusManager) removeStatus(name string) {
newStatuses := []appStatus{}
for _, status := range m.statuses {
@ -27,12 +31,12 @@ func (m *statusManager) removeStatus(name string) {
m.statuses = newStatuses
}
func (m *statusManager) addWaitingStatus(name string) {
func (m *statusManager) addStatus(name string, statusType string, duration int) {
m.removeStatus(name)
newStatus := appStatus{
name: name,
statusType: "waiting",
duration: 0,
statusType: statusType,
duration: duration,
}
m.statuses = append([]appStatus{newStatus}, m.statuses...)
}
@ -41,36 +45,26 @@ func (m *statusManager) getStatusString() string {
if len(m.statuses) == 0 {
return ""
}
topStatus := m.statuses[0]
if topStatus.statusType == "waiting" {
return topStatus.name + " " + utils.Loader()
} else if topStatus.statusType == "info" {
return topStatus.name
}
return topStatus.name
}
// WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
go func() {
gui.statusManager.addWaitingStatus(name)
go gui.Notify(name, "waiting", 0)()
defer func() {
gui.statusManager.removeStatus(name)
}()
go func() {
ticker := time.NewTicker(time.Millisecond * 50)
defer ticker.Stop()
for range ticker.C {
appStatus := gui.statusManager.getStatusString()
if appStatus == "" {
return
}
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
gui.Log.Warn(err)
}
}
}()
if err := f(); err != nil {
gui.g.Update(func(g *gocui.Gui) error {
return gui.createErrorPanel(err.Error())
@ -80,3 +74,37 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
return nil
}
// Notify sends static notification to the user.
// duration of 0 will disable the self-cleaning of the notification
func (gui *Gui) Notify(name string, statusType string, duration int) func() {
return func() {
gui.statusManager.addStatus(name, statusType, duration)
defer func() {
gui.statusManager.removeStatus(name)
}()
ticker := time.NewTicker(time.Millisecond * TickIntervalMs)
tickCount := 0
endTick := duration * 1000 / TickIntervalMs
defer ticker.Stop()
for range ticker.C {
tickCount++
// If no duration, don't terminate early
if duration > 0 && tickCount >= endTick {
return
}
appStatus := gui.statusManager.getStatusString()
if appStatus == "" {
return
}
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
gui.Log.Warn(err)
}
}
}
}

View file

@ -252,8 +252,10 @@ func (gui *Gui) refreshContainersAndServices() error {
}
// keep track of current service selected so that we can reposition our cursor if it moves position in the list
gui.DockerCommand.ServiceMutex.Lock()
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
gui.DockerCommand.ServiceMutex.Unlock()
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
gui.Panels.Services.List.GetAllItems(),
@ -358,6 +360,18 @@ func (gui *Gui) PauseContainer(container *commands.Container) error {
})
}
func (gui *Gui) handleCopyContainerId(g *gocui.Gui, v *gocui.View) error {
ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
formattedStatusText := fmt.Sprintf("Copied %s to clipboard", utils.TruncateWithEllipsis(ctr.ID, 10))
go gui.Notify(formattedStatusText, "info", 3)()
return gui.OSCommand.CopyToClipboard(ctr.ID)
}
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {

View file

@ -185,6 +185,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleDonate,
},
{
ViewName: "containers",
Key: 'y',
Modifier: gocui.ModNone,
Handler: gui.handleCopyContainerId,
Description: gui.Tr.CopyContainerId,
},
{
ViewName: "containers",
Key: 'd',

View file

@ -109,6 +109,7 @@ type TranslationSet struct {
FilterList string
OpenInBrowser string
SortContainersByState string
CopyContainerId string
LogsTitle string
ConfigTitle string
@ -216,6 +217,7 @@ func englishSet() TranslationSet {
FilterList: "filter list",
OpenInBrowser: "open in browser (first port is http)",
SortContainersByState: "sort containers by state",
CopyContainerId: "copy container ID",
GlobalTitle: "Global",
MainTitle: "Main",

View file

@ -11,6 +11,7 @@ import (
"sort"
"strings"
"time"
"unicode"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
@ -410,3 +411,23 @@ func marshalIntoFormat(data interface{}, format string) ([]byte, error) {
return nil, errors.New(fmt.Sprintf("Unsupported detailization format: %s", format))
}
}
func StringWidth(s string) int {
// We are intentionally not using a range loop here, because that would
// convert the characters to runes, which is unnecessary work in this case.
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return runewidth.StringWidth(s)
}
}
return len(s)
}
// TruncateWithEllipsis returns a string, truncated to a certain length, with an ellipsis
func TruncateWithEllipsis(str string, limit int) string {
if StringWidth(str) > limit && limit <= 2 {
return strings.Repeat(".", limit)
}
return runewidth.Truncate(str, limit, "…")
}

22
vendor/github.com/atotto/clipboard/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,22 @@
language: go
os:
- linux
- osx
- windows
go:
- go1.13.x
- go1.x
services:
- xvfb
before_install:
- export DISPLAY=:99.0
script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xsel; fi
- go test -v .
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xclip; fi
- go test -v .

27
vendor/github.com/atotto/clipboard/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2013 Ato Araki. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of @atotto. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

48
vendor/github.com/atotto/clipboard/README.md generated vendored Normal file
View file

@ -0,0 +1,48 @@
[![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard)
[![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard)
# Clipboard for Go
Provide copying and pasting to the Clipboard for Go.
Build:
$ go get github.com/atotto/clipboard
Platforms:
* OSX
* Windows 7 (probably work on other Windows)
* Linux, Unix (requires 'xclip' or 'xsel' command to be installed)
Document:
* http://godoc.org/github.com/atotto/clipboard
Notes:
* Text string only
* UTF-8 text encoding only (no conversion)
TODO:
* Clipboard watcher(?)
## Commands:
paste shell command:
$ go get github.com/atotto/clipboard/cmd/gopaste
$ # example:
$ gopaste > document.txt
copy shell command:
$ go get github.com/atotto/clipboard/cmd/gocopy
$ # example:
$ cat document.txt | gocopy

20
vendor/github.com/atotto/clipboard/clipboard.go generated vendored Normal file
View file

@ -0,0 +1,20 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package clipboard read/write on clipboard
package clipboard
// ReadAll read string from clipboard
func ReadAll() (string, error) {
return readAll()
}
// WriteAll write string to clipboard
func WriteAll(text string) error {
return writeAll(text)
}
// Unsupported might be set true during clipboard init, to help callers decide
// whether or not to offer clipboard options.
var Unsupported bool

52
vendor/github.com/atotto/clipboard/clipboard_darwin.go generated vendored Normal file
View file

@ -0,0 +1,52 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package clipboard
import (
"os/exec"
)
var (
pasteCmdArgs = "pbpaste"
copyCmdArgs = "pbcopy"
)
func getPasteCommand() *exec.Cmd {
return exec.Command(pasteCmdArgs)
}
func getCopyCommand() *exec.Cmd {
return exec.Command(copyCmdArgs)
}
func readAll() (string, error) {
pasteCmd := getPasteCommand()
out, err := pasteCmd.Output()
if err != nil {
return "", err
}
return string(out), nil
}
func writeAll(text string) error {
copyCmd := getCopyCommand()
in, err := copyCmd.StdinPipe()
if err != nil {
return err
}
if err := copyCmd.Start(); err != nil {
return err
}
if _, err := in.Write([]byte(text)); err != nil {
return err
}
if err := in.Close(); err != nil {
return err
}
return copyCmd.Wait()
}

42
vendor/github.com/atotto/clipboard/clipboard_plan9.go generated vendored Normal file
View file

@ -0,0 +1,42 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build plan9
package clipboard
import (
"os"
"io/ioutil"
)
func readAll() (string, error) {
f, err := os.Open("/dev/snarf")
if err != nil {
return "", err
}
defer f.Close()
str, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
return string(str), nil
}
func writeAll(text string) error {
f, err := os.OpenFile("/dev/snarf", os.O_WRONLY, 0666)
if err != nil {
return err
}
defer f.Close()
_, err = f.Write([]byte(text))
if err != nil {
return err
}
return nil
}

149
vendor/github.com/atotto/clipboard/clipboard_unix.go generated vendored Normal file
View file

@ -0,0 +1,149 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd linux netbsd openbsd solaris dragonfly
package clipboard
import (
"errors"
"os"
"os/exec"
)
const (
xsel = "xsel"
xclip = "xclip"
powershellExe = "powershell.exe"
clipExe = "clip.exe"
wlcopy = "wl-copy"
wlpaste = "wl-paste"
termuxClipboardGet = "termux-clipboard-get"
termuxClipboardSet = "termux-clipboard-set"
)
var (
Primary bool
trimDos bool
pasteCmdArgs []string
copyCmdArgs []string
xselPasteArgs = []string{xsel, "--output", "--clipboard"}
xselCopyArgs = []string{xsel, "--input", "--clipboard"}
xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"}
xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"}
powershellExePasteArgs = []string{powershellExe, "Get-Clipboard"}
clipExeCopyArgs = []string{clipExe}
wlpasteArgs = []string{wlpaste, "--no-newline"}
wlcopyArgs = []string{wlcopy}
termuxPasteArgs = []string{termuxClipboardGet}
termuxCopyArgs = []string{termuxClipboardSet}
missingCommands = errors.New("No clipboard utilities available. Please install xsel, xclip, wl-clipboard or Termux:API add-on for termux-clipboard-get/set.")
)
func init() {
if os.Getenv("WAYLAND_DISPLAY") != "" {
pasteCmdArgs = wlpasteArgs
copyCmdArgs = wlcopyArgs
if _, err := exec.LookPath(wlcopy); err == nil {
if _, err := exec.LookPath(wlpaste); err == nil {
return
}
}
}
pasteCmdArgs = xclipPasteArgs
copyCmdArgs = xclipCopyArgs
if _, err := exec.LookPath(xclip); err == nil {
return
}
pasteCmdArgs = xselPasteArgs
copyCmdArgs = xselCopyArgs
if _, err := exec.LookPath(xsel); err == nil {
return
}
pasteCmdArgs = termuxPasteArgs
copyCmdArgs = termuxCopyArgs
if _, err := exec.LookPath(termuxClipboardSet); err == nil {
if _, err := exec.LookPath(termuxClipboardGet); err == nil {
return
}
}
pasteCmdArgs = powershellExePasteArgs
copyCmdArgs = clipExeCopyArgs
trimDos = true
if _, err := exec.LookPath(clipExe); err == nil {
if _, err := exec.LookPath(powershellExe); err == nil {
return
}
}
Unsupported = true
}
func getPasteCommand() *exec.Cmd {
if Primary {
pasteCmdArgs = pasteCmdArgs[:1]
}
return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...)
}
func getCopyCommand() *exec.Cmd {
if Primary {
copyCmdArgs = copyCmdArgs[:1]
}
return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...)
}
func readAll() (string, error) {
if Unsupported {
return "", missingCommands
}
pasteCmd := getPasteCommand()
out, err := pasteCmd.Output()
if err != nil {
return "", err
}
result := string(out)
if trimDos && len(result) > 1 {
result = result[:len(result)-2]
}
return result, nil
}
func writeAll(text string) error {
if Unsupported {
return missingCommands
}
copyCmd := getCopyCommand()
in, err := copyCmd.StdinPipe()
if err != nil {
return err
}
if err := copyCmd.Start(); err != nil {
return err
}
if _, err := in.Write([]byte(text)); err != nil {
return err
}
if err := in.Close(); err != nil {
return err
}
return copyCmd.Wait()
}

157
vendor/github.com/atotto/clipboard/clipboard_windows.go generated vendored Normal file
View file

@ -0,0 +1,157 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package clipboard
import (
"runtime"
"syscall"
"time"
"unsafe"
)
const (
cfUnicodetext = 13
gmemMoveable = 0x0002
)
var (
user32 = syscall.MustLoadDLL("user32")
isClipboardFormatAvailable = user32.MustFindProc("IsClipboardFormatAvailable")
openClipboard = user32.MustFindProc("OpenClipboard")
closeClipboard = user32.MustFindProc("CloseClipboard")
emptyClipboard = user32.MustFindProc("EmptyClipboard")
getClipboardData = user32.MustFindProc("GetClipboardData")
setClipboardData = user32.MustFindProc("SetClipboardData")
kernel32 = syscall.NewLazyDLL("kernel32")
globalAlloc = kernel32.NewProc("GlobalAlloc")
globalFree = kernel32.NewProc("GlobalFree")
globalLock = kernel32.NewProc("GlobalLock")
globalUnlock = kernel32.NewProc("GlobalUnlock")
lstrcpy = kernel32.NewProc("lstrcpyW")
)
// waitOpenClipboard opens the clipboard, waiting for up to a second to do so.
func waitOpenClipboard() error {
started := time.Now()
limit := started.Add(time.Second)
var r uintptr
var err error
for time.Now().Before(limit) {
r, _, err = openClipboard.Call(0)
if r != 0 {
return nil
}
time.Sleep(time.Millisecond)
}
return err
}
func readAll() (string, error) {
// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if formatAvailable, _, err := isClipboardFormatAvailable.Call(cfUnicodetext); formatAvailable == 0 {
return "", err
}
err := waitOpenClipboard()
if err != nil {
return "", err
}
h, _, err := getClipboardData.Call(cfUnicodetext)
if h == 0 {
_, _, _ = closeClipboard.Call()
return "", err
}
l, _, err := globalLock.Call(h)
if l == 0 {
_, _, _ = closeClipboard.Call()
return "", err
}
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
r, _, err := globalUnlock.Call(h)
if r == 0 {
_, _, _ = closeClipboard.Call()
return "", err
}
closed, _, err := closeClipboard.Call()
if closed == 0 {
return "", err
}
return text, nil
}
func writeAll(text string) error {
// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := waitOpenClipboard()
if err != nil {
return err
}
r, _, err := emptyClipboard.Call(0)
if r == 0 {
_, _, _ = closeClipboard.Call()
return err
}
data := syscall.StringToUTF16(text)
// "If the hMem parameter identifies a memory object, the object must have
// been allocated using the function with the GMEM_MOVEABLE flag."
h, _, err := globalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
if h == 0 {
_, _, _ = closeClipboard.Call()
return err
}
defer func() {
if h != 0 {
globalFree.Call(h)
}
}()
l, _, err := globalLock.Call(h)
if l == 0 {
_, _, _ = closeClipboard.Call()
return err
}
r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0])))
if r == 0 {
_, _, _ = closeClipboard.Call()
return err
}
r, _, err = globalUnlock.Call(h)
if r == 0 {
if err.(syscall.Errno) != 0 {
_, _, _ = closeClipboard.Call()
return err
}
}
r, _, err = setClipboardData.Call(cfUnicodetext, h)
if r == 0 {
_, _, _ = closeClipboard.Call()
return err
}
h = 0 // suppress deferred cleanup
closed, _, err := closeClipboard.Call()
if closed == 0 {
return err
}
return nil
}

3
vendor/modules.txt vendored
View file

@ -8,6 +8,9 @@ github.com/Microsoft/go-winio/pkg/guid
# github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
## explicit
github.com/OpenPeeDeeP/xdg
# github.com/atotto/clipboard v0.1.4
## explicit
github.com/atotto/clipboard
# github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
## explicit
github.com/boz/go-throttle