@@ -18,6 +18,7 @@ if Sys.isapple()
1818 open (pipeline (pbcopy_cmd, stderr = stderr ), " w" ) do io
1919 print (io, x)
2020 end
21+ nothing
2122 end
2223 function clipboard ()
2324 pbpaste_cmd = ` pbpaste`
@@ -26,89 +27,113 @@ if Sys.isapple()
2627 if Sys. which (" reattach-to-user-namespace" ) != nothing
2728 pbcopy_cmd = ` reattach-to-user-namespace pbpaste`
2829 end
29- read (pbpaste_cmd, String)
30+ return read (pbpaste_cmd, String)
3031 end
3132
3233elseif Sys. islinux () || Sys. KERNEL === :FreeBSD
3334 _clipboardcmd = nothing
34- const _clipboardcmds = Dict (
35- :copy => Dict (
35+ const _clipboard_copy = Dict (
3636 :xsel => Sys. islinux () ?
37- ` xsel --nodetach --input --clipboard` : ` xsel -c` ,
37+ ` xsel --nodetach --input --clipboard` :
38+ ` xsel -c` ,
3839 :xclip => ` xclip -silent -in -selection clipboard` ,
39- ),
40- :paste => Dict (
40+ )
41+ const _clipboard_paste = Dict (
4142 :xsel => Sys. islinux () ?
42- ` xsel --nodetach --output --clipboard` : ` xsel -p` ,
43+ ` xsel --nodetach --output --clipboard` :
44+ ` xsel -p` ,
4345 :xclip => ` xclip -quiet -out -selection clipboard` ,
4446 )
45- )
4647 function clipboardcmd ()
4748 global _clipboardcmd
4849 _clipboardcmd != = nothing && return _clipboardcmd
4950 for cmd in (:xclip , :xsel )
5051 success (pipeline (` which $cmd ` , devnull )) && return _clipboardcmd = cmd
5152 end
52- pkgs = @static if Sys. islinux ()
53- " xsel or xclip"
54- elseif Sys. KERNEL === :FreeBSD
53+ pkgs = @static if Sys. KERNEL === :FreeBSD
5554 " x11/xsel or x11/xclip"
55+ else
56+ " xsel or xclip"
5657 end
5758 error (" no clipboard command found, please install $pkgs " )
5859 end
5960 function clipboard (x)
6061 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]
6563 open (pipeline (cmd, stderr = stderr ), " w" ) do io
6664 print (io, x)
6765 end
66+ nothing
6867 end
6968 function clipboard ()
7069 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)
7672 end
7773
7874elseif Sys. iswindows ()
79- # TODO : these functions leak memory and memory locks if they throw an error
8075 function clipboard (x:: AbstractString )
8176 if Base. containsnul (x)
8277 throw (ArgumentError (" Windows clipboard strings cannot contain NUL character" ))
8378 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, ()))
8679 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 )
8794 # 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}, (UInt16, Int32), 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}, Int), 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}, (UInt32, Ptr{UInt16}), 13 , pdata)
103+ pdata != pset && return cleanup (:SetClipboardData )
104+ cleanup (:success )
97105 end
98106 clipboard (x) = clipboard (sprint (print, x):: String )
99107 function clipboard ()
100- Base. windowserror (:OpenClipboard , 0 == ccall ((:OpenClipboard , " user32" ), stdcall, Cint, (Ptr{Cvoid},), C_NULL ))
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
101120 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, ()))
121+ pdata == C_NULL && return cleanup (:GetClipboardData )
104122 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
112137 return s
113138 end
114139
0 commit comments