diff options
author | simonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-16 18:03:32 +0000 |
---|---|---|
committer | simonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-16 18:03:32 +0000 |
commit | 98f99a057697e68d829598fdc4b738e71c43fc2b (patch) | |
tree | c0e375f0c31000c9a11c023aeb1cba8fa4c0eea8 /remoting | |
parent | e5895e65de6b1f2e28ae134f2e891d5c6334d200 (diff) | |
download | chromium_src-98f99a057697e68d829598fdc4b738e71c43fc2b.zip chromium_src-98f99a057697e68d829598fdc4b738e71c43fc2b.tar.gz chromium_src-98f99a057697e68d829598fdc4b738e71c43fc2b.tar.bz2 |
[Chromoting] The Windows IT2Me host gets any new text items it finds on the clipboard.
A follow-up CL will send those items to the client.
The approach used in this CL will do nothing on OSes earlier than Vista.
BUG=117473
Review URL: https://chromiumcodereview.appspot.com/10381115
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137455 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/clipboard.h | 7 | ||||
-rw-r--r-- | remoting/host/clipboard_win.cc | 124 | ||||
-rw-r--r-- | remoting/host/desktop_environment.cc | 8 | ||||
-rw-r--r-- | remoting/host/event_executor.h | 9 | ||||
-rw-r--r-- | remoting/host/event_executor_linux.cc | 5 | ||||
-rw-r--r-- | remoting/host/event_executor_mac.cc | 5 | ||||
-rw-r--r-- | remoting/host/event_executor_win.cc | 41 | ||||
-rw-r--r-- | remoting/host/simple_host_process.cc | 5 |
8 files changed, 161 insertions, 43 deletions
diff --git a/remoting/host/clipboard.h b/remoting/host/clipboard.h index c21a8ba..e02c09d 100644 --- a/remoting/host/clipboard.h +++ b/remoting/host/clipboard.h @@ -15,20 +15,19 @@ namespace protocol { class ClipboardEvent; } // namespace protocol +// All Clipboard methods should be run on the UI thread, so that the Clipboard +// can get change notifications. class Clipboard { public: virtual ~Clipboard() {}; // Initialises any objects needed to read from or write to the clipboard. - // This method must be called on the desktop thread. virtual void Start() = 0; // Destroys any objects initialised by Start(). - // This method must be called on the desktop thread. virtual void Stop() = 0; - // Writes an item to the clipboard. - // This method must be called on the desktop thread, after Start(). + // Writes an item to the clipboard. It must be called after Start(). virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event) = 0; static scoped_ptr<Clipboard> Create(); diff --git a/remoting/host/clipboard_win.cc b/remoting/host/clipboard_win.cc index d812d2f..b3fc0c1 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()); } diff --git a/remoting/host/desktop_environment.cc b/remoting/host/desktop_environment.cc index 412c27e..4b5c5c1 100644 --- a/remoting/host/desktop_environment.cc +++ b/remoting/host/desktop_environment.cc @@ -22,7 +22,9 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::Create( ChromotingHostContext* context) { scoped_ptr<Capturer> capturer(Capturer::Create()); scoped_ptr<EventExecutor> event_executor = - EventExecutor::Create(context->desktop_message_loop(), capturer.get()); + EventExecutor::Create(context->desktop_message_loop(), + context->ui_message_loop(), + capturer.get()); if (capturer.get() == NULL || event_executor.get() == NULL) { LOG(ERROR) << "Unable to create DesktopEnvironment"; @@ -40,7 +42,9 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::CreateForService( ChromotingHostContext* context) { scoped_ptr<Capturer> capturer(Capturer::Create()); scoped_ptr<EventExecutor> event_executor = - EventExecutor::Create(context->desktop_message_loop(), capturer.get()); + EventExecutor::Create(context->desktop_message_loop(), + context->ui_message_loop(), + capturer.get()); if (capturer.get() == NULL || event_executor.get() == NULL) { LOG(ERROR) << "Unable to create DesktopEnvironment"; diff --git a/remoting/host/event_executor.h b/remoting/host/event_executor.h index eb0a886..51dcfe3 100644 --- a/remoting/host/event_executor.h +++ b/remoting/host/event_executor.h @@ -10,14 +10,21 @@ class MessageLoop; +namespace base { +class MessageLoopProxy; +} // namespace base + namespace remoting { class Capturer; class EventExecutor : public protocol::HostEventStub { public: - // Creates default event executor for the current platform. + // Creates a default event executor for the current platform. + // This object should do as much work as possible on |message_loop|, using + // |ui_loop| only when necessary. static scoped_ptr<EventExecutor> Create(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, Capturer* capturer); // Initialises any objects needed to execute events. diff --git a/remoting/host/event_executor_linux.cc b/remoting/host/event_executor_linux.cc index bf8856f..5fe35e1 100644 --- a/remoting/host/event_executor_linux.cc +++ b/remoting/host/event_executor_linux.cc @@ -429,8 +429,9 @@ void EventExecutorLinux::OnSessionFinished() { } // namespace -scoped_ptr<EventExecutor> EventExecutor::Create( - MessageLoop* message_loop, Capturer* capturer) { +scoped_ptr<EventExecutor> EventExecutor::Create(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, + Capturer* capturer) { scoped_ptr<EventExecutorLinux> executor( new EventExecutorLinux(message_loop)); if (!executor->Init()) diff --git a/remoting/host/event_executor_mac.cc b/remoting/host/event_executor_mac.cc index aaf2187..6058b42 100644 --- a/remoting/host/event_executor_mac.cc +++ b/remoting/host/event_executor_mac.cc @@ -336,8 +336,9 @@ void EventExecutorMac::OnSessionFinished() { } // namespace -scoped_ptr<EventExecutor> EventExecutor::Create( - MessageLoop* message_loop, Capturer* capturer) { +scoped_ptr<EventExecutor> EventExecutor::Create(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, + Capturer* capturer) { return scoped_ptr<EventExecutor>( new EventExecutorMac(message_loop)); } diff --git a/remoting/host/event_executor_win.cc b/remoting/host/event_executor_win.cc index 007f31b..430266a 100644 --- a/remoting/host/event_executor_win.cc +++ b/remoting/host/event_executor_win.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/compiler_specific.h" #include "base/message_loop.h" +#include "base/message_loop_proxy.h" #include "remoting/host/capturer.h" #include "remoting/host/clipboard.h" #include "remoting/proto/event.pb.h" @@ -30,7 +31,9 @@ using protocol::MouseEvent; // A class to generate events on Windows. class EventExecutorWin : public EventExecutor { public: - EventExecutorWin(MessageLoop* message_loop, Capturer* capturer); + EventExecutorWin(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, + Capturer* capturer); virtual ~EventExecutorWin() {} // ClipboardStub interface. @@ -48,10 +51,9 @@ class EventExecutorWin : public EventExecutor { HKL GetForegroundKeyboardLayout(); void HandleKey(const KeyEvent& event); void HandleMouse(const MouseEvent& event); - void HandleSessionStarted(); - void HandleSessionFinished(); MessageLoop* message_loop_; + base::MessageLoopProxy* ui_loop_; Capturer* capturer_; scoped_ptr<Clipboard> clipboard_; @@ -59,15 +61,17 @@ class EventExecutorWin : public EventExecutor { }; EventExecutorWin::EventExecutorWin(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, Capturer* capturer) : message_loop_(message_loop), + ui_loop_(ui_loop), capturer_(capturer), clipboard_(Clipboard::Create()) { } void EventExecutorWin::InjectClipboardEvent(const ClipboardEvent& event) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask( + if (!ui_loop_->BelongsToCurrentThread()) { + ui_loop_->PostTask( FROM_HERE, base::Bind(&EventExecutorWin::InjectClipboardEvent, base::Unretained(this), @@ -103,27 +107,27 @@ void EventExecutorWin::InjectMouseEvent(const MouseEvent& event) { } void EventExecutorWin::OnSessionStarted() { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask( + if (!ui_loop_->BelongsToCurrentThread()) { + ui_loop_->PostTask( FROM_HERE, base::Bind(&EventExecutorWin::OnSessionStarted, base::Unretained(this))); return; } - HandleSessionStarted(); + clipboard_->Start(); } void EventExecutorWin::OnSessionFinished() { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask( + if (!ui_loop_->BelongsToCurrentThread()) { + ui_loop_->PostTask( FROM_HERE, base::Bind(&EventExecutorWin::OnSessionFinished, base::Unretained(this))); return; } - HandleSessionFinished(); + clipboard_->Stop(); } HKL EventExecutorWin::GetForegroundKeyboardLayout() { @@ -278,20 +282,13 @@ void EventExecutorWin::HandleMouse(const MouseEvent& event) { } } -void EventExecutorWin::HandleSessionStarted() { - clipboard_->Start(); -} - -void EventExecutorWin::HandleSessionFinished() { - clipboard_->Stop(); -} - } // namespace -scoped_ptr<EventExecutor> EventExecutor::Create( - MessageLoop* message_loop, Capturer* capturer) { +scoped_ptr<EventExecutor> EventExecutor::Create(MessageLoop* message_loop, + base::MessageLoopProxy* ui_loop, + Capturer* capturer) { return scoped_ptr<EventExecutor>( - new EventExecutorWin(message_loop, capturer)); + new EventExecutorWin(message_loop, ui_loop, capturer)); } } // namespace remoting diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc index fd78413..bb87ee4 100644 --- a/remoting/host/simple_host_process.cc +++ b/remoting/host/simple_host_process.cc @@ -224,8 +224,9 @@ class SimpleHost : public HeartbeatSender::Listener { if (fake_) { scoped_ptr<Capturer> capturer(new CapturerFake()); scoped_ptr<EventExecutor> event_executor = - EventExecutor::Create( - context_.desktop_message_loop(), capturer.get()); + EventExecutor::Create(context_.desktop_message_loop(), + context_.ui_message_loop(), + capturer.get()); desktop_environment_ = DesktopEnvironment::CreateFake( &context_, capturer.Pass(), event_executor.Pass()); } else { |