1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/time/default_tick_clock.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "ui/display/chromeos/x11/display_mode_x11.h"
823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "ui/display/chromeos/x11/display_snapshot_x11.h"
9c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/events/platform/platform_event_source.h"
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <X11/extensions/Xrandr.h>
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace ui {
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// static
16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst int NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs = 7000;
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)NativeDisplayEventDispatcherX11::NativeDisplayEventDispatcherX11(
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    NativeDisplayDelegateX11::HelperDelegate* delegate,
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int xrandr_event_base)
21effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    : delegate_(delegate),
22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      xrandr_event_base_(xrandr_event_base),
23c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      tick_clock_(new base::DefaultTickClock) {
24c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  startup_time_ = tick_clock_->NowTicks();
25c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)NativeDisplayEventDispatcherX11::~NativeDisplayEventDispatcherX11() {}
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativeDisplayEventDispatcherX11::CanDispatchEvent(
30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const PlatformEvent& event) {
31c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return (event->type - xrandr_event_base_ == RRScreenChangeNotify) ||
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         (event->type - xrandr_event_base_ == RRNotify);
33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
34c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
35c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochuint32_t NativeDisplayEventDispatcherX11::DispatchEvent(
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const PlatformEvent& event) {
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    VLOG(1) << "Received RRScreenChangeNotify event";
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    delegate_->UpdateXRandRConfiguration(event);
40c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return ui::POST_DISPATCH_PERFORM_DEFAULT;
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Bail out early for everything except RRNotify_OutputChange events
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // about an output getting connected or disconnected.
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (event->type - xrandr_event_base_ != RRNotify)
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return ui::POST_DISPATCH_PERFORM_DEFAULT;
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const XRRNotifyEvent* notify_event = reinterpret_cast<XRRNotifyEvent*>(event);
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (notify_event->subtype != RRNotify_OutputChange)
49c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return ui::POST_DISPATCH_PERFORM_DEFAULT;
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const XRROutputChangeNotifyEvent* output_change_event =
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      reinterpret_cast<XRROutputChangeNotifyEvent*>(event);
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const int action = output_change_event->connection;
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (action != RR_Connected && action != RR_Disconnected)
54c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return ui::POST_DISPATCH_PERFORM_DEFAULT;
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const bool connected = (action == RR_Connected);
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  VLOG(1) << "Received RRNotify_OutputChange event:"
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          << " output=" << output_change_event->output
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          << " crtc=" << output_change_event->crtc
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          << " mode=" << output_change_event->mode
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          << " action=" << (connected ? "connected" : "disconnected");
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
63c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  bool check_cache = (tick_clock_->NowTicks() - startup_time_)
64c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         .InMilliseconds() <= kUseCacheAfterStartupMs;
65c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
66c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (check_cache) {
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    bool found_changed_output = false;
68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    const std::vector<DisplaySnapshot*>& cached_outputs =
69c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        delegate_->GetCachedDisplays();
70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    for (std::vector<DisplaySnapshot*>::const_iterator it =
71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch             cached_outputs.begin();
72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch         it != cached_outputs.end();
73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch         ++it) {
74effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      const DisplaySnapshotX11* x11_output =
75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          static_cast<const DisplaySnapshotX11*>(*it);
76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      const DisplayModeX11* x11_mode =
77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          static_cast<const DisplayModeX11*>(x11_output->current_mode());
78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if (x11_output->output() == output_change_event->output) {
80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        if (connected && x11_output->crtc() == output_change_event->crtc &&
81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            x11_mode->mode_id() == output_change_event->mode) {
82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          VLOG(1) << "Ignoring event describing already-cached state";
83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          return POST_DISPATCH_PERFORM_DEFAULT;
84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        }
85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        found_changed_output = true;
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        break;
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (!connected && !found_changed_output) {
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      VLOG(1) << "Ignoring event describing already-disconnected output";
92c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return ui::POST_DISPATCH_PERFORM_DEFAULT;
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  delegate_->NotifyDisplayObservers();
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
98c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return ui::POST_DISPATCH_PERFORM_DEFAULT;
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid NativeDisplayEventDispatcherX11::SetTickClockForTest(
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    scoped_ptr<base::TickClock> tick_clock) {
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  tick_clock_ = tick_clock.Pass();
104c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  startup_time_ = tick_clock_->NowTicks();
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace ui
108