summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorsimonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:03:32 +0000
committersimonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:03:32 +0000
commit98f99a057697e68d829598fdc4b738e71c43fc2b (patch)
treec0e375f0c31000c9a11c023aeb1cba8fa4c0eea8 /remoting
parente5895e65de6b1f2e28ae134f2e891d5c6334d200 (diff)
downloadchromium_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.h7
-rw-r--r--remoting/host/clipboard_win.cc124
-rw-r--r--remoting/host/desktop_environment.cc8
-rw-r--r--remoting/host/event_executor.h9
-rw-r--r--remoting/host/event_executor_linux.cc5
-rw-r--r--remoting/host/event_executor_mac.cc5
-rw-r--r--remoting/host/event_executor_win.cc41
-rw-r--r--remoting/host/simple_host_process.cc5
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 {