@@ -18,6 +18,7 @@ if Sys.isapple()
18
18
open (pipeline (pbcopy_cmd, stderr = stderr ), " w" ) do io
19
19
print (io, x)
20
20
end
21
+ nothing
21
22
end
22
23
function clipboard ()
23
24
pbpaste_cmd = ` pbpaste`
@@ -26,89 +27,113 @@ if Sys.isapple()
26
27
if Sys. which (" reattach-to-user-namespace" ) != = nothing
27
28
pbcopy_cmd = ` reattach-to-user-namespace pbpaste`
28
29
end
29
- read (pbpaste_cmd, String)
30
+ return read (pbpaste_cmd, String)
30
31
end
31
32
32
33
elseif Sys. islinux () || Sys. KERNEL === :FreeBSD
33
34
_clipboardcmd = nothing
34
- const _clipboardcmds = Dict (
35
- :copy => Dict (
35
+ const _clipboard_copy = Dict (
36
36
:xsel => Sys. islinux () ?
37
- ` xsel --nodetach --input --clipboard` : ` xsel -c` ,
37
+ ` xsel --nodetach --input --clipboard` :
38
+ ` xsel -c` ,
38
39
:xclip => ` xclip -silent -in -selection clipboard` ,
39
- ),
40
- :paste => Dict (
40
+ )
41
+ const _clipboard_paste = Dict (
41
42
:xsel => Sys. islinux () ?
42
- ` xsel --nodetach --output --clipboard` : ` xsel -p` ,
43
+ ` xsel --nodetach --output --clipboard` :
44
+ ` xsel -p` ,
43
45
:xclip => ` xclip -quiet -out -selection clipboard` ,
44
46
)
45
- )
46
47
function clipboardcmd ()
47
48
global _clipboardcmd
48
49
_clipboardcmd != = nothing && return _clipboardcmd
49
50
for cmd in (:xclip , :xsel )
50
51
success (pipeline (` which $cmd ` , devnull )) && return _clipboardcmd = cmd
51
52
end
52
- pkgs = @static if Sys. islinux ()
53
- " xsel or xclip"
54
- elseif Sys. KERNEL === :FreeBSD
53
+ pkgs = @static if Sys. KERNEL === :FreeBSD
55
54
" x11/xsel or x11/xclip"
55
+ else
56
+ " xsel or xclip"
56
57
end
57
58
error (" no clipboard command found, please install $pkgs " )
58
59
end
59
60
function clipboard (x)
60
61
c = clipboardcmd ()
61
- cmd = get (_clipboardcmds[:copy ], c, nothing )
62
- if cmd === nothing
63
- error (" unexpected clipboard command: $c " )
64
- end
62
+ cmd = _clipboard_copy[c]
65
63
open (pipeline (cmd, stderr = stderr ), " w" ) do io
66
64
print (io, x)
67
65
end
66
+ nothing
68
67
end
69
68
function clipboard ()
70
69
c = clipboardcmd ()
71
- cmd = get (_clipboardcmds[:paste ], c, nothing )
72
- if cmd === nothing
73
- error (" unexpected clipboard command: $c " )
74
- end
75
- read (pipeline (cmd, stderr = stderr ), String)
70
+ cmd = _clipboardcmds_paste[c]
71
+ return read (pipeline (cmd, stderr = stderr ), String)
76
72
end
77
73
78
74
elseif Sys. iswindows ()
79
- # TODO : these functions leak memory and memory locks if they throw an error
80
75
function clipboard (x:: AbstractString )
81
76
if Base. containsnul (x)
82
77
throw (ArgumentError (" Windows clipboard strings cannot contain NUL character" ))
83
78
end
84
- Base. windowserror (:OpenClipboard , 0 == ccall ((:OpenClipboard , " user32" ), stdcall, Cint, (Ptr{Cvoid},), C_NULL ))
85
- Base. windowserror (:EmptyClipboard , 0 == ccall ((:EmptyClipboard , " user32" ), stdcall, Cint, ()))
86
79
x_u16 = Base. cwstring (x)
80
+ pdata = Ptr {UInt16} (C_NULL )
81
+ function cleanup (cause)
82
+ errno = cause == :success ? UInt32 (0 ) : Libc. GetLastError ()
83
+ if cause != = :OpenClipboard
84
+ if cause != = :success && pdata != C_NULL
85
+ ccall ((:GlobalFree , " kernel32" ), stdcall, Cint, (Ptr{UInt16},), pdata)
86
+ end
87
+ ccall ((:CloseClipboard , " user32" ), stdcall, Cint, ()) == 0 && Base. windowserror (:CloseClipboard ) # this should never fail
88
+ end
89
+ cause == :success || Base. windowserror (cause, errno)
90
+ nothing
91
+ end
92
+ ccall ((:OpenClipboard , " user32" ), stdcall, Cint, (Ptr{Cvoid},), C_NULL ) == 0 && return Base. windowserror (:OpenClipboard )
93
+ ccall ((:EmptyClipboard , " user32" ), stdcall, Cint, ()) == 0 && return cleanup (:EmptyClipboard )
87
94
# copy data to locked, allocated space
88
- p = ccall ((:GlobalAlloc , " kernel32" ), stdcall, Ptr{UInt16}, (UInt16, Int32), 2 , sizeof (x_u16))
89
- Base. windowserror (:GlobalAlloc , p== C_NULL )
90
- plock = ccall ((:GlobalLock , " kernel32" ), stdcall, Ptr{UInt16}, (Ptr{UInt16},), p)
91
- Base. windowserror (:GlobalLock , plock== C_NULL )
92
- ccall (:memcpy , Ptr{UInt16}, (Ptr{UInt16},Ptr{UInt16},Int), plock, x_u16, sizeof (x_u16))
93
- Base. windowserror (:GlobalUnlock , 0 == ccall ((:GlobalUnlock , " kernel32" ), stdcall, Cint, (Ptr{Cvoid},), plock))
94
- pdata = ccall ((:SetClipboardData , " user32" ), stdcall, Ptr{UInt16}, (UInt32, Ptr{UInt16}), 13 , p)
95
- Base. windowserror (:SetClipboardData , pdata!= p)
96
- ccall ((:CloseClipboard , " user32" ), stdcall, Cvoid, ())
95
+ pdata = ccall ((:GlobalAlloc , " kernel32" ), stdcall, Ptr{UInt16}, (Cuint, Csize_t), 2 #= GMEM_MOVEABLE=# , sizeof (x_u16))
96
+ pdata == C_NULL && return cleanup (:GlobalAlloc )
97
+ plock = ccall ((:GlobalLock , " kernel32" ), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata)
98
+ plock == C_NULL && return cleanup (:GlobalLock )
99
+ ccall (:memcpy , Ptr{UInt16}, (Ptr{UInt16}, Ptr{UInt16}, Csize_t), plock, x_u16, sizeof (x_u16))
100
+ unlock = ccall ((:GlobalUnlock , " kernel32" ), stdcall, Cint, (Ptr{UInt16},), pdata)
101
+ (unlock == 0 && Libc. GetLastError () == 0 ) || return cleanup (:GlobalUnlock ) # this should never fail
102
+ pset = ccall ((:SetClipboardData , " user32" ), stdcall, Ptr{UInt16}, (Cuint, Ptr{UInt16}), 13 , pdata)
103
+ pdata != pset && return cleanup (:SetClipboardData )
104
+ cleanup (:success )
97
105
end
98
106
clipboard (x) = clipboard (sprint (print, x):: String )
99
107
function clipboard ()
100
- Base. windowserror (:OpenClipboard , 0 == ccall ((:OpenClipboard , " user32" ), stdcall, Cint, (Ptr{Cvoid},), C_NULL ))
101
- pdata = ccall ((:GetClipboardData , " user32" ), stdcall, Ptr{UInt16}, (UInt32,), 13 )
102
- Base. windowserror (:GetClipboardData , pdata== C_NULL )
103
- Base. windowserror (:CloseClipboard , 0 == ccall ((:CloseClipboard , " user32" ), stdcall, Cint, ()))
108
+ function cleanup (cause)
109
+ errno = cause == :success ? UInt32 (0 ) : Libc. GetLastError ()
110
+ if cause != = :OpenClipboard
111
+ ccall ((:CloseClipboard , " user32" ), stdcall, Cint, ()) == 0 && Base. windowserror (:CloseClipboard ) # this should never fail
112
+ end
113
+ if cause != = :success && (cause != = :GetClipboardData || errno != 0 )
114
+ Base. windowserror (cause, errno)
115
+ end
116
+ " "
117
+ end
118
+ ccall ((:OpenClipboard , " user32" ), stdcall, Cint, (Ptr{Cvoid},), C_NULL ) == 0 && return Base. windowserror (:OpenClipboard )
119
+ ccall (:SetLastError , stdcall, Cvoid, (UInt32,), 0 ) # allow distinguishing if the clipboard simply didn't have text
120
+ pdata = ccall ((:GetClipboardData , " user32" ), stdcall, Ptr{UInt16}, (Cuint,), 13 )
121
+ pdata == C_NULL && return cleanup (:GetClipboardData )
104
122
plock = ccall ((:GlobalLock , " kernel32" ), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata)
105
- Base. windowserror (:GlobalLock , plock== C_NULL )
106
- # find NUL terminator (0x0000 16-bit code unit)
107
- len = 0
108
- while unsafe_load (plock, len+ 1 ) != 0 ; len += 1 ; end
109
- # get Vector{UInt16}, transcode data to UTF-8, make a String of it
110
- s = transcode (String, unsafe_wrap (Array, plock, len))
111
- Base. windowserror (:GlobalUnlock , 0 == ccall ((:GlobalUnlock , " kernel32" ), stdcall, Cint, (Ptr{UInt16},), plock))
123
+ plock == C_NULL && return cleanup (:GlobalLock )
124
+ s = try
125
+ # find NUL terminator (0x0000 16-bit code unit)
126
+ len = 0
127
+ while unsafe_load (plock, len + 1 ) != 0
128
+ len += 1
129
+ end
130
+ # get Vector{UInt16}, transcode data to UTF-8, make a String of it
131
+ transcode (String, unsafe_wrap (Array, plock, len))
132
+ finally
133
+ unlock = ccall ((:GlobalUnlock , " kernel32" ), stdcall, Cint, (Ptr{UInt16},), pdata)
134
+ (unlock != 0 || Libc. GetLastError () == 0 ) || return cleanup (:GlobalUnlock ) # this should never fail
135
+ cleanup (:success )
136
+ end
112
137
return s
113
138
end
114
139
0 commit comments