summaryrefslogtreecommitdiffstats
path: root/ui/aura/monitor_change_observer_x11.cc
blob: 8618d1fdf110903a3d13ca7627a36ed6a56ea079 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/aura/monitor_change_observer_x11.h"

#include <algorithm>
#include <map>
#include <set>
#include <vector>

#include <X11/extensions/Xrandr.h>

#include "base/message_pump_x.h"
#include "base/stl_util.h"
#include "ui/aura/env.h"
#include "ui/aura/dispatcher_linux.h"
#include "ui/aura/monitor.h"
#include "ui/aura/monitor_manager.h"

namespace aura {
namespace internal {

namespace {
XRRModeInfo* FindMode(XRRScreenResources* screen_resources, XID current_mode) {
  for (int m = 0; m < screen_resources->nmode; m++) {
    XRRModeInfo *mode = &screen_resources->modes[m];
    if (mode->id == current_mode)
      return mode;
  }
  return NULL;
}

bool CompareMonitorY(const Monitor* lhs, const Monitor* rhs) {
  return lhs->bounds().y() < rhs->bounds().y();
}

}  // namespace

MonitorChangeObserverX11::MonitorChangeObserverX11()
    : xdisplay_(base::MessagePumpX::GetDefaultXDisplay()),
      x_root_window_(DefaultRootWindow(xdisplay_)),
      xrandr_event_base_(0) {
  XRRSelectInput(xdisplay_, x_root_window_, RRScreenChangeNotifyMask);
  int error_base_ignored;
  XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
  static_cast<DispatcherLinux*>(
      Env::GetInstance()->GetDispatcher())->
      WindowDispatcherCreated(x_root_window_, this);
}

MonitorChangeObserverX11::~MonitorChangeObserverX11() {
  static_cast<DispatcherLinux*>(
      Env::GetInstance()->GetDispatcher())->
      WindowDispatcherDestroying(x_root_window_);
}

base::MessagePumpDispatcher::DispatchStatus
MonitorChangeObserverX11::Dispatch(XEvent* event) {
  if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
    NotifyMonitorChange();
    return base::MessagePumpDispatcher::EVENT_PROCESSED;
  }
  return base::MessagePumpDispatcher::EVENT_IGNORED;
}

void MonitorChangeObserverX11::NotifyMonitorChange() {
  if (!MonitorManager::use_fullscreen_host_window())
    return;  // Use the default monitor that monitor manager determined.

  XRRScreenResources* screen_resources =
      XRRGetScreenResources(xdisplay_, x_root_window_);
  std::map<XID, XRRCrtcInfo*> crtc_info_map;

  for (int c = 0; c < screen_resources->ncrtc; c++) {
    XID crtc_id = screen_resources->crtcs[c];
    XRRCrtcInfo *crtc_info =
        XRRGetCrtcInfo(xdisplay_, screen_resources, crtc_id);
    crtc_info_map[crtc_id] = crtc_info;
  }

  std::vector<const Monitor*> monitors;
  std::set<int> y_coords;
  for (int o = 0; o < screen_resources->noutput; o++) {
    XRROutputInfo *output_info =
        XRRGetOutputInfo(xdisplay_,
                         screen_resources,
                         screen_resources->outputs[o]);
    if (output_info->connection != RR_Connected) {
      XRRFreeOutputInfo(output_info);
      continue;
    }
    XRRCrtcInfo* crtc_info = crtc_info_map[output_info->crtc];
    if (!crtc_info) {
      LOG(WARNING) << "Crtc not found for output";
      continue;
    }
    XRRModeInfo* mode = FindMode(screen_resources, crtc_info->mode);
    CHECK(mode);
    // Mirrored monitors have the same y coordinates.
    if (y_coords.find(crtc_info->y) != y_coords.end())
      continue;
    Monitor* monitor = new Monitor;
    monitor->set_bounds(gfx::Rect(crtc_info->x, crtc_info->y,
                                  mode->width, mode->height));
    monitors.push_back(monitor);
    y_coords.insert(crtc_info->y);
    XRRFreeOutputInfo(output_info);
  }

  // Free all allocated resources.
  for (std::map<XID, XRRCrtcInfo*>::const_iterator iter = crtc_info_map.begin();
       iter != crtc_info_map.end(); ++iter) {
    XRRFreeCrtcInfo(iter->second);
  }
  XRRFreeScreenResources(screen_resources);

  // PowerManager lays out the outputs vertically. Sort them by Y
  // coordinates.
  std::sort(monitors.begin(), monitors.end(), CompareMonitorY);
  Env::GetInstance()->monitor_manager()
      ->OnNativeMonitorsChanged(monitors);
  STLDeleteContainerPointers(monitors.begin(), monitors.end());
}

}  // namespace internal
}  // namespace aura