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