This source file includes following definitions.
- SetOrClearBit
- CreateAndPostKeyEvent
- InjectClipboardEvent
- InjectKeyEvent
- InjectTextEvent
- InjectMouseEvent
- Start
- right_modifiers_
- InjectClipboardEvent
- InjectKeyEvent
- InjectTextEvent
- InjectMouseEvent
- Start
- Stop
- Create
#include "remoting/host/input_injector.h"
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
#include <algorithm>
#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 "base/strings/utf_string_conversions.h"
#include "remoting/host/clipboard.h"
#include "remoting/proto/internal.pb.h"
#include "remoting/protocol/message_decoder.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "ui/events/keycodes/dom4/keycode_converter.h"
namespace remoting {
namespace {
void SetOrClearBit(uint64_t &value, uint64_t bit, bool set_bit) {
value = set_bit ? (value | bit) : (value & ~bit);
}
void CreateAndPostKeyEvent(int keycode,
bool pressed,
int flags,
const base::string16& unicode) {
base::ScopedCFTypeRef<CGEventRef> eventRef(
CGEventCreateKeyboardEvent(NULL, keycode, pressed));
if (eventRef) {
CGEventSetFlags(eventRef, flags);
if (!unicode.empty())
CGEventKeyboardSetUnicodeString(eventRef, unicode.size(), &(unicode[0]));
CGEventPost(kCGSessionEventTap, eventRef);
}
}
const int kVK_RightCommand = 0x36;
using protocol::ClipboardEvent;
using protocol::KeyEvent;
using protocol::TextEvent;
using protocol::MouseEvent;
class InputInjectorMac : public InputInjector {
public:
explicit InputInjectorMac(
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
virtual ~InputInjectorMac();
virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
virtual void InjectTextEvent(const TextEvent& event) OVERRIDE;
virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
virtual void Start(
scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
private:
class Core : public base::RefCountedThreadSafe<Core> {
public:
explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
void InjectClipboardEvent(const ClipboardEvent& event);
void InjectKeyEvent(const KeyEvent& event);
void InjectTextEvent(const TextEvent& event);
void InjectMouseEvent(const MouseEvent& event);
void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
void Stop();
private:
friend class base::RefCountedThreadSafe<Core>;
virtual ~Core();
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
webrtc::DesktopVector mouse_pos_;
uint32 mouse_button_state_;
scoped_ptr<Clipboard> clipboard_;
CGEventFlags left_modifiers_;
CGEventFlags right_modifiers_;
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::InjectTextEvent(const TextEvent& event) {
core_->InjectTextEvent(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()),
left_modifiers_(0),
right_modifiers_(0) {
#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_->InjectClipboardEvent(event);
}
void InputInjectorMac::Core::InjectKeyEvent(const KeyEvent& event) {
if (!event.has_pressed() || !event.has_usb_keycode())
return;
ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
int keycode = key_converter->UsbKeycodeToNativeKeycode(event.usb_keycode());
VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
<< " to keycode: " << keycode << std::dec;
if (keycode == key_converter->InvalidNativeKeycode())
return;
if (keycode == kVK_Command) {
SetOrClearBit(left_modifiers_, kCGEventFlagMaskCommand, event.pressed());
} else if (keycode == kVK_Shift) {
SetOrClearBit(left_modifiers_, kCGEventFlagMaskShift, event.pressed());
} else if (keycode == kVK_Control) {
SetOrClearBit(left_modifiers_, kCGEventFlagMaskControl, event.pressed());
} else if (keycode == kVK_Option) {
SetOrClearBit(left_modifiers_, kCGEventFlagMaskAlternate, event.pressed());
} else if (keycode == kVK_RightCommand) {
SetOrClearBit(right_modifiers_, kCGEventFlagMaskCommand, event.pressed());
} else if (keycode == kVK_RightShift) {
SetOrClearBit(right_modifiers_, kCGEventFlagMaskShift, event.pressed());
} else if (keycode == kVK_RightControl) {
SetOrClearBit(right_modifiers_, kCGEventFlagMaskControl, event.pressed());
} else if (keycode == kVK_RightOption) {
SetOrClearBit(right_modifiers_, kCGEventFlagMaskAlternate, event.pressed());
}
uint64_t flags = left_modifiers_ | right_modifiers_;
if (event.lock_states() & protocol::KeyEvent::LOCK_STATES_CAPSLOCK)
flags |= kCGEventFlagMaskAlphaShift;
CreateAndPostKeyEvent(keycode, event.pressed(), flags, base::string16());
}
void InputInjectorMac::Core::InjectTextEvent(const TextEvent& event) {
DCHECK(event.has_text());
base::string16 text = base::UTF8ToUTF16(event.text());
CreateAndPostKeyEvent(kVK_Space, true, 0, text);
CreateAndPostKeyEvent(kVK_Space, false, 0, text);
}
void InputInjectorMac::Core::InjectMouseEvent(const MouseEvent& event) {
if (event.has_x() && event.has_y()) {
mouse_pos_.set(event.x(), event.y());
webrtc::MacDesktopConfiguration desktop_config =
webrtc::MacDesktopConfiguration::GetCurrent(
webrtc::MacDesktopConfiguration::TopLeftOrigin);
mouse_pos_.add(webrtc::DesktopVector(desktop_config.pixel_bounds.left(),
desktop_config.pixel_bounds.top()));
mouse_pos_.set(
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())));
mouse_pos_.set(mouse_pos_.x() / desktop_config.dip_to_pixel_scale,
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();
}
}
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::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() {}
}
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));
}
}