Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / ozone / platform / drm / host / drm_display_host_manager.cc
blob0305559fbe6dc5f2be5afa5377042504b7fa6f35
1 // Copyright 2014 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.
5 #include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
7 #include <fcntl.h>
8 #include <xf86drm.h>
10 #include "base/files/file_enumerator.h"
11 #include "base/posix/eintr_wrapper.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/threading/worker_pool.h"
15 #include "ui/display/types/display_snapshot.h"
16 #include "ui/events/ozone/device/device_event.h"
17 #include "ui/events/ozone/device/device_manager.h"
18 #include "ui/ozone/common/display_util.h"
19 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
20 #include "ui/ozone/platform/drm/common/drm_util.h"
21 #include "ui/ozone/platform/drm/host/drm_device_handle.h"
22 #include "ui/ozone/platform/drm/host/drm_display_host.h"
23 #include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
24 #include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
26 namespace ui {
28 namespace {
30 typedef base::Callback<void(const base::FilePath&, scoped_ptr<DrmDeviceHandle>)>
31 OnOpenDeviceReplyCallback;
33 const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d";
34 const char kVgemDevDriCardPath[] = "/dev/dri/";
35 const char kVgemSysCardPath[] = "/sys/bus/platform/devices/vgem/drm/";
37 const char* kDisplayActionString[] = {
38 "ADD",
39 "REMOVE",
40 "CHANGE",
43 void OpenDeviceOnWorkerThread(
44 const base::FilePath& path,
45 const scoped_refptr<base::TaskRunner>& reply_runner,
46 const OnOpenDeviceReplyCallback& callback) {
47 scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle());
48 handle->Initialize(path);
49 reply_runner->PostTask(
50 FROM_HERE, base::Bind(callback, path, base::Passed(handle.Pass())));
53 base::FilePath GetPrimaryDisplayCardPath() {
54 struct drm_mode_card_res res;
55 for (int i = 0; /* end on first card# that does not exist */; i++) {
56 std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i);
58 if (access(card_path.c_str(), F_OK) != 0)
59 break;
61 int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC);
62 if (fd < 0) {
63 VPLOG(1) << "Failed to open '" << card_path << "'";
64 continue;
67 memset(&res, 0, sizeof(struct drm_mode_card_res));
68 int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
69 close(fd);
70 if (ret == 0 && res.count_crtcs > 0) {
71 return base::FilePath(card_path);
74 VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'";
77 return base::FilePath();
80 base::FilePath GetVgemCardPath() {
81 base::FileEnumerator file_iter(base::FilePath(kVgemSysCardPath), false,
82 base::FileEnumerator::DIRECTORIES,
83 FILE_PATH_LITERAL("card*"));
85 while (!file_iter.Next().empty()) {
86 // Inspect the card%d directories in the directory and extract the filename.
87 std::string vgem_card_path =
88 kVgemDevDriCardPath + file_iter.GetInfo().GetName().BaseName().value();
89 DVLOG(1) << "VGEM card path is " << vgem_card_path;
90 return base::FilePath(vgem_card_path);
92 DVLOG(1) << "Don't support VGEM";
93 return base::FilePath();
96 class FindDrmDisplayHostById {
97 public:
98 explicit FindDrmDisplayHostById(int64_t display_id)
99 : display_id_(display_id) {}
101 bool operator()(const DrmDisplayHost* display) const {
102 return display->snapshot()->display_id() == display_id_;
105 private:
106 int64_t display_id_;
109 } // namespace
111 DrmDisplayHostManager::DrmDisplayHostManager(
112 DrmGpuPlatformSupportHost* proxy,
113 DeviceManager* device_manager,
114 InputControllerEvdev* input_controller)
115 : proxy_(proxy),
116 device_manager_(device_manager),
117 input_controller_(input_controller),
118 primary_graphics_card_path_(GetPrimaryDisplayCardPath()),
119 weak_ptr_factory_(this) {
121 // First device needs to be treated specially. We need to open this
122 // synchronously since the GPU process will need it to initialize the
123 // graphics state.
124 base::ThreadRestrictions::ScopedAllowIO allow_io;
125 primary_drm_device_handle_.reset(new DrmDeviceHandle());
126 if (!primary_drm_device_handle_->Initialize(primary_graphics_card_path_)) {
127 LOG(FATAL) << "Failed to open primary graphics card";
128 return;
130 drm_devices_.insert(primary_graphics_card_path_);
132 vgem_card_path_ = GetVgemCardPath();
133 if (!vgem_card_path_.empty()) {
134 int fd = HANDLE_EINTR(
135 open(vgem_card_path_.value().c_str(), O_RDWR | O_CLOEXEC));
136 if (fd < 0) {
137 PLOG(ERROR) << "Failed to open vgem: " << vgem_card_path_.value();
139 vgem_card_device_file_.reset(fd);
143 device_manager_->AddObserver(this);
144 proxy_->RegisterHandler(this);
146 ScopedVector<HardwareDisplayControllerInfo> display_infos =
147 GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd());
148 has_dummy_display_ = !display_infos.empty();
149 for (size_t i = 0; i < display_infos.size(); ++i) {
150 displays_.push_back(new DrmDisplayHost(
151 proxy_, CreateDisplaySnapshotParams(display_infos[i],
152 primary_drm_device_handle_->fd(), i,
153 gfx::Point()),
154 true /* is_dummy */));
158 DrmDisplayHostManager::~DrmDisplayHostManager() {
159 device_manager_->RemoveObserver(this);
160 proxy_->UnregisterHandler(this);
163 DrmDisplayHost* DrmDisplayHostManager::GetDisplay(int64_t display_id) {
164 auto it = std::find_if(displays_.begin(), displays_.end(),
165 FindDrmDisplayHostById(display_id));
166 if (it == displays_.end())
167 return nullptr;
169 return *it;
172 void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate* delegate) {
173 DCHECK(!delegate_);
174 delegate_ = delegate;
177 void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate* delegate) {
178 DCHECK_EQ(delegate_, delegate);
179 delegate_ = nullptr;
182 void DrmDisplayHostManager::TakeDisplayControl(
183 const DisplayControlCallback& callback) {
184 if (display_control_change_pending_) {
185 LOG(ERROR) << "TakeDisplayControl called while change already pending";
186 callback.Run(false);
187 return;
190 if (!display_externally_controlled_) {
191 LOG(ERROR) << "TakeDisplayControl called while display already owned";
192 callback.Run(true);
193 return;
196 take_display_control_callback_ = callback;
197 display_control_change_pending_ = true;
199 if (!proxy_->Send(new OzoneGpuMsg_TakeDisplayControl()))
200 OnTakeDisplayControl(false);
203 void DrmDisplayHostManager::RelinquishDisplayControl(
204 const DisplayControlCallback& callback) {
205 if (display_control_change_pending_) {
206 LOG(ERROR)
207 << "RelinquishDisplayControl called while change already pending";
208 callback.Run(false);
209 return;
212 if (display_externally_controlled_) {
213 LOG(ERROR) << "RelinquishDisplayControl called while display not owned";
214 callback.Run(true);
215 return;
218 relinquish_display_control_callback_ = callback;
219 display_control_change_pending_ = true;
221 if (!proxy_->Send(new OzoneGpuMsg_RelinquishDisplayControl()))
222 OnRelinquishDisplayControl(false);
225 void DrmDisplayHostManager::UpdateDisplays(
226 const GetDisplaysCallback& callback) {
227 get_displays_callback_ = callback;
228 if (!proxy_->Send(new OzoneGpuMsg_RefreshNativeDisplays())) {
229 get_displays_callback_.Reset();
230 RunUpdateDisplaysCallback(callback);
234 void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) {
235 if (event.device_type() != DeviceEvent::DISPLAY)
236 return;
238 event_queue_.push(DisplayEvent(event.action_type(), event.path()));
239 ProcessEvent();
242 void DrmDisplayHostManager::ProcessEvent() {
243 while (!event_queue_.empty() && !task_pending_) {
244 DisplayEvent event = event_queue_.front();
245 event_queue_.pop();
246 VLOG(1) << "Got display event " << kDisplayActionString[event.action_type]
247 << " for " << event.path.value();
248 switch (event.action_type) {
249 case DeviceEvent::ADD:
250 if (event.path == vgem_card_path_)
251 continue;
252 if (drm_devices_.find(event.path) == drm_devices_.end()) {
253 task_pending_ = base::WorkerPool::PostTask(
254 FROM_HERE,
255 base::Bind(&OpenDeviceOnWorkerThread, event.path,
256 base::ThreadTaskRunnerHandle::Get(),
257 base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice,
258 weak_ptr_factory_.GetWeakPtr())),
259 false /* task_is_slow */);
261 break;
262 case DeviceEvent::CHANGE:
263 task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
264 FROM_HERE,
265 base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice,
266 weak_ptr_factory_.GetWeakPtr()));
267 break;
268 case DeviceEvent::REMOVE:
269 DCHECK(event.path != primary_graphics_card_path_)
270 << "Removing primary graphics card";
271 DCHECK(event.path != vgem_card_path_) << "Removing VGEM device";
272 auto it = drm_devices_.find(event.path);
273 if (it != drm_devices_.end()) {
274 task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
275 FROM_HERE,
276 base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice,
277 weak_ptr_factory_.GetWeakPtr(), event.path));
278 drm_devices_.erase(it);
280 break;
285 void DrmDisplayHostManager::OnAddGraphicsDevice(
286 const base::FilePath& path,
287 scoped_ptr<DrmDeviceHandle> handle) {
288 if (handle->IsValid()) {
289 drm_devices_.insert(path);
290 proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
291 path, base::FileDescriptor(handle->PassFD())));
292 NotifyDisplayDelegate();
295 task_pending_ = false;
296 ProcessEvent();
299 void DrmDisplayHostManager::OnUpdateGraphicsDevice() {
300 NotifyDisplayDelegate();
301 task_pending_ = false;
302 ProcessEvent();
305 void DrmDisplayHostManager::OnRemoveGraphicsDevice(const base::FilePath& path) {
306 proxy_->Send(new OzoneGpuMsg_RemoveGraphicsDevice(path));
307 NotifyDisplayDelegate();
308 task_pending_ = false;
309 ProcessEvent();
312 void DrmDisplayHostManager::OnChannelEstablished(
313 int host_id,
314 scoped_refptr<base::SingleThreadTaskRunner> send_runner,
315 const base::Callback<void(IPC::Message*)>& send_callback) {
316 // If in the middle of a configuration, just respond with the old list of
317 // displays. This is fine, since after the DRM resources are initialized and
318 // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display
319 // delegate know that the display configuration changed and it needs to
320 // update it again.
321 if (!get_displays_callback_.is_null()) {
322 base::ThreadTaskRunnerHandle::Get()->PostTask(
323 FROM_HERE,
324 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
325 weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
326 get_displays_callback_.Reset();
329 // Signal that we're taking DRM master since we're going through the
330 // initialization process again and we'll take all the available resources.
331 if (!take_display_control_callback_.is_null())
332 OnTakeDisplayControl(true);
334 if (!relinquish_display_control_callback_.is_null())
335 OnRelinquishDisplayControl(false);
337 drm_devices_.clear();
338 drm_devices_.insert(primary_graphics_card_path_);
339 scoped_ptr<DrmDeviceHandle> handle = primary_drm_device_handle_.Pass();
340 if (!handle) {
341 base::ThreadRestrictions::ScopedAllowIO allow_io;
342 handle.reset(new DrmDeviceHandle());
343 if (!handle->Initialize(primary_graphics_card_path_))
344 LOG(FATAL) << "Failed to open primary graphics card";
347 // Send the primary device first since this is used to initialize graphics
348 // state.
349 proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
350 primary_graphics_card_path_, base::FileDescriptor(handle->PassFD())));
352 device_manager_->ScanDevices(this);
353 NotifyDisplayDelegate();
356 void DrmDisplayHostManager::OnChannelDestroyed(int host_id) {
359 bool DrmDisplayHostManager::OnMessageReceived(const IPC::Message& message) {
360 bool handled = true;
362 IPC_BEGIN_MESSAGE_MAP(DrmDisplayHostManager, message)
363 IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays, OnUpdateNativeDisplays)
364 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayConfigured, OnDisplayConfigured)
365 IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateReceived, OnHDCPStateReceived)
366 IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateUpdated, OnHDCPStateUpdated)
367 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlTaken, OnTakeDisplayControl)
368 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlRelinquished,
369 OnRelinquishDisplayControl)
370 IPC_MESSAGE_UNHANDLED(handled = false)
371 IPC_END_MESSAGE_MAP()
373 return handled;
376 void DrmDisplayHostManager::OnUpdateNativeDisplays(
377 const std::vector<DisplaySnapshot_Params>& params) {
378 ScopedVector<DrmDisplayHost> old_displays(displays_.Pass());
379 for (size_t i = 0; i < params.size(); ++i) {
380 auto it = std::find_if(old_displays.begin(), old_displays.end(),
381 FindDrmDisplayHostById(params[i].display_id));
382 if (it == old_displays.end()) {
383 displays_.push_back(
384 new DrmDisplayHost(proxy_, params[i], false /* is_dummy */));
385 } else {
386 (*it)->UpdateDisplaySnapshot(params[i]);
387 displays_.push_back(*it);
388 old_displays.weak_erase(it);
392 if (!get_displays_callback_.is_null()) {
393 base::ThreadTaskRunnerHandle::Get()->PostTask(
394 FROM_HERE,
395 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
396 weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
397 get_displays_callback_.Reset();
401 void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id,
402 bool status) {
403 DrmDisplayHost* display = GetDisplay(display_id);
404 if (display)
405 display->OnDisplayConfigured(status);
406 else
407 LOG(ERROR) << "Couldn't find display with id=" << display_id;
410 void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id,
411 bool status,
412 HDCPState state) {
413 DrmDisplayHost* display = GetDisplay(display_id);
414 if (display)
415 display->OnHDCPStateReceived(status, state);
416 else
417 LOG(ERROR) << "Couldn't find display with id=" << display_id;
420 void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id,
421 bool status) {
422 DrmDisplayHost* display = GetDisplay(display_id);
423 if (display)
424 display->OnHDCPStateUpdated(status);
425 else
426 LOG(ERROR) << "Couldn't find display with id=" << display_id;
429 void DrmDisplayHostManager::OnTakeDisplayControl(bool status) {
430 if (take_display_control_callback_.is_null()) {
431 LOG(ERROR) << "No callback for take display control";
432 return;
435 DCHECK(display_externally_controlled_);
436 DCHECK(display_control_change_pending_);
438 if (status) {
439 input_controller_->SetInputDevicesEnabled(true);
440 display_externally_controlled_ = false;
443 base::ThreadTaskRunnerHandle::Get()->PostTask(
444 FROM_HERE, base::Bind(take_display_control_callback_, status));
445 take_display_control_callback_.Reset();
446 display_control_change_pending_ = false;
449 void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status) {
450 if (relinquish_display_control_callback_.is_null()) {
451 LOG(ERROR) << "No callback for relinquish display control";
452 return;
455 DCHECK(!display_externally_controlled_);
456 DCHECK(display_control_change_pending_);
458 if (status) {
459 input_controller_->SetInputDevicesEnabled(false);
460 display_externally_controlled_ = true;
463 base::ThreadTaskRunnerHandle::Get()->PostTask(
464 FROM_HERE, base::Bind(relinquish_display_control_callback_, status));
465 relinquish_display_control_callback_.Reset();
466 display_control_change_pending_ = false;
469 void DrmDisplayHostManager::RunUpdateDisplaysCallback(
470 const GetDisplaysCallback& callback) const {
471 std::vector<DisplaySnapshot*> snapshots;
472 for (auto* display : displays_)
473 snapshots.push_back(display->snapshot());
475 callback.Run(snapshots);
478 void DrmDisplayHostManager::NotifyDisplayDelegate() const {
479 if (delegate_)
480 delegate_->OnConfigurationChanged();
483 } // namespace ui