OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/clipboard.h" | 5 #include "remoting/host/clipboard.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/process_util.h" | 12 #include "base/process_util.h" |
13 #include "base/string16.h" | 13 #include "base/string16.h" |
14 #include "base/threading/platform_thread.h" | 14 #include "base/threading/platform_thread.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "base/win/scoped_hglobal.h" | 16 #include "base/win/scoped_hglobal.h" |
17 #include "base/win/windows_version.h" | 17 #include "base/win/windows_version.h" |
18 #include "base/win/wrapped_window_proc.h" | 18 #include "base/win/wrapped_window_proc.h" |
19 #include "remoting/base/constants.h" | 19 #include "remoting/base/constants.h" |
20 #include "remoting/base/util.h" | 20 #include "remoting/base/util.h" |
21 #include "remoting/host/win/message_window.h" | |
21 #include "remoting/proto/event.pb.h" | 22 #include "remoting/proto/event.pb.h" |
22 #include "remoting/protocol/clipboard_stub.h" | 23 #include "remoting/protocol/clipboard_stub.h" |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 const WCHAR kWindowClassName[] = L"clipboardWindowClass"; | |
27 const WCHAR kWindowName[] = L"clipboardWindow"; | |
28 | |
29 // A scoper class that opens and closes the clipboard. | 27 // A scoper class that opens and closes the clipboard. |
30 // This class was adapted from the ScopedClipboard class in | 28 // This class was adapted from the ScopedClipboard class in |
31 // ui/base/clipboard/clipboard_win.cc. | 29 // ui/base/clipboard/clipboard_win.cc. |
32 class ScopedClipboard { | 30 class ScopedClipboard { |
33 public: | 31 public: |
34 ScopedClipboard() : opened_(false) { | 32 ScopedClipboard() : opened_(false) { |
35 } | 33 } |
36 | 34 |
37 ~ScopedClipboard() { | 35 ~ScopedClipboard() { |
38 if (opened_) { | 36 if (opened_) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
95 bool opened_; | 93 bool opened_; |
96 }; | 94 }; |
97 | 95 |
98 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND); | 96 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND); |
99 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND); | 97 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND); |
100 | 98 |
101 } // namespace | 99 } // namespace |
102 | 100 |
103 namespace remoting { | 101 namespace remoting { |
104 | 102 |
105 class ClipboardWin : public Clipboard { | 103 class ClipboardWin : public Clipboard, |
104 public win::MessageWindow::Delegate { | |
106 public: | 105 public: |
107 ClipboardWin(); | 106 ClipboardWin(); |
108 | 107 |
109 virtual void Start( | 108 virtual void Start( |
110 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE; | 109 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE; |
111 virtual void InjectClipboardEvent( | 110 virtual void InjectClipboardEvent( |
112 const protocol::ClipboardEvent& event) OVERRIDE; | 111 const protocol::ClipboardEvent& event) OVERRIDE; |
113 virtual void Stop() OVERRIDE; | 112 virtual void Stop() OVERRIDE; |
114 | 113 |
115 private: | 114 private: |
116 void OnClipboardUpdate(); | 115 void OnClipboardUpdate(); |
117 bool HaveClipboardListenerApi(); | |
118 | 116 |
119 static bool RegisterWindowClass(); | 117 // win::MessageWindow::Delegate interface. |
120 static LRESULT CALLBACK WndProc(HWND hwmd, UINT msg, WPARAM wParam, | 118 virtual bool HandleMessage(UINT message, WPARAM wparam, LPARAM lparam, |
121 LPARAM lParam); | 119 LRESULT* result) OVERRIDE; |
122 | 120 |
123 scoped_ptr<protocol::ClipboardStub> client_clipboard_; | 121 scoped_ptr<protocol::ClipboardStub> client_clipboard_; |
124 HWND hwnd_; | |
125 AddClipboardFormatListenerFn* add_clipboard_format_listener_; | 122 AddClipboardFormatListenerFn* add_clipboard_format_listener_; |
126 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; | 123 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; |
127 bool load_functions_tried_; | 124 |
125 // Used to subsribe to WM_CLIPBOARDUPDATE messages. | |
dcaiafa
2013/03/28 23:25:35
nit: subscribe
alexeypa (please no reviews)
2013/03/29 16:57:01
Done.
| |
126 scoped_ptr<win::MessageWindow> window_; | |
128 | 127 |
129 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); | 128 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); |
130 }; | 129 }; |
131 | 130 |
132 ClipboardWin::ClipboardWin() | 131 ClipboardWin::ClipboardWin() |
133 : hwnd_(NULL), | 132 : add_clipboard_format_listener_(NULL), |
134 add_clipboard_format_listener_(NULL), | 133 remove_clipboard_format_listener_(NULL) { |
135 remove_clipboard_format_listener_(NULL), | |
136 load_functions_tried_(false) { | |
137 } | 134 } |
138 | 135 |
139 void ClipboardWin::Start( | 136 void ClipboardWin::Start( |
140 scoped_ptr<protocol::ClipboardStub> client_clipboard) { | 137 scoped_ptr<protocol::ClipboardStub> client_clipboard) { |
138 DCHECK(!add_clipboard_format_listener_); | |
139 DCHECK(!remove_clipboard_format_listener_); | |
140 DCHECK(!window_); | |
141 | |
141 client_clipboard_.swap(client_clipboard); | 142 client_clipboard_.swap(client_clipboard); |
142 | 143 |
143 if (!load_functions_tried_) { | 144 // user32.dll is statically linked. |
144 load_functions_tried_ = true; | 145 HMODULE user32 = GetModuleHandle(L"user32.dll"); |
145 HMODULE user32_module = ::GetModuleHandle(L"user32.dll"); | 146 CHECK(user32); |
146 if (!user32_module) { | 147 |
147 LOG(WARNING) << "Couldn't find user32.dll."; | 148 add_clipboard_format_listener_ = |
148 } else { | 149 reinterpret_cast<AddClipboardFormatListenerFn*>( |
149 add_clipboard_format_listener_ = | 150 GetProcAddress(user32, "AddClipboardFormatListener")); |
150 reinterpret_cast<AddClipboardFormatListenerFn*>( | 151 if (add_clipboard_format_listener_) { |
151 ::GetProcAddress(user32_module, "AddClipboardFormatListener")); | 152 remove_clipboard_format_listener_ = |
152 remove_clipboard_format_listener_ = | 153 reinterpret_cast<RemoveClipboardFormatListenerFn*>( |
153 reinterpret_cast<RemoveClipboardFormatListenerFn*>( | 154 GetProcAddress(user32, "RemoveClipboardFormatListener")); |
154 ::GetProcAddress(user32_module, "RemoveClipboardFormatListener")); | 155 // If AddClipboardFormatListener() present, RemoveClipboardFormatListener() |
155 if (!HaveClipboardListenerApi()) { | 156 // should be available too. |
156 LOG(WARNING) << "Couldn't load AddClipboardFormatListener or " | 157 CHECK(remove_clipboard_format_listener_); |
157 << "RemoveClipboardFormatListener."; | 158 } else { |
158 } | 159 LOG(WARNING) << "AddClipboardFormatListener() is not available."; |
159 } | |
160 } | 160 } |
161 | 161 |
162 if (!RegisterWindowClass()) { | 162 window_.reset(new win::MessageWindow()); |
163 LOG(ERROR) << "Couldn't register clipboard window class."; | 163 if (!window_->Create(this)) { |
164 return; | |
165 } | |
166 hwnd_ = ::CreateWindow(kWindowClassName, | |
167 kWindowName, | |
168 0, 0, 0, 0, 0, | |
169 HWND_MESSAGE, | |
170 NULL, | |
171 base::GetModuleFromAddress(&WndProc), | |
172 this); | |
173 if (!hwnd_) { | |
174 LOG(ERROR) << "Couldn't create clipboard window."; | 164 LOG(ERROR) << "Couldn't create clipboard window."; |
165 window_.reset(); | |
175 return; | 166 return; |
176 } | 167 } |
177 | 168 |
178 if (HaveClipboardListenerApi()) { | 169 if (add_clipboard_format_listener_) { |
179 if (!(*add_clipboard_format_listener_)(hwnd_)) { | 170 if (!(*add_clipboard_format_listener_)(window_->hwnd())) { |
180 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); | 171 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); |
181 } | 172 } |
182 } | 173 } |
183 } | 174 } |
184 | 175 |
185 void ClipboardWin::Stop() { | 176 void ClipboardWin::Stop() { |
186 client_clipboard_.reset(); | 177 client_clipboard_.reset(); |
187 | 178 |
188 if (hwnd_) { | 179 if (window_ && remove_clipboard_format_listener_) |
189 if (HaveClipboardListenerApi()) { | 180 (*remove_clipboard_format_listener_)(window_->hwnd()); |
190 (*remove_clipboard_format_listener_)(hwnd_); | 181 |
191 } | 182 window_.reset(); |
192 ::DestroyWindow(hwnd_); | |
193 hwnd_ = NULL; | |
194 } | |
195 } | 183 } |
196 | 184 |
197 void ClipboardWin::InjectClipboardEvent( | 185 void ClipboardWin::InjectClipboardEvent( |
198 const protocol::ClipboardEvent& event) { | 186 const protocol::ClipboardEvent& event) { |
199 if (!hwnd_) | 187 if (!window_) |
200 return; | 188 return; |
201 | 189 |
202 // Currently we only handle UTF-8 text. | 190 // Currently we only handle UTF-8 text. |
203 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) | 191 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) |
204 return; | 192 return; |
205 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { | 193 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { |
206 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; | 194 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; |
207 return; | 195 return; |
208 } | 196 } |
209 | 197 |
210 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); | 198 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); |
211 | 199 |
212 ScopedClipboard clipboard; | 200 ScopedClipboard clipboard; |
213 if (!clipboard.Init(hwnd_)) { | 201 if (!clipboard.Init(window_->hwnd())) { |
214 LOG(WARNING) << "Couldn't open the clipboard."; | 202 LOG(WARNING) << "Couldn't open the clipboard."; |
215 return; | 203 return; |
216 } | 204 } |
217 | 205 |
218 clipboard.Empty(); | 206 clipboard.Empty(); |
219 | 207 |
220 HGLOBAL text_global = | 208 HGLOBAL text_global = |
221 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); | 209 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); |
222 if (!text_global) { | 210 if (!text_global) { |
223 LOG(WARNING) << "Couldn't allocate global memory."; | 211 LOG(WARNING) << "Couldn't allocate global memory."; |
224 return; | 212 return; |
225 } | 213 } |
226 | 214 |
227 LPWSTR text_global_locked = | 215 LPWSTR text_global_locked = |
228 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); | 216 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); |
229 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); | 217 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); |
230 text_global_locked[text.size()] = (WCHAR)0; | 218 text_global_locked[text.size()] = (WCHAR)0; |
231 ::GlobalUnlock(text_global); | 219 ::GlobalUnlock(text_global); |
232 | 220 |
233 clipboard.SetData(CF_UNICODETEXT, text_global); | 221 clipboard.SetData(CF_UNICODETEXT, text_global); |
234 } | 222 } |
235 | 223 |
236 void ClipboardWin::OnClipboardUpdate() { | 224 void ClipboardWin::OnClipboardUpdate() { |
237 DCHECK(hwnd_); | 225 DCHECK(window_); |
238 | 226 |
239 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { | 227 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { |
240 string16 text; | 228 string16 text; |
241 // Add a scope, so that we keep the clipboard open for as short a time as | 229 // Add a scope, so that we keep the clipboard open for as short a time as |
242 // possible. | 230 // possible. |
243 { | 231 { |
244 ScopedClipboard clipboard; | 232 ScopedClipboard clipboard; |
245 if (!clipboard.Init(hwnd_)) { | 233 if (!clipboard.Init(window_->hwnd())) { |
246 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); | 234 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); |
247 return; | 235 return; |
248 } | 236 } |
249 | 237 |
250 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); | 238 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); |
251 if (!text_global) { | 239 if (!text_global) { |
252 LOG(WARNING) << "Couldn't get data from the clipboard: " | 240 LOG(WARNING) << "Couldn't get data from the clipboard: " |
253 << GetLastError(); | 241 << GetLastError(); |
254 return; | 242 return; |
255 } | 243 } |
256 | 244 |
257 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); | 245 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); |
258 if (!text_lock.get()) { | 246 if (!text_lock.get()) { |
259 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); | 247 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); |
260 return; | 248 return; |
261 } | 249 } |
262 text.assign(text_lock.get()); | 250 text.assign(text_lock.get()); |
263 } | 251 } |
264 | 252 |
265 protocol::ClipboardEvent event; | 253 protocol::ClipboardEvent event; |
266 event.set_mime_type(kMimeTypeTextUtf8); | 254 event.set_mime_type(kMimeTypeTextUtf8); |
267 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); | 255 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); |
268 | 256 |
269 if (client_clipboard_.get()) { | 257 if (client_clipboard_.get()) { |
270 client_clipboard_->InjectClipboardEvent(event); | 258 client_clipboard_->InjectClipboardEvent(event); |
271 } | 259 } |
272 } | 260 } |
273 } | 261 } |
274 | 262 |
275 bool ClipboardWin::HaveClipboardListenerApi() { | 263 bool ClipboardWin::HandleMessage( |
276 return add_clipboard_format_listener_ && remove_clipboard_format_listener_; | 264 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) { |
277 } | 265 if (message == WM_CLIPBOARDUPDATE) { |
278 | 266 OnClipboardUpdate(); |
279 bool ClipboardWin::RegisterWindowClass() { | 267 *result = 0; |
280 // This method is only called on the UI thread, so it doesn't matter | |
281 // that the following test is not thread-safe. | |
282 static bool registered = false; | |
283 if (registered) { | |
284 return true; | 268 return true; |
285 } | 269 } |
286 | 270 |
287 WNDCLASSEX window_class; | 271 return false; |
288 base::win::InitializeWindowClass( | |
289 kWindowClassName, | |
290 base::win::WrappedWindowProc<WndProc>, | |
291 0, 0, 0, NULL, NULL, NULL, NULL, NULL, | |
292 &window_class); | |
293 if (!::RegisterClassEx(&window_class)) { | |
294 return false; | |
295 } | |
296 | |
297 registered = true; | |
298 return true; | |
299 } | |
300 | |
301 LRESULT CALLBACK ClipboardWin::WndProc(HWND hwnd, UINT msg, WPARAM wparam, | |
302 LPARAM lparam) { | |
303 if (msg == WM_CREATE) { | |
304 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam); | |
305 ::SetWindowLongPtr(hwnd, | |
306 GWLP_USERDATA, | |
307 reinterpret_cast<LONG_PTR>(cs->lpCreateParams)); | |
308 return 0; | |
309 } | |
310 ClipboardWin* clipboard = | |
311 reinterpret_cast<ClipboardWin*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); | |
312 switch (msg) { | |
313 case WM_CLIPBOARDUPDATE: | |
314 clipboard->OnClipboardUpdate(); | |
315 return 0; | |
316 } | |
317 return ::DefWindowProc(hwnd, msg, wparam, lparam); | |
318 } | 272 } |
319 | 273 |
320 scoped_ptr<Clipboard> Clipboard::Create() { | 274 scoped_ptr<Clipboard> Clipboard::Create() { |
321 return scoped_ptr<Clipboard>(new ClipboardWin()); | 275 return scoped_ptr<Clipboard>(new ClipboardWin()); |
322 } | 276 } |
323 | 277 |
324 } // namespace remoting | 278 } // namespace remoting |
OLD | NEW |