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_root_window_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/capture_client.h" 15 #include "ui/aura/client/cursor_client.h" 16 #include "ui/aura/root_window.h" 17 #include "ui/base/cursor/cursor_loader_win.h" 18 #include "ui/base/events/event_utils.h" 19 #include "ui/base/keycodes/keyboard_code_conversion_win.h" 20 #include "ui/base/view_prop.h" 21 #include "ui/gfx/insets.h" 22 #include "ui/metro_viewer/metro_viewer_messages.h" 23 24 namespace aura { 25 26 namespace { 27 28 const char* kRootWindowHostWinKey = "__AURA_REMOTE_ROOT_WINDOW_HOST_WIN__"; 29 30 // Sets the keystate for the virtual key passed in to down or up. 31 void SetKeyState(uint8* key_states, bool key_down, uint32 virtual_key_code) { 32 DCHECK(key_states); 33 34 if (key_down) 35 key_states[virtual_key_code] |= 0x80; 36 else 37 key_states[virtual_key_code] &= 0x7F; 38 } 39 40 // Sets the keyboard states for the Shift/Control/Alt/Caps lock keys. 41 void SetVirtualKeyStates(uint32 flags) { 42 uint8 keyboard_state[256] = {0}; 43 ::GetKeyboardState(keyboard_state); 44 45 SetKeyState(keyboard_state, !!(flags & ui::EF_SHIFT_DOWN), VK_SHIFT); 46 SetKeyState(keyboard_state, !!(flags & ui::EF_CONTROL_DOWN), VK_CONTROL); 47 SetKeyState(keyboard_state, !!(flags & ui::EF_ALT_DOWN), VK_MENU); 48 SetKeyState(keyboard_state, !!(flags & ui::EF_CAPS_LOCK_DOWN), VK_CAPITAL); 49 50 ::SetKeyboardState(keyboard_state); 51 } 52 53 } // namespace 54 55 void HandleOpenFile(const base::string16& title, 56 const base::FilePath& default_path, 57 const base::string16& filter, 58 const OpenFileCompletion& on_success, 59 const FileSelectionCanceled& on_failure) { 60 DCHECK(aura::RemoteRootWindowHostWin::Instance()); 61 aura::RemoteRootWindowHostWin::Instance()->HandleOpenFile(title, 62 default_path, 63 filter, 64 on_success, 65 on_failure); 66 } 67 68 void HandleOpenMultipleFiles(const base::string16& title, 69 const base::FilePath& default_path, 70 const base::string16& filter, 71 const OpenMultipleFilesCompletion& on_success, 72 const FileSelectionCanceled& on_failure) { 73 DCHECK(aura::RemoteRootWindowHostWin::Instance()); 74 aura::RemoteRootWindowHostWin::Instance()->HandleOpenMultipleFiles( 75 title, 76 default_path, 77 filter, 78 on_success, 79 on_failure); 80 } 81 82 void HandleSaveFile(const base::string16& title, 83 const base::FilePath& default_path, 84 const base::string16& filter, 85 int filter_index, 86 const base::string16& default_extension, 87 const SaveFileCompletion& on_success, 88 const FileSelectionCanceled& on_failure) { 89 DCHECK(aura::RemoteRootWindowHostWin::Instance()); 90 aura::RemoteRootWindowHostWin::Instance()->HandleSaveFile(title, 91 default_path, 92 filter, 93 filter_index, 94 default_extension, 95 on_success, 96 on_failure); 97 } 98 99 void HandleSelectFolder(const base::string16& title, 100 const SelectFolderCompletion& on_success, 101 const FileSelectionCanceled& on_failure) { 102 DCHECK(aura::RemoteRootWindowHostWin::Instance()); 103 aura::RemoteRootWindowHostWin::Instance()->HandleSelectFolder(title, 104 on_success, 105 on_failure); 106 } 107 108 RemoteRootWindowHostWin* g_instance = NULL; 109 110 RemoteRootWindowHostWin* RemoteRootWindowHostWin::Instance() { 111 return g_instance; 112 } 113 114 RemoteRootWindowHostWin* RemoteRootWindowHostWin::Create( 115 const gfx::Rect& bounds) { 116 g_instance = new RemoteRootWindowHostWin(bounds); 117 return g_instance; 118 } 119 120 RemoteRootWindowHostWin::RemoteRootWindowHostWin(const gfx::Rect& bounds) 121 : delegate_(NULL), 122 host_(NULL), 123 ignore_mouse_moves_until_set_cursor_ack_(false) { 124 prop_.reset(new ui::ViewProp(NULL, kRootWindowHostWinKey, this)); 125 } 126 127 RemoteRootWindowHostWin::~RemoteRootWindowHostWin() { 128 } 129 130 void RemoteRootWindowHostWin::Connected(IPC::Sender* host) { 131 CHECK(host_ == NULL); 132 host_ = host; 133 } 134 135 void RemoteRootWindowHostWin::Disconnected() { 136 CHECK(host_ != NULL); 137 host_ = NULL; 138 } 139 140 bool RemoteRootWindowHostWin::OnMessageReceived(const IPC::Message& message) { 141 bool handled = true; 142 IPC_BEGIN_MESSAGE_MAP(RemoteRootWindowHostWin, message) 143 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseMoved, OnMouseMoved) 144 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseButton, OnMouseButton) 145 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyDown, OnKeyDown) 146 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyUp, OnKeyUp) 147 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_Character, OnChar) 148 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_VisibilityChanged, 149 OnVisibilityChanged) 150 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchDown, 151 OnTouchDown) 152 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchUp, 153 OnTouchUp) 154 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchMoved, 155 OnTouchMoved) 156 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileSaveAsDone, 157 OnFileSaveAsDone) 158 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileOpenDone, 159 OnFileOpenDone) 160 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MultiFileOpenDone, 161 OnMultiFileOpenDone) 162 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SelectFolderDone, 163 OnSelectFolderDone) 164 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowActivated, 165 OnWindowActivated) 166 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPosAck, 167 OnSetCursorPosAck) 168 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowSizeChanged, 169 OnWindowSizeChanged) 170 IPC_MESSAGE_UNHANDLED(handled = false) 171 IPC_END_MESSAGE_MAP() 172 return handled; 173 } 174 175 void RemoteRootWindowHostWin::HandleOpenFile( 176 const base::string16& title, 177 const base::FilePath& default_path, 178 const base::string16& filter, 179 const OpenFileCompletion& on_success, 180 const FileSelectionCanceled& on_failure) { 181 if (!host_) 182 return; 183 184 // Can only have one of these operations in flight. 185 DCHECK(file_open_completion_callback_.is_null()); 186 DCHECK(failure_callback_.is_null()); 187 188 file_open_completion_callback_ = on_success; 189 failure_callback_ = on_failure; 190 191 host_->Send(new MetroViewerHostMsg_DisplayFileOpen(title, 192 filter, 193 default_path, 194 false)); 195 } 196 197 void RemoteRootWindowHostWin::HandleOpenMultipleFiles( 198 const base::string16& title, 199 const base::FilePath& default_path, 200 const base::string16& filter, 201 const OpenMultipleFilesCompletion& on_success, 202 const FileSelectionCanceled& on_failure) { 203 if (!host_) 204 return; 205 206 // Can only have one of these operations in flight. 207 DCHECK(multi_file_open_completion_callback_.is_null()); 208 DCHECK(failure_callback_.is_null()); 209 multi_file_open_completion_callback_ = on_success; 210 failure_callback_ = on_failure; 211 212 host_->Send(new MetroViewerHostMsg_DisplayFileOpen(title, 213 filter, 214 default_path, 215 true)); 216 } 217 218 void RemoteRootWindowHostWin::HandleSaveFile( 219 const base::string16& title, 220 const base::FilePath& default_path, 221 const base::string16& filter, 222 int filter_index, 223 const base::string16& default_extension, 224 const SaveFileCompletion& on_success, 225 const FileSelectionCanceled& on_failure) { 226 if (!host_) 227 return; 228 229 MetroViewerHostMsg_SaveAsDialogParams params; 230 params.title = title; 231 params.default_extension = default_extension; 232 params.filter = filter; 233 params.filter_index = filter_index; 234 params.suggested_name = default_path; 235 236 // Can only have one of these operations in flight. 237 DCHECK(file_saveas_completion_callback_.is_null()); 238 DCHECK(failure_callback_.is_null()); 239 file_saveas_completion_callback_ = on_success; 240 failure_callback_ = on_failure; 241 242 host_->Send(new MetroViewerHostMsg_DisplayFileSaveAs(params)); 243 } 244 245 void RemoteRootWindowHostWin::HandleSelectFolder( 246 const base::string16& title, 247 const SelectFolderCompletion& on_success, 248 const FileSelectionCanceled& on_failure) { 249 if (!host_) 250 return; 251 252 // Can only have one of these operations in flight. 253 DCHECK(select_folder_completion_callback_.is_null()); 254 DCHECK(failure_callback_.is_null()); 255 select_folder_completion_callback_ = on_success; 256 failure_callback_ = on_failure; 257 258 host_->Send(new MetroViewerHostMsg_DisplaySelectFolder(title)); 259 } 260 261 Window* RemoteRootWindowHostWin::GetAshWindow() { 262 return GetRootWindow(); 263 } 264 265 void RemoteRootWindowHostWin::SetDelegate(RootWindowHostDelegate* delegate) { 266 delegate_ = delegate; 267 } 268 269 RootWindow* RemoteRootWindowHostWin::GetRootWindow() { 270 return delegate_->AsRootWindow(); 271 } 272 273 gfx::AcceleratedWidget RemoteRootWindowHostWin::GetAcceleratedWidget() { 274 // TODO(cpu): This is bad. Chrome's compositor needs a valid window 275 // initially and then later on we swap it. Since the compositor never 276 // uses this initial window we tell ourselves this hack is ok to get 277 // thing off the ground. 278 return ::GetDesktopWindow(); 279 } 280 281 void RemoteRootWindowHostWin::Show() { 282 } 283 284 void RemoteRootWindowHostWin::Hide() { 285 NOTIMPLEMENTED(); 286 } 287 288 void RemoteRootWindowHostWin::ToggleFullScreen() { 289 } 290 291 gfx::Rect RemoteRootWindowHostWin::GetBounds() const { 292 gfx::Rect r(gfx::Point(0, 0), aura::RootWindowHost::GetNativeScreenSize()); 293 return r; 294 } 295 296 void RemoteRootWindowHostWin::SetBounds(const gfx::Rect& bounds) { 297 delegate_->OnHostResized(bounds.size()); 298 } 299 300 gfx::Insets RemoteRootWindowHostWin::GetInsets() const { 301 return gfx::Insets(); 302 } 303 304 void RemoteRootWindowHostWin::SetInsets(const gfx::Insets& insets) { 305 } 306 307 gfx::Point RemoteRootWindowHostWin::GetLocationOnNativeScreen() const { 308 return gfx::Point(0, 0); 309 } 310 311 void RemoteRootWindowHostWin::SetCursor(gfx::NativeCursor native_cursor) { 312 if (!host_) 313 return; 314 host_->Send( 315 new MetroViewerHostMsg_SetCursor(uint64(native_cursor.platform()))); 316 } 317 318 void RemoteRootWindowHostWin::SetCapture() { 319 } 320 321 void RemoteRootWindowHostWin::ReleaseCapture() { 322 } 323 324 bool RemoteRootWindowHostWin::QueryMouseLocation(gfx::Point* location_return) { 325 aura::client::CursorClient* cursor_client = 326 aura::client::GetCursorClient(GetRootWindow()); 327 if (cursor_client && !cursor_client->IsMouseEventsEnabled()) { 328 *location_return = gfx::Point(0, 0); 329 return false; 330 } 331 POINT pt; 332 GetCursorPos(&pt); 333 *location_return = 334 gfx::Point(static_cast<int>(pt.x), static_cast<int>(pt.y)); 335 return true; 336 } 337 338 bool RemoteRootWindowHostWin::ConfineCursorToRootWindow() { 339 return true; 340 } 341 342 bool RemoteRootWindowHostWin::CopyAreaToSkCanvas(const gfx::Rect& source_bounds, 343 const gfx::Point& dest_offset, 344 SkCanvas* canvas) { 345 NOTIMPLEMENTED(); 346 return false; 347 } 348 349 void RemoteRootWindowHostWin::UnConfineCursor() { 350 } 351 352 void RemoteRootWindowHostWin::OnCursorVisibilityChanged(bool show) { 353 NOTIMPLEMENTED(); 354 } 355 356 void RemoteRootWindowHostWin::MoveCursorTo(const gfx::Point& location) { 357 VLOG(1) << "In MoveCursorTo: " << location.x() << ", " << location.y(); 358 if (!host_) 359 return; 360 361 // This function can be called in cases like when the mouse cursor is 362 // restricted within a viewport (For e.g. LockCursor) which assumes that 363 // subsequent mouse moves would be received starting with the new cursor 364 // coordinates. This is a challenge for Windows ASH for the reasons 365 // outlined below. 366 // Other cases which don't expect this behavior should continue to work 367 // without issues. 368 369 // The mouse events are received by the viewer process and sent to the 370 // browser. If we invoke the SetCursor API here we continue to receive 371 // mouse messages from the viewer which were posted before the SetCursor 372 // API executes which messes up the state in the browser. To workaround 373 // this we invoke the SetCursor API in the viewer process and ignore 374 // mouse messages until we received an ACK from the viewer indicating that 375 // the SetCursor operation completed. 376 ignore_mouse_moves_until_set_cursor_ack_ = true; 377 VLOG(1) << "In MoveCursorTo. Sending IPC"; 378 host_->Send(new MetroViewerHostMsg_SetCursorPos(location.x(), location.y())); 379 } 380 381 void RemoteRootWindowHostWin::SetFocusWhenShown(bool focus_when_shown) { 382 NOTIMPLEMENTED(); 383 } 384 385 void RemoteRootWindowHostWin::PostNativeEvent( 386 const base::NativeEvent& native_event) { 387 } 388 389 void RemoteRootWindowHostWin::OnDeviceScaleFactorChanged( 390 float device_scale_factor) { 391 NOTIMPLEMENTED(); 392 } 393 394 void RemoteRootWindowHostWin::PrepareForShutdown() { 395 } 396 397 void RemoteRootWindowHostWin::OnMouseMoved(int32 x, int32 y, int32 flags) { 398 if (ignore_mouse_moves_until_set_cursor_ack_) 399 return; 400 401 gfx::Point location(x, y); 402 ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, flags); 403 delegate_->OnHostMouseEvent(&event); 404 } 405 406 void RemoteRootWindowHostWin::OnMouseButton( 407 int32 x, int32 y, int32 extra, ui::EventType type, ui::EventFlags flags) { 408 gfx::Point location(x, y); 409 ui::MouseEvent mouse_event(type, location, location, flags); 410 411 if (type == ui::ET_MOUSEWHEEL) { 412 ui::MouseWheelEvent wheel_event(mouse_event, 0, extra); 413 delegate_->OnHostMouseEvent(&wheel_event); 414 } else if (type == ui::ET_MOUSE_PRESSED) { 415 // TODO(shrikant): Ideally modify code in event.cc by adding automatic 416 // tracking of double clicks in synthetic MouseEvent constructor code. 417 // Non-synthetic MouseEvent constructor code does automatically track 418 // this. Need to use some caution while modifying synthetic constructor 419 // as many tests and other code paths depend on it and apparently 420 // specifically depend on non implicit tracking of previous mouse event. 421 if (last_mouse_click_event_ && 422 ui::MouseEvent::IsRepeatedClickEvent(mouse_event, 423 *last_mouse_click_event_)) { 424 mouse_event.SetClickCount(2); 425 } else { 426 mouse_event.SetClickCount(1); 427 } 428 last_mouse_click_event_ .reset(new ui::MouseEvent(mouse_event)); 429 delegate_->OnHostMouseEvent(&mouse_event); 430 } else { 431 delegate_->OnHostMouseEvent(&mouse_event); 432 } 433 } 434 435 void RemoteRootWindowHostWin::OnKeyDown(uint32 vkey, 436 uint32 repeat_count, 437 uint32 scan_code, 438 uint32 flags) { 439 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, vkey, repeat_count, scan_code, 440 flags, false); 441 } 442 443 void RemoteRootWindowHostWin::OnKeyUp(uint32 vkey, 444 uint32 repeat_count, 445 uint32 scan_code, 446 uint32 flags) { 447 DispatchKeyboardMessage(ui::ET_KEY_RELEASED, vkey, repeat_count, scan_code, 448 flags, false); 449 } 450 451 void RemoteRootWindowHostWin::OnChar(uint32 key_code, 452 uint32 repeat_count, 453 uint32 scan_code, 454 uint32 flags) { 455 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, key_code, repeat_count, 456 scan_code, flags, true); 457 } 458 459 void RemoteRootWindowHostWin::OnVisibilityChanged(bool visible) { 460 if (visible) 461 delegate_->OnHostActivated(); 462 } 463 464 void RemoteRootWindowHostWin::OnTouchDown(int32 x, 465 int32 y, 466 uint64 timestamp, 467 uint32 pointer_id) { 468 ui::TouchEvent event(ui::ET_TOUCH_PRESSED, 469 gfx::Point(x, y), 470 pointer_id, 471 base::TimeDelta::FromMicroseconds(timestamp)); 472 delegate_->OnHostTouchEvent(&event); 473 } 474 475 void RemoteRootWindowHostWin::OnTouchUp(int32 x, 476 int32 y, 477 uint64 timestamp, 478 uint32 pointer_id) { 479 ui::TouchEvent event(ui::ET_TOUCH_RELEASED, 480 gfx::Point(x, y), 481 pointer_id, 482 base::TimeDelta::FromMicroseconds(timestamp)); 483 delegate_->OnHostTouchEvent(&event); 484 } 485 486 void RemoteRootWindowHostWin::OnTouchMoved(int32 x, 487 int32 y, 488 uint64 timestamp, 489 uint32 pointer_id) { 490 ui::TouchEvent event(ui::ET_TOUCH_MOVED, 491 gfx::Point(x, y), 492 pointer_id, 493 base::TimeDelta::FromMicroseconds(timestamp)); 494 delegate_->OnHostTouchEvent(&event); 495 } 496 497 void RemoteRootWindowHostWin::OnFileSaveAsDone(bool success, 498 const base::FilePath& filename, 499 int filter_index) { 500 if (success) 501 file_saveas_completion_callback_.Run(filename, filter_index, NULL); 502 else 503 failure_callback_.Run(NULL); 504 file_saveas_completion_callback_.Reset(); 505 failure_callback_.Reset(); 506 } 507 508 509 void RemoteRootWindowHostWin::OnFileOpenDone(bool success, 510 const base::FilePath& filename) { 511 if (success) 512 file_open_completion_callback_.Run(base::FilePath(filename), 0, NULL); 513 else 514 failure_callback_.Run(NULL); 515 file_open_completion_callback_.Reset(); 516 failure_callback_.Reset(); 517 } 518 519 void RemoteRootWindowHostWin::OnMultiFileOpenDone( 520 bool success, 521 const std::vector<base::FilePath>& files) { 522 if (success) 523 multi_file_open_completion_callback_.Run(files, NULL); 524 else 525 failure_callback_.Run(NULL); 526 multi_file_open_completion_callback_.Reset(); 527 failure_callback_.Reset(); 528 } 529 530 void RemoteRootWindowHostWin::OnSelectFolderDone( 531 bool success, 532 const base::FilePath& folder) { 533 if (success) 534 select_folder_completion_callback_.Run(base::FilePath(folder), 0, NULL); 535 else 536 failure_callback_.Run(NULL); 537 select_folder_completion_callback_.Reset(); 538 failure_callback_.Reset(); 539 } 540 541 void RemoteRootWindowHostWin::OnWindowActivated(bool active) { 542 active ? GetRootWindow()->Focus() : GetRootWindow()->Blur(); 543 } 544 545 void RemoteRootWindowHostWin::OnSetCursorPosAck() { 546 DCHECK(ignore_mouse_moves_until_set_cursor_ack_); 547 ignore_mouse_moves_until_set_cursor_ack_ = false; 548 } 549 550 void RemoteRootWindowHostWin::OnWindowSizeChanged(uint32 width, uint32 height) { 551 SetBounds(gfx::Rect(0, 0, width, height)); 552 } 553 554 void RemoteRootWindowHostWin::DispatchKeyboardMessage(ui::EventType type, 555 uint32 vkey, 556 uint32 repeat_count, 557 uint32 scan_code, 558 uint32 flags, 559 bool is_character) { 560 if (base::MessageLoop::current()->IsNested()) { 561 SetVirtualKeyStates(flags); 562 563 uint32 message = is_character ? WM_CHAR : 564 (type == ui::ET_KEY_PRESSED ? WM_KEYDOWN : WM_KEYUP); 565 ::PostThreadMessage(::GetCurrentThreadId(), 566 message, 567 vkey, 568 repeat_count | scan_code >> 15); 569 } else { 570 ui::KeyEvent event(type, 571 ui::KeyboardCodeForWindowsKeyCode(vkey), 572 flags, 573 is_character); 574 delegate_->OnHostKeyEvent(&event); 575 } 576 } 577 578 } // namespace aura 579