1 /** 2 * OS clipboard interaction. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.sys.clipboard; 15 16 version (Windows) 17 { 18 import std.utf; 19 import std.conv; 20 21 import ae.sys.windows.exception; 22 23 import ae.sys.windows.imports; 24 mixin(importWin32!q{winbase}); 25 mixin(importWin32!q{windef}); 26 mixin(importWin32!q{winnls}); 27 mixin(importWin32!q{winuser}); 28 29 /// Put text into the clipboard. 30 void setClipboardText(string s) 31 { 32 auto ws = s.toUTF16(); 33 34 char[] as; 35 int readLen; 36 as.length = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, null, 0, null, null); 37 38 if (as.length) 39 { 40 readLen = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, as.ptr, to!int(as.length), null, null); 41 wenforce(readLen == as.length, "WideCharToMultiByte"); 42 } 43 44 as ~= 0; 45 ws ~= 0; 46 47 setClipboard([ 48 ClipboardFormat(CF_TEXT, as), 49 ClipboardFormat(CF_UNICODETEXT, ws), 50 ]); 51 } 52 53 /// Get text from the clipboard. 54 string getClipboardText() 55 { 56 static immutable DWORD[] textFormat = [CF_UNICODETEXT]; 57 auto format = getClipboard(textFormat)[0]; 58 wchar[] ws = (cast(wchar[])format.data)[0..$-1]; 59 return ws.toUTF8(); 60 } 61 62 // Windows-specific 63 64 /// One format entry in the Windows clipboard. 65 struct ClipboardFormat 66 { 67 DWORD format; /// The Windows clipboard format code, such as `CF_TEXT`. 68 const (void)[] data; /// Data in this format. 69 70 /// `GetClipboardFormatNameW` wrapper. 71 string getName() 72 { 73 import ae.utils.meta : progn; 74 75 wchar[256] sbuf; 76 wchar[] buf = sbuf[]; 77 int ret; 78 do 79 { 80 ret = wenforce(GetClipboardFormatNameW(format, buf.ptr, buf.length.to!DWORD), "GetClipboardFormatNameW"); 81 } while (ret == buf.length ? progn(buf.length *=2, true) : false); 82 return buf[0..ret].toUTF8(); 83 } 84 } 85 86 /// Get clipboard data for the specified (default: all) formats. 87 ClipboardFormat[] getClipboard(in DWORD[] desiredFormatsP = null) 88 { 89 const(DWORD)[] desiredFormats = desiredFormatsP; 90 91 wenforce(OpenClipboard(null), "OpenClipboard"); 92 scope(exit) wenforce(CloseClipboard(), "CloseClipboard"); 93 94 if (desiredFormats is null) 95 { 96 auto allFormats = new DWORD[CountClipboardFormats()]; 97 DWORD previous = 0; 98 foreach (ref f; allFormats) 99 f = previous = EnumClipboardFormats(previous); 100 desiredFormats = allFormats; 101 } 102 103 auto result = new ClipboardFormat[desiredFormats.length]; 104 foreach (n, ref r; result) 105 { 106 r.format = desiredFormats[n]; 107 auto hBuf = wenforce(GetClipboardData(r.format), "GetClipboardData"); 108 auto size = GlobalSize(hBuf); 109 LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock"); 110 r.data = buf[0..size].dup; 111 wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock"); 112 } 113 114 return result; 115 } 116 117 /// Set the clipboard with data in the specified formats. 118 void setClipboard(in ClipboardFormat[] formats) 119 { 120 wenforce(OpenClipboard(null), "OpenClipboard"); 121 scope(exit) wenforce(CloseClipboard(), "CloseClipboard"); 122 EmptyClipboard(); 123 foreach (ref format; formats) 124 { 125 HGLOBAL hBuf = wenforce(GlobalAlloc(GMEM_MOVEABLE, format.data.length.to!DWORD), "GlobalAlloc"); 126 scope(failure) wenforce(!GlobalFree(hBuf), "GlobalFree"); 127 LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock"); 128 buf[0..format.data.length] = format.data[]; 129 wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock"); 130 wenforce(SetClipboardData(format.format, hBuf), "SetClipboardData"); 131 } 132 } 133 } 134 else 135 version (Posix) 136 { 137 import std.process; 138 import std.exception; 139 140 import ae.utils.path; 141 142 /// Put text into the clipboard. 143 void setClipboardText(string s) 144 { 145 string[] cmdLine; 146 if (haveExecutable("xclip")) 147 cmdLine = ["xclip", "-in", "-selection", "clipboard"]; 148 else 149 if (haveExecutable("xsel")) 150 cmdLine = ["xsel", "--input", "--clipboard"]; 151 else 152 if (haveExecutable("pbcopy")) 153 cmdLine = ["pbcopy"]; 154 else 155 throw new Exception("No clipboard management programs detected"); 156 auto p = pipe(); 157 auto pid = spawnProcess(cmdLine, p.readEnd); 158 p.writeEnd.rawWrite(s); 159 p.writeEnd.close(); 160 enforce(pid.wait() == 0, cmdLine[0] ~ " failed"); 161 } 162 163 /// Get text from the clipboard. 164 string getClipboardText() 165 { 166 string[] cmdLine; 167 if (haveExecutable("xclip")) 168 cmdLine = ["xclip", "-out", "-selection", "clipboard"]; 169 else 170 if (haveExecutable("xsel")) 171 cmdLine = ["xsel", "--output", "--clipboard"]; 172 else 173 if (haveExecutable("pbpaste")) 174 cmdLine = ["pbpaste"]; 175 else 176 throw new Exception("No clipboard management programs detected"); 177 auto result = execute(cmdLine); 178 enforce(result.status == 0, cmdLine[0] ~ " failed"); 179 return result.output; 180 } 181 }