| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/mus/ws/window_manager_state.h" | 5 #include "components/mus/ws/window_manager_state.h" |
| 6 | 6 |
| 7 #include "components/mus/ws/connection_manager.h" | 7 #include "components/mus/ws/connection_manager.h" |
| 8 #include "components/mus/ws/display_manager.h" | 8 #include "components/mus/ws/display_manager.h" |
| 9 #include "components/mus/ws/platform_display.h" |
| 9 #include "components/mus/ws/server_window.h" | 10 #include "components/mus/ws/server_window.h" |
| 10 #include "components/mus/ws/user_display_manager.h" | 11 #include "components/mus/ws/user_display_manager.h" |
| 12 #include "components/mus/ws/user_id_tracker.h" |
| 13 #include "components/mus/ws/window_tree.h" |
| 14 #include "mojo/converters/input_events/input_events_type_converters.h" |
| 11 #include "mojo/shell/public/interfaces/connector.mojom.h" | 15 #include "mojo/shell/public/interfaces/connector.mojom.h" |
| 16 #include "ui/events/event.h" |
| 12 | 17 |
| 13 namespace mus { | 18 namespace mus { |
| 14 namespace ws { | 19 namespace ws { |
| 20 namespace { |
| 15 | 21 |
| 16 WindowManagerState::WindowManagerState(Display* display) | 22 base::TimeDelta GetDefaultAckTimerDelay() { |
| 17 : WindowManagerState(display, false, mojo::shell::mojom::kRootUserID) {} | 23 #if defined(NDEBUG) |
| 24 return base::TimeDelta::FromMilliseconds(100); |
| 25 #else |
| 26 return base::TimeDelta::FromMilliseconds(1000); |
| 27 #endif |
| 28 } |
| 18 | 29 |
| 19 WindowManagerState::WindowManagerState(Display* display, const UserId& user_id) | 30 bool EventsCanBeCoalesced(const ui::Event& one, const ui::Event& two) { |
| 20 : WindowManagerState(display, true, user_id) {} | 31 if (one.type() != two.type() || one.flags() != two.flags()) |
| 32 return false; |
| 33 |
| 34 // TODO(sad): wheel events can also be merged. |
| 35 if (one.type() != ui::ET_POINTER_MOVED) |
| 36 return false; |
| 37 |
| 38 return one.AsPointerEvent()->pointer_id() == |
| 39 two.AsPointerEvent()->pointer_id(); |
| 40 } |
| 41 |
| 42 scoped_ptr<ui::Event> CoalesceEvents(scoped_ptr<ui::Event> first, |
| 43 scoped_ptr<ui::Event> second) { |
| 44 DCHECK(first->type() == ui::ET_POINTER_MOVED) |
| 45 << " Non-move events cannot be merged yet."; |
| 46 // For mouse moves, the new event just replaces the old event. |
| 47 return second; |
| 48 } |
| 49 |
| 50 } // namespace |
| 51 |
| 52 class WindowManagerState::ProcessedEventTarget { |
| 53 public: |
| 54 ProcessedEventTarget(ServerWindow* window, bool in_nonclient_area) |
| 55 : in_nonclient_area_(in_nonclient_area) { |
| 56 tracker_.Add(window); |
| 57 } |
| 58 |
| 59 ~ProcessedEventTarget() {} |
| 60 |
| 61 // Return true if the event is still valid. The event becomes invalid if |
| 62 // the window is destroyed while waiting to dispatch. |
| 63 bool IsValid() const { return !tracker_.windows().empty(); } |
| 64 |
| 65 ServerWindow* window() { |
| 66 DCHECK(IsValid()); |
| 67 return tracker_.windows().front(); |
| 68 } |
| 69 |
| 70 bool in_nonclient_area() const { return in_nonclient_area_; } |
| 71 |
| 72 private: |
| 73 ServerWindowTracker tracker_; |
| 74 const bool in_nonclient_area_; |
| 75 |
| 76 DISALLOW_COPY_AND_ASSIGN(ProcessedEventTarget); |
| 77 }; |
| 78 |
| 79 WindowManagerState::QueuedEvent::QueuedEvent() {} |
| 80 WindowManagerState::QueuedEvent::~QueuedEvent() {} |
| 81 |
| 82 WindowManagerState::WindowManagerState(Display* display, |
| 83 PlatformDisplay* platform_display, |
| 84 cc::SurfaceId surface_id) |
| 85 : WindowManagerState(display, |
| 86 platform_display, |
| 87 surface_id, |
| 88 false, |
| 89 mojo::shell::mojom::kRootUserID) {} |
| 90 |
| 91 WindowManagerState::WindowManagerState(Display* display, |
| 92 PlatformDisplay* platform_display, |
| 93 cc::SurfaceId surface_id, |
| 94 const UserId& user_id) |
| 95 : WindowManagerState(display, platform_display, surface_id, true, user_id) { |
| 96 } |
| 21 | 97 |
| 22 WindowManagerState::~WindowManagerState() {} | 98 WindowManagerState::~WindowManagerState() {} |
| 23 | 99 |
| 100 void WindowManagerState::SetFrameDecorationValues( |
| 101 mojom::FrameDecorationValuesPtr values) { |
| 102 got_frame_decoration_values_ = true; |
| 103 frame_decoration_values_ = values.Clone(); |
| 104 display_->display_manager() |
| 105 ->GetUserDisplayManager(user_id_) |
| 106 ->OnFrameDecorationValuesChanged(this); |
| 107 } |
| 108 |
| 109 void WindowManagerState::SetCapture(ServerWindow* window, |
| 110 bool in_nonclient_area) { |
| 111 // TODO(sky): capture should be a singleton. Need to route to |
| 112 // ConnectionManager so that all other EventDispatchers are updated. |
| 113 DCHECK(IsActive()); |
| 114 if (capture_window() == window) |
| 115 return; |
| 116 DCHECK(!window || root_->Contains(window)); |
| 117 event_dispatcher_.SetCaptureWindow(window, in_nonclient_area); |
| 118 } |
| 119 |
| 120 mojom::DisplayPtr WindowManagerState::ToMojomDisplay() const { |
| 121 mojom::DisplayPtr display_ptr = display_->ToMojomDisplay(); |
| 122 // TODO(sky): set work area. |
| 123 display_ptr->work_area = display_ptr->bounds.Clone(); |
| 124 display_ptr->frame_decoration_values = frame_decoration_values_.Clone(); |
| 125 return display_ptr; |
| 126 } |
| 127 |
| 128 void WindowManagerState::OnWillDestroyTree(WindowTree* tree) { |
| 129 if (tree_awaiting_input_ack_ != tree) |
| 130 return; |
| 131 // The WindowTree is dying. So it's not going to ack the event. |
| 132 OnEventAck(tree_awaiting_input_ack_); |
| 133 } |
| 134 |
| 24 WindowManagerState::WindowManagerState(Display* display, | 135 WindowManagerState::WindowManagerState(Display* display, |
| 136 PlatformDisplay* platform_display, |
| 137 cc::SurfaceId surface_id, |
| 25 bool is_user_id_valid, | 138 bool is_user_id_valid, |
| 26 const UserId& user_id) | 139 const UserId& user_id) |
| 27 : display_(display), | 140 : display_(display), |
| 141 platform_display_(platform_display), |
| 28 is_user_id_valid_(is_user_id_valid), | 142 is_user_id_valid_(is_user_id_valid), |
| 29 user_id_(user_id) { | 143 user_id_(user_id), |
| 144 event_dispatcher_(this) { |
| 30 frame_decoration_values_ = mojom::FrameDecorationValues::New(); | 145 frame_decoration_values_ = mojom::FrameDecorationValues::New(); |
| 31 frame_decoration_values_->normal_client_area_insets = mojo::Insets::New(); | 146 frame_decoration_values_->normal_client_area_insets = mojo::Insets::New(); |
| 32 frame_decoration_values_->maximized_client_area_insets = mojo::Insets::New(); | 147 frame_decoration_values_->maximized_client_area_insets = mojo::Insets::New(); |
| 33 frame_decoration_values_->max_title_bar_button_width = 0u; | 148 frame_decoration_values_->max_title_bar_button_width = 0u; |
| 34 | 149 |
| 35 ConnectionManager* connection_manager = display_->connection_manager(); | 150 ConnectionManager* connection_manager = display_->connection_manager(); |
| 36 root_.reset(connection_manager->CreateServerWindow( | 151 root_.reset(connection_manager->CreateServerWindow( |
| 37 connection_manager->display_manager()->GetAndAdvanceNextRootId(), | 152 connection_manager->display_manager()->GetAndAdvanceNextRootId(), |
| 38 ServerWindow::Properties())); | 153 ServerWindow::Properties())); |
| 39 // Our root is always a child of the Display's root. Do this | 154 // Our root is always a child of the Display's root. Do this |
| 40 // before the WindowTree has been created so that the client doesn't get | 155 // before the WindowTree has been created so that the client doesn't get |
| 41 // notified of the add, bounds change and visibility change. | 156 // notified of the add, bounds change and visibility change. |
| 42 root_->SetBounds(gfx::Rect(display->root_window()->bounds().size())); | 157 root_->SetBounds(gfx::Rect(display->root_window()->bounds().size())); |
| 43 root_->SetVisible(true); | 158 root_->SetVisible(true); |
| 44 display->root_window()->Add(root_.get()); | 159 display->root_window()->Add(root_.get()); |
| 160 |
| 161 event_dispatcher_.set_root(root_.get()); |
| 162 event_dispatcher_.set_surface_id(surface_id); |
| 45 } | 163 } |
| 46 | 164 |
| 47 void WindowManagerState::SetFrameDecorationValues( | 165 bool WindowManagerState::IsActive() const { |
| 48 mojom::FrameDecorationValuesPtr values) { | 166 return display()->GetActiveWindowManagerState() == this; |
| 49 got_frame_decoration_values_ = true; | |
| 50 frame_decoration_values_ = values.Clone(); | |
| 51 display_->display_manager() | |
| 52 ->GetUserDisplayManager(user_id_) | |
| 53 ->OnFrameDecorationValuesChanged(this); | |
| 54 } | 167 } |
| 55 | 168 |
| 56 mojom::DisplayPtr WindowManagerState::ToMojomDisplay() const { | 169 void WindowManagerState::ProcessEvent(const ui::Event& event) { |
| 57 mojom::DisplayPtr display_ptr = display_->ToMojomDisplay(); | 170 mojom::EventPtr mojo_event(mojom::Event::From(event)); |
| 58 // TODO(sky): set work area. | 171 // If this is still waiting for an ack from a previously sent event, then |
| 59 display_ptr->work_area = display_ptr->bounds.Clone(); | 172 // queue up the event to be dispatched once the ack is received. |
| 60 display_ptr->frame_decoration_values = frame_decoration_values_.Clone(); | 173 if (event_ack_timer_.IsRunning()) { |
| 61 return display_ptr; | 174 if (!event_queue_.empty() && !event_queue_.back()->processed_target && |
| 175 EventsCanBeCoalesced(*event_queue_.back()->event, event)) { |
| 176 event_queue_.back()->event = CoalesceEvents( |
| 177 std::move(event_queue_.back()->event), ui::Event::Clone(event)); |
| 178 return; |
| 179 } |
| 180 QueueEvent(event, nullptr); |
| 181 return; |
| 182 } |
| 183 event_dispatcher_.ProcessEvent(event); |
| 184 } |
| 185 |
| 186 void WindowManagerState::OnEventAck(mojom::WindowTree* tree) { |
| 187 if (tree_awaiting_input_ack_ != tree) { |
| 188 // TODO(sad): The ack must have arrived after the timeout. We should do |
| 189 // something here, and in OnEventAckTimeout(). |
| 190 return; |
| 191 } |
| 192 tree_awaiting_input_ack_ = nullptr; |
| 193 event_ack_timer_.Stop(); |
| 194 ProcessNextEventFromQueue(); |
| 195 } |
| 196 |
| 197 ConnectionManager* WindowManagerState::connection_manager() { |
| 198 return display_->connection_manager(); |
| 199 } |
| 200 |
| 201 void WindowManagerState::OnEventAckTimeout() { |
| 202 // TODO(sad): Figure out what we should do. |
| 203 NOTIMPLEMENTED() << "Event ACK timed out."; |
| 204 OnEventAck(tree_awaiting_input_ack_); |
| 205 } |
| 206 |
| 207 void WindowManagerState::QueueEvent( |
| 208 const ui::Event& event, |
| 209 scoped_ptr<ProcessedEventTarget> processed_event_target) { |
| 210 scoped_ptr<QueuedEvent> queued_event(new QueuedEvent); |
| 211 queued_event->event = ui::Event::Clone(event); |
| 212 queued_event->processed_target = std::move(processed_event_target); |
| 213 event_queue_.push(std::move(queued_event)); |
| 214 } |
| 215 |
| 216 void WindowManagerState::ProcessNextEventFromQueue() { |
| 217 // Loop through |event_queue_| stopping after dispatching the first valid |
| 218 // event. |
| 219 while (!event_queue_.empty()) { |
| 220 scoped_ptr<QueuedEvent> queued_event = std::move(event_queue_.front()); |
| 221 event_queue_.pop(); |
| 222 if (!queued_event->processed_target) { |
| 223 event_dispatcher_.ProcessEvent(*queued_event->event); |
| 224 return; |
| 225 } |
| 226 if (queued_event->processed_target->IsValid()) { |
| 227 DispatchInputEventToWindowImpl( |
| 228 queued_event->processed_target->window(), |
| 229 queued_event->processed_target->in_nonclient_area(), |
| 230 *queued_event->event); |
| 231 return; |
| 232 } |
| 233 } |
| 234 } |
| 235 |
| 236 void WindowManagerState::DispatchInputEventToWindowImpl( |
| 237 ServerWindow* target, |
| 238 bool in_nonclient_area, |
| 239 const ui::Event& event) { |
| 240 if (target == root_->parent()) |
| 241 target = root_.get(); |
| 242 |
| 243 if (event.IsMousePointerEvent()) { |
| 244 DCHECK(event_dispatcher_.mouse_cursor_source_window()); |
| 245 display_->UpdateNativeCursor( |
| 246 event_dispatcher_.mouse_cursor_source_window()->cursor()); |
| 247 } |
| 248 |
| 249 // If the event is in the non-client area the event goes to the owner of |
| 250 // the window. Otherwise if the window is an embed root, forward to the |
| 251 // embedded window. |
| 252 WindowTree* tree = |
| 253 in_nonclient_area |
| 254 ? connection_manager()->GetTreeWithId(target->id().connection_id) |
| 255 : connection_manager()->GetTreeWithRoot(target); |
| 256 if (!tree) { |
| 257 DCHECK(!in_nonclient_area); |
| 258 tree = connection_manager()->GetTreeWithId(target->id().connection_id); |
| 259 } |
| 260 |
| 261 // TOOD(sad): Adjust this delay, possibly make this dynamic. |
| 262 const base::TimeDelta max_delay = base::debug::BeingDebugged() |
| 263 ? base::TimeDelta::FromDays(1) |
| 264 : GetDefaultAckTimerDelay(); |
| 265 event_ack_timer_.Start(FROM_HERE, max_delay, this, |
| 266 &WindowManagerState::OnEventAckTimeout); |
| 267 |
| 268 tree_awaiting_input_ack_ = tree; |
| 269 tree->DispatchInputEvent(target, mojom::Event::From(event)); |
| 270 } |
| 271 |
| 272 void WindowManagerState::OnAccelerator(uint32_t accelerator_id, |
| 273 const ui::Event& event) { |
| 274 DCHECK(IsActive()); |
| 275 tree_->OnAccelerator(accelerator_id, mojom::Event::From(event)); |
| 276 } |
| 277 |
| 278 void WindowManagerState::SetFocusedWindowFromEventDispatcher( |
| 279 ServerWindow* new_focused_window) { |
| 280 DCHECK(IsActive()); |
| 281 display_->SetFocusedWindow(new_focused_window); |
| 282 } |
| 283 |
| 284 ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher() { |
| 285 return display()->GetFocusedWindow(); |
| 286 } |
| 287 |
| 288 void WindowManagerState::SetNativeCapture() { |
| 289 DCHECK(IsActive()); |
| 290 platform_display_->SetCapture(); |
| 291 } |
| 292 |
| 293 void WindowManagerState::ReleaseNativeCapture() { |
| 294 platform_display_->ReleaseCapture(); |
| 295 } |
| 296 |
| 297 void WindowManagerState::OnServerWindowCaptureLost(ServerWindow* window) { |
| 298 DCHECK(window); |
| 299 display_->connection_manager()->ProcessLostCapture(window); |
| 300 } |
| 301 |
| 302 void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target, |
| 303 bool in_nonclient_area, |
| 304 const ui::Event& event) { |
| 305 DCHECK(IsActive()); |
| 306 // TODO(sky): this needs to see if another wms has capture and if so forward |
| 307 // to it. |
| 308 if (event_ack_timer_.IsRunning()) { |
| 309 scoped_ptr<ProcessedEventTarget> processed_event_target( |
| 310 new ProcessedEventTarget(target, in_nonclient_area)); |
| 311 QueueEvent(event, std::move(processed_event_target)); |
| 312 return; |
| 313 } |
| 314 |
| 315 DispatchInputEventToWindowImpl(target, in_nonclient_area, event); |
| 62 } | 316 } |
| 63 | 317 |
| 64 } // namespace ws | 318 } // namespace ws |
| 65 } // namespace mus | 319 } // namespace mus |
| OLD | NEW |