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