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