| OLD | NEW |
| (Empty) |
| 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/views/win/hwnd_message_handler.h" | |
| 6 | |
| 7 #include <dwmapi.h> | |
| 8 #include <oleacc.h> | |
| 9 #include <shellapi.h> | |
| 10 #include <wtsapi32.h> | |
| 11 #pragma comment(lib, "wtsapi32.lib") | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/debug/trace_event.h" | |
| 15 #include "base/win/win_util.h" | |
| 16 #include "base/win/windows_version.h" | |
| 17 #include "ui/base/touch/touch_enabled.h" | |
| 18 #include "ui/base/view_prop.h" | |
| 19 #include "ui/base/win/internal_constants.h" | |
| 20 #include "ui/base/win/lock_state.h" | |
| 21 #include "ui/base/win/mouse_wheel_util.h" | |
| 22 #include "ui/base/win/shell.h" | |
| 23 #include "ui/base/win/touch_input.h" | |
| 24 #include "ui/events/event.h" | |
| 25 #include "ui/events/event_utils.h" | |
| 26 #include "ui/events/keycodes/keyboard_code_conversion_win.h" | |
| 27 #include "ui/gfx/canvas.h" | |
| 28 #include "ui/gfx/canvas_skia_paint.h" | |
| 29 #include "ui/gfx/icon_util.h" | |
| 30 #include "ui/gfx/insets.h" | |
| 31 #include "ui/gfx/path.h" | |
| 32 #include "ui/gfx/path_win.h" | |
| 33 #include "ui/gfx/screen.h" | |
| 34 #include "ui/gfx/win/dpi.h" | |
| 35 #include "ui/gfx/win/hwnd_util.h" | |
| 36 #include "ui/native_theme/native_theme_win.h" | |
| 37 #include "ui/views/views_delegate.h" | |
| 38 #include "ui/views/widget/monitor_win.h" | |
| 39 #include "ui/views/widget/widget_hwnd_utils.h" | |
| 40 #include "ui/views/win/fullscreen_handler.h" | |
| 41 #include "ui/views/win/hwnd_message_handler_delegate.h" | |
| 42 #include "ui/views/win/scoped_fullscreen_visibility.h" | |
| 43 | |
| 44 namespace views { | |
| 45 namespace { | |
| 46 | |
| 47 // MoveLoopMouseWatcher is used to determine if the user canceled or completed a | |
| 48 // move. win32 doesn't appear to offer a way to determine the result of a move, | |
| 49 // so we install hooks to determine if we got a mouse up and assume the move | |
| 50 // completed. | |
| 51 class MoveLoopMouseWatcher { | |
| 52 public: | |
| 53 MoveLoopMouseWatcher(HWNDMessageHandler* host, bool hide_on_escape); | |
| 54 ~MoveLoopMouseWatcher(); | |
| 55 | |
| 56 // Returns true if the mouse is up, or if we couldn't install the hook. | |
| 57 bool got_mouse_up() const { return got_mouse_up_; } | |
| 58 | |
| 59 private: | |
| 60 // Instance that owns the hook. We only allow one instance to hook the mouse | |
| 61 // at a time. | |
| 62 static MoveLoopMouseWatcher* instance_; | |
| 63 | |
| 64 // Key and mouse callbacks from the hook. | |
| 65 static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param); | |
| 66 static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param); | |
| 67 | |
| 68 void Unhook(); | |
| 69 | |
| 70 // HWNDMessageHandler that created us. | |
| 71 HWNDMessageHandler* host_; | |
| 72 | |
| 73 // Should the window be hidden when escape is pressed? | |
| 74 const bool hide_on_escape_; | |
| 75 | |
| 76 // Did we get a mouse up? | |
| 77 bool got_mouse_up_; | |
| 78 | |
| 79 // Hook identifiers. | |
| 80 HHOOK mouse_hook_; | |
| 81 HHOOK key_hook_; | |
| 82 | |
| 83 DISALLOW_COPY_AND_ASSIGN(MoveLoopMouseWatcher); | |
| 84 }; | |
| 85 | |
| 86 // static | |
| 87 MoveLoopMouseWatcher* MoveLoopMouseWatcher::instance_ = NULL; | |
| 88 | |
| 89 MoveLoopMouseWatcher::MoveLoopMouseWatcher(HWNDMessageHandler* host, | |
| 90 bool hide_on_escape) | |
| 91 : host_(host), | |
| 92 hide_on_escape_(hide_on_escape), | |
| 93 got_mouse_up_(false), | |
| 94 mouse_hook_(NULL), | |
| 95 key_hook_(NULL) { | |
| 96 // Only one instance can be active at a time. | |
| 97 if (instance_) | |
| 98 instance_->Unhook(); | |
| 99 | |
| 100 mouse_hook_ = SetWindowsHookEx( | |
| 101 WH_MOUSE, &MouseHook, NULL, GetCurrentThreadId()); | |
| 102 if (mouse_hook_) { | |
| 103 instance_ = this; | |
| 104 // We don't care if setting the key hook succeeded. | |
| 105 key_hook_ = SetWindowsHookEx( | |
| 106 WH_KEYBOARD, &KeyHook, NULL, GetCurrentThreadId()); | |
| 107 } | |
| 108 if (instance_ != this) { | |
| 109 // Failed installation. Assume we got a mouse up in this case, otherwise | |
| 110 // we'll think all drags were canceled. | |
| 111 got_mouse_up_ = true; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 MoveLoopMouseWatcher::~MoveLoopMouseWatcher() { | |
| 116 Unhook(); | |
| 117 } | |
| 118 | |
| 119 void MoveLoopMouseWatcher::Unhook() { | |
| 120 if (instance_ != this) | |
| 121 return; | |
| 122 | |
| 123 DCHECK(mouse_hook_); | |
| 124 UnhookWindowsHookEx(mouse_hook_); | |
| 125 if (key_hook_) | |
| 126 UnhookWindowsHookEx(key_hook_); | |
| 127 key_hook_ = NULL; | |
| 128 mouse_hook_ = NULL; | |
| 129 instance_ = NULL; | |
| 130 } | |
| 131 | |
| 132 // static | |
| 133 LRESULT CALLBACK MoveLoopMouseWatcher::MouseHook(int n_code, | |
| 134 WPARAM w_param, | |
| 135 LPARAM l_param) { | |
| 136 DCHECK(instance_); | |
| 137 if (n_code == HC_ACTION && w_param == WM_LBUTTONUP) | |
| 138 instance_->got_mouse_up_ = true; | |
| 139 return CallNextHookEx(instance_->mouse_hook_, n_code, w_param, l_param); | |
| 140 } | |
| 141 | |
| 142 // static | |
| 143 LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code, | |
| 144 WPARAM w_param, | |
| 145 LPARAM l_param) { | |
| 146 if (n_code == HC_ACTION && w_param == VK_ESCAPE) { | |
| 147 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
| 148 int value = TRUE; | |
| 149 DwmSetWindowAttribute( | |
| 150 instance_->host_->hwnd(), | |
| 151 DWMWA_TRANSITIONS_FORCEDISABLED, | |
| 152 &value, | |
| 153 sizeof(value)); | |
| 154 } | |
| 155 if (instance_->hide_on_escape_) | |
| 156 instance_->host_->Hide(); | |
| 157 } | |
| 158 return CallNextHookEx(instance_->key_hook_, n_code, w_param, l_param); | |
| 159 } | |
| 160 | |
| 161 // Called from OnNCActivate. | |
| 162 BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) { | |
| 163 DWORD process_id; | |
| 164 GetWindowThreadProcessId(hwnd, &process_id); | |
| 165 int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; | |
| 166 if (process_id == GetCurrentProcessId()) | |
| 167 flags |= RDW_UPDATENOW; | |
| 168 RedrawWindow(hwnd, NULL, NULL, flags); | |
| 169 return TRUE; | |
| 170 } | |
| 171 | |
| 172 bool GetMonitorAndRects(const RECT& rect, | |
| 173 HMONITOR* monitor, | |
| 174 gfx::Rect* monitor_rect, | |
| 175 gfx::Rect* work_area) { | |
| 176 DCHECK(monitor); | |
| 177 DCHECK(monitor_rect); | |
| 178 DCHECK(work_area); | |
| 179 *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); | |
| 180 if (!*monitor) | |
| 181 return false; | |
| 182 MONITORINFO monitor_info = { 0 }; | |
| 183 monitor_info.cbSize = sizeof(monitor_info); | |
| 184 GetMonitorInfo(*monitor, &monitor_info); | |
| 185 *monitor_rect = gfx::Rect(monitor_info.rcMonitor); | |
| 186 *work_area = gfx::Rect(monitor_info.rcWork); | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 struct FindOwnedWindowsData { | |
| 191 HWND window; | |
| 192 std::vector<Widget*> owned_widgets; | |
| 193 }; | |
| 194 | |
| 195 // Enables or disables the menu item for the specified command and menu. | |
| 196 void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) { | |
| 197 UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); | |
| 198 EnableMenuItem(menu, command, flags); | |
| 199 } | |
| 200 | |
| 201 // Callback used to notify child windows that the top level window received a | |
| 202 // DWMCompositionChanged message. | |
| 203 BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) { | |
| 204 SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0); | |
| 205 return TRUE; | |
| 206 } | |
| 207 | |
| 208 // See comments in OnNCPaint() for details of this struct. | |
| 209 struct ClipState { | |
| 210 // The window being painted. | |
| 211 HWND parent; | |
| 212 | |
| 213 // DC painting to. | |
| 214 HDC dc; | |
| 215 | |
| 216 // Origin of the window in terms of the screen. | |
| 217 int x; | |
| 218 int y; | |
| 219 }; | |
| 220 | |
| 221 // See comments in OnNCPaint() for details of this function. | |
| 222 static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { | |
| 223 ClipState* clip_state = reinterpret_cast<ClipState*>(param); | |
| 224 if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { | |
| 225 RECT bounds; | |
| 226 GetWindowRect(window, &bounds); | |
| 227 ExcludeClipRect(clip_state->dc, | |
| 228 bounds.left - clip_state->x, | |
| 229 bounds.top - clip_state->y, | |
| 230 bounds.right - clip_state->x, | |
| 231 bounds.bottom - clip_state->y); | |
| 232 } | |
| 233 return TRUE; | |
| 234 } | |
| 235 | |
| 236 // The thickness of an auto-hide taskbar in pixels. | |
| 237 const int kAutoHideTaskbarThicknessPx = 2; | |
| 238 | |
| 239 bool IsTopLevelWindow(HWND window) { | |
| 240 long style = ::GetWindowLong(window, GWL_STYLE); | |
| 241 if (!(style & WS_CHILD)) | |
| 242 return true; | |
| 243 HWND parent = ::GetParent(window); | |
| 244 return !parent || (parent == ::GetDesktopWindow()); | |
| 245 } | |
| 246 | |
| 247 void AddScrollStylesToWindow(HWND window) { | |
| 248 if (::IsWindow(window)) { | |
| 249 long current_style = ::GetWindowLong(window, GWL_STYLE); | |
| 250 ::SetWindowLong(window, GWL_STYLE, | |
| 251 current_style | WS_VSCROLL | WS_HSCROLL); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 const int kTouchDownContextResetTimeout = 500; | |
| 256 | |
| 257 // Windows does not flag synthesized mouse messages from touch in all cases. | |
| 258 // This causes us grief as we don't want to process touch and mouse messages | |
| 259 // concurrently. Hack as per msdn is to check if the time difference between | |
| 260 // the touch message and the mouse move is within 500 ms and at the same | |
| 261 // location as the cursor. | |
| 262 const int kSynthesizedMouseTouchMessagesTimeDifference = 500; | |
| 263 | |
| 264 } // namespace | |
| 265 | |
| 266 // A scoping class that prevents a window from being able to redraw in response | |
| 267 // to invalidations that may occur within it for the lifetime of the object. | |
| 268 // | |
| 269 // Why would we want such a thing? Well, it turns out Windows has some | |
| 270 // "unorthodox" behavior when it comes to painting its non-client areas. | |
| 271 // Occasionally, Windows will paint portions of the default non-client area | |
| 272 // right over the top of the custom frame. This is not simply fixed by handling | |
| 273 // WM_NCPAINT/WM_PAINT, with some investigation it turns out that this | |
| 274 // rendering is being done *inside* the default implementation of some message | |
| 275 // handlers and functions: | |
| 276 // . WM_SETTEXT | |
| 277 // . WM_SETICON | |
| 278 // . WM_NCLBUTTONDOWN | |
| 279 // . EnableMenuItem, called from our WM_INITMENU handler | |
| 280 // The solution is to handle these messages and call DefWindowProc ourselves, | |
| 281 // but prevent the window from being able to update itself for the duration of | |
| 282 // the call. We do this with this class, which automatically calls its | |
| 283 // associated Window's lock and unlock functions as it is created and destroyed. | |
| 284 // See documentation in those methods for the technique used. | |
| 285 // | |
| 286 // The lock only has an effect if the window was visible upon lock creation, as | |
| 287 // it doesn't guard against direct visiblility changes, and multiple locks may | |
| 288 // exist simultaneously to handle certain nested Windows messages. | |
| 289 // | |
| 290 // IMPORTANT: Do not use this scoping object for large scopes or periods of | |
| 291 // time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh). | |
| 292 // | |
| 293 // I would love to hear Raymond Chen's explanation for all this. And maybe a | |
| 294 // list of other messages that this applies to ;-) | |
| 295 class HWNDMessageHandler::ScopedRedrawLock { | |
| 296 public: | |
| 297 explicit ScopedRedrawLock(HWNDMessageHandler* owner) | |
| 298 : owner_(owner), | |
| 299 hwnd_(owner_->hwnd()), | |
| 300 was_visible_(owner_->IsVisible()), | |
| 301 cancel_unlock_(false), | |
| 302 force_(!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION)) { | |
| 303 if (was_visible_ && ::IsWindow(hwnd_)) | |
| 304 owner_->LockUpdates(force_); | |
| 305 } | |
| 306 | |
| 307 ~ScopedRedrawLock() { | |
| 308 if (!cancel_unlock_ && was_visible_ && ::IsWindow(hwnd_)) | |
| 309 owner_->UnlockUpdates(force_); | |
| 310 } | |
| 311 | |
| 312 // Cancel the unlock operation, call this if the Widget is being destroyed. | |
| 313 void CancelUnlockOperation() { cancel_unlock_ = true; } | |
| 314 | |
| 315 private: | |
| 316 // The owner having its style changed. | |
| 317 HWNDMessageHandler* owner_; | |
| 318 // The owner's HWND, cached to avoid action after window destruction. | |
| 319 HWND hwnd_; | |
| 320 // Records the HWND visibility at the time of creation. | |
| 321 bool was_visible_; | |
| 322 // A flag indicating that the unlock operation was canceled. | |
| 323 bool cancel_unlock_; | |
| 324 // If true, perform the redraw lock regardless of Aero state. | |
| 325 bool force_; | |
| 326 | |
| 327 DISALLOW_COPY_AND_ASSIGN(ScopedRedrawLock); | |
| 328 }; | |
| 329 | |
| 330 //////////////////////////////////////////////////////////////////////////////// | |
| 331 // HWNDMessageHandler, public: | |
| 332 | |
| 333 long HWNDMessageHandler::last_touch_message_time_ = 0; | |
| 334 | |
| 335 HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) | |
| 336 : delegate_(delegate), | |
| 337 fullscreen_handler_(new FullscreenHandler), | |
| 338 weak_factory_(this), | |
| 339 waiting_for_close_now_(false), | |
| 340 remove_standard_frame_(false), | |
| 341 use_system_default_icon_(false), | |
| 342 restored_enabled_(false), | |
| 343 current_cursor_(NULL), | |
| 344 previous_cursor_(NULL), | |
| 345 active_mouse_tracking_flags_(0), | |
| 346 is_right_mouse_pressed_on_caption_(false), | |
| 347 lock_updates_count_(0), | |
| 348 ignore_window_pos_changes_(false), | |
| 349 last_monitor_(NULL), | |
| 350 use_layered_buffer_(false), | |
| 351 layered_alpha_(255), | |
| 352 waiting_for_redraw_layered_window_contents_(false), | |
| 353 is_first_nccalc_(true), | |
| 354 menu_depth_(0), | |
| 355 autohide_factory_(this), | |
| 356 id_generator_(0), | |
| 357 needs_scroll_styles_(false), | |
| 358 in_size_loop_(false), | |
| 359 touch_down_contexts_(0), | |
| 360 last_mouse_hwheel_time_(0), | |
| 361 msg_handled_(FALSE), | |
| 362 dwm_transition_desired_(false) { | |
| 363 } | |
| 364 | |
| 365 HWNDMessageHandler::~HWNDMessageHandler() { | |
| 366 delegate_ = NULL; | |
| 367 // Prevent calls back into this class via WNDPROC now that we've been | |
| 368 // destroyed. | |
| 369 ClearUserData(); | |
| 370 } | |
| 371 | |
| 372 void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { | |
| 373 TRACE_EVENT0("views", "HWNDMessageHandler::Init"); | |
| 374 GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_, | |
| 375 &last_work_area_); | |
| 376 | |
| 377 // Create the window. | |
| 378 WindowImpl::Init(parent, bounds); | |
| 379 // TODO(ananta) | |
| 380 // Remove the scrolling hack code once we have scrolling working well. | |
| 381 #if defined(ENABLE_SCROLL_HACK) | |
| 382 // Certain trackpad drivers on Windows have bugs where in they don't generate | |
| 383 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures | |
| 384 // unless there is an entry for Chrome with the class name of the Window. | |
| 385 // These drivers check if the window under the trackpoint has the WS_VSCROLL/ | |
| 386 // WS_HSCROLL style and if yes they generate the legacy WM_VSCROLL/WM_HSCROLL | |
| 387 // messages. We add these styles to ensure that trackpad/trackpoint scrolling | |
| 388 // work. | |
| 389 // TODO(ananta) | |
| 390 // Look into moving the WS_VSCROLL and WS_HSCROLL style setting logic to the | |
| 391 // CalculateWindowStylesFromInitParams function. Doing it there seems to | |
| 392 // cause some interactive tests to fail. Investigation needed. | |
| 393 if (IsTopLevelWindow(hwnd())) { | |
| 394 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE); | |
| 395 if (!(current_style & WS_POPUP)) { | |
| 396 AddScrollStylesToWindow(hwnd()); | |
| 397 needs_scroll_styles_ = true; | |
| 398 } | |
| 399 } | |
| 400 #endif | |
| 401 | |
| 402 prop_window_target_.reset(new ui::ViewProp(hwnd(), | |
| 403 ui::WindowEventTarget::kWin32InputEventTarget, | |
| 404 static_cast<ui::WindowEventTarget*>(this))); | |
| 405 } | |
| 406 | |
| 407 void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) { | |
| 408 if (modal_type == ui::MODAL_TYPE_NONE) | |
| 409 return; | |
| 410 // We implement modality by crawling up the hierarchy of windows starting | |
| 411 // at the owner, disabling all of them so that they don't receive input | |
| 412 // messages. | |
| 413 HWND start = ::GetWindow(hwnd(), GW_OWNER); | |
| 414 while (start) { | |
| 415 ::EnableWindow(start, FALSE); | |
| 416 start = ::GetParent(start); | |
| 417 } | |
| 418 } | |
| 419 | |
| 420 void HWNDMessageHandler::Close() { | |
| 421 if (!IsWindow(hwnd())) | |
| 422 return; // No need to do anything. | |
| 423 | |
| 424 // Let's hide ourselves right away. | |
| 425 Hide(); | |
| 426 | |
| 427 // Modal dialog windows disable their owner windows; re-enable them now so | |
| 428 // they can activate as foreground windows upon this window's destruction. | |
| 429 RestoreEnabledIfNecessary(); | |
| 430 | |
| 431 if (!waiting_for_close_now_) { | |
| 432 // And we delay the close so that if we are called from an ATL callback, | |
| 433 // we don't destroy the window before the callback returned (as the caller | |
| 434 // may delete ourselves on destroy and the ATL callback would still | |
| 435 // dereference us when the callback returns). | |
| 436 waiting_for_close_now_ = true; | |
| 437 base::MessageLoop::current()->PostTask( | |
| 438 FROM_HERE, | |
| 439 base::Bind(&HWNDMessageHandler::CloseNow, weak_factory_.GetWeakPtr())); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 void HWNDMessageHandler::CloseNow() { | |
| 444 // We may already have been destroyed if the selection resulted in a tab | |
| 445 // switch which will have reactivated the browser window and closed us, so | |
| 446 // we need to check to see if we're still a window before trying to destroy | |
| 447 // ourself. | |
| 448 waiting_for_close_now_ = false; | |
| 449 if (IsWindow(hwnd())) | |
| 450 DestroyWindow(hwnd()); | |
| 451 } | |
| 452 | |
| 453 gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const { | |
| 454 RECT r; | |
| 455 GetWindowRect(hwnd(), &r); | |
| 456 return gfx::Rect(r); | |
| 457 } | |
| 458 | |
| 459 gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const { | |
| 460 RECT r; | |
| 461 GetClientRect(hwnd(), &r); | |
| 462 POINT point = { r.left, r.top }; | |
| 463 ClientToScreen(hwnd(), &point); | |
| 464 return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); | |
| 465 } | |
| 466 | |
| 467 gfx::Rect HWNDMessageHandler::GetRestoredBounds() const { | |
| 468 // If we're in fullscreen mode, we've changed the normal bounds to the monitor | |
| 469 // rect, so return the saved bounds instead. | |
| 470 if (fullscreen_handler_->fullscreen()) | |
| 471 return fullscreen_handler_->GetRestoreBounds(); | |
| 472 | |
| 473 gfx::Rect bounds; | |
| 474 GetWindowPlacement(&bounds, NULL); | |
| 475 return bounds; | |
| 476 } | |
| 477 | |
| 478 gfx::Rect HWNDMessageHandler::GetClientAreaBounds() const { | |
| 479 if (IsMinimized()) | |
| 480 return gfx::Rect(); | |
| 481 if (delegate_->WidgetSizeIsClientSize()) | |
| 482 return GetClientAreaBoundsInScreen(); | |
| 483 return GetWindowBoundsInScreen(); | |
| 484 } | |
| 485 | |
| 486 void HWNDMessageHandler::GetWindowPlacement( | |
| 487 gfx::Rect* bounds, | |
| 488 ui::WindowShowState* show_state) const { | |
| 489 WINDOWPLACEMENT wp; | |
| 490 wp.length = sizeof(wp); | |
| 491 const bool succeeded = !!::GetWindowPlacement(hwnd(), &wp); | |
| 492 DCHECK(succeeded); | |
| 493 | |
| 494 if (bounds != NULL) { | |
| 495 if (wp.showCmd == SW_SHOWNORMAL) { | |
| 496 // GetWindowPlacement can return misleading position if a normalized | |
| 497 // window was resized using Aero Snap feature (see comment 9 in bug | |
| 498 // 36421). As a workaround, using GetWindowRect for normalized windows. | |
| 499 const bool succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0; | |
| 500 DCHECK(succeeded); | |
| 501 | |
| 502 *bounds = gfx::Rect(wp.rcNormalPosition); | |
| 503 } else { | |
| 504 MONITORINFO mi; | |
| 505 mi.cbSize = sizeof(mi); | |
| 506 const bool succeeded = GetMonitorInfo( | |
| 507 MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0; | |
| 508 DCHECK(succeeded); | |
| 509 | |
| 510 *bounds = gfx::Rect(wp.rcNormalPosition); | |
| 511 // Convert normal position from workarea coordinates to screen | |
| 512 // coordinates. | |
| 513 bounds->Offset(mi.rcWork.left - mi.rcMonitor.left, | |
| 514 mi.rcWork.top - mi.rcMonitor.top); | |
| 515 } | |
| 516 } | |
| 517 | |
| 518 if (show_state) { | |
| 519 if (wp.showCmd == SW_SHOWMAXIMIZED) | |
| 520 *show_state = ui::SHOW_STATE_MAXIMIZED; | |
| 521 else if (wp.showCmd == SW_SHOWMINIMIZED) | |
| 522 *show_state = ui::SHOW_STATE_MINIMIZED; | |
| 523 else | |
| 524 *show_state = ui::SHOW_STATE_NORMAL; | |
| 525 } | |
| 526 } | |
| 527 | |
| 528 void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels, | |
| 529 bool force_size_changed) { | |
| 530 LONG style = GetWindowLong(hwnd(), GWL_STYLE); | |
| 531 if (style & WS_MAXIMIZE) | |
| 532 SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); | |
| 533 | |
| 534 gfx::Size old_size = GetClientAreaBounds().size(); | |
| 535 SetWindowPos(hwnd(), NULL, bounds_in_pixels.x(), bounds_in_pixels.y(), | |
| 536 bounds_in_pixels.width(), bounds_in_pixels.height(), | |
| 537 SWP_NOACTIVATE | SWP_NOZORDER); | |
| 538 | |
| 539 // If HWND size is not changed, we will not receive standard size change | |
| 540 // notifications. If |force_size_changed| is |true|, we should pretend size is | |
| 541 // changed. | |
| 542 if (old_size == bounds_in_pixels.size() && force_size_changed) { | |
| 543 delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); | |
| 544 ResetWindowRegion(false, true); | |
| 545 } | |
| 546 } | |
| 547 | |
| 548 void HWNDMessageHandler::SetSize(const gfx::Size& size) { | |
| 549 SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(), | |
| 550 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); | |
| 551 } | |
| 552 | |
| 553 void HWNDMessageHandler::CenterWindow(const gfx::Size& size) { | |
| 554 HWND parent = GetParent(hwnd()); | |
| 555 if (!IsWindow(hwnd())) | |
| 556 parent = ::GetWindow(hwnd(), GW_OWNER); | |
| 557 gfx::CenterAndSizeWindow(parent, hwnd(), size); | |
| 558 } | |
| 559 | |
| 560 void HWNDMessageHandler::SetRegion(HRGN region) { | |
| 561 custom_window_region_.Set(region); | |
| 562 ResetWindowRegion(false, true); | |
| 563 UpdateDwmNcRenderingPolicy(); | |
| 564 } | |
| 565 | |
| 566 void HWNDMessageHandler::StackAbove(HWND other_hwnd) { | |
| 567 SetWindowPos(hwnd(), other_hwnd, 0, 0, 0, 0, | |
| 568 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); | |
| 569 } | |
| 570 | |
| 571 void HWNDMessageHandler::StackAtTop() { | |
| 572 SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, | |
| 573 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); | |
| 574 } | |
| 575 | |
| 576 void HWNDMessageHandler::Show() { | |
| 577 if (IsWindow(hwnd())) { | |
| 578 if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && | |
| 579 !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { | |
| 580 ShowWindowWithState(ui::SHOW_STATE_NORMAL); | |
| 581 } else { | |
| 582 ShowWindowWithState(ui::SHOW_STATE_INACTIVE); | |
| 583 } | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { | |
| 588 TRACE_EVENT0("views", "HWNDMessageHandler::ShowWindowWithState"); | |
| 589 DWORD native_show_state; | |
| 590 switch (show_state) { | |
| 591 case ui::SHOW_STATE_INACTIVE: | |
| 592 native_show_state = SW_SHOWNOACTIVATE; | |
| 593 break; | |
| 594 case ui::SHOW_STATE_MAXIMIZED: | |
| 595 native_show_state = SW_SHOWMAXIMIZED; | |
| 596 break; | |
| 597 case ui::SHOW_STATE_MINIMIZED: | |
| 598 native_show_state = SW_SHOWMINIMIZED; | |
| 599 break; | |
| 600 default: | |
| 601 native_show_state = delegate_->GetInitialShowState(); | |
| 602 break; | |
| 603 } | |
| 604 | |
| 605 ShowWindow(hwnd(), native_show_state); | |
| 606 // When launched from certain programs like bash and Windows Live Messenger, | |
| 607 // show_state is set to SW_HIDE, so we need to correct that condition. We | |
| 608 // don't just change show_state to SW_SHOWNORMAL because MSDN says we must | |
| 609 // always first call ShowWindow with the specified value from STARTUPINFO, | |
| 610 // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead, | |
| 611 // we call ShowWindow again in this case. | |
| 612 if (native_show_state == SW_HIDE) { | |
| 613 native_show_state = SW_SHOWNORMAL; | |
| 614 ShowWindow(hwnd(), native_show_state); | |
| 615 } | |
| 616 | |
| 617 // We need to explicitly activate the window if we've been shown with a state | |
| 618 // that should activate, because if we're opened from a desktop shortcut while | |
| 619 // an existing window is already running it doesn't seem to be enough to use | |
| 620 // one of these flags to activate the window. | |
| 621 if (native_show_state == SW_SHOWNORMAL || | |
| 622 native_show_state == SW_SHOWMAXIMIZED) | |
| 623 Activate(); | |
| 624 | |
| 625 if (!delegate_->HandleInitialFocus(show_state)) | |
| 626 SetInitialFocus(); | |
| 627 } | |
| 628 | |
| 629 void HWNDMessageHandler::ShowMaximizedWithBounds(const gfx::Rect& bounds) { | |
| 630 WINDOWPLACEMENT placement = { 0 }; | |
| 631 placement.length = sizeof(WINDOWPLACEMENT); | |
| 632 placement.showCmd = SW_SHOWMAXIMIZED; | |
| 633 placement.rcNormalPosition = bounds.ToRECT(); | |
| 634 SetWindowPlacement(hwnd(), &placement); | |
| 635 } | |
| 636 | |
| 637 void HWNDMessageHandler::Hide() { | |
| 638 if (IsWindow(hwnd())) { | |
| 639 // NOTE: Be careful not to activate any windows here (for example, calling | |
| 640 // ShowWindow(SW_HIDE) will automatically activate another window). This | |
| 641 // code can be called while a window is being deactivated, and activating | |
| 642 // another window will screw up the activation that is already in progress. | |
| 643 SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, | |
| 644 SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | | |
| 645 SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); | |
| 646 } | |
| 647 } | |
| 648 | |
| 649 void HWNDMessageHandler::Maximize() { | |
| 650 ExecuteSystemMenuCommand(SC_MAXIMIZE); | |
| 651 } | |
| 652 | |
| 653 void HWNDMessageHandler::Minimize() { | |
| 654 ExecuteSystemMenuCommand(SC_MINIMIZE); | |
| 655 delegate_->HandleNativeBlur(NULL); | |
| 656 } | |
| 657 | |
| 658 void HWNDMessageHandler::Restore() { | |
| 659 ExecuteSystemMenuCommand(SC_RESTORE); | |
| 660 } | |
| 661 | |
| 662 void HWNDMessageHandler::Activate() { | |
| 663 if (IsMinimized()) | |
| 664 ::ShowWindow(hwnd(), SW_RESTORE); | |
| 665 ::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | |
| 666 SetForegroundWindow(hwnd()); | |
| 667 } | |
| 668 | |
| 669 void HWNDMessageHandler::Deactivate() { | |
| 670 HWND next_hwnd = ::GetNextWindow(hwnd(), GW_HWNDNEXT); | |
| 671 while (next_hwnd) { | |
| 672 if (::IsWindowVisible(next_hwnd)) { | |
| 673 ::SetForegroundWindow(next_hwnd); | |
| 674 return; | |
| 675 } | |
| 676 next_hwnd = ::GetNextWindow(next_hwnd, GW_HWNDNEXT); | |
| 677 } | |
| 678 } | |
| 679 | |
| 680 void HWNDMessageHandler::SetAlwaysOnTop(bool on_top) { | |
| 681 ::SetWindowPos(hwnd(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST, | |
| 682 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); | |
| 683 } | |
| 684 | |
| 685 bool HWNDMessageHandler::IsVisible() const { | |
| 686 return !!::IsWindowVisible(hwnd()); | |
| 687 } | |
| 688 | |
| 689 bool HWNDMessageHandler::IsActive() const { | |
| 690 return GetActiveWindow() == hwnd(); | |
| 691 } | |
| 692 | |
| 693 bool HWNDMessageHandler::IsMinimized() const { | |
| 694 return !!::IsIconic(hwnd()); | |
| 695 } | |
| 696 | |
| 697 bool HWNDMessageHandler::IsMaximized() const { | |
| 698 return !!::IsZoomed(hwnd()); | |
| 699 } | |
| 700 | |
| 701 bool HWNDMessageHandler::IsAlwaysOnTop() const { | |
| 702 return (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; | |
| 703 } | |
| 704 | |
| 705 bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset, | |
| 706 bool hide_on_escape) { | |
| 707 ReleaseCapture(); | |
| 708 MoveLoopMouseWatcher watcher(this, hide_on_escape); | |
| 709 // In Aura, we handle touch events asynchronously. So we need to allow nested | |
| 710 // tasks while in windows move loop. | |
| 711 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | |
| 712 base::MessageLoop::current()); | |
| 713 | |
| 714 SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos()); | |
| 715 // Windows doesn't appear to offer a way to determine whether the user | |
| 716 // canceled the move or not. We assume if the user released the mouse it was | |
| 717 // successful. | |
| 718 return watcher.got_mouse_up(); | |
| 719 } | |
| 720 | |
| 721 void HWNDMessageHandler::EndMoveLoop() { | |
| 722 SendMessage(hwnd(), WM_CANCELMODE, 0, 0); | |
| 723 } | |
| 724 | |
| 725 void HWNDMessageHandler::SendFrameChanged() { | |
| 726 SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, | |
| 727 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | | |
| 728 SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION | | |
| 729 SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); | |
| 730 } | |
| 731 | |
| 732 void HWNDMessageHandler::FlashFrame(bool flash) { | |
| 733 FLASHWINFO fwi; | |
| 734 fwi.cbSize = sizeof(fwi); | |
| 735 fwi.hwnd = hwnd(); | |
| 736 if (flash) { | |
| 737 fwi.dwFlags = FLASHW_ALL; | |
| 738 fwi.uCount = 4; | |
| 739 fwi.dwTimeout = 0; | |
| 740 } else { | |
| 741 fwi.dwFlags = FLASHW_STOP; | |
| 742 } | |
| 743 FlashWindowEx(&fwi); | |
| 744 } | |
| 745 | |
| 746 void HWNDMessageHandler::ClearNativeFocus() { | |
| 747 ::SetFocus(hwnd()); | |
| 748 } | |
| 749 | |
| 750 void HWNDMessageHandler::SetCapture() { | |
| 751 DCHECK(!HasCapture()); | |
| 752 ::SetCapture(hwnd()); | |
| 753 } | |
| 754 | |
| 755 void HWNDMessageHandler::ReleaseCapture() { | |
| 756 if (HasCapture()) | |
| 757 ::ReleaseCapture(); | |
| 758 } | |
| 759 | |
| 760 bool HWNDMessageHandler::HasCapture() const { | |
| 761 return ::GetCapture() == hwnd(); | |
| 762 } | |
| 763 | |
| 764 void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { | |
| 765 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
| 766 int dwm_value = enabled ? FALSE : TRUE; | |
| 767 DwmSetWindowAttribute( | |
| 768 hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, sizeof(dwm_value)); | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 bool HWNDMessageHandler::SetTitle(const base::string16& title) { | |
| 773 base::string16 current_title; | |
| 774 size_t len_with_null = GetWindowTextLength(hwnd()) + 1; | |
| 775 if (len_with_null == 1 && title.length() == 0) | |
| 776 return false; | |
| 777 if (len_with_null - 1 == title.length() && | |
| 778 GetWindowText( | |
| 779 hwnd(), WriteInto(¤t_title, len_with_null), len_with_null) && | |
| 780 current_title == title) | |
| 781 return false; | |
| 782 SetWindowText(hwnd(), title.c_str()); | |
| 783 return true; | |
| 784 } | |
| 785 | |
| 786 void HWNDMessageHandler::SetCursor(HCURSOR cursor) { | |
| 787 if (cursor) { | |
| 788 previous_cursor_ = ::SetCursor(cursor); | |
| 789 current_cursor_ = cursor; | |
| 790 } else if (previous_cursor_) { | |
| 791 ::SetCursor(previous_cursor_); | |
| 792 previous_cursor_ = NULL; | |
| 793 } | |
| 794 } | |
| 795 | |
| 796 void HWNDMessageHandler::FrameTypeChanged() { | |
| 797 if (base::win::GetVersion() < base::win::VERSION_VISTA) { | |
| 798 // Don't redraw the window here, because we invalidate the window later. | |
| 799 ResetWindowRegion(true, false); | |
| 800 // The non-client view needs to update too. | |
| 801 delegate_->HandleFrameChanged(); | |
| 802 InvalidateRect(hwnd(), NULL, FALSE); | |
| 803 } else { | |
| 804 if (!custom_window_region_ && !delegate_->IsUsingCustomFrame()) | |
| 805 dwm_transition_desired_ = true; | |
| 806 if (!dwm_transition_desired_ || !fullscreen_handler_->fullscreen()) | |
| 807 PerformDwmTransition(); | |
| 808 } | |
| 809 } | |
| 810 | |
| 811 void HWNDMessageHandler::SchedulePaintInRect(const gfx::Rect& rect) { | |
| 812 if (use_layered_buffer_) { | |
| 813 // We must update the back-buffer immediately, since Windows' handling of | |
| 814 // invalid rects is somewhat mysterious. | |
| 815 invalid_rect_.Union(rect); | |
| 816 | |
| 817 // In some situations, such as drag and drop, when Windows itself runs a | |
| 818 // nested message loop our message loop appears to be starved and we don't | |
| 819 // receive calls to DidProcessMessage(). This only seems to affect layered | |
| 820 // windows, so we schedule a redraw manually using a task, since those never | |
| 821 // seem to be starved. Also, wtf. | |
| 822 if (!waiting_for_redraw_layered_window_contents_) { | |
| 823 waiting_for_redraw_layered_window_contents_ = true; | |
| 824 base::MessageLoop::current()->PostTask( | |
| 825 FROM_HERE, | |
| 826 base::Bind(&HWNDMessageHandler::RedrawLayeredWindowContents, | |
| 827 weak_factory_.GetWeakPtr())); | |
| 828 } | |
| 829 } else { | |
| 830 // InvalidateRect() expects client coordinates. | |
| 831 RECT r = rect.ToRECT(); | |
| 832 InvalidateRect(hwnd(), &r, FALSE); | |
| 833 } | |
| 834 } | |
| 835 | |
| 836 void HWNDMessageHandler::SetOpacity(BYTE opacity) { | |
| 837 layered_alpha_ = opacity; | |
| 838 } | |
| 839 | |
| 840 void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, | |
| 841 const gfx::ImageSkia& app_icon) { | |
| 842 if (!window_icon.isNull()) { | |
| 843 HICON windows_icon = IconUtil::CreateHICONFromSkBitmap( | |
| 844 *window_icon.bitmap()); | |
| 845 // We need to make sure to destroy the previous icon, otherwise we'll leak | |
| 846 // these GDI objects until we crash! | |
| 847 HICON old_icon = reinterpret_cast<HICON>( | |
| 848 SendMessage(hwnd(), WM_SETICON, ICON_SMALL, | |
| 849 reinterpret_cast<LPARAM>(windows_icon))); | |
| 850 if (old_icon) | |
| 851 DestroyIcon(old_icon); | |
| 852 } | |
| 853 if (!app_icon.isNull()) { | |
| 854 HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap()); | |
| 855 HICON old_icon = reinterpret_cast<HICON>( | |
| 856 SendMessage(hwnd(), WM_SETICON, ICON_BIG, | |
| 857 reinterpret_cast<LPARAM>(windows_icon))); | |
| 858 if (old_icon) | |
| 859 DestroyIcon(old_icon); | |
| 860 } | |
| 861 } | |
| 862 | |
| 863 void HWNDMessageHandler::SetFullscreen(bool fullscreen) { | |
| 864 fullscreen_handler()->SetFullscreen(fullscreen); | |
| 865 // If we are out of fullscreen and there was a pending DWM transition for the | |
| 866 // window, then go ahead and do it now. | |
| 867 if (!fullscreen && dwm_transition_desired_) | |
| 868 PerformDwmTransition(); | |
| 869 } | |
| 870 | |
| 871 void HWNDMessageHandler::SizeConstraintsChanged() { | |
| 872 LONG style = GetWindowLong(hwnd(), GWL_STYLE); | |
| 873 // Ignore if this is not a standard window. | |
| 874 if (!(style & WS_OVERLAPPED)) | |
| 875 return; | |
| 876 | |
| 877 if (delegate_->CanResize()) { | |
| 878 style |= WS_THICKFRAME | WS_MAXIMIZEBOX; | |
| 879 if (!delegate_->CanMaximize()) | |
| 880 style &= ~WS_MAXIMIZEBOX; | |
| 881 } else { | |
| 882 style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); | |
| 883 } | |
| 884 if (delegate_->CanMinimize()) { | |
| 885 style |= WS_MINIMIZEBOX; | |
| 886 } else { | |
| 887 style &= ~WS_MINIMIZEBOX; | |
| 888 } | |
| 889 SetWindowLong(hwnd(), GWL_STYLE, style); | |
| 890 } | |
| 891 | |
| 892 //////////////////////////////////////////////////////////////////////////////// | |
| 893 // HWNDMessageHandler, InputMethodDelegate implementation: | |
| 894 | |
| 895 void HWNDMessageHandler::DispatchKeyEventPostIME(const ui::KeyEvent& key) { | |
| 896 SetMsgHandled(delegate_->HandleKeyEvent(key)); | |
| 897 } | |
| 898 | |
| 899 //////////////////////////////////////////////////////////////////////////////// | |
| 900 // HWNDMessageHandler, gfx::WindowImpl overrides: | |
| 901 | |
| 902 HICON HWNDMessageHandler::GetDefaultWindowIcon() const { | |
| 903 if (use_system_default_icon_) | |
| 904 return NULL; | |
| 905 return ViewsDelegate::views_delegate ? | |
| 906 ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL; | |
| 907 } | |
| 908 | |
| 909 LRESULT HWNDMessageHandler::OnWndProc(UINT message, | |
| 910 WPARAM w_param, | |
| 911 LPARAM l_param) { | |
| 912 HWND window = hwnd(); | |
| 913 LRESULT result = 0; | |
| 914 | |
| 915 if (delegate_ && delegate_->PreHandleMSG(message, w_param, l_param, &result)) | |
| 916 return result; | |
| 917 | |
| 918 // Otherwise we handle everything else. | |
| 919 // NOTE: We inline ProcessWindowMessage() as 'this' may be destroyed during | |
| 920 // dispatch and ProcessWindowMessage() doesn't deal with that well. | |
| 921 const BOOL old_msg_handled = msg_handled_; | |
| 922 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 923 const BOOL processed = | |
| 924 _ProcessWindowMessage(window, message, w_param, l_param, result, 0); | |
| 925 if (!ref) | |
| 926 return 0; | |
| 927 msg_handled_ = old_msg_handled; | |
| 928 | |
| 929 if (!processed) { | |
| 930 result = DefWindowProc(window, message, w_param, l_param); | |
| 931 // DefWindowProc() may have destroyed the window and/or us in a nested | |
| 932 // message loop. | |
| 933 if (!ref || !::IsWindow(window)) | |
| 934 return result; | |
| 935 } | |
| 936 | |
| 937 if (delegate_) { | |
| 938 delegate_->PostHandleMSG(message, w_param, l_param); | |
| 939 if (message == WM_NCDESTROY) | |
| 940 delegate_->HandleDestroyed(); | |
| 941 } | |
| 942 | |
| 943 if (message == WM_ACTIVATE && IsTopLevelWindow(window)) | |
| 944 PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(w_param)); | |
| 945 return result; | |
| 946 } | |
| 947 | |
| 948 LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message, | |
| 949 WPARAM w_param, | |
| 950 LPARAM l_param, | |
| 951 bool* handled) { | |
| 952 // Don't track forwarded mouse messages. We expect the caller to track the | |
| 953 // mouse. | |
| 954 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 955 LRESULT ret = HandleMouseEventInternal(message, w_param, l_param, false); | |
| 956 *handled = IsMsgHandled(); | |
| 957 return ret; | |
| 958 } | |
| 959 | |
| 960 LRESULT HWNDMessageHandler::HandleKeyboardMessage(unsigned int message, | |
| 961 WPARAM w_param, | |
| 962 LPARAM l_param, | |
| 963 bool* handled) { | |
| 964 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 965 LRESULT ret = OnKeyEvent(message, w_param, l_param); | |
| 966 *handled = IsMsgHandled(); | |
| 967 return ret; | |
| 968 } | |
| 969 | |
| 970 LRESULT HWNDMessageHandler::HandleTouchMessage(unsigned int message, | |
| 971 WPARAM w_param, | |
| 972 LPARAM l_param, | |
| 973 bool* handled) { | |
| 974 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 975 LRESULT ret = OnTouchEvent(message, w_param, l_param); | |
| 976 *handled = IsMsgHandled(); | |
| 977 return ret; | |
| 978 } | |
| 979 | |
| 980 LRESULT HWNDMessageHandler::HandleScrollMessage(unsigned int message, | |
| 981 WPARAM w_param, | |
| 982 LPARAM l_param, | |
| 983 bool* handled) { | |
| 984 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 985 LRESULT ret = OnScrollMessage(message, w_param, l_param); | |
| 986 *handled = IsMsgHandled(); | |
| 987 return ret; | |
| 988 } | |
| 989 | |
| 990 LRESULT HWNDMessageHandler::HandleNcHitTestMessage(unsigned int message, | |
| 991 WPARAM w_param, | |
| 992 LPARAM l_param, | |
| 993 bool* handled) { | |
| 994 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 995 LRESULT ret = OnNCHitTest( | |
| 996 gfx::Point(CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param))); | |
| 997 *handled = IsMsgHandled(); | |
| 998 return ret; | |
| 999 } | |
| 1000 | |
| 1001 //////////////////////////////////////////////////////////////////////////////// | |
| 1002 // HWNDMessageHandler, private: | |
| 1003 | |
| 1004 int HWNDMessageHandler::GetAppbarAutohideEdges(HMONITOR monitor) { | |
| 1005 autohide_factory_.InvalidateWeakPtrs(); | |
| 1006 return ViewsDelegate::views_delegate ? | |
| 1007 ViewsDelegate::views_delegate->GetAppbarAutohideEdges( | |
| 1008 monitor, | |
| 1009 base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged, | |
| 1010 autohide_factory_.GetWeakPtr())) : | |
| 1011 ViewsDelegate::EDGE_BOTTOM; | |
| 1012 } | |
| 1013 | |
| 1014 void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() { | |
| 1015 // This triggers querying WM_NCCALCSIZE again. | |
| 1016 RECT client; | |
| 1017 GetWindowRect(hwnd(), &client); | |
| 1018 SetWindowPos(hwnd(), NULL, client.left, client.top, | |
| 1019 client.right - client.left, client.bottom - client.top, | |
| 1020 SWP_FRAMECHANGED); | |
| 1021 } | |
| 1022 | |
| 1023 void HWNDMessageHandler::SetInitialFocus() { | |
| 1024 if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && | |
| 1025 !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { | |
| 1026 // The window does not get keyboard messages unless we focus it. | |
| 1027 SetFocus(hwnd()); | |
| 1028 } | |
| 1029 } | |
| 1030 | |
| 1031 void HWNDMessageHandler::PostProcessActivateMessage(int activation_state, | |
| 1032 bool minimized) { | |
| 1033 DCHECK(IsTopLevelWindow(hwnd())); | |
| 1034 const bool active = activation_state != WA_INACTIVE && !minimized; | |
| 1035 if (delegate_->CanActivate()) | |
| 1036 delegate_->HandleActivationChanged(active); | |
| 1037 } | |
| 1038 | |
| 1039 void HWNDMessageHandler::RestoreEnabledIfNecessary() { | |
| 1040 if (delegate_->IsModal() && !restored_enabled_) { | |
| 1041 restored_enabled_ = true; | |
| 1042 // If we were run modally, we need to undo the disabled-ness we inflicted on | |
| 1043 // the owner's parent hierarchy. | |
| 1044 HWND start = ::GetWindow(hwnd(), GW_OWNER); | |
| 1045 while (start) { | |
| 1046 ::EnableWindow(start, TRUE); | |
| 1047 start = ::GetParent(start); | |
| 1048 } | |
| 1049 } | |
| 1050 } | |
| 1051 | |
| 1052 void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) { | |
| 1053 if (command) | |
| 1054 SendMessage(hwnd(), WM_SYSCOMMAND, command, 0); | |
| 1055 } | |
| 1056 | |
| 1057 void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) { | |
| 1058 // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE | |
| 1059 // when the user moves the mouse outside this HWND's bounds. | |
| 1060 if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) { | |
| 1061 if (mouse_tracking_flags & TME_CANCEL) { | |
| 1062 // We're about to cancel active mouse tracking, so empty out the stored | |
| 1063 // state. | |
| 1064 active_mouse_tracking_flags_ = 0; | |
| 1065 } else { | |
| 1066 active_mouse_tracking_flags_ = mouse_tracking_flags; | |
| 1067 } | |
| 1068 | |
| 1069 TRACKMOUSEEVENT tme; | |
| 1070 tme.cbSize = sizeof(tme); | |
| 1071 tme.dwFlags = mouse_tracking_flags; | |
| 1072 tme.hwndTrack = hwnd(); | |
| 1073 tme.dwHoverTime = 0; | |
| 1074 TrackMouseEvent(&tme); | |
| 1075 } else if (mouse_tracking_flags != active_mouse_tracking_flags_) { | |
| 1076 TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL); | |
| 1077 TrackMouseEvents(mouse_tracking_flags); | |
| 1078 } | |
| 1079 } | |
| 1080 | |
| 1081 void HWNDMessageHandler::ClientAreaSizeChanged() { | |
| 1082 gfx::Size s = GetClientAreaBounds().size(); | |
| 1083 delegate_->HandleClientSizeChanged(s); | |
| 1084 if (use_layered_buffer_) | |
| 1085 layered_window_contents_.reset(new gfx::Canvas(s, 1.0f, false)); | |
| 1086 } | |
| 1087 | |
| 1088 bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets) const { | |
| 1089 if (delegate_->GetClientAreaInsets(insets)) | |
| 1090 return true; | |
| 1091 DCHECK(insets->empty()); | |
| 1092 | |
| 1093 // Returning false causes the default handling in OnNCCalcSize() to | |
| 1094 // be invoked. | |
| 1095 if (!delegate_->IsWidgetWindow() || | |
| 1096 (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) { | |
| 1097 return false; | |
| 1098 } | |
| 1099 | |
| 1100 if (IsMaximized()) { | |
| 1101 // Windows automatically adds a standard width border to all sides when a | |
| 1102 // window is maximized. | |
| 1103 int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); | |
| 1104 if (remove_standard_frame_) | |
| 1105 border_thickness -= 1; | |
| 1106 *insets = gfx::Insets( | |
| 1107 border_thickness, border_thickness, border_thickness, border_thickness); | |
| 1108 return true; | |
| 1109 } | |
| 1110 | |
| 1111 *insets = gfx::Insets(); | |
| 1112 return true; | |
| 1113 } | |
| 1114 | |
| 1115 void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { | |
| 1116 // A native frame uses the native window region, and we don't want to mess | |
| 1117 // with it. | |
| 1118 // WS_EX_COMPOSITED is used instead of WS_EX_LAYERED under aura. WS_EX_LAYERED | |
| 1119 // automatically makes clicks on transparent pixels fall through, that isn't | |
| 1120 // the case with WS_EX_COMPOSITED. So, we route WS_EX_COMPOSITED through to | |
| 1121 // the delegate to allow for a custom hit mask. | |
| 1122 if ((window_ex_style() & WS_EX_COMPOSITED) == 0 && !custom_window_region_ && | |
| 1123 (!delegate_->IsUsingCustomFrame() || !delegate_->IsWidgetWindow())) { | |
| 1124 if (force) | |
| 1125 SetWindowRgn(hwnd(), NULL, redraw); | |
| 1126 return; | |
| 1127 } | |
| 1128 | |
| 1129 // Changing the window region is going to force a paint. Only change the | |
| 1130 // window region if the region really differs. | |
| 1131 HRGN current_rgn = CreateRectRgn(0, 0, 0, 0); | |
| 1132 int current_rgn_result = GetWindowRgn(hwnd(), current_rgn); | |
| 1133 | |
| 1134 RECT window_rect; | |
| 1135 GetWindowRect(hwnd(), &window_rect); | |
| 1136 HRGN new_region; | |
| 1137 if (custom_window_region_) { | |
| 1138 new_region = ::CreateRectRgn(0, 0, 0, 0); | |
| 1139 ::CombineRgn(new_region, custom_window_region_.Get(), NULL, RGN_COPY); | |
| 1140 } else if (IsMaximized()) { | |
| 1141 HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST); | |
| 1142 MONITORINFO mi; | |
| 1143 mi.cbSize = sizeof mi; | |
| 1144 GetMonitorInfo(monitor, &mi); | |
| 1145 RECT work_rect = mi.rcWork; | |
| 1146 OffsetRect(&work_rect, -window_rect.left, -window_rect.top); | |
| 1147 new_region = CreateRectRgnIndirect(&work_rect); | |
| 1148 } else { | |
| 1149 gfx::Path window_mask; | |
| 1150 delegate_->GetWindowMask(gfx::Size(window_rect.right - window_rect.left, | |
| 1151 window_rect.bottom - window_rect.top), | |
| 1152 &window_mask); | |
| 1153 new_region = gfx::CreateHRGNFromSkPath(window_mask); | |
| 1154 } | |
| 1155 | |
| 1156 if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) { | |
| 1157 // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion. | |
| 1158 SetWindowRgn(hwnd(), new_region, redraw); | |
| 1159 } else { | |
| 1160 DeleteObject(new_region); | |
| 1161 } | |
| 1162 | |
| 1163 DeleteObject(current_rgn); | |
| 1164 } | |
| 1165 | |
| 1166 void HWNDMessageHandler::UpdateDwmNcRenderingPolicy() { | |
| 1167 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
| 1168 return; | |
| 1169 | |
| 1170 if (fullscreen_handler_->fullscreen()) | |
| 1171 return; | |
| 1172 | |
| 1173 DWMNCRENDERINGPOLICY policy = | |
| 1174 custom_window_region_ || delegate_->IsUsingCustomFrame() ? | |
| 1175 DWMNCRP_DISABLED : DWMNCRP_ENABLED; | |
| 1176 | |
| 1177 DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY, | |
| 1178 &policy, sizeof(DWMNCRENDERINGPOLICY)); | |
| 1179 } | |
| 1180 | |
| 1181 LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message, | |
| 1182 WPARAM w_param, | |
| 1183 LPARAM l_param) { | |
| 1184 ScopedRedrawLock lock(this); | |
| 1185 // The Widget and HWND can be destroyed in the call to DefWindowProc, so use | |
| 1186 // the WeakPtrFactory to avoid unlocking (and crashing) after destruction. | |
| 1187 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 1188 LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param); | |
| 1189 if (!ref) | |
| 1190 lock.CancelUnlockOperation(); | |
| 1191 return result; | |
| 1192 } | |
| 1193 | |
| 1194 void HWNDMessageHandler::LockUpdates(bool force) { | |
| 1195 // We skip locked updates when Aero is on for two reasons: | |
| 1196 // 1. Because it isn't necessary | |
| 1197 // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is | |
| 1198 // attempting to present a child window's backbuffer onscreen. When these | |
| 1199 // two actions race with one another, the child window will either flicker | |
| 1200 // or will simply stop updating entirely. | |
| 1201 if ((force || !ui::win::IsAeroGlassEnabled()) && ++lock_updates_count_ == 1) { | |
| 1202 SetWindowLong(hwnd(), GWL_STYLE, | |
| 1203 GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE); | |
| 1204 } | |
| 1205 } | |
| 1206 | |
| 1207 void HWNDMessageHandler::UnlockUpdates(bool force) { | |
| 1208 if ((force || !ui::win::IsAeroGlassEnabled()) && --lock_updates_count_ <= 0) { | |
| 1209 SetWindowLong(hwnd(), GWL_STYLE, | |
| 1210 GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); | |
| 1211 lock_updates_count_ = 0; | |
| 1212 } | |
| 1213 } | |
| 1214 | |
| 1215 void HWNDMessageHandler::RedrawLayeredWindowContents() { | |
| 1216 waiting_for_redraw_layered_window_contents_ = false; | |
| 1217 if (invalid_rect_.IsEmpty()) | |
| 1218 return; | |
| 1219 | |
| 1220 // We need to clip to the dirty rect ourselves. | |
| 1221 layered_window_contents_->sk_canvas()->save(); | |
| 1222 double scale = gfx::win::GetDeviceScaleFactor(); | |
| 1223 layered_window_contents_->sk_canvas()->scale( | |
| 1224 SkScalar(scale),SkScalar(scale)); | |
| 1225 layered_window_contents_->ClipRect(invalid_rect_); | |
| 1226 delegate_->PaintLayeredWindow(layered_window_contents_.get()); | |
| 1227 layered_window_contents_->sk_canvas()->scale( | |
| 1228 SkScalar(1.0/scale),SkScalar(1.0/scale)); | |
| 1229 layered_window_contents_->sk_canvas()->restore(); | |
| 1230 | |
| 1231 RECT wr; | |
| 1232 GetWindowRect(hwnd(), &wr); | |
| 1233 SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; | |
| 1234 POINT position = {wr.left, wr.top}; | |
| 1235 HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas()); | |
| 1236 POINT zero = {0, 0}; | |
| 1237 BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; | |
| 1238 UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero, | |
| 1239 RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); | |
| 1240 invalid_rect_.SetRect(0, 0, 0, 0); | |
| 1241 skia::EndPlatformPaint(layered_window_contents_->sk_canvas()); | |
| 1242 } | |
| 1243 | |
| 1244 void HWNDMessageHandler::ForceRedrawWindow(int attempts) { | |
| 1245 if (ui::IsWorkstationLocked()) { | |
| 1246 // Presents will continue to fail as long as the input desktop is | |
| 1247 // unavailable. | |
| 1248 if (--attempts <= 0) | |
| 1249 return; | |
| 1250 base::MessageLoop::current()->PostDelayedTask( | |
| 1251 FROM_HERE, | |
| 1252 base::Bind(&HWNDMessageHandler::ForceRedrawWindow, | |
| 1253 weak_factory_.GetWeakPtr(), | |
| 1254 attempts), | |
| 1255 base::TimeDelta::FromMilliseconds(500)); | |
| 1256 return; | |
| 1257 } | |
| 1258 InvalidateRect(hwnd(), NULL, FALSE); | |
| 1259 } | |
| 1260 | |
| 1261 // Message handlers ------------------------------------------------------------ | |
| 1262 | |
| 1263 void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { | |
| 1264 if (delegate_->IsWidgetWindow() && !active && | |
| 1265 thread_id != GetCurrentThreadId()) { | |
| 1266 delegate_->HandleAppDeactivated(); | |
| 1267 // Also update the native frame if it is rendering the non-client area. | |
| 1268 if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) | |
| 1269 DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); | |
| 1270 } | |
| 1271 } | |
| 1272 | |
| 1273 BOOL HWNDMessageHandler::OnAppCommand(HWND window, | |
| 1274 short command, | |
| 1275 WORD device, | |
| 1276 int keystate) { | |
| 1277 BOOL handled = !!delegate_->HandleAppCommand(command); | |
| 1278 SetMsgHandled(handled); | |
| 1279 // Make sure to return TRUE if the event was handled or in some cases the | |
| 1280 // system will execute the default handler which can cause bugs like going | |
| 1281 // forward or back two pages instead of one. | |
| 1282 return handled; | |
| 1283 } | |
| 1284 | |
| 1285 void HWNDMessageHandler::OnCancelMode() { | |
| 1286 delegate_->HandleCancelMode(); | |
| 1287 // Need default handling, otherwise capture and other things aren't canceled. | |
| 1288 SetMsgHandled(FALSE); | |
| 1289 } | |
| 1290 | |
| 1291 void HWNDMessageHandler::OnCaptureChanged(HWND window) { | |
| 1292 delegate_->HandleCaptureLost(); | |
| 1293 } | |
| 1294 | |
| 1295 void HWNDMessageHandler::OnClose() { | |
| 1296 delegate_->HandleClose(); | |
| 1297 } | |
| 1298 | |
| 1299 void HWNDMessageHandler::OnCommand(UINT notification_code, | |
| 1300 int command, | |
| 1301 HWND window) { | |
| 1302 // If the notification code is > 1 it means it is control specific and we | |
| 1303 // should ignore it. | |
| 1304 if (notification_code > 1 || delegate_->HandleAppCommand(command)) | |
| 1305 SetMsgHandled(FALSE); | |
| 1306 } | |
| 1307 | |
| 1308 LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { | |
| 1309 use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED); | |
| 1310 | |
| 1311 if (window_ex_style() & WS_EX_COMPOSITED) { | |
| 1312 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
| 1313 // This is part of the magic to emulate layered windows with Aura | |
| 1314 // see the explanation elsewere when we set WS_EX_COMPOSITED style. | |
| 1315 MARGINS margins = {-1,-1,-1,-1}; | |
| 1316 DwmExtendFrameIntoClientArea(hwnd(), &margins); | |
| 1317 } | |
| 1318 } | |
| 1319 | |
| 1320 fullscreen_handler_->set_hwnd(hwnd()); | |
| 1321 | |
| 1322 // This message initializes the window so that focus border are shown for | |
| 1323 // windows. | |
| 1324 SendMessage(hwnd(), | |
| 1325 WM_CHANGEUISTATE, | |
| 1326 MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), | |
| 1327 0); | |
| 1328 | |
| 1329 if (remove_standard_frame_) { | |
| 1330 SetWindowLong(hwnd(), GWL_STYLE, | |
| 1331 GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); | |
| 1332 SendFrameChanged(); | |
| 1333 } | |
| 1334 | |
| 1335 // Get access to a modifiable copy of the system menu. | |
| 1336 GetSystemMenu(hwnd(), false); | |
| 1337 | |
| 1338 if (base::win::GetVersion() >= base::win::VERSION_WIN7 && | |
| 1339 ui::AreTouchEventsEnabled()) | |
| 1340 RegisterTouchWindow(hwnd(), TWF_WANTPALM); | |
| 1341 | |
| 1342 // We need to allow the delegate to size its contents since the window may not | |
| 1343 // receive a size notification when its initial bounds are specified at window | |
| 1344 // creation time. | |
| 1345 ClientAreaSizeChanged(); | |
| 1346 | |
| 1347 delegate_->HandleCreate(); | |
| 1348 | |
| 1349 WTSRegisterSessionNotification(hwnd(), NOTIFY_FOR_THIS_SESSION); | |
| 1350 | |
| 1351 // TODO(beng): move more of NWW::OnCreate here. | |
| 1352 return 0; | |
| 1353 } | |
| 1354 | |
| 1355 void HWNDMessageHandler::OnDestroy() { | |
| 1356 WTSUnRegisterSessionNotification(hwnd()); | |
| 1357 delegate_->HandleDestroying(); | |
| 1358 } | |
| 1359 | |
| 1360 void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, | |
| 1361 const gfx::Size& screen_size) { | |
| 1362 delegate_->HandleDisplayChange(); | |
| 1363 } | |
| 1364 | |
| 1365 LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg, | |
| 1366 WPARAM w_param, | |
| 1367 LPARAM l_param) { | |
| 1368 if (!delegate_->IsWidgetWindow()) { | |
| 1369 SetMsgHandled(FALSE); | |
| 1370 return 0; | |
| 1371 } | |
| 1372 | |
| 1373 FrameTypeChanged(); | |
| 1374 return 0; | |
| 1375 } | |
| 1376 | |
| 1377 void HWNDMessageHandler::OnEnterMenuLoop(BOOL from_track_popup_menu) { | |
| 1378 if (menu_depth_++ == 0) | |
| 1379 delegate_->HandleMenuLoop(true); | |
| 1380 } | |
| 1381 | |
| 1382 void HWNDMessageHandler::OnEnterSizeMove() { | |
| 1383 // Please refer to the comments in the OnSize function about the scrollbar | |
| 1384 // hack. | |
| 1385 // Hide the Windows scrollbar if the scroll styles are present to ensure | |
| 1386 // that a paint flicker does not occur while sizing. | |
| 1387 if (in_size_loop_ && needs_scroll_styles_) | |
| 1388 ShowScrollBar(hwnd(), SB_BOTH, FALSE); | |
| 1389 | |
| 1390 delegate_->HandleBeginWMSizeMove(); | |
| 1391 SetMsgHandled(FALSE); | |
| 1392 } | |
| 1393 | |
| 1394 LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) { | |
| 1395 // Needed to prevent resize flicker. | |
| 1396 return 1; | |
| 1397 } | |
| 1398 | |
| 1399 void HWNDMessageHandler::OnExitMenuLoop(BOOL is_shortcut_menu) { | |
| 1400 if (--menu_depth_ == 0) | |
| 1401 delegate_->HandleMenuLoop(false); | |
| 1402 DCHECK_GE(0, menu_depth_); | |
| 1403 } | |
| 1404 | |
| 1405 void HWNDMessageHandler::OnExitSizeMove() { | |
| 1406 delegate_->HandleEndWMSizeMove(); | |
| 1407 SetMsgHandled(FALSE); | |
| 1408 // Please refer to the notes in the OnSize function for information about | |
| 1409 // the scrolling hack. | |
| 1410 // We hide the Windows scrollbar in the OnEnterSizeMove function. We need | |
| 1411 // to add the scroll styles back to ensure that scrolling works in legacy | |
| 1412 // trackpoint drivers. | |
| 1413 if (in_size_loop_ && needs_scroll_styles_) | |
| 1414 AddScrollStylesToWindow(hwnd()); | |
| 1415 } | |
| 1416 | |
| 1417 void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { | |
| 1418 gfx::Size min_window_size; | |
| 1419 gfx::Size max_window_size; | |
| 1420 delegate_->GetMinMaxSize(&min_window_size, &max_window_size); | |
| 1421 min_window_size = gfx::win::DIPToScreenSize(min_window_size); | |
| 1422 max_window_size = gfx::win::DIPToScreenSize(max_window_size); | |
| 1423 | |
| 1424 | |
| 1425 // Add the native frame border size to the minimum and maximum size if the | |
| 1426 // view reports its size as the client size. | |
| 1427 if (delegate_->WidgetSizeIsClientSize()) { | |
| 1428 RECT client_rect, window_rect; | |
| 1429 GetClientRect(hwnd(), &client_rect); | |
| 1430 GetWindowRect(hwnd(), &window_rect); | |
| 1431 CR_DEFLATE_RECT(&window_rect, &client_rect); | |
| 1432 min_window_size.Enlarge(window_rect.right - window_rect.left, | |
| 1433 window_rect.bottom - window_rect.top); | |
| 1434 // Either axis may be zero, so enlarge them independently. | |
| 1435 if (max_window_size.width()) | |
| 1436 max_window_size.Enlarge(window_rect.right - window_rect.left, 0); | |
| 1437 if (max_window_size.height()) | |
| 1438 max_window_size.Enlarge(0, window_rect.bottom - window_rect.top); | |
| 1439 } | |
| 1440 minmax_info->ptMinTrackSize.x = min_window_size.width(); | |
| 1441 minmax_info->ptMinTrackSize.y = min_window_size.height(); | |
| 1442 if (max_window_size.width() || max_window_size.height()) { | |
| 1443 if (!max_window_size.width()) | |
| 1444 max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK)); | |
| 1445 if (!max_window_size.height()) | |
| 1446 max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK)); | |
| 1447 minmax_info->ptMaxTrackSize.x = max_window_size.width(); | |
| 1448 minmax_info->ptMaxTrackSize.y = max_window_size.height(); | |
| 1449 } | |
| 1450 SetMsgHandled(FALSE); | |
| 1451 } | |
| 1452 | |
| 1453 LRESULT HWNDMessageHandler::OnGetObject(UINT message, | |
| 1454 WPARAM w_param, | |
| 1455 LPARAM l_param) { | |
| 1456 LRESULT reference_result = static_cast<LRESULT>(0L); | |
| 1457 | |
| 1458 // Only the lower 32 bits of l_param are valid when checking the object id | |
| 1459 // because it sometimes gets sign-extended incorrectly (but not always). | |
| 1460 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param)); | |
| 1461 | |
| 1462 // Accessibility readers will send an OBJID_CLIENT message | |
| 1463 if (OBJID_CLIENT == obj_id) { | |
| 1464 // Retrieve MSAA dispatch object for the root view. | |
| 1465 base::win::ScopedComPtr<IAccessible> root( | |
| 1466 delegate_->GetNativeViewAccessible()); | |
| 1467 | |
| 1468 // Create a reference that MSAA will marshall to the client. | |
| 1469 reference_result = LresultFromObject(IID_IAccessible, w_param, | |
| 1470 static_cast<IAccessible*>(root.Detach())); | |
| 1471 } | |
| 1472 | |
| 1473 return reference_result; | |
| 1474 } | |
| 1475 | |
| 1476 LRESULT HWNDMessageHandler::OnImeMessages(UINT message, | |
| 1477 WPARAM w_param, | |
| 1478 LPARAM l_param) { | |
| 1479 LRESULT result = 0; | |
| 1480 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 1481 const bool msg_handled = | |
| 1482 delegate_->HandleIMEMessage(message, w_param, l_param, &result); | |
| 1483 if (ref.get()) | |
| 1484 SetMsgHandled(msg_handled); | |
| 1485 return result; | |
| 1486 } | |
| 1487 | |
| 1488 void HWNDMessageHandler::OnInitMenu(HMENU menu) { | |
| 1489 bool is_fullscreen = fullscreen_handler_->fullscreen(); | |
| 1490 bool is_minimized = IsMinimized(); | |
| 1491 bool is_maximized = IsMaximized(); | |
| 1492 bool is_restored = !is_fullscreen && !is_minimized && !is_maximized; | |
| 1493 | |
| 1494 ScopedRedrawLock lock(this); | |
| 1495 EnableMenuItemByCommand(menu, SC_RESTORE, delegate_->CanResize() && | |
| 1496 (is_minimized || is_maximized)); | |
| 1497 EnableMenuItemByCommand(menu, SC_MOVE, is_restored); | |
| 1498 EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored); | |
| 1499 EnableMenuItemByCommand(menu, SC_MAXIMIZE, delegate_->CanMaximize() && | |
| 1500 !is_fullscreen && !is_maximized); | |
| 1501 EnableMenuItemByCommand(menu, SC_MINIMIZE, delegate_->CanMinimize() && | |
| 1502 !is_minimized); | |
| 1503 | |
| 1504 if (is_maximized && delegate_->CanResize()) | |
| 1505 ::SetMenuDefaultItem(menu, SC_RESTORE, FALSE); | |
| 1506 else if (!is_maximized && delegate_->CanMaximize()) | |
| 1507 ::SetMenuDefaultItem(menu, SC_MAXIMIZE, FALSE); | |
| 1508 } | |
| 1509 | |
| 1510 void HWNDMessageHandler::OnInputLangChange(DWORD character_set, | |
| 1511 HKL input_language_id) { | |
| 1512 delegate_->HandleInputLanguageChange(character_set, input_language_id); | |
| 1513 } | |
| 1514 | |
| 1515 LRESULT HWNDMessageHandler::OnKeyEvent(UINT message, | |
| 1516 WPARAM w_param, | |
| 1517 LPARAM l_param) { | |
| 1518 MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime() }; | |
| 1519 ui::KeyEvent key(msg); | |
| 1520 if (!delegate_->HandleUntranslatedKeyEvent(key)) | |
| 1521 DispatchKeyEventPostIME(key); | |
| 1522 return 0; | |
| 1523 } | |
| 1524 | |
| 1525 void HWNDMessageHandler::OnKillFocus(HWND focused_window) { | |
| 1526 delegate_->HandleNativeBlur(focused_window); | |
| 1527 SetMsgHandled(FALSE); | |
| 1528 } | |
| 1529 | |
| 1530 LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, | |
| 1531 WPARAM w_param, | |
| 1532 LPARAM l_param) { | |
| 1533 // Please refer to the comments in the header for the touch_down_contexts_ | |
| 1534 // member for the if statement below. | |
| 1535 if (touch_down_contexts_) | |
| 1536 return MA_NOACTIVATE; | |
| 1537 | |
| 1538 // On Windows, if we select the menu item by touch and if the window at the | |
| 1539 // location is another window on the same thread, that window gets a | |
| 1540 // WM_MOUSEACTIVATE message and ends up activating itself, which is not | |
| 1541 // correct. We workaround this by setting a property on the window at the | |
| 1542 // current cursor location. We check for this property in our | |
| 1543 // WM_MOUSEACTIVATE handler and don't activate the window if the property is | |
| 1544 // set. | |
| 1545 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) { | |
| 1546 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow); | |
| 1547 return MA_NOACTIVATE; | |
| 1548 } | |
| 1549 // A child window activation should be treated as if we lost activation. | |
| 1550 POINT cursor_pos = {0}; | |
| 1551 ::GetCursorPos(&cursor_pos); | |
| 1552 ::ScreenToClient(hwnd(), &cursor_pos); | |
| 1553 // The code below exists for child windows like NPAPI plugins etc which need | |
| 1554 // to be activated whenever we receive a WM_MOUSEACTIVATE message. Don't put | |
| 1555 // transparent child windows in this bucket as they are not supposed to grab | |
| 1556 // activation. | |
| 1557 // TODO(ananta) | |
| 1558 // Get rid of this code when we deprecate NPAPI plugins. | |
| 1559 HWND child = ::RealChildWindowFromPoint(hwnd(), cursor_pos); | |
| 1560 if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child) && | |
| 1561 !(::GetWindowLong(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) | |
| 1562 PostProcessActivateMessage(WA_INACTIVE, false); | |
| 1563 | |
| 1564 // TODO(beng): resolve this with the GetWindowLong() check on the subsequent | |
| 1565 // line. | |
| 1566 if (delegate_->IsWidgetWindow()) | |
| 1567 return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT; | |
| 1568 if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) | |
| 1569 return MA_NOACTIVATE; | |
| 1570 SetMsgHandled(FALSE); | |
| 1571 return MA_ACTIVATE; | |
| 1572 } | |
| 1573 | |
| 1574 LRESULT HWNDMessageHandler::OnMouseRange(UINT message, | |
| 1575 WPARAM w_param, | |
| 1576 LPARAM l_param) { | |
| 1577 return HandleMouseEventInternal(message, w_param, l_param, true); | |
| 1578 } | |
| 1579 | |
| 1580 void HWNDMessageHandler::OnMove(const gfx::Point& point) { | |
| 1581 delegate_->HandleMove(); | |
| 1582 SetMsgHandled(FALSE); | |
| 1583 } | |
| 1584 | |
| 1585 void HWNDMessageHandler::OnMoving(UINT param, const RECT* new_bounds) { | |
| 1586 delegate_->HandleMove(); | |
| 1587 } | |
| 1588 | |
| 1589 LRESULT HWNDMessageHandler::OnNCActivate(UINT message, | |
| 1590 WPARAM w_param, | |
| 1591 LPARAM l_param) { | |
| 1592 // Per MSDN, w_param is either TRUE or FALSE. However, MSDN also hints that: | |
| 1593 // "If the window is minimized when this message is received, the application | |
| 1594 // should pass the message to the DefWindowProc function." | |
| 1595 // It is found out that the high word of w_param might be set when the window | |
| 1596 // is minimized or restored. To handle this, w_param's high word should be | |
| 1597 // cleared before it is converted to BOOL. | |
| 1598 BOOL active = static_cast<BOOL>(LOWORD(w_param)); | |
| 1599 | |
| 1600 bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled(); | |
| 1601 | |
| 1602 if (!delegate_->IsWidgetWindow()) { | |
| 1603 SetMsgHandled(FALSE); | |
| 1604 return 0; | |
| 1605 } | |
| 1606 | |
| 1607 if (!delegate_->CanActivate()) | |
| 1608 return TRUE; | |
| 1609 | |
| 1610 // On activation, lift any prior restriction against rendering as inactive. | |
| 1611 if (active && inactive_rendering_disabled) | |
| 1612 delegate_->EnableInactiveRendering(); | |
| 1613 | |
| 1614 if (delegate_->IsUsingCustomFrame()) { | |
| 1615 // TODO(beng, et al): Hack to redraw this window and child windows | |
| 1616 // synchronously upon activation. Not all child windows are redrawing | |
| 1617 // themselves leading to issues like http://crbug.com/74604 | |
| 1618 // We redraw out-of-process HWNDs asynchronously to avoid hanging the | |
| 1619 // whole app if a child HWND belonging to a hung plugin is encountered. | |
| 1620 RedrawWindow(hwnd(), NULL, NULL, | |
| 1621 RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); | |
| 1622 EnumChildWindows(hwnd(), EnumChildWindowsForRedraw, NULL); | |
| 1623 } | |
| 1624 | |
| 1625 // The frame may need to redraw as a result of the activation change. | |
| 1626 // We can get WM_NCACTIVATE before we're actually visible. If we're not | |
| 1627 // visible, no need to paint. | |
| 1628 if (IsVisible()) | |
| 1629 delegate_->SchedulePaint(); | |
| 1630 | |
| 1631 // Avoid DefWindowProc non-client rendering over our custom frame on newer | |
| 1632 // Windows versions only (breaks taskbar activation indication on XP/Vista). | |
| 1633 if (delegate_->IsUsingCustomFrame() && | |
| 1634 base::win::GetVersion() > base::win::VERSION_VISTA) { | |
| 1635 SetMsgHandled(TRUE); | |
| 1636 return TRUE; | |
| 1637 } | |
| 1638 | |
| 1639 return DefWindowProcWithRedrawLock( | |
| 1640 WM_NCACTIVATE, inactive_rendering_disabled || active, 0); | |
| 1641 } | |
| 1642 | |
| 1643 LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { | |
| 1644 // We only override the default handling if we need to specify a custom | |
| 1645 // non-client edge width. Note that in most cases "no insets" means no | |
| 1646 // custom width, but in fullscreen mode or when the NonClientFrameView | |
| 1647 // requests it, we want a custom width of 0. | |
| 1648 | |
| 1649 // Let User32 handle the first nccalcsize for captioned windows | |
| 1650 // so it updates its internal structures (specifically caption-present) | |
| 1651 // Without this Tile & Cascade windows won't work. | |
| 1652 // See http://code.google.com/p/chromium/issues/detail?id=900 | |
| 1653 if (is_first_nccalc_) { | |
| 1654 is_first_nccalc_ = false; | |
| 1655 if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) { | |
| 1656 SetMsgHandled(FALSE); | |
| 1657 return 0; | |
| 1658 } | |
| 1659 } | |
| 1660 | |
| 1661 gfx::Insets insets; | |
| 1662 bool got_insets = GetClientAreaInsets(&insets); | |
| 1663 if (!got_insets && !fullscreen_handler_->fullscreen() && | |
| 1664 !(mode && remove_standard_frame_)) { | |
| 1665 SetMsgHandled(FALSE); | |
| 1666 return 0; | |
| 1667 } | |
| 1668 | |
| 1669 RECT* client_rect = mode ? | |
| 1670 &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) : | |
| 1671 reinterpret_cast<RECT*>(l_param); | |
| 1672 client_rect->left += insets.left(); | |
| 1673 client_rect->top += insets.top(); | |
| 1674 client_rect->bottom -= insets.bottom(); | |
| 1675 client_rect->right -= insets.right(); | |
| 1676 if (IsMaximized()) { | |
| 1677 // Find all auto-hide taskbars along the screen edges and adjust in by the | |
| 1678 // thickness of the auto-hide taskbar on each such edge, so the window isn't | |
| 1679 // treated as a "fullscreen app", which would cause the taskbars to | |
| 1680 // disappear. | |
| 1681 HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); | |
| 1682 if (!monitor) { | |
| 1683 // We might end up here if the window was previously minimized and the | |
| 1684 // user clicks on the taskbar button to restore it in the previously | |
| 1685 // maximized position. In that case WM_NCCALCSIZE is sent before the | |
| 1686 // window coordinates are restored to their previous values, so our | |
| 1687 // (left,top) would probably be (-32000,-32000) like all minimized | |
| 1688 // windows. So the above MonitorFromWindow call fails, but if we check | |
| 1689 // the window rect given with WM_NCCALCSIZE (which is our previous | |
| 1690 // restored window position) we will get the correct monitor handle. | |
| 1691 monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); | |
| 1692 if (!monitor) { | |
| 1693 // This is probably an extreme case that we won't hit, but if we don't | |
| 1694 // intersect any monitor, let us not adjust the client rect since our | |
| 1695 // window will not be visible anyway. | |
| 1696 return 0; | |
| 1697 } | |
| 1698 } | |
| 1699 const int autohide_edges = GetAppbarAutohideEdges(monitor); | |
| 1700 if (autohide_edges & ViewsDelegate::EDGE_LEFT) | |
| 1701 client_rect->left += kAutoHideTaskbarThicknessPx; | |
| 1702 if (autohide_edges & ViewsDelegate::EDGE_TOP) { | |
| 1703 if (!delegate_->IsUsingCustomFrame()) { | |
| 1704 // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of | |
| 1705 // WM_NCHITTEST, having any nonclient area atop the window causes the | |
| 1706 // caption buttons to draw onscreen but not respond to mouse | |
| 1707 // hover/clicks. | |
| 1708 // So for a taskbar at the screen top, we can't push the | |
| 1709 // client_rect->top down; instead, we move the bottom up by one pixel, | |
| 1710 // which is the smallest change we can make and still get a client area | |
| 1711 // less than the screen size. This is visibly ugly, but there seems to | |
| 1712 // be no better solution. | |
| 1713 --client_rect->bottom; | |
| 1714 } else { | |
| 1715 client_rect->top += kAutoHideTaskbarThicknessPx; | |
| 1716 } | |
| 1717 } | |
| 1718 if (autohide_edges & ViewsDelegate::EDGE_RIGHT) | |
| 1719 client_rect->right -= kAutoHideTaskbarThicknessPx; | |
| 1720 if (autohide_edges & ViewsDelegate::EDGE_BOTTOM) | |
| 1721 client_rect->bottom -= kAutoHideTaskbarThicknessPx; | |
| 1722 | |
| 1723 // We cannot return WVR_REDRAW when there is nonclient area, or Windows | |
| 1724 // exhibits bugs where client pixels and child HWNDs are mispositioned by | |
| 1725 // the width/height of the upper-left nonclient area. | |
| 1726 return 0; | |
| 1727 } | |
| 1728 | |
| 1729 // If the window bounds change, we're going to relayout and repaint anyway. | |
| 1730 // Returning WVR_REDRAW avoids an extra paint before that of the old client | |
| 1731 // pixels in the (now wrong) location, and thus makes actions like resizing a | |
| 1732 // window from the left edge look slightly less broken. | |
| 1733 // We special case when left or top insets are 0, since these conditions | |
| 1734 // actually require another repaint to correct the layout after glass gets | |
| 1735 // turned on and off. | |
| 1736 if (insets.left() == 0 || insets.top() == 0) | |
| 1737 return 0; | |
| 1738 return mode ? WVR_REDRAW : 0; | |
| 1739 } | |
| 1740 | |
| 1741 LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { | |
| 1742 if (!delegate_->IsWidgetWindow()) { | |
| 1743 SetMsgHandled(FALSE); | |
| 1744 return 0; | |
| 1745 } | |
| 1746 | |
| 1747 // If the DWM is rendering the window controls, we need to give the DWM's | |
| 1748 // default window procedure first chance to handle hit testing. | |
| 1749 if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) { | |
| 1750 LRESULT result; | |
| 1751 if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, | |
| 1752 MAKELPARAM(point.x(), point.y()), &result)) { | |
| 1753 return result; | |
| 1754 } | |
| 1755 } | |
| 1756 | |
| 1757 // First, give the NonClientView a chance to test the point to see if it | |
| 1758 // provides any of the non-client area. | |
| 1759 POINT temp = { point.x(), point.y() }; | |
| 1760 MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1); | |
| 1761 int component = delegate_->GetNonClientComponent(gfx::Point(temp)); | |
| 1762 if (component != HTNOWHERE) | |
| 1763 return component; | |
| 1764 | |
| 1765 // Otherwise, we let Windows do all the native frame non-client handling for | |
| 1766 // us. | |
| 1767 LRESULT hit_test_code = DefWindowProc(hwnd(), WM_NCHITTEST, 0, | |
| 1768 MAKELPARAM(point.x(), point.y())); | |
| 1769 if (needs_scroll_styles_) { | |
| 1770 switch (hit_test_code) { | |
| 1771 // If we faked the WS_VSCROLL and WS_HSCROLL styles for this window, then | |
| 1772 // Windows returns the HTVSCROLL or HTHSCROLL hit test codes if we hover | |
| 1773 // or click on the non client portions of the window where the OS | |
| 1774 // scrollbars would be drawn. These hittest codes are returned even when | |
| 1775 // the scrollbars are hidden, which is the case in Aura. We fake the | |
| 1776 // hittest code as HTCLIENT in this case to ensure that we receive client | |
| 1777 // mouse messages as opposed to non client mouse messages. | |
| 1778 case HTVSCROLL: | |
| 1779 case HTHSCROLL: | |
| 1780 hit_test_code = HTCLIENT; | |
| 1781 break; | |
| 1782 | |
| 1783 case HTBOTTOMRIGHT: { | |
| 1784 // Normally the HTBOTTOMRIGHT hittest code is received when we hover | |
| 1785 // near the bottom right of the window. However due to our fake scroll | |
| 1786 // styles, we get this code even when we hover around the area where | |
| 1787 // the vertical scrollar down arrow would be drawn. | |
| 1788 // We check if the hittest coordinates lie in this region and if yes | |
| 1789 // we return HTCLIENT. | |
| 1790 int border_width = ::GetSystemMetrics(SM_CXSIZEFRAME); | |
| 1791 int border_height = ::GetSystemMetrics(SM_CYSIZEFRAME); | |
| 1792 int scroll_width = ::GetSystemMetrics(SM_CXVSCROLL); | |
| 1793 int scroll_height = ::GetSystemMetrics(SM_CYVSCROLL); | |
| 1794 RECT window_rect; | |
| 1795 ::GetWindowRect(hwnd(), &window_rect); | |
| 1796 window_rect.bottom -= border_height; | |
| 1797 window_rect.right -= border_width; | |
| 1798 window_rect.left = window_rect.right - scroll_width; | |
| 1799 window_rect.top = window_rect.bottom - scroll_height; | |
| 1800 POINT pt; | |
| 1801 pt.x = point.x(); | |
| 1802 pt.y = point.y(); | |
| 1803 if (::PtInRect(&window_rect, pt)) | |
| 1804 hit_test_code = HTCLIENT; | |
| 1805 break; | |
| 1806 } | |
| 1807 | |
| 1808 default: | |
| 1809 break; | |
| 1810 } | |
| 1811 } | |
| 1812 return hit_test_code; | |
| 1813 } | |
| 1814 | |
| 1815 void HWNDMessageHandler::OnNCPaint(HRGN rgn) { | |
| 1816 // We only do non-client painting if we're not using the native frame. | |
| 1817 // It's required to avoid some native painting artifacts from appearing when | |
| 1818 // the window is resized. | |
| 1819 if (!delegate_->IsWidgetWindow() || !delegate_->IsUsingCustomFrame()) { | |
| 1820 SetMsgHandled(FALSE); | |
| 1821 return; | |
| 1822 } | |
| 1823 | |
| 1824 // We have an NC region and need to paint it. We expand the NC region to | |
| 1825 // include the dirty region of the root view. This is done to minimize | |
| 1826 // paints. | |
| 1827 RECT window_rect; | |
| 1828 GetWindowRect(hwnd(), &window_rect); | |
| 1829 | |
| 1830 gfx::Size root_view_size = delegate_->GetRootViewSize(); | |
| 1831 if (gfx::Size(window_rect.right - window_rect.left, | |
| 1832 window_rect.bottom - window_rect.top) != root_view_size) { | |
| 1833 // If the size of the window differs from the size of the root view it | |
| 1834 // means we're being asked to paint before we've gotten a WM_SIZE. This can | |
| 1835 // happen when the user is interactively resizing the window. To avoid | |
| 1836 // mass flickering we don't do anything here. Once we get the WM_SIZE we'll | |
| 1837 // reset the region of the window which triggers another WM_NCPAINT and | |
| 1838 // all is well. | |
| 1839 return; | |
| 1840 } | |
| 1841 | |
| 1842 RECT dirty_region; | |
| 1843 // A value of 1 indicates paint all. | |
| 1844 if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { | |
| 1845 dirty_region.left = 0; | |
| 1846 dirty_region.top = 0; | |
| 1847 dirty_region.right = window_rect.right - window_rect.left; | |
| 1848 dirty_region.bottom = window_rect.bottom - window_rect.top; | |
| 1849 } else { | |
| 1850 RECT rgn_bounding_box; | |
| 1851 GetRgnBox(rgn, &rgn_bounding_box); | |
| 1852 if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) | |
| 1853 return; // Dirty region doesn't intersect window bounds, bale. | |
| 1854 | |
| 1855 // rgn_bounding_box is in screen coordinates. Map it to window coordinates. | |
| 1856 OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); | |
| 1857 } | |
| 1858 | |
| 1859 // In theory GetDCEx should do what we want, but I couldn't get it to work. | |
| 1860 // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell | |
| 1861 // it doesn't work at all. So, instead we get the DC for the window then | |
| 1862 // manually clip out the children. | |
| 1863 HDC dc = GetWindowDC(hwnd()); | |
| 1864 ClipState clip_state; | |
| 1865 clip_state.x = window_rect.left; | |
| 1866 clip_state.y = window_rect.top; | |
| 1867 clip_state.parent = hwnd(); | |
| 1868 clip_state.dc = dc; | |
| 1869 EnumChildWindows(hwnd(), &ClipDCToChild, | |
| 1870 reinterpret_cast<LPARAM>(&clip_state)); | |
| 1871 | |
| 1872 gfx::Rect old_paint_region = invalid_rect_; | |
| 1873 if (!old_paint_region.IsEmpty()) { | |
| 1874 // The root view has a region that needs to be painted. Include it in the | |
| 1875 // region we're going to paint. | |
| 1876 | |
| 1877 RECT old_paint_region_crect = old_paint_region.ToRECT(); | |
| 1878 RECT tmp = dirty_region; | |
| 1879 UnionRect(&dirty_region, &tmp, &old_paint_region_crect); | |
| 1880 } | |
| 1881 | |
| 1882 SchedulePaintInRect(gfx::Rect(dirty_region)); | |
| 1883 | |
| 1884 // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap | |
| 1885 // the following in a block to force paint to occur so that we can release | |
| 1886 // the dc. | |
| 1887 if (!delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region))) { | |
| 1888 gfx::CanvasSkiaPaint canvas(dc, | |
| 1889 true, | |
| 1890 dirty_region.left, | |
| 1891 dirty_region.top, | |
| 1892 dirty_region.right - dirty_region.left, | |
| 1893 dirty_region.bottom - dirty_region.top); | |
| 1894 delegate_->HandlePaint(&canvas); | |
| 1895 } | |
| 1896 | |
| 1897 ReleaseDC(hwnd(), dc); | |
| 1898 // When using a custom frame, we want to avoid calling DefWindowProc() since | |
| 1899 // that may render artifacts. | |
| 1900 SetMsgHandled(delegate_->IsUsingCustomFrame()); | |
| 1901 } | |
| 1902 | |
| 1903 LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message, | |
| 1904 WPARAM w_param, | |
| 1905 LPARAM l_param) { | |
| 1906 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for | |
| 1907 // an explanation about why we need to handle this message. | |
| 1908 SetMsgHandled(delegate_->IsUsingCustomFrame()); | |
| 1909 return 0; | |
| 1910 } | |
| 1911 | |
| 1912 LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message, | |
| 1913 WPARAM w_param, | |
| 1914 LPARAM l_param) { | |
| 1915 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for | |
| 1916 // an explanation about why we need to handle this message. | |
| 1917 SetMsgHandled(delegate_->IsUsingCustomFrame()); | |
| 1918 return 0; | |
| 1919 } | |
| 1920 | |
| 1921 LRESULT HWNDMessageHandler::OnNotify(int w_param, NMHDR* l_param) { | |
| 1922 LRESULT l_result = 0; | |
| 1923 SetMsgHandled(delegate_->HandleTooltipNotify(w_param, l_param, &l_result)); | |
| 1924 return l_result; | |
| 1925 } | |
| 1926 | |
| 1927 void HWNDMessageHandler::OnPaint(HDC dc) { | |
| 1928 // Call BeginPaint()/EndPaint() around the paint handling, as that seems | |
| 1929 // to do more to actually validate the window's drawing region. This only | |
| 1930 // appears to matter for Windows that have the WS_EX_COMPOSITED style set | |
| 1931 // but will be valid in general too. | |
| 1932 PAINTSTRUCT ps; | |
| 1933 HDC display_dc = BeginPaint(hwnd(), &ps); | |
| 1934 CHECK(display_dc); | |
| 1935 | |
| 1936 // Try to paint accelerated first. | |
| 1937 if (!IsRectEmpty(&ps.rcPaint) && | |
| 1938 !delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint))) { | |
| 1939 delegate_->HandlePaint(NULL); | |
| 1940 } | |
| 1941 | |
| 1942 EndPaint(hwnd(), &ps); | |
| 1943 } | |
| 1944 | |
| 1945 LRESULT HWNDMessageHandler::OnReflectedMessage(UINT message, | |
| 1946 WPARAM w_param, | |
| 1947 LPARAM l_param) { | |
| 1948 SetMsgHandled(FALSE); | |
| 1949 return 0; | |
| 1950 } | |
| 1951 | |
| 1952 LRESULT HWNDMessageHandler::OnScrollMessage(UINT message, | |
| 1953 WPARAM w_param, | |
| 1954 LPARAM l_param) { | |
| 1955 MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime() }; | |
| 1956 ui::ScrollEvent event(msg); | |
| 1957 delegate_->HandleScrollEvent(event); | |
| 1958 return 0; | |
| 1959 } | |
| 1960 | |
| 1961 void HWNDMessageHandler::OnSessionChange(WPARAM status_code, | |
| 1962 PWTSSESSION_NOTIFICATION session_id) { | |
| 1963 // Direct3D presents are ignored while the screen is locked, so force the | |
| 1964 // window to be redrawn on unlock. | |
| 1965 if (status_code == WTS_SESSION_UNLOCK) | |
| 1966 ForceRedrawWindow(10); | |
| 1967 | |
| 1968 SetMsgHandled(FALSE); | |
| 1969 } | |
| 1970 | |
| 1971 LRESULT HWNDMessageHandler::OnSetCursor(UINT message, | |
| 1972 WPARAM w_param, | |
| 1973 LPARAM l_param) { | |
| 1974 // Reimplement the necessary default behavior here. Calling DefWindowProc can | |
| 1975 // trigger weird non-client painting for non-glass windows with custom frames. | |
| 1976 // Using a ScopedRedrawLock to prevent caption rendering artifacts may allow | |
| 1977 // content behind this window to incorrectly paint in front of this window. | |
| 1978 // Invalidating the window to paint over either set of artifacts is not ideal. | |
| 1979 wchar_t* cursor = IDC_ARROW; | |
| 1980 switch (LOWORD(l_param)) { | |
| 1981 case HTSIZE: | |
| 1982 cursor = IDC_SIZENWSE; | |
| 1983 break; | |
| 1984 case HTLEFT: | |
| 1985 case HTRIGHT: | |
| 1986 cursor = IDC_SIZEWE; | |
| 1987 break; | |
| 1988 case HTTOP: | |
| 1989 case HTBOTTOM: | |
| 1990 cursor = IDC_SIZENS; | |
| 1991 break; | |
| 1992 case HTTOPLEFT: | |
| 1993 case HTBOTTOMRIGHT: | |
| 1994 cursor = IDC_SIZENWSE; | |
| 1995 break; | |
| 1996 case HTTOPRIGHT: | |
| 1997 case HTBOTTOMLEFT: | |
| 1998 cursor = IDC_SIZENESW; | |
| 1999 break; | |
| 2000 case HTCLIENT: | |
| 2001 SetCursor(current_cursor_); | |
| 2002 return 1; | |
| 2003 case LOWORD(HTERROR): // Use HTERROR's LOWORD value for valid comparison. | |
| 2004 SetMsgHandled(FALSE); | |
| 2005 break; | |
| 2006 default: | |
| 2007 // Use the default value, IDC_ARROW. | |
| 2008 break; | |
| 2009 } | |
| 2010 ::SetCursor(LoadCursor(NULL, cursor)); | |
| 2011 return 1; | |
| 2012 } | |
| 2013 | |
| 2014 void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) { | |
| 2015 delegate_->HandleNativeFocus(last_focused_window); | |
| 2016 SetMsgHandled(FALSE); | |
| 2017 } | |
| 2018 | |
| 2019 LRESULT HWNDMessageHandler::OnSetIcon(UINT size_type, HICON new_icon) { | |
| 2020 // Use a ScopedRedrawLock to avoid weird non-client painting. | |
| 2021 return DefWindowProcWithRedrawLock(WM_SETICON, size_type, | |
| 2022 reinterpret_cast<LPARAM>(new_icon)); | |
| 2023 } | |
| 2024 | |
| 2025 LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) { | |
| 2026 // Use a ScopedRedrawLock to avoid weird non-client painting. | |
| 2027 return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL, | |
| 2028 reinterpret_cast<LPARAM>(text)); | |
| 2029 } | |
| 2030 | |
| 2031 void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { | |
| 2032 if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA) && | |
| 2033 !delegate_->WillProcessWorkAreaChange()) { | |
| 2034 // Fire a dummy SetWindowPos() call, so we'll trip the code in | |
| 2035 // OnWindowPosChanging() below that notices work area changes. | |
| 2036 ::SetWindowPos(hwnd(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | | |
| 2037 SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); | |
| 2038 SetMsgHandled(TRUE); | |
| 2039 } else { | |
| 2040 if (flags == SPI_SETWORKAREA) | |
| 2041 delegate_->HandleWorkAreaChanged(); | |
| 2042 SetMsgHandled(FALSE); | |
| 2043 } | |
| 2044 } | |
| 2045 | |
| 2046 void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) { | |
| 2047 RedrawWindow(hwnd(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); | |
| 2048 // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've | |
| 2049 // invoked OnSize we ensure the RootView has been laid out. | |
| 2050 ResetWindowRegion(false, true); | |
| 2051 | |
| 2052 // We add the WS_VSCROLL and WS_HSCROLL styles to top level windows to ensure | |
| 2053 // that legacy trackpad/trackpoint drivers generate the WM_VSCROLL and | |
| 2054 // WM_HSCROLL messages and scrolling works. | |
| 2055 // We want the scroll styles to be present on the window. However we don't | |
| 2056 // want Windows to draw the scrollbars. To achieve this we hide the scroll | |
| 2057 // bars and readd them to the window style in a posted task to ensure that we | |
| 2058 // don't get nested WM_SIZE messages. | |
| 2059 if (needs_scroll_styles_ && !in_size_loop_) { | |
| 2060 ShowScrollBar(hwnd(), SB_BOTH, FALSE); | |
| 2061 base::MessageLoop::current()->PostTask( | |
| 2062 FROM_HERE, base::Bind(&AddScrollStylesToWindow, hwnd())); | |
| 2063 } | |
| 2064 } | |
| 2065 | |
| 2066 void HWNDMessageHandler::OnSysCommand(UINT notification_code, | |
| 2067 const gfx::Point& point) { | |
| 2068 if (!delegate_->ShouldHandleSystemCommands()) | |
| 2069 return; | |
| 2070 | |
| 2071 // Windows uses the 4 lower order bits of |notification_code| for type- | |
| 2072 // specific information so we must exclude this when comparing. | |
| 2073 static const int sc_mask = 0xFFF0; | |
| 2074 // Ignore size/move/maximize in fullscreen mode. | |
| 2075 if (fullscreen_handler_->fullscreen() && | |
| 2076 (((notification_code & sc_mask) == SC_SIZE) || | |
| 2077 ((notification_code & sc_mask) == SC_MOVE) || | |
| 2078 ((notification_code & sc_mask) == SC_MAXIMIZE))) | |
| 2079 return; | |
| 2080 if (delegate_->IsUsingCustomFrame()) { | |
| 2081 if ((notification_code & sc_mask) == SC_MINIMIZE || | |
| 2082 (notification_code & sc_mask) == SC_MAXIMIZE || | |
| 2083 (notification_code & sc_mask) == SC_RESTORE) { | |
| 2084 delegate_->ResetWindowControls(); | |
| 2085 } else if ((notification_code & sc_mask) == SC_MOVE || | |
| 2086 (notification_code & sc_mask) == SC_SIZE) { | |
| 2087 if (!IsVisible()) { | |
| 2088 // Circumvent ScopedRedrawLocks and force visibility before entering a | |
| 2089 // resize or move modal loop to get continuous sizing/moving feedback. | |
| 2090 SetWindowLong(hwnd(), GWL_STYLE, | |
| 2091 GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); | |
| 2092 } | |
| 2093 } | |
| 2094 } | |
| 2095 | |
| 2096 // Handle SC_KEYMENU, which means that the user has pressed the ALT | |
| 2097 // key and released it, so we should focus the menu bar. | |
| 2098 if ((notification_code & sc_mask) == SC_KEYMENU && point.x() == 0) { | |
| 2099 int modifiers = ui::EF_NONE; | |
| 2100 if (base::win::IsShiftPressed()) | |
| 2101 modifiers |= ui::EF_SHIFT_DOWN; | |
| 2102 if (base::win::IsCtrlPressed()) | |
| 2103 modifiers |= ui::EF_CONTROL_DOWN; | |
| 2104 // Retrieve the status of shift and control keys to prevent consuming | |
| 2105 // shift+alt keys, which are used by Windows to change input languages. | |
| 2106 ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU), | |
| 2107 modifiers); | |
| 2108 delegate_->HandleAccelerator(accelerator); | |
| 2109 return; | |
| 2110 } | |
| 2111 | |
| 2112 // If the delegate can't handle it, the system implementation will be called. | |
| 2113 if (!delegate_->HandleCommand(notification_code)) { | |
| 2114 // If the window is being resized by dragging the borders of the window | |
| 2115 // with the mouse/touch/keyboard, we flag as being in a size loop. | |
| 2116 if ((notification_code & sc_mask) == SC_SIZE) | |
| 2117 in_size_loop_ = true; | |
| 2118 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 2119 DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, | |
| 2120 MAKELPARAM(point.x(), point.y())); | |
| 2121 if (!ref.get()) | |
| 2122 return; | |
| 2123 in_size_loop_ = false; | |
| 2124 } | |
| 2125 } | |
| 2126 | |
| 2127 void HWNDMessageHandler::OnThemeChanged() { | |
| 2128 ui::NativeThemeWin::instance()->CloseHandles(); | |
| 2129 } | |
| 2130 | |
| 2131 LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, | |
| 2132 WPARAM w_param, | |
| 2133 LPARAM l_param) { | |
| 2134 // Handle touch events only on Aura for now. | |
| 2135 int num_points = LOWORD(w_param); | |
| 2136 scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]); | |
| 2137 if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), | |
| 2138 num_points, input.get(), | |
| 2139 sizeof(TOUCHINPUT))) { | |
| 2140 int flags = ui::GetModifiersFromKeyState(); | |
| 2141 TouchEvents touch_events; | |
| 2142 for (int i = 0; i < num_points; ++i) { | |
| 2143 POINT point; | |
| 2144 point.x = TOUCH_COORD_TO_PIXEL(input[i].x); | |
| 2145 point.y = TOUCH_COORD_TO_PIXEL(input[i].y); | |
| 2146 | |
| 2147 if (base::win::GetVersion() == base::win::VERSION_WIN7) { | |
| 2148 // Windows 7 sends touch events for touches in the non-client area, | |
| 2149 // whereas Windows 8 does not. In order to unify the behaviour, always | |
| 2150 // ignore touch events in the non-client area. | |
| 2151 LPARAM l_param_ht = MAKELPARAM(point.x, point.y); | |
| 2152 LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); | |
| 2153 | |
| 2154 if (hittest != HTCLIENT) | |
| 2155 return 0; | |
| 2156 } | |
| 2157 | |
| 2158 ScreenToClient(hwnd(), &point); | |
| 2159 | |
| 2160 last_touch_message_time_ = ::GetMessageTime(); | |
| 2161 | |
| 2162 ui::EventType touch_event_type = ui::ET_UNKNOWN; | |
| 2163 | |
| 2164 if (input[i].dwFlags & TOUCHEVENTF_DOWN) { | |
| 2165 touch_ids_.insert(input[i].dwID); | |
| 2166 touch_event_type = ui::ET_TOUCH_PRESSED; | |
| 2167 touch_down_contexts_++; | |
| 2168 base::MessageLoop::current()->PostDelayedTask( | |
| 2169 FROM_HERE, | |
| 2170 base::Bind(&HWNDMessageHandler::ResetTouchDownContext, | |
| 2171 weak_factory_.GetWeakPtr()), | |
| 2172 base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout)); | |
| 2173 } else if (input[i].dwFlags & TOUCHEVENTF_UP) { | |
| 2174 touch_ids_.erase(input[i].dwID); | |
| 2175 touch_event_type = ui::ET_TOUCH_RELEASED; | |
| 2176 } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) { | |
| 2177 touch_event_type = ui::ET_TOUCH_MOVED; | |
| 2178 } | |
| 2179 if (touch_event_type != ui::ET_UNKNOWN) { | |
| 2180 base::TimeTicks now; | |
| 2181 // input[i].dwTime doesn't necessarily relate to the system time at all, | |
| 2182 // so use base::TimeTicks::HighResNow() if possible, or | |
| 2183 // base::TimeTicks::Now() otherwise. | |
| 2184 if (base::TimeTicks::IsHighResNowFastAndReliable()) | |
| 2185 now = base::TimeTicks::HighResNow(); | |
| 2186 else | |
| 2187 now = base::TimeTicks::Now(); | |
| 2188 ui::TouchEvent event(touch_event_type, | |
| 2189 gfx::Point(point.x, point.y), | |
| 2190 id_generator_.GetGeneratedID(input[i].dwID), | |
| 2191 now - base::TimeTicks()); | |
| 2192 event.set_flags(flags); | |
| 2193 event.latency()->AddLatencyNumberWithTimestamp( | |
| 2194 ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, | |
| 2195 0, | |
| 2196 0, | |
| 2197 base::TimeTicks::FromInternalValue( | |
| 2198 event.time_stamp().ToInternalValue()), | |
| 2199 1); | |
| 2200 | |
| 2201 touch_events.push_back(event); | |
| 2202 if (touch_event_type == ui::ET_TOUCH_RELEASED) | |
| 2203 id_generator_.ReleaseNumber(input[i].dwID); | |
| 2204 } | |
| 2205 } | |
| 2206 // Handle the touch events asynchronously. We need this because touch | |
| 2207 // events on windows don't fire if we enter a modal loop in the context of | |
| 2208 // a touch event. | |
| 2209 base::MessageLoop::current()->PostTask( | |
| 2210 FROM_HERE, | |
| 2211 base::Bind(&HWNDMessageHandler::HandleTouchEvents, | |
| 2212 weak_factory_.GetWeakPtr(), touch_events)); | |
| 2213 } | |
| 2214 CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param)); | |
| 2215 SetMsgHandled(FALSE); | |
| 2216 return 0; | |
| 2217 } | |
| 2218 | |
| 2219 void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { | |
| 2220 if (ignore_window_pos_changes_) { | |
| 2221 // If somebody's trying to toggle our visibility, change the nonclient area, | |
| 2222 // change our Z-order, or activate us, we should probably let it go through. | |
| 2223 if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | | |
| 2224 SWP_FRAMECHANGED)) && | |
| 2225 (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { | |
| 2226 // Just sizing/moving the window; ignore. | |
| 2227 window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; | |
| 2228 window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); | |
| 2229 } | |
| 2230 } else if (!GetParent(hwnd())) { | |
| 2231 RECT window_rect; | |
| 2232 HMONITOR monitor; | |
| 2233 gfx::Rect monitor_rect, work_area; | |
| 2234 if (GetWindowRect(hwnd(), &window_rect) && | |
| 2235 GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { | |
| 2236 bool work_area_changed = (monitor_rect == last_monitor_rect_) && | |
| 2237 (work_area != last_work_area_); | |
| 2238 if (monitor && (monitor == last_monitor_) && | |
| 2239 ((fullscreen_handler_->fullscreen() && | |
| 2240 !fullscreen_handler_->metro_snap()) || | |
| 2241 work_area_changed)) { | |
| 2242 // A rect for the monitor we're on changed. Normally Windows notifies | |
| 2243 // us about this (and thus we're reaching here due to the SetWindowPos() | |
| 2244 // call in OnSettingChange() above), but with some software (e.g. | |
| 2245 // nVidia's nView desktop manager) the work area can change asynchronous | |
| 2246 // to any notification, and we're just sent a SetWindowPos() call with a | |
| 2247 // new (frequently incorrect) position/size. In either case, the best | |
| 2248 // response is to throw away the existing position/size information in | |
| 2249 // |window_pos| and recalculate it based on the new work rect. | |
| 2250 gfx::Rect new_window_rect; | |
| 2251 if (fullscreen_handler_->fullscreen()) { | |
| 2252 new_window_rect = monitor_rect; | |
| 2253 } else if (IsMaximized()) { | |
| 2254 new_window_rect = work_area; | |
| 2255 int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); | |
| 2256 new_window_rect.Inset(-border_thickness, -border_thickness); | |
| 2257 } else { | |
| 2258 new_window_rect = gfx::Rect(window_rect); | |
| 2259 new_window_rect.AdjustToFit(work_area); | |
| 2260 } | |
| 2261 window_pos->x = new_window_rect.x(); | |
| 2262 window_pos->y = new_window_rect.y(); | |
| 2263 window_pos->cx = new_window_rect.width(); | |
| 2264 window_pos->cy = new_window_rect.height(); | |
| 2265 // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child | |
| 2266 // HWNDs for some reason. | |
| 2267 window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); | |
| 2268 window_pos->flags |= SWP_NOCOPYBITS; | |
| 2269 | |
| 2270 // Now ignore all immediately-following SetWindowPos() changes. Windows | |
| 2271 // likes to (incorrectly) recalculate what our position/size should be | |
| 2272 // and send us further updates. | |
| 2273 ignore_window_pos_changes_ = true; | |
| 2274 base::MessageLoop::current()->PostTask( | |
| 2275 FROM_HERE, | |
| 2276 base::Bind(&HWNDMessageHandler::StopIgnoringPosChanges, | |
| 2277 weak_factory_.GetWeakPtr())); | |
| 2278 } | |
| 2279 last_monitor_ = monitor; | |
| 2280 last_monitor_rect_ = monitor_rect; | |
| 2281 last_work_area_ = work_area; | |
| 2282 } | |
| 2283 } | |
| 2284 | |
| 2285 if (DidClientAreaSizeChange(window_pos)) | |
| 2286 delegate_->HandleWindowSizeChanging(); | |
| 2287 | |
| 2288 if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { | |
| 2289 // Prevent the window from being made visible if we've been asked to do so. | |
| 2290 // See comment in header as to why we might want this. | |
| 2291 window_pos->flags &= ~SWP_SHOWWINDOW; | |
| 2292 } | |
| 2293 | |
| 2294 if (window_pos->flags & SWP_SHOWWINDOW) | |
| 2295 delegate_->HandleVisibilityChanging(true); | |
| 2296 else if (window_pos->flags & SWP_HIDEWINDOW) | |
| 2297 delegate_->HandleVisibilityChanging(false); | |
| 2298 | |
| 2299 SetMsgHandled(FALSE); | |
| 2300 } | |
| 2301 | |
| 2302 void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { | |
| 2303 if (DidClientAreaSizeChange(window_pos)) | |
| 2304 ClientAreaSizeChanged(); | |
| 2305 if (remove_standard_frame_ && window_pos->flags & SWP_FRAMECHANGED && | |
| 2306 ui::win::IsAeroGlassEnabled() && | |
| 2307 (window_ex_style() & WS_EX_COMPOSITED) == 0) { | |
| 2308 MARGINS m = {10, 10, 10, 10}; | |
| 2309 DwmExtendFrameIntoClientArea(hwnd(), &m); | |
| 2310 } | |
| 2311 if (window_pos->flags & SWP_SHOWWINDOW) | |
| 2312 delegate_->HandleVisibilityChanged(true); | |
| 2313 else if (window_pos->flags & SWP_HIDEWINDOW) | |
| 2314 delegate_->HandleVisibilityChanged(false); | |
| 2315 SetMsgHandled(FALSE); | |
| 2316 } | |
| 2317 | |
| 2318 void HWNDMessageHandler::HandleTouchEvents(const TouchEvents& touch_events) { | |
| 2319 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 2320 for (size_t i = 0; i < touch_events.size() && ref; ++i) | |
| 2321 delegate_->HandleTouchEvent(touch_events[i]); | |
| 2322 } | |
| 2323 | |
| 2324 void HWNDMessageHandler::ResetTouchDownContext() { | |
| 2325 touch_down_contexts_--; | |
| 2326 } | |
| 2327 | |
| 2328 LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, | |
| 2329 WPARAM w_param, | |
| 2330 LPARAM l_param, | |
| 2331 bool track_mouse) { | |
| 2332 if (!touch_ids_.empty()) | |
| 2333 return 0; | |
| 2334 // We handle touch events on Windows Aura. Windows generates synthesized | |
| 2335 // mouse messages in response to touch which we should ignore. However touch | |
| 2336 // messages are only received for the client area. We need to ignore the | |
| 2337 // synthesized mouse messages for all points in the client area and places | |
| 2338 // which return HTNOWHERE. | |
| 2339 if (ui::IsMouseEventFromTouch(message)) { | |
| 2340 LPARAM l_param_ht = l_param; | |
| 2341 // For mouse events (except wheel events), location is in window coordinates | |
| 2342 // and should be converted to screen coordinates for WM_NCHITTEST. | |
| 2343 if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) { | |
| 2344 POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht); | |
| 2345 MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); | |
| 2346 l_param_ht = MAKELPARAM(screen_point.x, screen_point.y); | |
| 2347 } | |
| 2348 LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); | |
| 2349 if (hittest == HTCLIENT || hittest == HTNOWHERE) | |
| 2350 return 0; | |
| 2351 } | |
| 2352 | |
| 2353 // Certain logitech drivers send the WM_MOUSEHWHEEL message to the parent | |
| 2354 // followed by WM_MOUSEWHEEL messages to the child window causing a vertical | |
| 2355 // scroll. We treat these WM_MOUSEWHEEL messages as WM_MOUSEHWHEEL | |
| 2356 // messages. | |
| 2357 if (message == WM_MOUSEHWHEEL) | |
| 2358 last_mouse_hwheel_time_ = ::GetMessageTime(); | |
| 2359 | |
| 2360 if (message == WM_MOUSEWHEEL && | |
| 2361 ::GetMessageTime() == last_mouse_hwheel_time_) { | |
| 2362 message = WM_MOUSEHWHEEL; | |
| 2363 } | |
| 2364 | |
| 2365 if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { | |
| 2366 is_right_mouse_pressed_on_caption_ = false; | |
| 2367 ReleaseCapture(); | |
| 2368 // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() | |
| 2369 // expect screen coordinates. | |
| 2370 POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); | |
| 2371 MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); | |
| 2372 w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, | |
| 2373 MAKELPARAM(screen_point.x, screen_point.y)); | |
| 2374 if (w_param == HTCAPTION || w_param == HTSYSMENU) { | |
| 2375 gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point)); | |
| 2376 return 0; | |
| 2377 } | |
| 2378 } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { | |
| 2379 switch (w_param) { | |
| 2380 case HTCLOSE: | |
| 2381 case HTMINBUTTON: | |
| 2382 case HTMAXBUTTON: { | |
| 2383 // When the mouse is pressed down in these specific non-client areas, | |
| 2384 // we need to tell the RootView to send the mouse pressed event (which | |
| 2385 // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ | |
| 2386 // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be | |
| 2387 // sent by the applicable button's ButtonListener. We _have_ to do this | |
| 2388 // way rather than letting Windows just send the syscommand itself (as | |
| 2389 // would happen if we never did this dance) because for some insane | |
| 2390 // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed | |
| 2391 // window control button appearance, in the Windows classic style, over | |
| 2392 // our view! Ick! By handling this message we prevent Windows from | |
| 2393 // doing this undesirable thing, but that means we need to roll the | |
| 2394 // sys-command handling ourselves. | |
| 2395 // Combine |w_param| with common key state message flags. | |
| 2396 w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0; | |
| 2397 w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0; | |
| 2398 } | |
| 2399 } | |
| 2400 } else if (message == WM_NCRBUTTONDOWN && | |
| 2401 (w_param == HTCAPTION || w_param == HTSYSMENU)) { | |
| 2402 is_right_mouse_pressed_on_caption_ = true; | |
| 2403 // We SetCapture() to ensure we only show the menu when the button | |
| 2404 // down and up are both on the caption. Note: this causes the button up to | |
| 2405 // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. | |
| 2406 SetCapture(); | |
| 2407 } | |
| 2408 long message_time = GetMessageTime(); | |
| 2409 MSG msg = { hwnd(), message, w_param, l_param, message_time, | |
| 2410 { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } }; | |
| 2411 ui::MouseEvent event(msg); | |
| 2412 if (IsSynthesizedMouseMessage(message, message_time, l_param)) | |
| 2413 event.set_flags(event.flags() | ui::EF_FROM_TOUCH); | |
| 2414 | |
| 2415 if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture() && track_mouse) { | |
| 2416 // Windows only fires WM_MOUSELEAVE events if the application begins | |
| 2417 // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. | |
| 2418 // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. | |
| 2419 TrackMouseEvents((message == WM_NCMOUSEMOVE) ? | |
| 2420 TME_NONCLIENT | TME_LEAVE : TME_LEAVE); | |
| 2421 } else if (event.type() == ui::ET_MOUSE_EXITED) { | |
| 2422 // Reset our tracking flags so future mouse movement over this | |
| 2423 // NativeWidget results in a new tracking session. Fall through for | |
| 2424 // OnMouseEvent. | |
| 2425 active_mouse_tracking_flags_ = 0; | |
| 2426 } else if (event.type() == ui::ET_MOUSEWHEEL) { | |
| 2427 // Reroute the mouse wheel to the window under the pointer if applicable. | |
| 2428 return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || | |
| 2429 delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; | |
| 2430 } | |
| 2431 | |
| 2432 // There are cases where the code handling the message destroys the window, | |
| 2433 // so use the weak ptr to check if destruction occured or not. | |
| 2434 base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); | |
| 2435 bool handled = delegate_->HandleMouseEvent(event); | |
| 2436 if (!ref.get()) | |
| 2437 return 0; | |
| 2438 if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && | |
| 2439 delegate_->IsUsingCustomFrame()) { | |
| 2440 // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. | |
| 2441 // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we | |
| 2442 // need to call it inside a ScopedRedrawLock. This may cause other negative | |
| 2443 // side-effects (ex/ stifling non-client mouse releases). | |
| 2444 DefWindowProcWithRedrawLock(message, w_param, l_param); | |
| 2445 handled = true; | |
| 2446 } | |
| 2447 | |
| 2448 if (ref.get()) | |
| 2449 SetMsgHandled(handled); | |
| 2450 return 0; | |
| 2451 } | |
| 2452 | |
| 2453 bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message, | |
| 2454 int message_time, | |
| 2455 LPARAM l_param) { | |
| 2456 if (ui::IsMouseEventFromTouch(message)) | |
| 2457 return true; | |
| 2458 // Ignore mouse messages which occur at the same location as the current | |
| 2459 // cursor position and within a time difference of 500 ms from the last | |
| 2460 // touch message. | |
| 2461 if (last_touch_message_time_ && message_time >= last_touch_message_time_ && | |
| 2462 ((message_time - last_touch_message_time_) <= | |
| 2463 kSynthesizedMouseTouchMessagesTimeDifference)) { | |
| 2464 POINT mouse_location = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); | |
| 2465 ::ClientToScreen(hwnd(), &mouse_location); | |
| 2466 POINT cursor_pos = {0}; | |
| 2467 ::GetCursorPos(&cursor_pos); | |
| 2468 if (memcmp(&cursor_pos, &mouse_location, sizeof(POINT))) | |
| 2469 return false; | |
| 2470 return true; | |
| 2471 } | |
| 2472 return false; | |
| 2473 } | |
| 2474 | |
| 2475 void HWNDMessageHandler::PerformDwmTransition() { | |
| 2476 dwm_transition_desired_ = false; | |
| 2477 | |
| 2478 UpdateDwmNcRenderingPolicy(); | |
| 2479 // Don't redraw the window here, because we need to hide and show the window | |
| 2480 // which will also trigger a redraw. | |
| 2481 ResetWindowRegion(true, false); | |
| 2482 // The non-client view needs to update too. | |
| 2483 delegate_->HandleFrameChanged(); | |
| 2484 | |
| 2485 if (IsVisible() && !delegate_->IsUsingCustomFrame()) { | |
| 2486 // For some reason, we need to hide the window after we change from a custom | |
| 2487 // frame to a native frame. If we don't, the client area will be filled | |
| 2488 // with black. This seems to be related to an interaction between DWM and | |
| 2489 // SetWindowRgn, but the details aren't clear. Additionally, we need to | |
| 2490 // specify SWP_NOZORDER here, otherwise if you have multiple chrome windows | |
| 2491 // open they will re-appear with a non-deterministic Z-order. | |
| 2492 UINT flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; | |
| 2493 SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, flags | SWP_HIDEWINDOW); | |
| 2494 SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, flags | SWP_SHOWWINDOW); | |
| 2495 } | |
| 2496 // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want | |
| 2497 // to notify our children too, since we can have MDI child windows who need to | |
| 2498 // update their appearance. | |
| 2499 EnumChildWindows(hwnd(), &SendDwmCompositionChanged, NULL); | |
| 2500 } | |
| 2501 | |
| 2502 } // namespace views | |
| OLD | NEW |