Home | History | Annotate | Download | only in host
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/single_window_input_injector.h"
      6 
      7 #include <ApplicationServices/ApplicationServices.h>
      8 #include <Carbon/Carbon.h>
      9 
     10 #include "base/mac/foundation_util.h"
     11 #include "base/mac/scoped_cftyperef.h"
     12 #include "remoting/proto/event.pb.h"
     13 #include "third_party/webrtc/modules/desktop_capture/mac/desktop_configuration.h"
     14 
     15 namespace remoting {
     16 
     17 using protocol::ClipboardEvent;
     18 using protocol::KeyEvent;
     19 using protocol::TextEvent;
     20 using protocol::MouseEvent;
     21 
     22 class SingleWindowInputInjectorMac : public SingleWindowInputInjector {
     23  public:
     24   SingleWindowInputInjectorMac(
     25       webrtc::WindowId window_id,
     26       scoped_ptr<InputInjector> input_injector);
     27   virtual ~SingleWindowInputInjectorMac();
     28 
     29   // InputInjector interface.
     30   virtual void Start(
     31       scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
     32   virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
     33   virtual void InjectTextEvent(const TextEvent& event) OVERRIDE;
     34   virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
     35   virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
     36 
     37  private:
     38   CGRect FindCGRectOfWindow();
     39 
     40   CGWindowID window_id_;
     41   scoped_ptr<InputInjector> input_injector_;
     42 
     43   DISALLOW_COPY_AND_ASSIGN(SingleWindowInputInjectorMac);
     44 };
     45 
     46 SingleWindowInputInjectorMac::SingleWindowInputInjectorMac(
     47     webrtc::WindowId window_id,
     48     scoped_ptr<InputInjector> input_injector)
     49     : window_id_(static_cast<CGWindowID>(window_id)),
     50       input_injector_(input_injector.Pass()) {
     51 }
     52 
     53 SingleWindowInputInjectorMac::~SingleWindowInputInjectorMac() {
     54 }
     55 
     56 void SingleWindowInputInjectorMac::Start(
     57     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
     58   input_injector_->Start(client_clipboard.Pass());
     59 }
     60 
     61 void SingleWindowInputInjectorMac::InjectKeyEvent(const KeyEvent& event) {
     62   input_injector_->InjectKeyEvent(event);
     63 }
     64 
     65 void SingleWindowInputInjectorMac::InjectTextEvent(const TextEvent& event) {
     66   input_injector_->InjectTextEvent(event);
     67 }
     68 
     69 void SingleWindowInputInjectorMac::InjectMouseEvent(const MouseEvent& event) {
     70   if (event.has_x() && event.has_y()) {
     71     CGRect window_rect = FindCGRectOfWindow();
     72     if (CGRectIsNull(window_rect)) {
     73       LOG(ERROR) << "Window rect is null, so forwarding unmodified MouseEvent";
     74       input_injector_->InjectMouseEvent(event);
     75       return;
     76     }
     77 
     78     webrtc::MacDesktopConfiguration desktop_config =
     79         webrtc::MacDesktopConfiguration::GetCurrent(
     80             webrtc::MacDesktopConfiguration::TopLeftOrigin);
     81 
     82     // Create a vector that has the origin of the window.
     83     webrtc::DesktopVector window_pos(window_rect.origin.x,
     84                                      window_rect.origin.y);
     85 
     86     // The underlying InputInjector expects coordinates relative to the
     87     // top-left of the top-left-most monitor, so translate the window origin
     88     // to that coordinate scheme.
     89     window_pos.subtract(
     90         webrtc::DesktopVector(desktop_config.pixel_bounds.left(),
     91                               desktop_config.pixel_bounds.top()));
     92 
     93     // We must make sure we are taking into account the fact that when we
     94     // find the window on the host it returns its coordinates in Density
     95     // Independent coordinates. We have to convert to Density Dependent
     96     // because InputInjector assumes Density Dependent coordinates in the
     97     // MouseEvent.
     98     window_pos.set(window_pos.x() * desktop_config.dip_to_pixel_scale,
     99                    window_pos.y() * desktop_config.dip_to_pixel_scale);
    100 
    101     // Create a new event with coordinates that are in respect to the window.
    102     MouseEvent modified_event(event);
    103     modified_event.set_x(event.x() + window_pos.x());
    104     modified_event.set_y(event.y() + window_pos.y());
    105     input_injector_->InjectMouseEvent(modified_event);
    106   } else {
    107     input_injector_->InjectMouseEvent(event);
    108   }
    109 }
    110 
    111 void SingleWindowInputInjectorMac::InjectClipboardEvent(
    112     const ClipboardEvent& event) {
    113   input_injector_->InjectClipboardEvent(event);
    114 }
    115 
    116 // This method finds the rectangle of the window we are streaming using
    117 // |window_id_|. The InputInjector can then use this rectangle
    118 // to translate the input event to coordinates of the window rather
    119 // than the screen.
    120 CGRect SingleWindowInputInjectorMac::FindCGRectOfWindow() {
    121   CGRect rect;
    122   CGWindowID ids[1] = {window_id_};
    123   base::ScopedCFTypeRef<CFArrayRef> window_id_array(
    124       CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL));
    125 
    126   base::ScopedCFTypeRef<CFArrayRef> window_array(
    127       CGWindowListCreateDescriptionFromArray(window_id_array));
    128 
    129   if (window_array == NULL || CFArrayGetCount(window_array) == 0) {
    130     // Could not find the window. It might have been closed.
    131     LOG(ERROR) << "Specified window to stream not found for id: "
    132                << window_id_;
    133     return CGRectNull;
    134   }
    135 
    136   // We don't use ScopedCFTypeRef for |window_array| because the
    137   // CFDictionaryRef returned by CFArrayGetValueAtIndex is owned by
    138   // window_array. The same is true of the |bounds|.
    139   CFDictionaryRef window =
    140       base::mac::CFCast<CFDictionaryRef>(
    141           CFArrayGetValueAtIndex(window_array, 0));
    142 
    143   if (CFDictionaryContainsKey(window, kCGWindowBounds)) {
    144     CFDictionaryRef bounds =
    145         base::mac::GetValueFromDictionary<CFDictionaryRef>(
    146             window, kCGWindowBounds);
    147 
    148     if (bounds) {
    149       if (CGRectMakeWithDictionaryRepresentation(bounds, &rect)) {
    150         return rect;
    151       }
    152     }
    153   }
    154 
    155   return CGRectNull;
    156 }
    157 
    158 scoped_ptr<InputInjector> SingleWindowInputInjector::CreateForWindow(
    159     webrtc::WindowId window_id,
    160     scoped_ptr<InputInjector> input_injector) {
    161   scoped_ptr<SingleWindowInputInjectorMac> injector(
    162       new SingleWindowInputInjectorMac(window_id, input_injector.Pass()));
    163   return injector.PassAs<InputInjector>();
    164 }
    165 
    166 }  // namespace remoting
    167