16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
26e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
36e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// found in the LICENSE file.
46e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "remoting/host/single_window_input_injector.h"
66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include <ApplicationServices/ApplicationServices.h>
86e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include <Carbon/Carbon.h>
96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/mac/foundation_util.h"
116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/mac/scoped_cftyperef.h"
126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "remoting/proto/event.pb.h"
136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/mac/desktop_configuration.h"
146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace remoting {
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using protocol::ClipboardEvent;
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using protocol::KeyEvent;
196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using protocol::TextEvent;
206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using protocol::MouseEvent;
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class SingleWindowInputInjectorMac : public SingleWindowInputInjector {
236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public:
246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  SingleWindowInputInjectorMac(
256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      webrtc::WindowId window_id,
266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      scoped_ptr<InputInjector> input_injector);
276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual ~SingleWindowInputInjectorMac();
286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // InputInjector interface.
306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void Start(
316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void InjectTextEvent(const TextEvent& event) OVERRIDE;
346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CGRect FindCGRectOfWindow();
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CGWindowID window_id_;
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<InputInjector> input_injector_;
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(SingleWindowInputInjectorMac);
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)SingleWindowInputInjectorMac::SingleWindowInputInjectorMac(
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    webrtc::WindowId window_id,
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<InputInjector> input_injector)
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    : window_id_(static_cast<CGWindowID>(window_id)),
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      input_injector_(input_injector.Pass()) {
516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)SingleWindowInputInjectorMac::~SingleWindowInputInjectorMac() {
546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void SingleWindowInputInjectorMac::Start(
576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  input_injector_->Start(client_clipboard.Pass());
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void SingleWindowInputInjectorMac::InjectKeyEvent(const KeyEvent& event) {
626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  input_injector_->InjectKeyEvent(event);
636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void SingleWindowInputInjectorMac::InjectTextEvent(const TextEvent& event) {
666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  input_injector_->InjectTextEvent(event);
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void SingleWindowInputInjectorMac::InjectMouseEvent(const MouseEvent& event) {
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (event.has_x() && event.has_y()) {
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    CGRect window_rect = FindCGRectOfWindow();
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (CGRectIsNull(window_rect)) {
736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      LOG(ERROR) << "Window rect is null, so forwarding unmodified MouseEvent";
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      input_injector_->InjectMouseEvent(event);
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    webrtc::MacDesktopConfiguration desktop_config =
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        webrtc::MacDesktopConfiguration::GetCurrent(
806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            webrtc::MacDesktopConfiguration::TopLeftOrigin);
816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Create a vector that has the origin of the window.
836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    webrtc::DesktopVector window_pos(window_rect.origin.x,
846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                     window_rect.origin.y);
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // The underlying InputInjector expects coordinates relative to the
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // top-left of the top-left-most monitor, so translate the window origin
886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // to that coordinate scheme.
896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    window_pos.subtract(
906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        webrtc::DesktopVector(desktop_config.pixel_bounds.left(),
916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                              desktop_config.pixel_bounds.top()));
926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // We must make sure we are taking into account the fact that when we
946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // find the window on the host it returns its coordinates in Density
956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Independent coordinates. We have to convert to Density Dependent
966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // because InputInjector assumes Density Dependent coordinates in the
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // MouseEvent.
986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    window_pos.set(window_pos.x() * desktop_config.dip_to_pixel_scale,
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   window_pos.y() * desktop_config.dip_to_pixel_scale);
1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Create a new event with coordinates that are in respect to the window.
1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    MouseEvent modified_event(event);
1036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    modified_event.set_x(event.x() + window_pos.x());
1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    modified_event.set_y(event.y() + window_pos.y());
1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    input_injector_->InjectMouseEvent(modified_event);
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  } else {
1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    input_injector_->InjectMouseEvent(event);
1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void SingleWindowInputInjectorMac::InjectClipboardEvent(
1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const ClipboardEvent& event) {
1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  input_injector_->InjectClipboardEvent(event);
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// This method finds the rectangle of the window we are streaming using
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// |window_id_|. The InputInjector can then use this rectangle
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// to translate the input event to coordinates of the window rather
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// than the screen.
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)CGRect SingleWindowInputInjectorMac::FindCGRectOfWindow() {
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CGRect rect;
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CGWindowID ids[1] = {window_id_};
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  base::ScopedCFTypeRef<CFArrayRef> window_id_array(
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL));
1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  base::ScopedCFTypeRef<CFArrayRef> window_array(
1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      CGWindowListCreateDescriptionFromArray(window_id_array));
1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (window_array == NULL || CFArrayGetCount(window_array) == 0) {
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Could not find the window. It might have been closed.
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    LOG(ERROR) << "Specified window to stream not found for id: "
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)               << window_id_;
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return CGRectNull;
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // We don't use ScopedCFTypeRef for |window_array| because the
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // CFDictionaryRef returned by CFArrayGetValueAtIndex is owned by
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // window_array. The same is true of the |bounds|.
1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CFDictionaryRef window =
1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      base::mac::CFCast<CFDictionaryRef>(
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          CFArrayGetValueAtIndex(window_array, 0));
1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (CFDictionaryContainsKey(window, kCGWindowBounds)) {
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    CFDictionaryRef bounds =
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::mac::GetValueFromDictionary<CFDictionaryRef>(
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            window, kCGWindowBounds);
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (bounds) {
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (CGRectMakeWithDictionaryRepresentation(bounds, &rect)) {
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        return rect;
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      }
1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return CGRectNull;
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)scoped_ptr<InputInjector> SingleWindowInputInjector::CreateForWindow(
1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    webrtc::WindowId window_id,
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<InputInjector> input_injector) {
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<SingleWindowInputInjectorMac> injector(
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      new SingleWindowInputInjectorMac(window_id, input_injector.Pass()));
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return injector.PassAs<InputInjector>();
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace remoting
167