Index: remoting/host/clipboard_win.cc |
diff --git a/remoting/host/clipboard_win.cc b/remoting/host/clipboard_win.cc |
index d812d2f86ba71531f61e339cf41a2de64abf771c..b3fc0c10effe4a647f671110859bcb7c3b2ef430 100644 |
--- a/remoting/host/clipboard_win.cc |
+++ b/remoting/host/clipboard_win.cc |
@@ -12,6 +12,8 @@ |
#include "base/process_util.h" |
#include "base/string16.h" |
#include "base/utf_string_conversions.h" |
+#include "base/win/scoped_hglobal.h" |
+#include "base/win/windows_version.h" |
#include "base/win/wrapped_window_proc.h" |
#include "remoting/base/constants.h" |
#include "remoting/proto/event.pb.h" |
@@ -45,7 +47,7 @@ class ScopedClipboard { |
return true; |
} |
- // This code runs on the desktop thread, so blocking briefly is acceptable. |
+ // This code runs on the UI thread, so we can block only very briefly. |
for (int attempt = 0; attempt < kMaxAttemptsToOpenClipboard; ++attempt) { |
if (attempt > 0) { |
base::PlatformThread::Sleep(kSleepTimeBetweenAttempts); |
@@ -75,10 +77,24 @@ class ScopedClipboard { |
::SetClipboardData(uFormat, hMem); |
} |
+ // The caller must not free the handle. The caller should lock the handle, |
+ // copy the clipboard data, and unlock the handle. All this must be done |
+ // before this ScopedClipboard is destroyed. |
+ HANDLE GetData(UINT format) { |
+ if (!opened_) { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ return ::GetClipboardData(format); |
+ } |
+ |
private: |
bool opened_; |
}; |
+typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND); |
+typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND); |
+ |
} // namespace |
namespace remoting { |
@@ -93,20 +109,48 @@ class ClipboardWin : public Clipboard { |
virtual void Stop() OVERRIDE; |
private: |
- static LRESULT CALLBACK WndProc(HWND hwmd, UINT msg, WPARAM wParam, |
- LPARAM lParam); |
+ void OnClipboardUpdate(); |
+ bool HaveClipboardListenerApi(); |
static bool RegisterWindowClass(); |
+ static LRESULT CALLBACK WndProc(HWND hwmd, UINT msg, WPARAM wParam, |
+ LPARAM lParam); |
HWND hwnd_; |
+ AddClipboardFormatListenerFn* add_clipboard_format_listener_; |
+ RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; |
+ bool load_functions_tried_; |
DISALLOW_COPY_AND_ASSIGN(ClipboardWin); |
}; |
-ClipboardWin::ClipboardWin() : hwnd_(NULL) { |
+ClipboardWin::ClipboardWin() |
+ : hwnd_(NULL), |
+ add_clipboard_format_listener_(NULL), |
+ remove_clipboard_format_listener_(NULL), |
+ load_functions_tried_(false) { |
} |
void ClipboardWin::Start() { |
+ if (!load_functions_tried_) { |
+ load_functions_tried_ = true; |
+ HMODULE user32_module = ::GetModuleHandle(L"user32.dll"); |
+ if (!user32_module) { |
+ LOG(WARNING) << "Couldn't find user32.dll."; |
+ } else { |
+ add_clipboard_format_listener_ = |
+ reinterpret_cast<AddClipboardFormatListenerFn*>( |
+ ::GetProcAddress(user32_module, "AddClipboardFormatListener")); |
+ remove_clipboard_format_listener_ = |
+ reinterpret_cast<RemoveClipboardFormatListenerFn*>( |
+ ::GetProcAddress(user32_module, "RemoveClipboardFormatListener")); |
+ if (!HaveClipboardListenerApi()) { |
+ LOG(WARNING) << "Couldn't load AddClipboardFormatListener or " |
+ << "RemoveClipboardFormatListener."; |
+ } |
+ } |
+ } |
+ |
if (!RegisterWindowClass()) { |
LOG(FATAL) << "Couldn't register clipboard window class."; |
return; |
@@ -122,10 +166,19 @@ void ClipboardWin::Start() { |
LOG(FATAL) << "Couldn't create clipboard window."; |
return; |
} |
+ |
+ if (HaveClipboardListenerApi()) { |
+ if (!(*add_clipboard_format_listener_)(hwnd_)) { |
+ LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); |
+ } |
+ } |
} |
void ClipboardWin::Stop() { |
if (hwnd_) { |
+ if (HaveClipboardListenerApi()) { |
+ (*remove_clipboard_format_listener_)(hwnd_); |
+ } |
::DestroyWindow(hwnd_); |
hwnd_ = NULL; |
} |
@@ -166,13 +219,49 @@ void ClipboardWin::InjectClipboardEvent( |
clipboard.SetData(CF_UNICODETEXT, text_global); |
} |
-LRESULT CALLBACK ClipboardWin::WndProc(HWND hwnd, UINT msg, WPARAM wParam, |
- LPARAM lParam) { |
- return ::DefWindowProc(hwnd, msg, wParam, lParam); |
+void ClipboardWin::OnClipboardUpdate() { |
+ DCHECK(hwnd_); |
+ |
+ if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { |
+ string16 text; |
+ // Add a scope, so that we keep the clipboard open for as short a time as |
+ // possible. |
+ { |
+ ScopedClipboard clipboard; |
+ if (!clipboard.Init(hwnd_)) { |
+ LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); |
+ return; |
+ } |
+ |
+ HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); |
+ if (!text_global) { |
+ LOG(WARNING) << "Couldn't get data from the clipboard: " |
+ << GetLastError(); |
+ return; |
+ } |
+ |
+ base::win::ScopedHGlobal<WCHAR> text_lock(text_global); |
+ if (!text_lock.get()) { |
+ LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); |
+ return; |
+ } |
+ text.assign(text_lock.get()); |
+ } |
+ |
+ protocol::ClipboardEvent event; |
+ event.set_mime_type(kMimeTypeTextUtf8); |
+ event.set_data(UTF16ToUTF8(text)); |
+ |
+ // TODO(simonmorris): Send the event to the client. |
+ } |
+} |
+ |
+bool ClipboardWin::HaveClipboardListenerApi() { |
+ return add_clipboard_format_listener_ && remove_clipboard_format_listener_; |
} |
bool ClipboardWin::RegisterWindowClass() { |
- // This method is only called on the desktop thread, so it doesn't matter |
+ // This method is only called on the UI thread, so it doesn't matter |
// that the following test is not thread-safe. |
static bool registered = false; |
if (registered) { |
@@ -193,6 +282,25 @@ bool ClipboardWin::RegisterWindowClass() { |
return true; |
} |
+LRESULT CALLBACK ClipboardWin::WndProc(HWND hwnd, UINT msg, WPARAM wparam, |
+ LPARAM lparam) { |
+ if (msg == WM_CREATE) { |
+ CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam); |
+ ::SetWindowLongPtr(hwnd, |
+ GWLP_USERDATA, |
+ reinterpret_cast<LONG_PTR>(cs->lpCreateParams)); |
+ return 0; |
+ } |
+ ClipboardWin* clipboard = |
+ reinterpret_cast<ClipboardWin*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); |
+ switch (msg) { |
+ case WM_CLIPBOARDUPDATE: |
+ clipboard->OnClipboardUpdate(); |
+ return 0; |
+ } |
+ return ::DefWindowProc(hwnd, msg, wparam, lparam); |
+} |
+ |
scoped_ptr<Clipboard> Clipboard::Create() { |
return scoped_ptr<Clipboard>(new ClipboardWin()); |
} |