/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
stdlib/InteractiveUtils/src/clipboard.jl
187 строк
7 KB
Keno Fischer
REPL: add OSC 52 clipboard fallback and graceful degradation (#61151)
03 мар 2026, 08:42
Не верифицирован
03 мар 2026, 08:42
ab7ba34
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license # clipboard copy and paste """ has_system_clipboard() -> Bool Return `true` if the operating system has a clipboard mechanism available. On macOS and Windows this is always `true`. On Linux and FreeBSD, it checks for the presence of `xclip`, `xsel`, or `wl-copy` (from `wl-clipboard`). On other systems, returns `false`. !!! compat "Julia 1.14" This function requires Julia 1.14 or later. See also [`clipboard`](@ref). """ function has_system_clipboard end if Sys.isapple() has_system_clipboard() = true function clipboard(x) pbcopy_cmd = `pbcopy` # OSX shells, especially when run within `tmux` or `screen`, can be # disconnected from the global shell namespace, which causes problems # with clipboards. Luckily, the `reattach-to-user-namespace` utility # dodges these issues quite nicely, so we automatically utilize it if # it is installed. if Sys.which("reattach-to-user-namespace") !== nothing pbcopy_cmd = `reattach-to-user-namespace pbcopy` end open(pipeline(pbcopy_cmd, stderr=stderr), "w") do io print(io, x) end nothing end function clipboard() pbpaste_cmd = `pbpaste` # See above comment in `clipboard(x)` if Sys.which("reattach-to-user-namespace") !== nothing pbpaste_cmd = `reattach-to-user-namespace pbpaste` end return read(pbpaste_cmd, String) end elseif Sys.islinux() || Sys.KERNEL === :FreeBSD function has_system_clipboard() try clipboardcmd() return true catch return false end end _clipboardcmd = nothing const _clipboard_copy = Dict( :xsel => Sys.islinux() ? `xsel --input --clipboard` : `xsel -c`, :xclip => `xclip -silent -in -selection clipboard`, :wlclipboard => `wl-copy` ) const _clipboard_paste = Dict( :xsel => Sys.islinux() ? `xsel --nodetach --output --clipboard` : `xsel -p`, :xclip => `xclip -quiet -out -selection clipboard`, :wlclipboard => `wl-paste` ) function clipboardcmd() global _clipboardcmd _clipboardcmd !== nothing && return _clipboardcmd for cmd in (:xclip, :xsel, :wlclipboard) # wl-clipboard ships wl-copy/paste individually c = cmd === :wlclipboard ? Symbol("wl-copy") : cmd success(pipeline(`which $c`, devnull)) && return _clipboardcmd = cmd end pkgs = @static if Sys.KERNEL === :FreeBSD "x11/xsel or x11/xclip" else "xsel or xclip or wl-clipboard" end error("no clipboard command found, please install $pkgs") end function clipboard(x) c = clipboardcmd() cmd = _clipboard_copy[c] open(pipeline(cmd, stderr=stderr), "w") do io print(io, x) end nothing end function clipboard() c = clipboardcmd() cmd = _clipboard_paste[c] return read(pipeline(cmd, stderr=stderr), String) end elseif Sys.iswindows() has_system_clipboard() = true function clipboard(x::AbstractString) if Base.containsnul(x) throw(ArgumentError("Windows clipboard strings cannot contain NUL character")) end x_u16 = Base.cwstring(x) pdata = Ref(Ptr{UInt16}(C_NULL)) function cleanup(cause) errno = cause === :success ? UInt32(0) : Libc.GetLastError() if cause !== :OpenClipboard if cause !== :success && pdata[] != C_NULL ccall((:GlobalFree, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata[]) end ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror(:CloseClipboard) # this should never fail end cause === :success || Base.windowserror(cause, errno) nothing end ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL) == 0 && return Base.windowserror(:OpenClipboard) ccall((:EmptyClipboard, "user32"), stdcall, Cint, ()) == 0 && return cleanup(:EmptyClipboard) # copy data to locked, allocated space pdata[] = ccall((:GlobalAlloc, "kernel32"), stdcall, Ptr{UInt16}, (Cuint, Csize_t), 2 #=GMEM_MOVEABLE=#, sizeof(x_u16)) pdata[] == C_NULL && return cleanup(:GlobalAlloc) plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata[]) plock == C_NULL && return cleanup(:GlobalLock) GC.@preserve x_u16 memcpy(plock, Base.unsafe_convert(Ptr{UInt16}, Base.cconvert(Ptr{UInt16}, x_u16)), sizeof(x_u16)) unlock = ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata[]) (unlock == 0 && Libc.GetLastError() == 0) || return cleanup(:GlobalUnlock) # this should never fail pset = ccall((:SetClipboardData, "user32"), stdcall, Ptr{UInt16}, (Cuint, Ptr{UInt16}), 13, pdata[]) # CF_UNICODETEXT pdata[] != pset && return cleanup(:SetClipboardData) cleanup(:success) end clipboard(x) = clipboard(sprint(print, x)::String) function clipboard() function cleanup(cause) errno = cause === :success ? UInt32(0) : Libc.GetLastError() if cause !== :OpenClipboard ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror(:CloseClipboard) # this should never fail end if cause !== :success && !(cause === :GetClipboardData && (errno == 0x8004006A || errno == 0x800401D3)) # ignore DV_E_CLIPFORMAT and CLIPBRD_E_BAD_DATA from GetClipboardData Base.windowserror(cause, errno) end "" end ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL) == 0 && return Base.windowserror(:OpenClipboard) ccall(:SetLastError, stdcall, Cvoid, (UInt32,), 0) # allow distinguishing if the clipboard simply didn't have text pdata = ccall((:GetClipboardData, "user32"), stdcall, Ptr{UInt16}, (Cuint,), 13) # CF_UNICODETEXT pdata == C_NULL && return cleanup(:GetClipboardData) plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata) plock == C_NULL && return cleanup(:GlobalLock) s = try # find NUL terminator (0x0000 16-bit code unit) len = 0 while unsafe_load(plock, len + 1) != 0 len += 1 end # get Vector{UInt16}, transcode data to UTF-8, make a String of it transcode(String, unsafe_wrap(Array, plock, len)) finally unlock = ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata) (unlock != 0 || Libc.GetLastError() == 0) || return cleanup(:GlobalUnlock) # this should never fail cleanup(:success) end return s end else has_system_clipboard() = false clipboard(x="") = error("`clipboard` function not implemented for $(Sys.KERNEL)") end """ clipboard(x) Send a printed form of `x` to the operating system clipboard ("copy"). """ clipboard(x) """ clipboard()::String Return a string with the contents of the operating system clipboard ("paste"). """ clipboard()