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 win32.winbase;
19 	import win32.windef;
20 	import win32.winnls;
21 	import win32.winuser;
22 	import ae.sys.windows.exception;
23 	import std.utf;
24 	import std.conv;
25 
26 	void setClipboardText(string s)
27 	{
28 		auto ws = s.toUTF16();
29 
30 		char[] as;
31 		int readLen;
32 		as.length = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, null, 0, null, null);
33 
34 		if (as.length)
35 		{
36 			readLen = WideCharToMultiByte(0, 0, ws.ptr, ws.length.to!DWORD, as.ptr, to!int(as.length), null, null);
37 			wenforce(readLen == as.length, "WideCharToMultiByte");
38 		}
39 
40 		as ~= 0;
41 		ws ~= 0;
42 
43 		setClipboard([
44 			ClipboardFormat(CF_TEXT,        as),
45 			ClipboardFormat(CF_UNICODETEXT, ws),
46 		]);
47 	}
48 
49 	string getClipboardText()
50 	{
51 		static immutable DWORD[] textFormat = [CF_UNICODETEXT];
52 		auto format = getClipboard(textFormat)[0];
53 		wchar[] ws = (cast(wchar[])format.data)[0..$-1];
54 		return ws.toUTF8();
55 	}
56 
57 	// Windows-specific
58 
59 	/// One format entry in the Windows clipboard.
60 	struct ClipboardFormat
61 	{
62 		DWORD format;
63 		const (void)[] data;
64 
65 		string getName()
66 		{
67 			wchar[256] sbuf;
68 			wchar[] buf = sbuf[];
69 			int ret;
70 			do
71 			{
72 				ret = wenforce(GetClipboardFormatNameW(format, buf.ptr, buf.length.to!DWORD), "GetClipboardFormatNameW");
73 			} while (ret == buf.length ? (buf.length *=2, true) : false);
74 			return buf[0..ret].toUTF8();
75 		}
76 	}
77 
78 	/// Get clipboard data for the specified (default: all) formats.
79 	ClipboardFormat[] getClipboard(in DWORD[] desiredFormatsP = null)
80 	{
81 		const(DWORD)[] desiredFormats = desiredFormatsP;
82 
83 		wenforce(OpenClipboard(null), "OpenClipboard");
84 		scope(exit) wenforce(CloseClipboard(), "CloseClipboard");
85 
86 		if (desiredFormats is null)
87 		{
88 			auto allFormats = new DWORD[CountClipboardFormats()];
89 			DWORD previous = 0;
90 			foreach (ref f; allFormats)
91 				f = previous = EnumClipboardFormats(previous);
92 			desiredFormats = allFormats;
93 		}
94 
95 		auto result = new ClipboardFormat[desiredFormats.length];
96 		foreach (n, ref r; result)
97 		{
98 			r.format = desiredFormats[n];
99 			auto hBuf = wenforce(GetClipboardData(r.format), "GetClipboardData");
100 			auto size = GlobalSize(hBuf);
101 			LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock");
102 			r.data = buf[0..size].dup;
103 			wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock");
104 		}
105 
106 		return result;
107 	}
108 
109 	void setClipboard(in ClipboardFormat[] formats)
110 	{
111 		wenforce(OpenClipboard(null), "OpenClipboard");
112 		scope(exit) wenforce(CloseClipboard(), "CloseClipboard");
113 		EmptyClipboard();
114 		foreach (ref format; formats)
115 		{
116 			HGLOBAL hBuf = wenforce(GlobalAlloc(GMEM_MOVEABLE, format.data.length.to!DWORD), "GlobalAlloc");
117 			scope(failure) wenforce(!GlobalFree(hBuf), "GlobalFree");
118 			LPVOID buf = wenforce(GlobalLock(hBuf), "GlobalLock");
119 			buf[0..format.data.length] = format.data[];
120 			wenforce(GlobalUnlock(hBuf) || GetLastError()==NO_ERROR, "GlobalUnlock");
121 			wenforce(SetClipboardData(format.format, hBuf), "SetClipboardData");
122 		}
123 	}
124 }