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 			wchar[256] sbuf;
71 			wchar[] buf = sbuf[];
72 			int ret;
73 			do
74 			{
75 				ret = wenforce(GetClipboardFormatNameW(format, buf.ptr, buf.length.to!DWORD), "GetClipboardFormatNameW");
76 			} while (ret == buf.length ? (buf.length *=2, true) : false);
77 			return buf[0..ret].toUTF8();
78 		}
79 	}
80 
81 	/// Get clipboard data for the specified (default: all) formats.
82 	ClipboardFormat[] getClipboard(in DWORD[] desiredFormatsP = null)
83 	{
84 		const(DWORD)[] desiredFormats = desiredFormatsP;
85 
86 		wenforce(OpenClipboard(null), "OpenClipboard");
87 		scope(exit) wenforce(CloseClipboard(), "CloseClipboard");
88 
89 		if (desiredFormats is null)
90 		{
91 			auto allFormats = new DWORD[CountClipboardFormats()];
92 			DWORD previous = 0;
93 			foreach (ref f; allFormats)
94 				f = previous = EnumClipboardFormats(previous);
95 			desiredFormats = allFormats;
96 		}
97 
98 		auto result = new ClipboardFormat[desiredFormats.length];
99 		foreach (n, ref r; result)
100 		{
101 			r.format = desiredFormats[n];
102 			auto hBuf = wenforce(GetClipboardData(r.format), "GetClipboardData");
103 			auto size = GlobalSize(hBuf);
104 			LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock");
105 			r.data = buf[0..size].dup;
106 			wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock");
107 		}
108 
109 		return result;
110 	}
111 
112 	void setClipboard(in ClipboardFormat[] formats)
113 	{
114 		wenforce(OpenClipboard(null), "OpenClipboard");
115 		scope(exit) wenforce(CloseClipboard(), "CloseClipboard");
116 		EmptyClipboard();
117 		foreach (ref format; formats)
118 		{
119 			HGLOBAL hBuf = wenforce(GlobalAlloc(GMEM_MOVEABLE, format.data.length.to!DWORD), "GlobalAlloc");
120 			scope(failure) wenforce(!GlobalFree(hBuf), "GlobalFree");
121 			LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock");
122 			buf[0..format.data.length] = format.data[];
123 			wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock");
124 			wenforce(SetClipboardData(format.format, hBuf), "SetClipboardData");
125 		}
126 	}
127 }
128 else
129 version (Posix)
130 {
131 	import std.process;
132 	import std.exception;
133 
134 	import ae.utils.path;
135 
136 	void setClipboardText(string s)
137 	{
138 		string[] cmdLine;
139 		if (haveExecutable("xclip"))
140 			cmdLine = ["xclip", "-in", "-selection", "clipboard"];
141 		else
142 		if (haveExecutable("xsel"))
143 			cmdLine = ["xsel", "--input", "--clipboard"];
144 		else
145 		if (haveExecutable("pbcopy"))
146 			cmdLine = ["pbcopy"];
147 		else
148 			throw new Exception("No clipboard management programs detected");
149 		auto p = pipe();
150 		auto pid = spawnProcess(cmdLine, p.readEnd);
151 		p.writeEnd.rawWrite(s);
152 		p.writeEnd.close();
153 		enforce(pid.wait() == 0, cmdLine[0] ~ " failed");
154 	}
155 
156 	string getClipboardText()
157 	{
158 		string[] cmdLine;
159 		if (haveExecutable("xclip"))
160 			cmdLine = ["xclip", "-out", "-selection", "clipboard"];
161 		else
162 		if (haveExecutable("xsel"))
163 			cmdLine = ["xsel", "--output", "--clipboard"];
164 		else
165 		if (haveExecutable("pbpaste"))
166 			cmdLine = ["pbpaste"];
167 		else
168 			throw new Exception("No clipboard management programs detected");
169 		auto result = execute(cmdLine);
170 		enforce(result.status == 0, cmdLine[0] ~ " failed");
171 		return result.output;
172 	}
173 }