summaryrefslogtreecommitdiffstats
path: root/remoting/host/input_injector_mac.cc
diff options
context:
space:
mode:
authorwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-24 03:42:42 +0000
committerwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-24 03:42:42 +0000
commitb0b72f11d870061817de63d71f4b7e4d6583cf28 (patch)
treed92187bd48462ef36e0e20294cf6632c96e099bf /remoting/host/input_injector_mac.cc
parent69abcb227ca8bb3012c383d712d65cf00b7ec834 (diff)
downloadchromium_src-b0b72f11d870061817de63d71f4b7e4d6583cf28.zip
chromium_src-b0b72f11d870061817de63d71f4b7e4d6583cf28.tar.gz
chromium_src-b0b72f11d870061817de63d71f4b7e4d6583cf28.tar.bz2
Rename EventExecutor to InputInjector.
This is part of general naming clean-up under remoting/ and will help keep lambroslambrou@ happy. Review URL: https://chromiumcodereview.appspot.com/12760012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190163 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/input_injector_mac.cc')
-rw-r--r--remoting/host/input_injector_mac.cc294
1 files changed, 294 insertions, 0 deletions
diff --git a/remoting/host/input_injector_mac.cc b/remoting/host/input_injector_mac.cc
new file mode 100644
index 0000000..8993e86
--- /dev/null
+++ b/remoting/host/input_injector_mac.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/input_injector.h"
+
+#include <algorithm>
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "media/video/capture/screen/mac/desktop_configuration.h"
+#include "remoting/host/clipboard.h"
+#include "remoting/proto/internal.pb.h"
+#include "remoting/protocol/message_decoder.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace remoting {
+
+namespace {
+
+using protocol::ClipboardEvent;
+using protocol::KeyEvent;
+using protocol::MouseEvent;
+
+// USB to Mac keycode mapping table.
+#define USB_KEYMAP(usb, xkb, win, mac) {usb, mac}
+#include "ui/base/keycodes/usb_keycode_map.h"
+#undef USB_KEYMAP
+
+// skia/ext/skia_utils_mac.h only defines CGRectToSkRect().
+SkIRect CGRectToSkIRect(const CGRect& rect) {
+ SkIRect result;
+ gfx::CGRectToSkRect(rect).round(&result);
+ return result;
+}
+
+// A class to generate events on Mac.
+class InputInjectorMac : public InputInjector {
+ public:
+ explicit InputInjectorMac(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ virtual ~InputInjectorMac();
+
+ // ClipboardStub interface.
+ virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
+
+ // InputStub interface.
+ virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
+ virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
+
+ // InputInjector interface.
+ virtual void Start(
+ scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
+
+ private:
+ // The actual implementation resides in InputInjectorMac::Core class.
+ class Core : public base::RefCountedThreadSafe<Core> {
+ public:
+ explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ // Mirrors the ClipboardStub interface.
+ void InjectClipboardEvent(const ClipboardEvent& event);
+
+ // Mirrors the InputStub interface.
+ void InjectKeyEvent(const KeyEvent& event);
+ void InjectMouseEvent(const MouseEvent& event);
+
+ // Mirrors the InputInjector interface.
+ void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
+
+ void Stop();
+
+ private:
+ friend class base::RefCountedThreadSafe<Core>;
+ virtual ~Core();
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ SkIPoint mouse_pos_;
+ uint32 mouse_button_state_;
+ scoped_ptr<Clipboard> clipboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+ };
+
+ scoped_refptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputInjectorMac);
+};
+
+InputInjectorMac::InputInjectorMac(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ core_ = new Core(task_runner);
+}
+
+InputInjectorMac::~InputInjectorMac() {
+ core_->Stop();
+}
+
+void InputInjectorMac::InjectClipboardEvent(const ClipboardEvent& event) {
+ core_->InjectClipboardEvent(event);
+}
+
+void InputInjectorMac::InjectKeyEvent(const KeyEvent& event) {
+ core_->InjectKeyEvent(event);
+}
+
+void InputInjectorMac::InjectMouseEvent(const MouseEvent& event) {
+ core_->InjectMouseEvent(event);
+}
+
+void InputInjectorMac::Start(
+ scoped_ptr<protocol::ClipboardStub> client_clipboard) {
+ core_->Start(client_clipboard.Pass());
+}
+
+InputInjectorMac::Core::Core(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : task_runner_(task_runner),
+ mouse_button_state_(0),
+ clipboard_(Clipboard::Create()) {
+ // Ensure that local hardware events are not suppressed after injecting
+ // input events. This allows LocalInputMonitor to detect if the local mouse
+ // is being moved whilst a remote user is connected.
+ // This API is deprecated, but it is needed when using the deprecated
+ // injection APIs.
+ // If the non-deprecated injection APIs were used instead, the equivalent of
+ // this line would not be needed, as OS X defaults to _not_ suppressing local
+ // inputs in that case.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ CGSetLocalEventsSuppressionInterval(0.0);
+#pragma clang diagnostic pop
+}
+
+void InputInjectorMac::Core::InjectClipboardEvent(const ClipboardEvent& event) {
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
+ return;
+ }
+
+ // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
+ clipboard_->InjectClipboardEvent(event);
+}
+
+void InputInjectorMac::Core::InjectKeyEvent(const KeyEvent& event) {
+ // HostEventDispatcher should filter events missing the pressed field.
+ if (!event.has_pressed() || !event.has_usb_keycode())
+ return;
+
+ int keycode = UsbKeycodeToNativeKeycode(event.usb_keycode());
+
+ VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
+ << " to keycode: " << keycode << std::dec;
+
+ // If we couldn't determine the Mac virtual key code then ignore the event.
+ if (keycode == InvalidNativeKeycode())
+ return;
+
+ // We use the deprecated event injection API because the new one doesn't
+ // work with switched-out sessions (curtain mode).
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ CGError error = CGPostKeyboardEvent(0, keycode, event.pressed());
+#pragma clang diagnostic pop
+ if (error != kCGErrorSuccess)
+ LOG(WARNING) << "CGPostKeyboardEvent error " << error;
+}
+
+void InputInjectorMac::Core::InjectMouseEvent(const MouseEvent& event) {
+ if (event.has_x() && event.has_y()) {
+ // On multi-monitor systems (0,0) refers to the top-left of the "main"
+ // display, whereas our coordinate scheme places (0,0) at the top-left of
+ // the bounding rectangle around all the displays, so we need to translate
+ // accordingly.
+
+ // Set the mouse position assuming single-monitor.
+ mouse_pos_ = SkIPoint::Make(event.x(), event.y());
+
+ // Fetch the desktop configuration.
+ // TODO(wez): Optimize this out, or at least only enumerate displays in
+ // response to display-changed events. VideoFrameCapturer's VideoFrames
+ // could be augmented to include native cursor coordinates for use by
+ // MouseClampingFilter, removing the need for translation here.
+ media::MacDesktopConfiguration desktop_config =
+ media::MacDesktopConfiguration::GetCurrent(
+ media::MacDesktopConfiguration::TopLeftOrigin);
+
+ // Translate the mouse position into desktop coordinates.
+ mouse_pos_ += SkIPoint::Make(desktop_config.pixel_bounds.left(),
+ desktop_config.pixel_bounds.top());
+
+ // Constrain the mouse position to the desktop coordinates.
+ mouse_pos_ = SkIPoint::Make(
+ std::max(desktop_config.pixel_bounds.left(),
+ std::min(desktop_config.pixel_bounds.right(), mouse_pos_.x())),
+ std::max(desktop_config.pixel_bounds.top(),
+ std::min(desktop_config.pixel_bounds.bottom(), mouse_pos_.y())));
+
+ // Convert from pixel to Density Independent Pixel coordinates.
+ mouse_pos_ = SkIPoint::Make(
+ SkScalarRound(mouse_pos_.x() / desktop_config.dip_to_pixel_scale),
+ SkScalarRound(mouse_pos_.y() / desktop_config.dip_to_pixel_scale));
+
+ VLOG(3) << "Moving mouse to " << mouse_pos_.x() << "," << mouse_pos_.y();
+ }
+ if (event.has_button() && event.has_button_down()) {
+ if (event.button() >= 1 && event.button() <= 3) {
+ VLOG(2) << "Button " << event.button()
+ << (event.button_down() ? " down" : " up");
+ int button_change = 1 << (event.button() - 1);
+ if (event.button_down())
+ mouse_button_state_ |= button_change;
+ else
+ mouse_button_state_ &= ~button_change;
+ } else {
+ VLOG(1) << "Unknown mouse button: " << event.button();
+ }
+ }
+ // We use the deprecated CGPostMouseEvent API because we receive low-level
+ // mouse events, whereas CGEventCreateMouseEvent is for injecting higher-level
+ // events. For example, the deprecated APIs will detect double-clicks or drags
+ // in a way that is consistent with how they would be generated using a local
+ // mouse, whereas the new APIs expect us to inject these higher-level events
+ // directly.
+ CGPoint position = CGPointMake(mouse_pos_.x(), mouse_pos_.y());
+ enum {
+ LeftBit = 1 << (MouseEvent::BUTTON_LEFT - 1),
+ MiddleBit = 1 << (MouseEvent::BUTTON_MIDDLE - 1),
+ RightBit = 1 << (MouseEvent::BUTTON_RIGHT - 1)
+ };
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ CGError error = CGPostMouseEvent(position, true, 3,
+ (mouse_button_state_ & LeftBit) != 0,
+ (mouse_button_state_ & RightBit) != 0,
+ (mouse_button_state_ & MiddleBit) != 0);
+#pragma clang diagnostic pop
+ if (error != kCGErrorSuccess)
+ LOG(WARNING) << "CGPostMouseEvent error " << error;
+
+ if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
+ int delta_x = static_cast<int>(event.wheel_delta_x());
+ int delta_y = static_cast<int>(event.wheel_delta_y());
+ base::mac::ScopedCFTypeRef<CGEventRef> event(
+ CGEventCreateScrollWheelEvent(
+ NULL, kCGScrollEventUnitPixel, 2, delta_y, delta_x));
+ if (event)
+ CGEventPost(kCGSessionEventTap, event);
+ }
+}
+
+void InputInjectorMac::Core::Start(
+ scoped_ptr<protocol::ClipboardStub> client_clipboard) {
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
+ return;
+ }
+
+ clipboard_->Start(client_clipboard.Pass());
+}
+
+void InputInjectorMac::Core::Stop() {
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
+ return;
+ }
+
+ clipboard_->Stop();
+}
+
+InputInjectorMac::Core::~Core() {
+}
+
+} // namespace
+
+scoped_ptr<InputInjector> InputInjector::Create(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+ return scoped_ptr<InputInjector>(new InputInjectorMac(main_task_runner));
+}
+
+} // namespace remoting