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 <vladimir@thecybershadow.net> 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 void setClipboardText(string s) 30 { 31 auto ws = s.toUTF16(); 32 33 char[] as; 34 int readLen; 35 as.length = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, null, 0, null, null); 36 37 if (as.length) 38 { 39 readLen = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, as.ptr, to!int(as.length), null, null); 40 wenforce(readLen == as.length, "WideCharToMultiByte"); 41 } 42 43 as ~= 0; 44 ws ~= 0; 45 46 setClipboard([ 47 ClipboardFormat(CF_TEXT, as), 48 ClipboardFormat(CF_UNICODETEXT, ws), 49 ]); 50 } 51 52 string getClipboardText() 53 { 54 static immutable DWORD[] textFormat = [CF_UNICODETEXT]; 55 auto format = getClipboard(textFormat)[0]; 56 wchar[] ws = (cast(wchar[])format.data)[0..$-1]; 57 return ws.toUTF8(); 58 } 59 60 // Windows-specific 61 62 /// One format entry in the Windows clipboard. 63 struct ClipboardFormat 64 { 65 DWORD format; 66 const (void)[] data; 67 68 string getName() 69 { 70 import ae.utils.meta : progn; 71 72 wchar[256] sbuf; 73 wchar[] buf = sbuf[]; 74 int ret; 75 do 76 { 77 ret = wenforce(GetClipboardFormatNameW(format, buf.ptr, buf.length.to!DWORD), "GetClipboardFormatNameW"); 78 } while (ret == buf.length ? progn(buf.length *=2, true) : false); 79 return buf[0..ret].toUTF8(); 80 } 81 } 82 83 /// Get clipboard data for the specified (default: all) formats. 84 ClipboardFormat[] getClipboard(in DWORD[] desiredFormatsP = null) 85 { 86 const(DWORD)[] desiredFormats = desiredFormatsP; 87 88 wenforce(OpenClipboard(null), "OpenClipboard"); 89 scope(exit) wenforce(CloseClipboard(), "CloseClipboard"); 90 91 if (desiredFormats is null) 92 { 93 auto allFormats = new DWORD[CountClipboardFormats()]; 94 DWORD previous = 0; 95 foreach (ref f; allFormats) 96 f = previous = EnumClipboardFormats(previous); 97 desiredFormats = allFormats; 98 } 99 100 auto result = new ClipboardFormat[desiredFormats.length]; 101 foreach (n, ref r; result) 102 { 103 r.format = desiredFormats[n]; 104 auto hBuf = wenforce(GetClipboardData(r.format), "GetClipboardData"); 105 auto size = GlobalSize(hBuf); 106 LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock"); 107 r.data = buf[0..size].dup; 108 wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock"); 109 } 110 111 return result; 112 } 113 114 void setClipboard(in ClipboardFormat[] formats) 115 { 116 wenforce(OpenClipboard(null), "OpenClipboard"); 117 scope(exit) wenforce(CloseClipboard(), "CloseClipboard"); 118 EmptyClipboard(); 119 foreach (ref format; formats) 120 { 121 HGLOBAL hBuf = wenforce(GlobalAlloc(GMEM_MOVEABLE, format.data.length.to!DWORD), "GlobalAlloc"); 122 scope(failure) wenforce(!GlobalFree(hBuf), "GlobalFree"); 123 LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock"); 124 buf[0..format.data.length] = format.data[]; 125 wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock"); 126 wenforce(SetClipboardData(format.format, hBuf), "SetClipboardData"); 127 } 128 } 129 } 130 else 131 version (Posix) 132 { 133 import std.process; 134 import std.exception; 135 136 import ae.utils.path; 137 138 void setClipboardText(string s) 139 { 140 string[] cmdLine; 141 if (haveExecutable("xclip")) 142 cmdLine = ["xclip", "-in", "-selection", "clipboard"]; 143 else 144 if (haveExecutable("xsel")) 145 cmdLine = ["xsel", "--input", "--clipboard"]; 146 else 147 if (haveExecutable("pbcopy")) 148 cmdLine = ["pbcopy"]; 149 else 150 throw new Exception("No clipboard management programs detected"); 151 auto p = pipe(); 152 auto pid = spawnProcess(cmdLine, p.readEnd); 153 p.writeEnd.rawWrite(s); 154 p.writeEnd.close(); 155 enforce(pid.wait() == 0, cmdLine[0] ~ " failed"); 156 } 157 158 string getClipboardText() 159 { 160 string[] cmdLine; 161 if (haveExecutable("xclip")) 162 cmdLine = ["xclip", "-out", "-selection", "clipboard"]; 163 else 164 if (haveExecutable("xsel")) 165 cmdLine = ["xsel", "--output", "--clipboard"]; 166 else 167 if (haveExecutable("pbpaste")) 168 cmdLine = ["pbpaste"]; 169 else 170 throw new Exception("No clipboard management programs detected"); 171 auto result = execute(cmdLine); 172 enforce(result.status == 0, cmdLine[0] ~ " failed"); 173 return result.output; 174 } 175 }