1// Copyright (c) 2012 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 "ui/aura/remote_window_tree_host_win.h" 6 7#include <windows.h> 8 9#include <algorithm> 10 11#include "base/message_loop/message_loop.h" 12#include "ipc/ipc_message.h" 13#include "ipc/ipc_sender.h" 14#include "ui/aura/client/aura_constants.h" 15#include "ui/aura/client/cursor_client.h" 16#include "ui/aura/window_event_dispatcher.h" 17#include "ui/aura/window_property.h" 18#include "ui/base/cursor/cursor_loader_win.h" 19#include "ui/base/ime/composition_text.h" 20#include "ui/base/ime/input_method.h" 21#include "ui/base/ime/remote_input_method_win.h" 22#include "ui/base/ime/text_input_client.h" 23#include "ui/base/view_prop.h" 24#include "ui/events/event_utils.h" 25#include "ui/events/keycodes/keyboard_code_conversion_win.h" 26#include "ui/gfx/insets.h" 27#include "ui/gfx/win/dpi.h" 28#include "ui/metro_viewer/metro_viewer_messages.h" 29 30namespace aura { 31 32namespace { 33 34const char* kWindowTreeHostWinKey = "__AURA_REMOTE_WINDOW_TREE_HOST_WIN__"; 35 36// Sets the keystate for the virtual key passed in to down or up. 37void SetKeyState(uint8* key_states, bool key_down, uint32 virtual_key_code) { 38 DCHECK(key_states); 39 40 if (key_down) 41 key_states[virtual_key_code] |= 0x80; 42 else 43 key_states[virtual_key_code] &= 0x7F; 44} 45 46// Sets the keyboard states for the Shift/Control/Alt/Caps lock keys. 47void SetVirtualKeyStates(uint32 flags) { 48 uint8 keyboard_state[256] = {0}; 49 ::GetKeyboardState(keyboard_state); 50 51 SetKeyState(keyboard_state, !!(flags & ui::EF_SHIFT_DOWN), VK_SHIFT); 52 SetKeyState(keyboard_state, !!(flags & ui::EF_CONTROL_DOWN), VK_CONTROL); 53 SetKeyState(keyboard_state, !!(flags & ui::EF_ALT_DOWN), VK_MENU); 54 SetKeyState(keyboard_state, !!(flags & ui::EF_CAPS_LOCK_DOWN), VK_CAPITAL); 55 SetKeyState(keyboard_state, !!(flags & ui::EF_LEFT_MOUSE_BUTTON), VK_LBUTTON); 56 SetKeyState(keyboard_state, !!(flags & ui::EF_RIGHT_MOUSE_BUTTON), 57 VK_RBUTTON); 58 SetKeyState(keyboard_state, !!(flags & ui::EF_MIDDLE_MOUSE_BUTTON), 59 VK_MBUTTON); 60 61 ::SetKeyboardState(keyboard_state); 62} 63 64void FillCompositionText( 65 const base::string16& text, 66 int32 selection_start, 67 int32 selection_end, 68 const std::vector<metro_viewer::UnderlineInfo>& underlines, 69 ui::CompositionText* composition_text) { 70 composition_text->Clear(); 71 composition_text->text = text; 72 composition_text->selection.set_start(selection_start); 73 composition_text->selection.set_end(selection_end); 74 composition_text->underlines.resize(underlines.size()); 75 for (size_t i = 0; i < underlines.size(); ++i) { 76 composition_text->underlines[i].start_offset = underlines[i].start_offset; 77 composition_text->underlines[i].end_offset = underlines[i].end_offset; 78 composition_text->underlines[i].color = SK_ColorBLACK; 79 composition_text->underlines[i].thick = underlines[i].thick; 80 composition_text->underlines[i].background_color = SK_ColorTRANSPARENT; 81 } 82} 83 84} // namespace 85 86RemoteWindowTreeHostWin* g_instance = NULL; 87 88// static 89RemoteWindowTreeHostWin* RemoteWindowTreeHostWin::Instance() { 90 return g_instance; 91} 92 93RemoteWindowTreeHostWin::RemoteWindowTreeHostWin() 94 : remote_window_(NULL), 95 host_(NULL), 96 ignore_mouse_moves_until_set_cursor_ack_(false), 97 event_flags_(0), 98 window_size_(aura::WindowTreeHost::GetNativeScreenSize()) { 99 CHECK(!g_instance); 100 g_instance = this; 101 prop_.reset(new ui::ViewProp(NULL, kWindowTreeHostWinKey, this)); 102 CreateCompositor(GetAcceleratedWidget()); 103} 104 105RemoteWindowTreeHostWin::~RemoteWindowTreeHostWin() { 106 DestroyCompositor(); 107 DestroyDispatcher(); 108 DCHECK_EQ(g_instance, this); 109 g_instance = NULL; 110} 111 112// static 113bool RemoteWindowTreeHostWin::IsValid() { 114 return Instance()->remote_window_ != NULL; 115} 116 117void RemoteWindowTreeHostWin::SetRemoteWindowHandle(HWND remote_window) { 118 remote_window_ = remote_window; 119} 120 121void RemoteWindowTreeHostWin::Connected(IPC::Sender* host) { 122 CHECK(host_ == NULL); 123 DCHECK(remote_window_); 124 host_ = host; 125 // Recreate the compositor for the target surface represented by the 126 // remote_window HWND. 127 CreateCompositor(remote_window_); 128 InitCompositor(); 129} 130 131void RemoteWindowTreeHostWin::Disconnected() { 132 // Don't CHECK here, Disconnected is called on a channel error which can 133 // happen before we're successfully Connected. 134 if (!host_) 135 return; 136 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 137 GetRemoteInputMethodPrivate(); 138 if (remote_input_method_private) 139 remote_input_method_private->SetRemoteDelegate(NULL); 140 host_ = NULL; 141 remote_window_ = NULL; 142} 143 144bool RemoteWindowTreeHostWin::OnMessageReceived(const IPC::Message& message) { 145 bool handled = true; 146 IPC_BEGIN_MESSAGE_MAP(RemoteWindowTreeHostWin, message) 147 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseMoved, OnMouseMoved) 148 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseButton, OnMouseButton) 149 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyDown, OnKeyDown) 150 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyUp, OnKeyUp) 151 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_Character, OnChar) 152 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowActivated, 153 OnWindowActivated) 154 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_EdgeGesture, OnEdgeGesture) 155 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchDown, 156 OnTouchDown) 157 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchUp, 158 OnTouchUp) 159 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchMoved, 160 OnTouchMoved) 161 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPosAck, 162 OnSetCursorPosAck) 163 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCandidatePopupChanged, 164 OnImeCandidatePopupChanged) 165 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCompositionChanged, 166 OnImeCompositionChanged) 167 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextCommitted, 168 OnImeTextCommitted) 169 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeInputSourceChanged, 170 OnImeInputSourceChanged) 171 IPC_MESSAGE_UNHANDLED(handled = false) 172 IPC_END_MESSAGE_MAP() 173 return handled; 174} 175 176void RemoteWindowTreeHostWin::HandleOpenURLOnDesktop( 177 const base::FilePath& shortcut, 178 const base::string16& url) { 179 if (!host_) 180 return; 181 host_->Send(new MetroViewerHostMsg_OpenURLOnDesktop(shortcut, url)); 182} 183 184void RemoteWindowTreeHostWin::HandleWindowSizeChanged(uint32 width, 185 uint32 height) { 186 SetBounds(gfx::Rect(0, 0, width, height)); 187} 188 189bool RemoteWindowTreeHostWin::IsForegroundWindow() { 190 return ::GetForegroundWindow() == remote_window_; 191} 192 193Window* RemoteWindowTreeHostWin::GetAshWindow() { 194 return window(); 195} 196 197ui::EventSource* RemoteWindowTreeHostWin::GetEventSource() { 198 return this; 199} 200 201gfx::AcceleratedWidget RemoteWindowTreeHostWin::GetAcceleratedWidget() { 202 if (remote_window_) 203 return remote_window_; 204 // Getting here should only happen for ash_unittests.exe and related code. 205 return ::GetDesktopWindow(); 206} 207 208void RemoteWindowTreeHostWin::Show() { 209 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 210 GetRemoteInputMethodPrivate(); 211 if (remote_input_method_private) 212 remote_input_method_private->SetRemoteDelegate(this); 213} 214 215void RemoteWindowTreeHostWin::Hide() { 216 NOTIMPLEMENTED(); 217} 218 219gfx::Rect RemoteWindowTreeHostWin::GetBounds() const { 220 return gfx::Rect(window_size_); 221} 222 223void RemoteWindowTreeHostWin::SetBounds(const gfx::Rect& bounds) { 224 window_size_ = bounds.size(); 225 OnHostResized(bounds.size()); 226} 227 228gfx::Point RemoteWindowTreeHostWin::GetLocationOnNativeScreen() const { 229 return gfx::Point(0, 0); 230} 231 232void RemoteWindowTreeHostWin::SetCapture() { 233} 234 235void RemoteWindowTreeHostWin::ReleaseCapture() { 236} 237 238void RemoteWindowTreeHostWin::SetCursorNative(gfx::NativeCursor native_cursor) { 239 if (!host_) 240 return; 241 host_->Send( 242 new MetroViewerHostMsg_SetCursor(uint64(native_cursor.platform()))); 243} 244 245void RemoteWindowTreeHostWin::MoveCursorToNative(const gfx::Point& location) { 246 VLOG(1) << "In MoveCursorTo: " << location.x() << ", " << location.y(); 247 if (!host_) 248 return; 249 250 // This function can be called in cases like when the mouse cursor is 251 // restricted within a viewport (For e.g. LockCursor) which assumes that 252 // subsequent mouse moves would be received starting with the new cursor 253 // coordinates. This is a challenge for Windows ASH for the reasons 254 // outlined below. 255 // Other cases which don't expect this behavior should continue to work 256 // without issues. 257 258 // The mouse events are received by the viewer process and sent to the 259 // browser. If we invoke the SetCursor API here we continue to receive 260 // mouse messages from the viewer which were posted before the SetCursor 261 // API executes which messes up the state in the browser. To workaround 262 // this we invoke the SetCursor API in the viewer process and ignore 263 // mouse messages until we received an ACK from the viewer indicating that 264 // the SetCursor operation completed. 265 ignore_mouse_moves_until_set_cursor_ack_ = true; 266 VLOG(1) << "In MoveCursorTo. Sending IPC"; 267 host_->Send(new MetroViewerHostMsg_SetCursorPos(location.x(), location.y())); 268} 269 270void RemoteWindowTreeHostWin::OnCursorVisibilityChangedNative(bool show) { 271 NOTIMPLEMENTED(); 272} 273 274void RemoteWindowTreeHostWin::PostNativeEvent( 275 const base::NativeEvent& native_event) { 276} 277 278void RemoteWindowTreeHostWin::OnDeviceScaleFactorChanged( 279 float device_scale_factor) { 280 NOTIMPLEMENTED(); 281} 282 283ui::EventProcessor* RemoteWindowTreeHostWin::GetEventProcessor() { 284 return dispatcher(); 285} 286 287void RemoteWindowTreeHostWin::CancelComposition() { 288 if (!host_) 289 return; 290 host_->Send(new MetroViewerHostMsg_ImeCancelComposition); 291} 292 293void RemoteWindowTreeHostWin::OnTextInputClientUpdated( 294 const std::vector<int32>& input_scopes, 295 const std::vector<gfx::Rect>& composition_character_bounds) { 296 if (!host_) 297 return; 298 std::vector<metro_viewer::CharacterBounds> character_bounds; 299 for (size_t i = 0; i < composition_character_bounds.size(); ++i) { 300 const gfx::Rect& rect = composition_character_bounds[i]; 301 metro_viewer::CharacterBounds bounds; 302 bounds.left = rect.x(); 303 bounds.top = rect.y(); 304 bounds.right = rect.right(); 305 bounds.bottom = rect.bottom(); 306 character_bounds.push_back(bounds); 307 } 308 host_->Send(new MetroViewerHostMsg_ImeTextInputClientUpdated( 309 input_scopes, character_bounds)); 310} 311 312gfx::Point PointFromNativeEvent(int32 x, int32 y) { 313 static float scale_factor = gfx::GetDPIScale(); 314 gfx::Point result( x * scale_factor, y * scale_factor); 315 return result; 316} 317 318void RemoteWindowTreeHostWin::OnMouseMoved(int32 x, int32 y, int32 flags) { 319 if (ignore_mouse_moves_until_set_cursor_ack_) 320 return; 321 322 gfx::Point location = PointFromNativeEvent(x, y); 323 ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, flags, 0); 324 SendEventToProcessor(&event); 325} 326 327void RemoteWindowTreeHostWin::OnMouseButton( 328 const MetroViewerHostMsg_MouseButtonParams& params) { 329 gfx::Point location = PointFromNativeEvent(params.x, params.y); 330 ui::MouseEvent mouse_event(params.event_type, location, location, 331 static_cast<int>(params.flags), 332 static_cast<int>(params.changed_button)); 333 334 SetEventFlags(params.flags | key_event_flags()); 335 if (params.event_type == ui::ET_MOUSEWHEEL) { 336 int x_offset = params.is_horizontal_wheel ? params.extra : 0; 337 int y_offset = !params.is_horizontal_wheel ? params.extra : 0; 338 ui::MouseWheelEvent wheel_event(mouse_event, x_offset, y_offset); 339 SendEventToProcessor(&wheel_event); 340 } else if (params.event_type == ui::ET_MOUSE_PRESSED) { 341 // TODO(shrikant): Ideally modify code in event.cc by adding automatic 342 // tracking of double clicks in synthetic MouseEvent constructor code. 343 // Non-synthetic MouseEvent constructor code does automatically track 344 // this. Need to use some caution while modifying synthetic constructor 345 // as many tests and other code paths depend on it and apparently 346 // specifically depend on non implicit tracking of previous mouse event. 347 if (last_mouse_click_event_ && 348 ui::MouseEvent::IsRepeatedClickEvent(mouse_event, 349 *last_mouse_click_event_)) { 350 mouse_event.SetClickCount(2); 351 } else { 352 mouse_event.SetClickCount(1); 353 } 354 last_mouse_click_event_ .reset(new ui::MouseEvent(mouse_event)); 355 SendEventToProcessor(&mouse_event); 356 } else { 357 SendEventToProcessor(&mouse_event); 358 } 359} 360 361void RemoteWindowTreeHostWin::OnKeyDown(uint32 vkey, 362 uint32 repeat_count, 363 uint32 scan_code, 364 uint32 flags) { 365 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, vkey, repeat_count, scan_code, 366 flags, false); 367} 368 369void RemoteWindowTreeHostWin::OnKeyUp(uint32 vkey, 370 uint32 repeat_count, 371 uint32 scan_code, 372 uint32 flags) { 373 DispatchKeyboardMessage(ui::ET_KEY_RELEASED, vkey, repeat_count, scan_code, 374 flags, false); 375} 376 377void RemoteWindowTreeHostWin::OnChar(uint32 key_code, 378 uint32 repeat_count, 379 uint32 scan_code, 380 uint32 flags) { 381 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, key_code, repeat_count, 382 scan_code, flags, true); 383} 384 385void RemoteWindowTreeHostWin::OnWindowActivated() { 386 OnHostActivated(); 387} 388 389void RemoteWindowTreeHostWin::OnEdgeGesture() { 390 ui::GestureEvent event( 391 ui::ET_GESTURE_WIN8_EDGE_SWIPE, 392 0, 393 0, 394 0, 395 ui::EventTimeForNow(), 396 ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE, 0, 0), 397 0); 398 SendEventToProcessor(&event); 399} 400 401void RemoteWindowTreeHostWin::OnTouchDown(int32 x, 402 int32 y, 403 uint64 timestamp, 404 uint32 pointer_id) { 405 gfx::Point location = PointFromNativeEvent(x, y); 406 ui::TouchEvent event(ui::ET_TOUCH_PRESSED, 407 location, 408 pointer_id, 409 base::TimeDelta::FromMicroseconds(timestamp)); 410 SendEventToProcessor(&event); 411} 412 413void RemoteWindowTreeHostWin::OnTouchUp(int32 x, 414 int32 y, 415 uint64 timestamp, 416 uint32 pointer_id) { 417 gfx::Point location = PointFromNativeEvent(x, y); 418 ui::TouchEvent event(ui::ET_TOUCH_RELEASED, 419 location, 420 pointer_id, 421 base::TimeDelta::FromMicroseconds(timestamp)); 422 SendEventToProcessor(&event); 423} 424 425void RemoteWindowTreeHostWin::OnTouchMoved(int32 x, 426 int32 y, 427 uint64 timestamp, 428 uint32 pointer_id) { 429 gfx::Point location = PointFromNativeEvent(x, y); 430 ui::TouchEvent event(ui::ET_TOUCH_MOVED, 431 location, 432 pointer_id, 433 base::TimeDelta::FromMicroseconds(timestamp)); 434 SendEventToProcessor(&event); 435} 436 437void RemoteWindowTreeHostWin::OnSetCursorPosAck() { 438 DCHECK(ignore_mouse_moves_until_set_cursor_ack_); 439 ignore_mouse_moves_until_set_cursor_ack_ = false; 440} 441 442ui::RemoteInputMethodPrivateWin* 443RemoteWindowTreeHostWin::GetRemoteInputMethodPrivate() { 444 ui::InputMethod* input_method = GetAshWindow()->GetProperty( 445 aura::client::kRootWindowInputMethodKey); 446 return ui::RemoteInputMethodPrivateWin::Get(input_method); 447} 448 449void RemoteWindowTreeHostWin::OnImeCandidatePopupChanged(bool visible) { 450 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 451 GetRemoteInputMethodPrivate(); 452 if (!remote_input_method_private) 453 return; 454 remote_input_method_private->OnCandidatePopupChanged(visible); 455} 456 457void RemoteWindowTreeHostWin::OnImeCompositionChanged( 458 const base::string16& text, 459 int32 selection_start, 460 int32 selection_end, 461 const std::vector<metro_viewer::UnderlineInfo>& underlines) { 462 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 463 GetRemoteInputMethodPrivate(); 464 if (!remote_input_method_private) 465 return; 466 ui::CompositionText composition_text; 467 FillCompositionText( 468 text, selection_start, selection_end, underlines, &composition_text); 469 remote_input_method_private->OnCompositionChanged(composition_text); 470} 471 472void RemoteWindowTreeHostWin::OnImeTextCommitted(const base::string16& text) { 473 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 474 GetRemoteInputMethodPrivate(); 475 if (!remote_input_method_private) 476 return; 477 remote_input_method_private->OnTextCommitted(text); 478} 479 480void RemoteWindowTreeHostWin::OnImeInputSourceChanged(uint16 language_id, 481 bool is_ime) { 482 ui::RemoteInputMethodPrivateWin* remote_input_method_private = 483 GetRemoteInputMethodPrivate(); 484 if (!remote_input_method_private) 485 return; 486 remote_input_method_private->OnInputSourceChanged(language_id, is_ime); 487} 488 489void RemoteWindowTreeHostWin::DispatchKeyboardMessage(ui::EventType type, 490 uint32 vkey, 491 uint32 repeat_count, 492 uint32 scan_code, 493 uint32 flags, 494 bool is_character) { 495 SetEventFlags(flags | mouse_event_flags()); 496 if (base::MessageLoop::current()->IsNested()) { 497 int index = (flags & ui::EF_ALT_DOWN) ? 1 : 0; 498 const int char_message[] = {WM_CHAR, WM_SYSCHAR}; 499 const int keydown_message[] = {WM_KEYDOWN, WM_SYSKEYDOWN}; 500 const int keyup_message[] = {WM_KEYUP, WM_SYSKEYUP}; 501 uint32 message = is_character 502 ? char_message[index] 503 : (type == ui::ET_KEY_PRESSED ? keydown_message[index] 504 : keyup_message[index]); 505 ::PostThreadMessage(::GetCurrentThreadId(), 506 message, 507 vkey, 508 repeat_count | scan_code >> 15); 509 } else { 510 ui::KeyEvent event(type, 511 ui::KeyboardCodeForWindowsKeyCode(vkey), 512 flags, 513 is_character); 514 SendEventToProcessor(&event); 515 } 516} 517 518void RemoteWindowTreeHostWin::SetEventFlags(uint32 flags) { 519 if (flags == event_flags_) 520 return; 521 event_flags_ = flags; 522 SetVirtualKeyStates(event_flags_); 523} 524 525} // namespace aura 526