Mir

Merge lp:~raof/mir/rearrange-drm-configuration into lp:mir

Proposed by Chris Halse Rogers
Status: Merged
Merged at revision: 4103
Proposed branch: lp:~raof/mir/rearrange-drm-configuration
Merge into: lp:mir
Diff against target: 2183 lines (+633/-642)
17 files modified
src/platforms/mesa/server/kms/cursor.cpp (+4/-5)
src/platforms/mesa/server/kms/display.cpp (+9/-11)
src/platforms/mesa/server/kms/display.h (+1/-1)
src/platforms/mesa/server/kms/kms_display_configuration.h (+6/-3)
src/platforms/mesa/server/kms/kms_output.h (+16/-0)
src/platforms/mesa/server/kms/kms_output_container.h (+5/-2)
src/platforms/mesa/server/kms/real_kms_display_configuration.cpp (+92/-293)
src/platforms/mesa/server/kms/real_kms_display_configuration.h (+6/-7)
src/platforms/mesa/server/kms/real_kms_output.cpp (+237/-11)
src/platforms/mesa/server/kms/real_kms_output.h (+7/-4)
src/platforms/mesa/server/kms/real_kms_output_container.cpp (+27/-19)
src/platforms/mesa/server/kms/real_kms_output_container.h (+2/-2)
tests/acceptance-tests/test_client_input.cpp (+2/-2)
tests/unit-tests/platforms/mesa/kms/mock_kms_output.h (+3/-0)
tests/unit-tests/platforms/mesa/kms/test_cursor.cpp (+120/-107)
tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp (+53/-141)
tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp (+43/-34)
To merge this branch: bzr merge lp:~raof/mir/rearrange-drm-configuration
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Needs Fixing
Kevin DuBois (community) Approve
Alan Griffiths Approve
Review via email: mp+319756@code.launchpad.net

Commit message

Clarify KMS display configuration responsibilities.

This moves responsibility for converting between KMS hardware state and Mir DisplayOutputConfiguration to KMSOutput, moves DisplayConfigurationOutputId → KMSOutput lookup into KMSDisplayConfiguration, and makes KMSOutputContainer responsible for maintaining the set of KMSOutput as they appear and disappear.

To post a comment you must log in.
Revision history for this message
Chris Halse Rogers (raof) wrote :

This makes the follow-on changes required to have KMSOutputs from multiple GPUs significantly cleaner.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4080
https://mir-jenkins.ubuntu.com/job/mir-ci/3143/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4217/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4304/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4294/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4294/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4294/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4244/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4244/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4244/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4244/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4244/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4244/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3143/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4082
https://mir-jenkins.ubuntu.com/job/mir-ci/3187/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4285/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4372
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4362
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4362
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4362
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4312
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4312/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4312
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4312/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4312
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4312/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4312/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4312/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4312
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4312/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3187/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:4083
https://mir-jenkins.ubuntu.com/job/mir-ci/3188/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4287
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4374
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4364
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4364
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4364
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4314/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4314/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4314/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4314/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4314/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4314
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4314/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3188/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Sure.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

lgtm

review: Approve
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://mir-jenkins.ubuntu.com/job/mir-autolanding/1211/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4305/console
    None: https://mir-jenkins.ubuntu.com/job/generic-land-mp/1277/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4392
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4382
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4382
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4382
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4334
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4334/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4334
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4334/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4334
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4334/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4334/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4334/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4334
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4334/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Autolanding failed due to my (reverted) fiddling with CI. Trying again!

Revision history for this message
Mir CI Bot (mir-ci-bot) :
review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://mir-jenkins.ubuntu.com/job/mir-autolanding/1217/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4320
    FAILURE: https://mir-jenkins.ubuntu.com/job/generic-land-mp/1284/console
    None: https://mir-jenkins.ubuntu.com/job/generic-land-mp/1285/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4407
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4397
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4397
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4397
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4352/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4352/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4352/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4352/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4352/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4352
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4352/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/platforms/mesa/server/kms/cursor.cpp'
2--- src/platforms/mesa/server/kms/cursor.cpp 2017-02-15 07:38:33 +0000
3+++ src/platforms/mesa/server/kms/cursor.cpp 2017-03-20 02:44:46 +0000
4@@ -217,9 +217,9 @@
5 void mir::graphics::mesa::Cursor::clear(std::lock_guard<std::mutex> const&)
6 {
7 last_set_failed = false;
8- output_container.for_each_output([&](KMSOutput& output)
9+ output_container.for_each_output([&](std::shared_ptr<KMSOutput> const& output)
10 {
11- if (!output.clear_cursor())
12+ if (!output->clear_cursor())
13 last_set_failed = true;
14 });
15 }
16@@ -240,14 +240,13 @@
17 std::function<void(KMSOutput&, geom::Rectangle const&, MirOrientation orientation)> const& f)
18 {
19 current_configuration->with_current_configuration_do(
20- [this,&f](KMSDisplayConfiguration const& kms_conf)
21+ [&f](KMSDisplayConfiguration const& kms_conf)
22 {
23 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)
24 {
25 if (conf_output.used)
26 {
27- uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id);
28- auto output = output_container.get_kms_output_for(connector_id);
29+ auto output = kms_conf.get_output_for(conf_output.id);
30
31 f(*output, conf_output.extents(), conf_output.orientation);
32 }
33
34=== modified file 'src/platforms/mesa/server/kms/display.cpp'
35--- src/platforms/mesa/server/kms/display.cpp 2017-03-13 08:12:52 +0000
36+++ src/platforms/mesa/server/kms/display.cpp 2017-03-20 02:44:46 +0000
37@@ -93,9 +93,9 @@
38 listener(listener),
39 monitor(mir::udev::Context()),
40 shared_egl{*gl_config},
41- output_container{drm->fd,
42- std::make_shared<KMSPageFlipper>(drm->fd, listener)},
43- current_display_configuration{drm->fd},
44+ output_container{std::make_shared<RealKMSOutputContainer>(drm->fd,
45+ std::make_shared<KMSPageFlipper>(drm->fd, listener))},
46+ current_display_configuration{output_container},
47 dirty_configuration{false},
48 bypass_option(bypass_option),
49 gl_config{gl_config}
50@@ -261,7 +261,7 @@
51 try
52 {
53 locked_cursor = std::make_shared<Cursor>(gbm->device,
54- output_container,
55+ *output_container,
56 std::make_shared<KMSCurrentConfiguration>(*this));
57 }
58 catch (std::runtime_error const&)
59@@ -287,8 +287,7 @@
60 if (conf_output.connected &&
61 (!conf_output.used || (conf_output.power_mode != mir_power_mode_on)))
62 {
63- uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id);
64- auto kms_output = output_container.get_kms_output_for(connector_id);
65+ auto kms_output = current_display_configuration.get_output_for(conf_output.id);
66
67 kms_output->clear_crtc();
68 kms_output->set_power_mode(conf_output.power_mode);
69@@ -327,7 +326,8 @@
70
71 mg::Frame mgm::Display::last_frame_on(unsigned output_id) const
72 {
73- auto output = output_container.get_kms_output_for(output_id);
74+ auto output = current_display_configuration.get_output_for(
75+ DisplayConfigurationOutputId{static_cast<int>(output_id)});
76 return output->last_frame();
77 }
78
79@@ -359,8 +359,7 @@
80 kms_conf.for_each_output(
81 [&](DisplayConfigurationOutput const& conf_output)
82 {
83- uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id);
84- auto kms_output = output_container.get_kms_output_for(connector_id);
85+ auto kms_output = current_display_configuration.get_output_for(conf_output.id);
86 kms_output->clear_cursor();
87 kms_output->reset();
88 });
89@@ -380,8 +379,7 @@
90 group.for_each_output(
91 [&](DisplayConfigurationOutput const& conf_output)
92 {
93- uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id);
94- auto kms_output = output_container.get_kms_output_for(connector_id);
95+ auto kms_output = current_display_configuration.get_output_for(conf_output.id);
96
97 auto const mode_index = kms_conf.get_kms_mode_index(conf_output.id,
98 conf_output.current_mode_index);
99
100=== modified file 'src/platforms/mesa/server/kms/display.h'
101--- src/platforms/mesa/server/kms/display.h 2017-03-15 13:53:42 +0000
102+++ src/platforms/mesa/server/kms/display.h 2017-03-20 02:44:46 +0000
103@@ -113,7 +113,7 @@
104 mir::udev::Monitor monitor;
105 helpers::EGLHelper shared_egl;
106 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;
107- mutable RealKMSOutputContainer output_container;
108+ std::shared_ptr<KMSOutputContainer> const output_container;
109 mutable RealKMSDisplayConfiguration current_display_configuration;
110 mutable std::atomic<bool> dirty_configuration;
111
112
113=== modified file 'src/platforms/mesa/server/kms/kms_display_configuration.h'
114--- src/platforms/mesa/server/kms/kms_display_configuration.h 2016-01-29 08:18:22 +0000
115+++ src/platforms/mesa/server/kms/kms_display_configuration.h 2017-03-20 02:44:46 +0000
116@@ -20,6 +20,7 @@
117 #define MIR_GRAPHICS_MESA_KMS_DISPLAY_CONFIGURATION_H_
118
119 #include "mir/graphics/display_configuration.h"
120+#include <memory>
121
122 namespace mir
123 {
124@@ -27,15 +28,17 @@
125 {
126 namespace mesa
127 {
128+class KMSOutput;
129
130 class DRMModeResources;
131
132 class KMSDisplayConfiguration : public DisplayConfiguration
133 {
134 public:
135- virtual uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const = 0;
136- virtual size_t get_kms_mode_index(DisplayConfigurationOutputId id,
137- size_t conf_mode_index) const = 0;
138+ virtual std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const = 0;
139+ virtual size_t get_kms_mode_index(
140+ DisplayConfigurationOutputId id,
141+ size_t conf_mode_index) const = 0;
142 virtual void update() = 0;
143 };
144
145
146=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
147--- src/platforms/mesa/server/kms/kms_output.h 2017-03-13 08:12:52 +0000
148+++ src/platforms/mesa/server/kms/kms_output.h 2017-03-20 02:44:46 +0000
149@@ -32,6 +32,8 @@
150 {
151 namespace graphics
152 {
153+class DisplayConfigurationOutput;
154+
155 namespace mesa
156 {
157
158@@ -68,6 +70,20 @@
159 virtual void set_gamma(GammaCurves const& gamma) = 0;
160 virtual Frame last_frame() const = 0;
161
162+ /**
163+ * Re-probe the hardware state of this connector.
164+ *
165+ * \throws std::system_error if the underlying DRM connector has disappeared.
166+ */
167+ virtual void refresh_hardware_state() = 0;
168+ /**
169+ * Translate and copy the cached hardware state into a Mir display configuration object.
170+ *
171+ * \param [out] to_update The Mir display configuration object to update with new
172+ * hardware state. Only hardware state (modes, dimensions, etc)
173+ * is touched.
174+ */
175+ virtual void update_from_hardware_state(DisplayConfigurationOutput& to_update) const = 0;
176 virtual FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const = 0;
177
178 protected:
179
180=== modified file 'src/platforms/mesa/server/kms/kms_output_container.h'
181--- src/platforms/mesa/server/kms/kms_output_container.h 2016-01-29 08:18:22 +0000
182+++ src/platforms/mesa/server/kms/kms_output_container.h 2017-03-20 02:44:46 +0000
183@@ -36,9 +36,12 @@
184 public:
185 virtual ~KMSOutputContainer() = default;
186
187- virtual std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id) = 0;
188- virtual void for_each_output(std::function<void(KMSOutput&)> functor) const = 0;
189+ virtual void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const = 0;
190
191+ /**
192+ * Re-probe hardware state and update output list.
193+ */
194+ virtual void update_from_hardware_state() = 0;
195 protected:
196 KMSOutputContainer() = default;
197 KMSOutputContainer(KMSOutputContainer const&) = delete;
198
199=== modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.cpp'
200--- src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-14 02:26:28 +0000
201+++ src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-20 02:44:46 +0000
202@@ -17,9 +17,10 @@
203 */
204
205 #include "real_kms_display_configuration.h"
206-#include "kms-utils/drm_mode_resources.h"
207 #include "mir/graphics/pixel_format_utils.h"
208 #include "mir/log.h"
209+#include "kms_output_container.h"
210+#include "kms_output.h"
211
212 #include <cmath>
213 #include <limits>
214@@ -35,77 +36,21 @@
215 namespace mgk = mir::graphics::kms;
216 namespace geom = mir::geometry;
217
218-namespace
219-{
220-
221-bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)
222-{
223- return (info1.clock == info2.clock &&
224- info1.hdisplay == info2.hdisplay &&
225- info1.hsync_start == info2.hsync_start &&
226- info1.hsync_end == info2.hsync_end &&
227- info1.htotal == info2.htotal &&
228- info1.hskew == info2.hskew &&
229- info1.vdisplay == info2.vdisplay &&
230- info1.vsync_start == info2.vsync_start &&
231- info1.vsync_end == info2.vsync_end &&
232- info1.vtotal == info2.vtotal);
233-}
234-
235-double calculate_vrefresh_hz(drmModeModeInfo const& mode)
236-{
237- if (mode.htotal == 0 || mode.vtotal == 0)
238- return 0.0;
239-
240- /* mode.clock is in KHz */
241- double hz = (mode.clock * 100000LL /
242- ((long)mode.htotal * (long)mode.vtotal)
243- ) / 100.0;
244-
245- // Actually we don't need floating point at all for this...
246- // TODO: Consider converting our structs to fixed-point ints
247- return hz;
248-}
249-
250-mg::DisplayConfigurationOutputType
251-kms_connector_type_to_output_type(uint32_t connector_type)
252-{
253- return static_cast<mg::DisplayConfigurationOutputType>(connector_type);
254-}
255-
256-MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel)
257-{
258- switch (subpixel)
259- {
260- case DRM_MODE_SUBPIXEL_UNKNOWN:
261- return mir_subpixel_arrangement_unknown;
262- case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
263- return mir_subpixel_arrangement_horizontal_rgb;
264- case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
265- return mir_subpixel_arrangement_horizontal_bgr;
266- case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
267- return mir_subpixel_arrangement_vertical_rgb;
268- case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
269- return mir_subpixel_arrangement_vertical_bgr;
270- case DRM_MODE_SUBPIXEL_NONE:
271- return mir_subpixel_arrangement_none;
272- default:
273- return mir_subpixel_arrangement_unknown;
274- }
275-}
276-
277-}
278-
279-mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(int drm_fd)
280- : drm_fd{drm_fd}
281+mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(
282+ std::shared_ptr<KMSOutputContainer> const& displays)
283+ : displays{displays},
284+ card{mg::DisplayConfigurationCardId{0}, 0}
285 {
286 update();
287 }
288
289 mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(
290 RealKMSDisplayConfiguration const& conf)
291- : KMSDisplayConfiguration(), drm_fd{conf.drm_fd},
292- card(conf.card), outputs{conf.outputs}
293+ : KMSDisplayConfiguration(),
294+ displays{conf.displays},
295+ // Vivid GCC is apparently confused by trying to copy-construct card from conf.card?
296+ card{conf.card.id, conf.card.max_simultaneous_outputs},
297+ outputs{conf.outputs}
298 {
299 }
300
301@@ -114,7 +59,7 @@
302 {
303 if (&conf != this)
304 {
305- drm_fd = conf.drm_fd;
306+ displays = conf.displays;
307 card = conf.card;
308 outputs = conf.outputs;
309 }
310@@ -131,16 +76,16 @@
311 void mgm::RealKMSDisplayConfiguration::for_each_output(
312 std::function<void(DisplayConfigurationOutput const&)> f) const
313 {
314- for (auto const& output : outputs)
315- f(output);
316+ for (auto const& output_pair : outputs)
317+ f(output_pair.first);
318 }
319
320 void mgm::RealKMSDisplayConfiguration::for_each_output(
321 std::function<void(UserDisplayConfigurationOutput&)> f)
322 {
323- for (auto& output : outputs)
324+ for (auto& output_pair : outputs)
325 {
326- UserDisplayConfigurationOutput user(output);
327+ UserDisplayConfigurationOutput user(output_pair.first);
328 f(user);
329 }
330 }
331@@ -150,233 +95,87 @@
332 return std::make_unique<RealKMSDisplayConfiguration>(*this);
333 }
334
335-uint32_t mgm::RealKMSDisplayConfiguration::get_kms_connector_id(
336+std::shared_ptr<mgm::KMSOutput> mgm::RealKMSDisplayConfiguration::get_output_for(
337 DisplayConfigurationOutputId id) const
338 {
339- auto iter = find_output_with_id(id);
340-
341- if (iter == outputs.end())
342- {
343- BOOST_THROW_EXCEPTION(
344- std::runtime_error("Failed to find DisplayConfigurationOutput with provided id"));
345- }
346-
347- return id.as_value();
348+ return outputs.at(id.as_value()).second;
349 }
350
351 size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index(
352 DisplayConfigurationOutputId id,
353 size_t conf_mode_index) const
354 {
355- auto iter = find_output_with_id(id);
356-
357- if (iter == outputs.end() || conf_mode_index >= iter->modes.size())
358- {
359- BOOST_THROW_EXCEPTION(
360- std::runtime_error("Failed to find valid mode index for DisplayConfigurationOutput with provided id/mode_index"));
361+ if (static_cast<size_t>(id.as_value()) > outputs.size())
362+ {
363+ BOOST_THROW_EXCEPTION(std::invalid_argument("Request for KMS mode index of invalid output ID"));
364+ }
365+ if (conf_mode_index > outputs[id.as_value()].first.modes.size())
366+ {
367+ BOOST_THROW_EXCEPTION(std::invalid_argument("Request for out-of-bounds KMS mode index"));
368 }
369
370 return conf_mode_index;
371 }
372
373+namespace
374+{
375+void populate_default_mir_config(mg::DisplayConfigurationOutput& to_populate)
376+{
377+ to_populate.card_id = mg::DisplayConfigurationCardId{0};
378+ to_populate.gamma_supported = mir_output_gamma_supported;
379+ to_populate.orientation = mir_orientation_normal;
380+ to_populate.form_factor = mir_form_factor_monitor;
381+ to_populate.scale = 1.0f;
382+ to_populate.top_left = geom::Point{};
383+ to_populate.used = false;
384+ to_populate.pixel_formats = {mir_pixel_format_xrgb_8888, mir_pixel_format_argb_8888};
385+ to_populate.current_format = mir_pixel_format_xrgb_8888;
386+ to_populate.current_mode_index = std::numeric_limits<uint32_t>::max();
387+}
388+}
389+
390 void mgm::RealKMSDisplayConfiguration::update()
391 {
392- kms::DRMModeResources resources{drm_fd};
393-
394- size_t max_outputs = std::min(resources.num_crtcs(), resources.num_connectors());
395- card = {DisplayConfigurationCardId{0}, max_outputs};
396-
397- resources.for_each_connector([&](kms::DRMModeConnectorUPtr connector)
398- {
399- add_or_update_output(resources, *connector);
400- });
401-}
402-
403-namespace
404-{
405-std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id)
406-{
407- std::vector<uint8_t> edid;
408-
409- mgk::ObjectProperties connector_props{
410- drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR};
411-
412- if (connector_props.has_property("EDID"))
413- {
414- /*
415- * We don't technically need the property information here, but query it
416- * anyway so we can detect if our assumptions about DRM behaviour
417- * become invalid.
418- */
419- auto property = mgk::DRMModePropertyUPtr{
420- drmModeGetProperty(drm_fd, connector_props.id_for("EDID")),
421- &drmModeFreeProperty};
422-
423- if (!property)
424- {
425- mir::log_warning(
426- "Failed to get EDID property for connector %u: %i (%s)",
427- connector_id,
428- errno,
429- ::strerror(errno));
430- return edid;
431- }
432-
433- if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB))
434- {
435- mir::log_warning(
436- "EDID property on connector %u has unexpected type %u",
437- connector_id,
438- property->flags);
439- return edid;
440- }
441-
442- // A property ID of 0 means invalid.
443- if (connector_props["EDID"] == 0)
444- {
445- /*
446- * Log a debug message only. This will trigger for broken monitors which
447- * don't provide an EDID, which is not as unusual as you might think...
448- */
449- mir::log_debug("No EDID data available on connector %u", connector_id);
450- return edid;
451- }
452-
453- auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]);
454-
455- if (!blob)
456- {
457- mir::log_warning(
458- "Failed to get EDID property blob for connector %u: %i (%s)",
459- connector_id,
460- errno,
461- ::strerror(errno));
462-
463- return edid;
464- }
465-
466- edid.reserve(blob->length);
467- edid.insert(edid.begin(),
468- reinterpret_cast<uint8_t*>(blob->data),
469- reinterpret_cast<uint8_t*>(blob->data) + blob->length);
470-
471- drmModeFreePropertyBlob(blob);
472-
473- edid.shrink_to_fit();
474- }
475-
476- return edid;
477-}
478-}
479-
480-void mgm::RealKMSDisplayConfiguration::add_or_update_output(
481- kms::DRMModeResources const& resources,
482- drmModeConnector const& connector)
483-{
484- DisplayConfigurationOutputId id{static_cast<int>(connector.connector_id)};
485- DisplayConfigurationCardId card_id{0};
486- DisplayConfigurationOutputType const type{
487- kms_connector_type_to_output_type(connector.connector_type)};
488- geom::Size physical_size{connector.mmWidth, connector.mmHeight};
489- bool connected{connector.connection == DRM_MODE_CONNECTED};
490- uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max();
491- uint32_t current_mode_index{invalid_mode_index};
492- uint32_t preferred_mode_index{invalid_mode_index};
493- std::vector<DisplayConfigurationMode> modes;
494- std::vector<MirPixelFormat> formats {mir_pixel_format_argb_8888,
495- mir_pixel_format_xrgb_8888};
496-
497- std::vector<uint8_t> edid;
498- if (connected)
499- {
500- /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID
501- * when there is no monitor connected!
502- */
503- edid = edid_for_connector(drm_fd, connector.connector_id);
504- }
505-
506- drmModeModeInfo current_mode_info = drmModeModeInfo();
507- GammaCurves gamma;
508-
509- /* Get information about the current mode */
510- if (connector.encoder_id)
511- {
512- auto encoder = resources.encoder(connector.encoder_id);
513- if (encoder->crtc_id)
514- {
515- auto crtc = resources.crtc(encoder->crtc_id);
516- current_mode_info = crtc->mode;
517-
518- if (crtc->gamma_size > 0)
519- gamma = mg::LinearGammaLUTs(crtc->gamma_size);
520- }
521- }
522-
523- /* Add all the available modes and find the current and preferred one */
524- for (int m = 0; m < connector.count_modes; m++)
525- {
526- drmModeModeInfo& mode_info = connector.modes[m];
527-
528- geom::Size size{mode_info.hdisplay, mode_info.vdisplay};
529-
530- double vrefresh_hz = calculate_vrefresh_hz(mode_info);
531-
532- modes.push_back({size, vrefresh_hz});
533-
534- if (kms_modes_are_equal(mode_info, current_mode_info))
535- current_mode_index = m;
536-
537- if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED)
538- preferred_mode_index = m;
539- }
540-
541- /* Add or update the output */
542- auto iter = find_output_with_id(id);
543-
544- if (iter == outputs.end())
545- {
546- outputs.push_back({id, card_id, type, formats, modes, preferred_mode_index,
547- physical_size, connected, false, geom::Point(),
548- current_mode_index, mir_pixel_format_xrgb_8888,
549- mir_power_mode_on, mir_orientation_normal,
550- 1.0f, mir_form_factor_monitor,
551- kms_subpixel_to_mir_subpixel(connector.subpixel),
552- gamma, mir_output_gamma_supported, std::move(edid)});
553- }
554- else
555- {
556- auto& output = *iter;
557-
558- output.current_mode_index = current_mode_index;
559- output.modes = modes;
560- output.preferred_mode_index = preferred_mode_index;
561- output.physical_size_mm = physical_size;
562- output.connected = connected;
563- output.current_format = mir_pixel_format_xrgb_8888;
564- output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector.subpixel);
565- output.gamma = gamma;
566- output.edid = edid;
567- }
568-}
569-
570-std::vector<mg::DisplayConfigurationOutput>::iterator
571-mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id)
572-{
573- return std::find_if(outputs.begin(), outputs.end(),
574- [id](DisplayConfigurationOutput const& output)
575- {
576- return output.id == id;
577- });
578-}
579-
580-std::vector<mg::DisplayConfigurationOutput>::const_iterator
581-mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) const
582-{
583- return std::find_if(outputs.begin(), outputs.end(),
584- [id](DisplayConfigurationOutput const& output)
585- {
586- return output.id == id;
587- });
588+ decltype(outputs) new_outputs;
589+
590+ int counter = 0;
591+ displays->update_from_hardware_state();
592+ displays->for_each_output(
593+ [this, &new_outputs, &counter](auto const& output) mutable
594+ {
595+ DisplayConfigurationOutput mir_config;
596+
597+ auto const existing_output = std::find_if(
598+ outputs.begin(),
599+ outputs.end(),
600+ [&output](auto const& candidate)
601+ {
602+ // Pointer comparison; is this KMSOutput object already present?
603+ return candidate.second == output;
604+ });
605+ if (existing_output == outputs.end())
606+ {
607+ populate_default_mir_config(mir_config);
608+ }
609+ else
610+ {
611+ mir_config = existing_output->first;
612+ }
613+
614+ output->update_from_hardware_state(mir_config);
615+ mir_config.id = DisplayConfigurationOutputId{counter};
616+ counter++;
617+
618+ new_outputs.push_back(std::make_pair(mir_config, output));
619+ });
620+
621+ outputs = new_outputs;
622+
623+ /*
624+ * This is not the true max simultaneous outputs, but it's unclear whether it's possible
625+ * to provide a max_simultaneous_outputs value that is useful to clients.
626+ */
627+ card.max_simultaneous_outputs = outputs.size();
628 }
629
630 // Compatibility means conf1 can be attained from conf2 (and vice versa)
631@@ -386,9 +185,9 @@
632 // to be allocated/destroyed, and hence should not be considered compatible.
633 bool mgm::compatible(mgm::RealKMSDisplayConfiguration const& conf1, mgm::RealKMSDisplayConfiguration const& conf2)
634 {
635- bool compatible{(conf1.drm_fd == conf2.drm_fd) &&
636- (conf1.card == conf2.card) &&
637- (conf1.outputs.size() == conf2.outputs.size())};
638+ bool compatible{
639+ (conf1.card == conf2.card) &&
640+ (conf1.outputs.size() == conf2.outputs.size())};
641
642 if (compatible)
643 {
644@@ -396,17 +195,17 @@
645
646 for (unsigned int i = 0; i < count; ++i)
647 {
648- compatible &= (conf1.outputs[i].power_mode == conf2.outputs[i].power_mode);
649+ compatible &= (conf1.outputs[i].first.power_mode == conf2.outputs[i].first.power_mode);
650 if (compatible)
651 {
652- auto clone = conf2.outputs[i];
653+ auto clone = conf2.outputs[i].first;
654
655 // ignore difference in orientation, scale factor, form factor, subpixel arrangement
656- clone.orientation = conf1.outputs[i].orientation;
657- clone.subpixel_arrangement = conf1.outputs[i].subpixel_arrangement;
658- clone.scale = conf1.outputs[i].scale;
659- clone.form_factor = conf1.outputs[i].form_factor;
660- compatible &= (conf1.outputs[i] == clone);
661+ clone.orientation = conf1.outputs[i].first.orientation;
662+ clone.subpixel_arrangement = conf1.outputs[i].first.subpixel_arrangement;
663+ clone.scale = conf1.outputs[i].first.scale;
664+ clone.form_factor = conf1.outputs[i].first.form_factor;
665+ compatible &= (conf1.outputs[i].first == clone);
666 }
667 else
668 break;
669
670=== modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.h'
671--- src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-01-18 02:29:37 +0000
672+++ src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-03-20 02:44:46 +0000
673@@ -30,13 +30,15 @@
674 {
675 namespace mesa
676 {
677+class KMSOutput;
678+class KMSOutputContainer;
679
680 class RealKMSDisplayConfiguration : public KMSDisplayConfiguration
681 {
682 friend bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);
683
684 public:
685- RealKMSDisplayConfiguration(int drm_fd);
686+ RealKMSDisplayConfiguration(std::shared_ptr<KMSOutputContainer> const& displays);
687 RealKMSDisplayConfiguration(RealKMSDisplayConfiguration const& conf);
688 RealKMSDisplayConfiguration& operator=(RealKMSDisplayConfiguration const& conf);
689
690@@ -45,18 +47,15 @@
691 void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override;
692 std::unique_ptr<DisplayConfiguration> clone() const override;
693
694- uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const override;
695+ std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const override;
696 size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const override;
697 void update() override;
698
699 private:
700- void add_or_update_output(kms::DRMModeResources const& resources, drmModeConnector const& connector);
701- std::vector<DisplayConfigurationOutput>::iterator find_output_with_id(DisplayConfigurationOutputId id);
702- std::vector<DisplayConfigurationOutput>::const_iterator find_output_with_id(DisplayConfigurationOutputId id) const;
703
704- int drm_fd;
705+ std::shared_ptr<KMSOutputContainer> displays;
706 DisplayConfigurationCard card;
707- std::vector<DisplayConfigurationOutput> outputs;
708+ std::vector<std::pair< DisplayConfigurationOutput, std::shared_ptr<KMSOutput>>> outputs;
709 };
710
711 bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);
712
713=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
714--- src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-13 08:12:52 +0000
715+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-20 02:44:46 +0000
716@@ -17,6 +17,7 @@
717 */
718
719 #include "real_kms_output.h"
720+#include "mir/graphics/display_configuration.h"
721 #include "page_flipper.h"
722 #include "kms-utils/kms_connector.h"
723 #include "mir/fatal.h"
724@@ -68,20 +69,27 @@
725
726 }
727
728-mgm::RealKMSOutput::RealKMSOutput(int drm_fd, uint32_t connector_id,
729- std::shared_ptr<PageFlipper> const& page_flipper)
730- : drm_fd{drm_fd}, connector_id{connector_id}, page_flipper{page_flipper},
731- connector(), mode_index{0}, current_crtc(), saved_crtc(),
732- using_saved_crtc{true}, has_cursor_{false},
733+mgm::RealKMSOutput::RealKMSOutput(
734+ int drm_fd,
735+ kms::DRMModeConnectorUPtr&& connector,
736+ std::shared_ptr<PageFlipper> const& page_flipper)
737+ : drm_fd{drm_fd},
738+ page_flipper{page_flipper},
739+ connector{std::move(connector)},
740+ mode_index{0},
741+ current_crtc(),
742+ saved_crtc(),
743+ using_saved_crtc{true},
744+ has_cursor_{false},
745 power_mode(mir_power_mode_on)
746 {
747 reset();
748
749 kms::DRMModeResources resources{drm_fd};
750
751- if (connector->encoder_id)
752+ if (this->connector->encoder_id)
753 {
754- auto encoder = resources.encoder(connector->encoder_id);
755+ auto encoder = resources.encoder(this->connector->encoder_id);
756 if (encoder->crtc_id)
757 {
758 saved_crtc = *resources.crtc(encoder->crtc_id);
759@@ -101,7 +109,7 @@
760 /* Update the connector to ensure we have the latest information */
761 try
762 {
763- connector = resources.connector(connector_id);
764+ connector = resources.connector(connector->connector_id);
765 }
766 catch (std::exception const& e)
767 {
768@@ -207,7 +215,10 @@
769 mgk::connector_name(connector).c_str());
770 return false;
771 }
772- return page_flipper->schedule_flip(current_crtc->crtc_id, fb.get_drm_fb_id(), connector_id);
773+ return page_flipper->schedule_flip(
774+ current_crtc->crtc_id,
775+ fb.get_drm_fb_id(),
776+ connector->connector_id);
777 }
778
779 void mgm::RealKMSOutput::wait_for_page_flip()
780@@ -321,8 +332,11 @@
781 if (power_mode != mode)
782 {
783 power_mode = mode;
784- drmModeConnectorSetProperty(drm_fd, connector_id,
785- dpms_enum_id, mode);
786+ drmModeConnectorSetProperty(
787+ drm_fd,
788+ connector->connector_id,
789+ dpms_enum_id,
790+ mode);
791 }
792 }
793
794@@ -357,6 +371,218 @@
795 // TODO: return bool in future? Then do what with it?
796 }
797
798+void mgm::RealKMSOutput::refresh_hardware_state()
799+{
800+ connector = kms::get_connector(drm_fd, connector->connector_id);
801+ current_crtc = nullptr;
802+
803+ if (connector->encoder_id)
804+ {
805+ auto encoder = kms::get_encoder(drm_fd, connector->encoder_id);
806+
807+ if (encoder->crtc_id)
808+ {
809+ current_crtc = kms::get_crtc(drm_fd, encoder->crtc_id);
810+ }
811+ }
812+}
813+
814+ namespace
815+{
816+
817+bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)
818+{
819+ return (info1.clock == info2.clock &&
820+ info1.hdisplay == info2.hdisplay &&
821+ info1.hsync_start == info2.hsync_start &&
822+ info1.hsync_end == info2.hsync_end &&
823+ info1.htotal == info2.htotal &&
824+ info1.hskew == info2.hskew &&
825+ info1.vdisplay == info2.vdisplay &&
826+ info1.vsync_start == info2.vsync_start &&
827+ info1.vsync_end == info2.vsync_end &&
828+ info1.vtotal == info2.vtotal);
829+}
830+
831+double calculate_vrefresh_hz(drmModeModeInfo const& mode)
832+{
833+ if (mode.htotal == 0 || mode.vtotal == 0)
834+ return 0.0;
835+
836+ /* mode.clock is in KHz */
837+ double hz = (mode.clock * 100000LL /
838+ ((long)mode.htotal * (long)mode.vtotal)
839+ ) / 100.0;
840+
841+ // Actually we don't need floating point at all for this...
842+ // TODO: Consider converting our structs to fixed-point ints
843+ return hz;
844+}
845+
846+mg::DisplayConfigurationOutputType
847+kms_connector_type_to_output_type(uint32_t connector_type)
848+{
849+ return static_cast<mg::DisplayConfigurationOutputType>(connector_type);
850+}
851+
852+MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel)
853+{
854+ switch (subpixel)
855+ {
856+ case DRM_MODE_SUBPIXEL_UNKNOWN:
857+ return mir_subpixel_arrangement_unknown;
858+ case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
859+ return mir_subpixel_arrangement_horizontal_rgb;
860+ case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
861+ return mir_subpixel_arrangement_horizontal_bgr;
862+ case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
863+ return mir_subpixel_arrangement_vertical_rgb;
864+ case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
865+ return mir_subpixel_arrangement_vertical_bgr;
866+ case DRM_MODE_SUBPIXEL_NONE:
867+ return mir_subpixel_arrangement_none;
868+ default:
869+ return mir_subpixel_arrangement_unknown;
870+ }
871+}
872+
873+std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id)
874+{
875+ std::vector<uint8_t> edid;
876+
877+ mgk::ObjectProperties connector_props{
878+ drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR};
879+
880+ if (connector_props.has_property("EDID"))
881+ {
882+ /*
883+ * We don't technically need the property information here, but query it
884+ * anyway so we can detect if our assumptions about DRM behaviour
885+ * become invalid.
886+ */
887+ auto property = mgk::DRMModePropertyUPtr{
888+ drmModeGetProperty(drm_fd, connector_props.id_for("EDID")),
889+ &drmModeFreeProperty};
890+
891+ if (!property)
892+ {
893+ mir::log_warning(
894+ "Failed to get EDID property for connector %u: %i (%s)",
895+ connector_id,
896+ errno,
897+ ::strerror(errno));
898+ return edid;
899+ }
900+
901+ if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB))
902+ {
903+ mir::log_warning(
904+ "EDID property on connector %u has unexpected type %u",
905+ connector_id,
906+ property->flags);
907+ return edid;
908+ }
909+
910+ // A property ID of 0 means invalid.
911+ if (connector_props["EDID"] == 0)
912+ {
913+ /*
914+ * Log a debug message only. This will trigger for broken monitors which
915+ * don't provide an EDID, which is not as unusual as you might think...
916+ */
917+ mir::log_debug("No EDID data available on connector %u", connector_id);
918+ return edid;
919+ }
920+
921+ auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]);
922+
923+ if (!blob)
924+ {
925+ mir::log_warning(
926+ "Failed to get EDID property blob for connector %u: %i (%s)",
927+ connector_id,
928+ errno,
929+ ::strerror(errno));
930+
931+ return edid;
932+ }
933+
934+ edid.reserve(blob->length);
935+ edid.insert(edid.begin(),
936+ reinterpret_cast<uint8_t*>(blob->data),
937+ reinterpret_cast<uint8_t*>(blob->data) + blob->length);
938+
939+ drmModeFreePropertyBlob(blob);
940+
941+ edid.shrink_to_fit();
942+ }
943+
944+ return edid;
945+}
946+}
947+
948+void mgm::RealKMSOutput::update_from_hardware_state(
949+ DisplayConfigurationOutput& output) const
950+{
951+ DisplayConfigurationOutputType const type{
952+ kms_connector_type_to_output_type(connector->connector_type)};
953+ geom::Size physical_size{connector->mmWidth, connector->mmHeight};
954+ bool connected{connector->connection == DRM_MODE_CONNECTED};
955+ uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max();
956+ uint32_t current_mode_index{invalid_mode_index};
957+ uint32_t preferred_mode_index{invalid_mode_index};
958+ std::vector<DisplayConfigurationMode> modes;
959+ std::vector<MirPixelFormat> formats{mir_pixel_format_argb_8888,
960+ mir_pixel_format_xrgb_8888};
961+
962+ std::vector<uint8_t> edid;
963+ if (connected) {
964+ /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID
965+ * when there is no monitor connected!
966+ */
967+ edid = edid_for_connector(drm_fd, connector->connector_id);
968+ }
969+
970+ drmModeModeInfo current_mode_info = drmModeModeInfo();
971+ GammaCurves gamma;
972+
973+ /* Get information about the current mode */
974+ if (current_crtc) {
975+ current_mode_info = current_crtc->mode;
976+
977+ if (current_crtc->gamma_size > 0)
978+ gamma = mg::LinearGammaLUTs(current_crtc->gamma_size);
979+ }
980+
981+ /* Add all the available modes and find the current and preferred one */
982+ for (int m = 0; m < connector->count_modes; m++) {
983+ drmModeModeInfo &mode_info = connector->modes[m];
984+
985+ geom::Size size{mode_info.hdisplay, mode_info.vdisplay};
986+
987+ double vrefresh_hz = calculate_vrefresh_hz(mode_info);
988+
989+ modes.push_back({size, vrefresh_hz});
990+
991+ if (kms_modes_are_equal(mode_info, current_mode_info))
992+ current_mode_index = m;
993+
994+ if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED)
995+ preferred_mode_index = m;
996+ }
997+
998+ output.type = type;
999+ output.modes = modes;
1000+ output.preferred_mode_index = preferred_mode_index;
1001+ output.physical_size_mm = physical_size;
1002+ output.connected = connected;
1003+ output.current_format = mir_pixel_format_xrgb_8888;
1004+ output.current_mode_index = current_mode_index;
1005+ output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector->subpixel);
1006+ output.gamma = gamma;
1007+ output.edid = edid;
1008+}
1009+
1010 mgm::FBHandle* mgm::RealKMSOutput::fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const
1011 {
1012 if (!bo)
1013
1014=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
1015--- src/platforms/mesa/server/kms/real_kms_output.h 2017-03-13 08:12:52 +0000
1016+++ src/platforms/mesa/server/kms/real_kms_output.h 2017-03-20 02:44:46 +0000
1017@@ -38,8 +38,10 @@
1018 class RealKMSOutput : public KMSOutput
1019 {
1020 public:
1021- RealKMSOutput(int drm_fd, uint32_t connector_id,
1022- std::shared_ptr<PageFlipper> const& page_flipper);
1023+ RealKMSOutput(
1024+ int drm_fd,
1025+ kms::DRMModeConnectorUPtr&& connector,
1026+ std::shared_ptr<PageFlipper> const& page_flipper);
1027 ~RealKMSOutput();
1028
1029 void reset() override;
1030@@ -62,14 +64,15 @@
1031
1032 Frame last_frame() const override;
1033
1034+ void refresh_hardware_state() override;
1035+ void update_from_hardware_state(DisplayConfigurationOutput& output) const override;
1036+
1037 FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const override;
1038-
1039 private:
1040 bool ensure_crtc();
1041 void restore_saved_crtc();
1042
1043 int const drm_fd;
1044- uint32_t const connector_id;
1045 std::shared_ptr<PageFlipper> const page_flipper;
1046
1047 kms::DRMModeConnectorUPtr connector;
1048
1049=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.cpp'
1050--- src/platforms/mesa/server/kms/real_kms_output_container.cpp 2016-01-29 08:18:22 +0000
1051+++ src/platforms/mesa/server/kms/real_kms_output_container.cpp 2017-03-20 02:44:46 +0000
1052@@ -18,6 +18,7 @@
1053
1054 #include "real_kms_output_container.h"
1055 #include "real_kms_output.h"
1056+#include "kms-utils/drm_mode_resources.h"
1057
1058 namespace mgm = mir::graphics::mesa;
1059
1060@@ -28,27 +29,34 @@
1061 {
1062 }
1063
1064-std::shared_ptr<mgm::KMSOutput>
1065-mgm::RealKMSOutputContainer::get_kms_output_for(uint32_t connector_id)
1066+void mgm::RealKMSOutputContainer::for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const
1067 {
1068- std::shared_ptr<KMSOutput> output;
1069-
1070- auto output_iter = outputs.find(connector_id);
1071- if (output_iter == outputs.end())
1072- {
1073- output = std::make_shared<RealKMSOutput>(drm_fd, connector_id, page_flipper);
1074- outputs[connector_id] = output;
1075- }
1076- else
1077- {
1078- output = output_iter->second;
1079- }
1080-
1081- return output;
1082+ for(auto& output: outputs)
1083+ functor(output.second);
1084 }
1085
1086-void mgm::RealKMSOutputContainer::for_each_output(std::function<void(KMSOutput&)> functor) const
1087+void mgm::RealKMSOutputContainer::update_from_hardware_state()
1088 {
1089- for(auto& pair: outputs)
1090- functor(*pair.second);
1091+ kms::DRMModeResources resources{drm_fd};
1092+
1093+ decltype(outputs) new_outputs;
1094+
1095+ for (auto&& connector : resources.connectors())
1096+ {
1097+ if (outputs.count(connector->connector_id))
1098+ {
1099+ new_outputs[connector->connector_id] = std::move(outputs[connector->connector_id]);
1100+ new_outputs[connector->connector_id]->refresh_hardware_state();
1101+ }
1102+ else
1103+ {
1104+ auto const id = connector->connector_id;
1105+ new_outputs[id] = std::make_shared<RealKMSOutput>(
1106+ drm_fd,
1107+ std::move(connector),
1108+ page_flipper);
1109+ }
1110+ }
1111+
1112+ outputs = new_outputs;
1113 }
1114
1115=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.h'
1116--- src/platforms/mesa/server/kms/real_kms_output_container.h 2016-01-29 08:18:22 +0000
1117+++ src/platforms/mesa/server/kms/real_kms_output_container.h 2017-03-20 02:44:46 +0000
1118@@ -36,9 +36,9 @@
1119 public:
1120 RealKMSOutputContainer(int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper);
1121
1122- std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id);
1123- void for_each_output(std::function<void(KMSOutput&)> functor) const;
1124+ void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const override;
1125
1126+ void update_from_hardware_state() override;
1127 private:
1128 int const drm_fd;
1129 std::unordered_map<uint32_t,std::shared_ptr<KMSOutput>> outputs;
1130
1131=== modified file 'tests/acceptance-tests/test_client_input.cpp'
1132--- tests/acceptance-tests/test_client_input.cpp 2017-03-14 02:26:28 +0000
1133+++ tests/acceptance-tests/test_client_input.cpp 2017-03-20 02:44:46 +0000
1134@@ -1578,7 +1578,7 @@
1135
1136 mt::Signal touchscreen_ready;
1137 fake_touch_screen->on_new_configuration_do(
1138- [&touchscreen_ready, second_output](mi::InputDevice const& dev)
1139+ [&touchscreen_ready](mi::InputDevice const& dev)
1140 {
1141 auto ts = dev.get_touchscreen_settings();
1142 if (ts.is_set() && ts.value().output_id == second_output)
1143@@ -1650,7 +1650,7 @@
1144
1145 mt::Signal touchscreen_ready;
1146 fake_touch_screen->on_new_configuration_do(
1147- [&touchscreen_ready, second_output](mi::InputDevice const& dev)
1148+ [&touchscreen_ready](mi::InputDevice const& dev)
1149 {
1150 auto ts = dev.get_touchscreen_settings();
1151 if (ts.is_set()
1152
1153=== modified file 'tests/unit-tests/platforms/mesa/kms/mock_kms_output.h'
1154--- tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-13 08:12:52 +0000
1155+++ tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-20 02:44:46 +0000
1156@@ -68,6 +68,9 @@
1157 MOCK_METHOD1(set_power_mode, void(MirPowerMode));
1158 MOCK_METHOD1(set_gamma, void(mir::graphics::GammaCurves const&));
1159
1160+ MOCK_METHOD0(refresh_hardware_state, void());
1161+ MOCK_CONST_METHOD1(update_from_hardware_state, void(graphics::DisplayConfigurationOutput&));
1162+
1163 MOCK_CONST_METHOD3(fb_for, graphics::mesa::FBHandle*(gbm_bo*, uint32_t, uint32_t));
1164 };
1165
1166
1167=== modified file 'tests/unit-tests/platforms/mesa/kms/test_cursor.cpp'
1168--- tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-02-15 07:38:33 +0000
1169+++ tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-03-20 02:44:46 +0000
1170@@ -56,14 +56,14 @@
1171 {
1172 StubKMSOutputContainer()
1173 : outputs{
1174- {10, std::make_shared<testing::NiceMock<MockKMSOutput>>()},
1175- {11, std::make_shared<testing::NiceMock<MockKMSOutput>>()},
1176- {12, std::make_shared<testing::NiceMock<MockKMSOutput>>()}}
1177+ std::make_shared<testing::NiceMock<MockKMSOutput>>(),
1178+ std::make_shared<testing::NiceMock<MockKMSOutput>>(),
1179+ std::make_shared<testing::NiceMock<MockKMSOutput>>()}
1180 {
1181 // These need to be established before Cursor construction:
1182- for (auto& entry : outputs)
1183+ for (auto& output : outputs)
1184 {
1185- auto& out = *entry.second;
1186+ auto& out = *output;
1187 ON_CALL(out, has_cursor())
1188 .WillByDefault(Return(true));
1189 ON_CALL(out, set_cursor(_))
1190@@ -73,33 +73,33 @@
1191 }
1192 }
1193
1194- std::shared_ptr<mgm::KMSOutput> get_kms_output_for(uint32_t connector_id)
1195- {
1196- return outputs[connector_id];
1197- }
1198-
1199- void for_each_output(std::function<void(mgm::KMSOutput&)> functor) const
1200- {
1201- for (auto const& pair : outputs)
1202- functor(*pair.second);
1203+ void for_each_output(std::function<void(std::shared_ptr<mgm::KMSOutput> const&)> functor) const
1204+ {
1205+ for (auto const& output : outputs)
1206+ functor(output);
1207 }
1208
1209 void verify_and_clear_expectations()
1210 {
1211- for (auto const& pair : outputs)
1212- ::testing::Mock::VerifyAndClearExpectations(pair.second.get());
1213- }
1214-
1215- std::unordered_map<uint32_t,std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs;
1216+ for (auto const& output : outputs)
1217+ ::testing::Mock::VerifyAndClearExpectations(output.get());
1218+ }
1219+
1220+ void update_from_hardware_state()
1221+ {
1222+ }
1223+
1224+ std::vector<std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs;
1225 };
1226
1227 struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration
1228 {
1229- StubKMSDisplayConfiguration()
1230+ StubKMSDisplayConfiguration(mgm::KMSOutputContainer& container)
1231 : mgm::KMSDisplayConfiguration(),
1232+ container{container},
1233 stub_config{
1234 {{
1235- mg::DisplayConfigurationOutputId{10},
1236+ mg::DisplayConfigurationOutputId{0},
1237 mg::DisplayConfigurationCardId{},
1238 mg::DisplayConfigurationOutputType::vga,
1239 {},
1240@@ -124,7 +124,7 @@
1241 {}
1242 },
1243 {
1244- mg::DisplayConfigurationOutputId{11},
1245+ mg::DisplayConfigurationOutputId{1},
1246 mg::DisplayConfigurationCardId{},
1247 mg::DisplayConfigurationOutputType::vga,
1248 {},
1249@@ -149,7 +149,7 @@
1250 {}
1251 },
1252 {
1253- mg::DisplayConfigurationOutputId{12},
1254+ mg::DisplayConfigurationOutputId{2},
1255 mg::DisplayConfigurationCardId{},
1256 mg::DisplayConfigurationOutputType::vga,
1257 {},
1258@@ -174,6 +174,7 @@
1259 {}
1260 }}}
1261 {
1262+ update();
1263 }
1264
1265 void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override
1266@@ -201,9 +202,9 @@
1267 return stub_config.valid();
1268 }
1269
1270- uint32_t get_kms_connector_id(mg::DisplayConfigurationOutputId id) const override
1271+ std::shared_ptr<mgm::KMSOutput> get_output_for(mg::DisplayConfigurationOutputId id) const override
1272 {
1273- return id.as_value();
1274+ return outputs[id.as_value()];
1275 }
1276
1277 size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override
1278@@ -213,6 +214,11 @@
1279
1280 void update() override
1281 {
1282+ container.for_each_output(
1283+ [this](auto const& output)
1284+ {
1285+ outputs.push_back(output);
1286+ });
1287 }
1288
1289 void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation)
1290@@ -225,11 +231,18 @@
1291 });
1292 }
1293
1294+ mgm::KMSOutputContainer& container;
1295 mtd::StubDisplayConfig stub_config;
1296+ std::vector<std::shared_ptr<mgm::KMSOutput>> outputs;
1297 };
1298
1299 struct StubCurrentConfiguration : public mgm::CurrentConfiguration
1300 {
1301+ StubCurrentConfiguration(mgm::KMSOutputContainer& container)
1302+ : conf(container)
1303+ {
1304+ }
1305+
1306 void with_current_configuration_do(
1307 std::function<void(mgm::KMSDisplayConfiguration const&)> const& exec)
1308 {
1309@@ -304,9 +317,9 @@
1310 }
1311
1312 testing::NiceMock<mtd::MockDRM> mock_drm;
1313- StubCurrentConfiguration current_configuration;
1314 StubCursorImage stub_image;
1315 StubKMSOutputContainer output_container;
1316+ StubCurrentConfiguration current_configuration{output_container};
1317 mgm::Cursor cursor;
1318 };
1319
1320@@ -336,7 +349,7 @@
1321 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE));
1322
1323 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1324- std::make_shared<StubCurrentConfiguration>()};
1325+ std::make_shared<StubCurrentConfiguration>(output_container)};
1326 }
1327
1328 TEST_F(MesaCursorTest, queries_received_cursor_size)
1329@@ -347,7 +360,7 @@
1330 EXPECT_CALL(mock_gbm, gbm_bo_get_height(_));
1331
1332 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1333- std::make_shared<StubCurrentConfiguration>()};
1334+ std::make_shared<StubCurrentConfiguration>(output_container)};
1335 }
1336
1337 TEST_F(MesaCursorTest, respects_drm_cap_cursor)
1338@@ -362,7 +375,7 @@
1339 EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _));
1340
1341 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1342- std::make_shared<StubCurrentConfiguration>()};
1343+ std::make_shared<StubCurrentConfiguration>(output_container)};
1344 }
1345
1346 TEST_F(MesaCursorTest, can_force_64x64_cursor)
1347@@ -379,7 +392,7 @@
1348 EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _));
1349
1350 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1351- std::make_shared<StubCurrentConfiguration>()};
1352+ std::make_shared<StubCurrentConfiguration>(output_container)};
1353 }
1354
1355 TEST_F(MesaCursorTest, show_cursor_writes_to_bo)
1356@@ -443,7 +456,7 @@
1357 EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes));
1358
1359 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1360- std::make_shared<StubCurrentConfiguration>()};
1361+ std::make_shared<StubCurrentConfiguration>(output_container)};
1362 cursor_tmp.show(SinglePixelCursorImage());
1363 }
1364
1365@@ -470,17 +483,17 @@
1366 {
1367 using namespace testing;
1368
1369- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1370- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1371- EXPECT_CALL(*output_container.outputs[12], clear_cursor());
1372+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1373+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1374+ EXPECT_CALL(*output_container.outputs[2], clear_cursor());
1375
1376 /* No checking of existing cursor state */
1377- EXPECT_CALL(*output_container.outputs[10], has_cursor()).Times(0);
1378- EXPECT_CALL(*output_container.outputs[11], has_cursor()).Times(0);
1379- EXPECT_CALL(*output_container.outputs[12], has_cursor()).Times(0);
1380+ EXPECT_CALL(*output_container.outputs[0], has_cursor()).Times(0);
1381+ EXPECT_CALL(*output_container.outputs[1], has_cursor()).Times(0);
1382+ EXPECT_CALL(*output_container.outputs[2], has_cursor()).Times(0);
1383
1384 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
1385- std::make_shared<StubCurrentConfiguration>()};
1386+ std::make_shared<StubCurrentConfiguration>(output_container)};
1387
1388 output_container.verify_and_clear_expectations();
1389 }
1390@@ -489,16 +502,16 @@
1391 {
1392 using namespace testing;
1393
1394- ON_CALL(*output_container.outputs[10], clear_cursor())
1395- .WillByDefault(Return(false));
1396- ON_CALL(*output_container.outputs[10], set_cursor(_))
1397- .WillByDefault(Return(false));
1398- ON_CALL(*output_container.outputs[10], has_cursor())
1399+ ON_CALL(*output_container.outputs[0], clear_cursor())
1400+ .WillByDefault(Return(false));
1401+ ON_CALL(*output_container.outputs[0], set_cursor(_))
1402+ .WillByDefault(Return(false));
1403+ ON_CALL(*output_container.outputs[0], has_cursor())
1404 .WillByDefault(Return(false));
1405
1406 EXPECT_THROW(
1407 mgm::Cursor cursor_tmp(mock_gbm.fake_gbm.device, output_container,
1408- std::make_shared<StubCurrentConfiguration>());
1409+ std::make_shared<StubCurrentConfiguration>(output_container));
1410 , std::runtime_error);
1411 }
1412
1413@@ -508,14 +521,14 @@
1414
1415 cursor.show(stub_image);
1416
1417- EXPECT_CALL(*output_container.outputs[10], has_cursor())
1418+ EXPECT_CALL(*output_container.outputs[0], has_cursor())
1419 .WillOnce(Return(false))
1420 .WillOnce(Return(true));
1421- EXPECT_CALL(*output_container.outputs[10], set_cursor(_));
1422+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
1423
1424- EXPECT_CALL(*output_container.outputs[11], has_cursor())
1425+ EXPECT_CALL(*output_container.outputs[1], has_cursor())
1426 .WillOnce(Return(true));
1427- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1428+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1429
1430 cursor.move_to({10, 10});
1431
1432@@ -528,14 +541,14 @@
1433
1434 cursor.show(stub_image);
1435
1436- EXPECT_CALL(*output_container.outputs[10], has_cursor())
1437+ EXPECT_CALL(*output_container.outputs[0], has_cursor())
1438 .WillOnce(Return(true));
1439- EXPECT_CALL(*output_container.outputs[10], set_cursor(_))
1440+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_))
1441 .Times(0);
1442
1443- EXPECT_CALL(*output_container.outputs[11], has_cursor())
1444+ EXPECT_CALL(*output_container.outputs[1], has_cursor())
1445 .WillOnce(Return(false));
1446- EXPECT_CALL(*output_container.outputs[11], clear_cursor())
1447+ EXPECT_CALL(*output_container.outputs[1], clear_cursor())
1448 .Times(0);
1449
1450 cursor.move_to({10, 10});
1451@@ -549,32 +562,32 @@
1452
1453 cursor.show(stub_image);
1454
1455- EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{10,10}));
1456- EXPECT_CALL(*output_container.outputs[11], move_cursor(_))
1457+ EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{10,10}));
1458+ EXPECT_CALL(*output_container.outputs[1], move_cursor(_))
1459 .Times(0);
1460
1461 cursor.move_to({10, 10});
1462
1463 output_container.verify_and_clear_expectations();
1464
1465- EXPECT_CALL(*output_container.outputs[10], move_cursor(_))
1466+ EXPECT_CALL(*output_container.outputs[0], move_cursor(_))
1467 .Times(0);
1468- EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,100}));
1469+ EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,100}));
1470
1471 cursor.move_to({150, 150});
1472
1473 output_container.verify_and_clear_expectations();
1474
1475- EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));
1476- EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));
1477+ EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
1478+ EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
1479
1480 cursor.move_to({150, 75});
1481
1482 output_container.verify_and_clear_expectations();
1483
1484- EXPECT_CALL(*output_container.outputs[10], move_cursor(_))
1485+ EXPECT_CALL(*output_container.outputs[0], move_cursor(_))
1486 .Times(0);
1487- EXPECT_CALL(*output_container.outputs[11], move_cursor(_))
1488+ EXPECT_CALL(*output_container.outputs[1], move_cursor(_))
1489 .Times(0);
1490
1491 cursor.move_to({-1, -1});
1492@@ -588,10 +601,10 @@
1493
1494 cursor.show(stub_image);
1495
1496- current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_left);
1497+ current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_left);
1498
1499- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{112,100}));
1500- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{150,96}));
1501+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{112,100}));
1502+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{150,96}));
1503
1504 cursor.move_to({766, 112});
1505 cursor.move_to({770, 150});
1506@@ -606,11 +619,11 @@
1507
1508 cursor.show(stub_image);
1509
1510- current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_right);
1511-
1512-
1513- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{688,100}));
1514- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{650,104}));
1515+ current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_right);
1516+
1517+
1518+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{688,100}));
1519+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{650,104}));
1520
1521 cursor.move_to({766, 112});
1522 cursor.move_to({770, 150});
1523@@ -624,10 +637,10 @@
1524
1525 cursor.show(stub_image);
1526
1527- current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_inverted);
1528+ current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_inverted);
1529
1530- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{700,88}));
1531- EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{696,50}));
1532+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{700,88}));
1533+ EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{696,50}));
1534
1535 cursor.move_to({766, 112});
1536 cursor.move_to({770, 150});
1537@@ -639,9 +652,9 @@
1538 {
1539 using namespace testing;
1540
1541- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1542- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1543- EXPECT_CALL(*output_container.outputs[12], clear_cursor());
1544+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1545+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1546+ EXPECT_CALL(*output_container.outputs[2], clear_cursor());
1547
1548 cursor.hide();
1549
1550@@ -652,12 +665,12 @@
1551 {
1552 using namespace testing;
1553
1554- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1555- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1556- EXPECT_CALL(*output_container.outputs[12], clear_cursor());
1557- EXPECT_CALL(*output_container.outputs[10], move_cursor(_)).Times(0);
1558- EXPECT_CALL(*output_container.outputs[11], move_cursor(_)).Times(0);
1559- EXPECT_CALL(*output_container.outputs[12], move_cursor(_)).Times(0);
1560+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1561+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1562+ EXPECT_CALL(*output_container.outputs[2], clear_cursor());
1563+ EXPECT_CALL(*output_container.outputs[0], move_cursor(_)).Times(0);
1564+ EXPECT_CALL(*output_container.outputs[1], move_cursor(_)).Times(0);
1565+ EXPECT_CALL(*output_container.outputs[2], move_cursor(_)).Times(0);
1566
1567 cursor.hide();
1568 cursor.move_to({17, 29});
1569@@ -669,9 +682,9 @@
1570 {
1571 using namespace testing;
1572
1573- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1574- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1575- EXPECT_CALL(*output_container.outputs[12], clear_cursor());
1576+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1577+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1578+ EXPECT_CALL(*output_container.outputs[2], clear_cursor());
1579 }
1580
1581 TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume)
1582@@ -680,23 +693,23 @@
1583
1584 cursor.show(stub_image);
1585
1586- EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));
1587- EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));
1588- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1589- EXPECT_CALL(*output_container.outputs[11], clear_cursor());
1590- EXPECT_CALL(*output_container.outputs[12], has_cursor())
1591+ EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
1592+ EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
1593+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1594+ EXPECT_CALL(*output_container.outputs[1], clear_cursor());
1595+ EXPECT_CALL(*output_container.outputs[2], has_cursor())
1596 .WillRepeatedly(Return(false));
1597- EXPECT_CALL(*output_container.outputs[12], clear_cursor());
1598+ EXPECT_CALL(*output_container.outputs[2], clear_cursor());
1599
1600 cursor.move_to({150, 75});
1601 cursor.suspend();
1602
1603 output_container.verify_and_clear_expectations();
1604
1605- EXPECT_CALL(*output_container.outputs[10], set_cursor(_));
1606- EXPECT_CALL(*output_container.outputs[11], set_cursor(_));
1607- EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));
1608- EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));
1609+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
1610+ EXPECT_CALL(*output_container.outputs[1], set_cursor(_));
1611+ EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
1612+ EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
1613
1614 cursor.resume();
1615 output_container.verify_and_clear_expectations();
1616@@ -706,18 +719,18 @@
1617 {
1618 using namespace testing;
1619
1620- EXPECT_CALL(*output_container.outputs[10], clear_cursor()).Times(AnyNumber());
1621- EXPECT_CALL(*output_container.outputs[11], clear_cursor()).Times(AnyNumber());
1622- EXPECT_CALL(*output_container.outputs[12], clear_cursor()).Times(AnyNumber());
1623+ EXPECT_CALL(*output_container.outputs[0], clear_cursor()).Times(AnyNumber());
1624+ EXPECT_CALL(*output_container.outputs[1], clear_cursor()).Times(AnyNumber());
1625+ EXPECT_CALL(*output_container.outputs[2], clear_cursor()).Times(AnyNumber());
1626
1627 cursor.hide();
1628 cursor.suspend();
1629
1630 output_container.verify_and_clear_expectations();
1631
1632- EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(0);
1633- EXPECT_CALL(*output_container.outputs[11], set_cursor(_)).Times(0);
1634- EXPECT_CALL(*output_container.outputs[12], set_cursor(_)).Times(0);
1635+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(0);
1636+ EXPECT_CALL(*output_container.outputs[1], set_cursor(_)).Times(0);
1637+ EXPECT_CALL(*output_container.outputs[2], set_cursor(_)).Times(0);
1638
1639 cursor.resume();
1640 output_container.verify_and_clear_expectations();
1641@@ -725,24 +738,24 @@
1642
1643 TEST_F(MesaCursorTest, show_with_param_places_cursor_on_output)
1644 {
1645- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1646+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1647 cursor.hide();
1648
1649 output_container.verify_and_clear_expectations();
1650
1651- EXPECT_CALL(*output_container.outputs[10], set_cursor(_));
1652+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
1653 cursor.show(stub_image);
1654 }
1655
1656 TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output)
1657 {
1658 using namespace testing;
1659- EXPECT_CALL(*output_container.outputs[10], clear_cursor());
1660+ EXPECT_CALL(*output_container.outputs[0], clear_cursor());
1661
1662 cursor.hide();
1663 output_container.verify_and_clear_expectations();
1664
1665- EXPECT_CALL(*output_container.outputs[10], set_cursor(_));
1666+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
1667 cursor.show();
1668 }
1669
1670@@ -772,15 +785,15 @@
1671
1672
1673 EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber());
1674- EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(AnyNumber());
1675+ EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(AnyNumber());
1676
1677 // When we set the image with the hotspot, first we should see the cursor move from its initial
1678 // location, to account for the displacement. Further movement should be offset by the hotspot.
1679 {
1680 InSequence seq;
1681- EXPECT_CALL(*output_container.outputs[10], move_cursor(initial_buffer_location));
1682- EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_1));
1683- EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_2));
1684+ EXPECT_CALL(*output_container.outputs[0], move_cursor(initial_buffer_location));
1685+ EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_1));
1686+ EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_2));
1687 }
1688
1689 cursor.show(HotspotCursor());
1690
1691=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp'
1692--- tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-14 02:26:28 +0000
1693+++ tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-20 02:44:46 +0000
1694@@ -162,6 +162,41 @@
1695 mtf::UdevEnvironment fake_devices;
1696 };
1697
1698+MATCHER_P(Unique, scratch_vector, "")
1699+{
1700+ if (std::any_of(
1701+ scratch_vector.begin(),
1702+ scratch_vector.end(),
1703+ [&arg](auto const& candidate) { return arg == candidate; }))
1704+ {
1705+ return false;
1706+ }
1707+ const_cast<typename std::remove_const<scratch_vector_type>::type&>(scratch_vector).push_back(arg);
1708+ return true;
1709+}
1710+
1711+MATCHER_P(DisplayConfigsAreEquivalent, expected_configs, "")
1712+{
1713+ using namespace testing;
1714+
1715+ std::vector<mg::DisplayConfigurationOutput> outputs;
1716+ std::vector<mg::DisplayConfigurationOutputId> output_ids;
1717+ arg->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1718+ {
1719+ outputs.push_back(output);
1720+ output_ids.push_back(output.id);
1721+ outputs.back().id = mg::DisplayConfigurationOutputId{0};
1722+ });
1723+
1724+
1725+ Matcher<decltype(outputs)> config_matcher = UnorderedElementsAreArray(expected_configs);
1726+ std::vector<mg::DisplayConfigurationOutputId> scratch_space;
1727+ Matcher<decltype(output_ids)> ids_are_unique = Each(Unique(scratch_space));
1728+
1729+ return
1730+ config_matcher.MatchAndExplain(outputs, result_listener) &&
1731+ ids_are_unique.MatchAndExplain(output_ids, result_listener);
1732+}
1733 }
1734
1735 TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly)
1736@@ -209,7 +244,7 @@
1737 std::vector<mg::DisplayConfigurationOutput> const expected_outputs =
1738 {
1739 {
1740- mg::DisplayConfigurationOutputId{connector0_id},
1741+ mg::DisplayConfigurationOutputId{0},
1742 mg::DisplayConfigurationCardId{0},
1743 mg::DisplayConfigurationOutputType::hdmia,
1744 {},
1745@@ -231,7 +266,7 @@
1746 {}
1747 },
1748 {
1749- mg::DisplayConfigurationOutputId{connector1_id},
1750+ mg::DisplayConfigurationOutputId{0},
1751 mg::DisplayConfigurationCardId{0},
1752 mg::DisplayConfigurationOutputType::unknown,
1753 {},
1754@@ -253,7 +288,7 @@
1755 {}
1756 },
1757 {
1758- mg::DisplayConfigurationOutputId{connector2_id},
1759+ mg::DisplayConfigurationOutputId{0},
1760 mg::DisplayConfigurationCardId{0},
1761 mg::DisplayConfigurationOutputType::edp,
1762 {},
1763@@ -281,16 +316,7 @@
1764
1765 auto conf = display->configuration();
1766
1767- size_t output_count{0};
1768-
1769- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1770- {
1771- ASSERT_LT(output_count, expected_outputs.size());
1772- EXPECT_EQ(expected_outputs[output_count], output) << "output_count: " << output_count;
1773- ++output_count;
1774- });
1775-
1776- EXPECT_EQ(expected_outputs.size(), output_count);
1777+ EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs));
1778 }
1779
1780 TEST_F(MesaDisplayConfigurationTest, reads_subpixel_information_correctly)
1781@@ -442,88 +468,6 @@
1782 }
1783 }
1784
1785-TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_returns_correct_id)
1786-{
1787- uint32_t const crtc0_id{10};
1788- uint32_t const encoder0_id{20};
1789- uint32_t const possible_crtcs_mask_empty{0};
1790- std::vector<uint32_t> const connector_ids{30, 31};
1791- std::vector<uint32_t> encoder_ids{20};
1792-
1793- /* Set up DRM resources */
1794- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
1795-
1796- resources.reset();
1797-
1798- resources.add_crtc(crtc0_id, modes0[1]);
1799- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
1800- for (auto id : connector_ids)
1801- {
1802- resources.add_connector(id, DRM_MODE_CONNECTOR_DVID,
1803- DRM_MODE_CONNECTED, encoder0_id,
1804- modes0, encoder_ids,
1805- geom::Size());
1806- }
1807-
1808- resources.prepare();
1809-
1810- /* Test body */
1811- auto display = create_display(create_platform());
1812-
1813- std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration();
1814- auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf);
1815-
1816- size_t output_count{0};
1817-
1818- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1819- {
1820- ASSERT_LT(output_count, connector_ids.size());
1821-
1822- EXPECT_EQ(connector_ids[output_count],
1823- kms_conf->get_kms_connector_id(output.id));
1824- ++output_count;
1825- });
1826-}
1827-
1828-TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_throws_on_invalid_id)
1829-{
1830- uint32_t const crtc0_id{10};
1831- uint32_t const encoder0_id{20};
1832- uint32_t const possible_crtcs_mask_empty{0};
1833- std::vector<uint32_t> const connector_ids{30, 31};
1834- std::vector<uint32_t> encoder_ids{20};
1835-
1836- /* Set up DRM resources */
1837- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
1838-
1839- resources.reset();
1840-
1841- resources.add_crtc(crtc0_id, modes0[1]);
1842- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
1843- for (auto id : connector_ids)
1844- {
1845- resources.add_connector(id, DRM_MODE_CONNECTOR_VGA,
1846- DRM_MODE_CONNECTED, encoder0_id,
1847- modes0, encoder_ids,
1848- geom::Size());
1849- }
1850-
1851- resources.prepare();
1852-
1853- /* Test body */
1854- auto display = create_display(create_platform());
1855-
1856- std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration();
1857- auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf);
1858-
1859- EXPECT_THROW({
1860- kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{29});
1861- }, std::runtime_error);
1862- EXPECT_THROW({
1863- kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{32});
1864- }, std::runtime_error);
1865-}
1866-
1867 TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration)
1868 {
1869 using namespace ::testing;
1870@@ -545,7 +489,7 @@
1871 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =
1872 {
1873 {
1874- mg::DisplayConfigurationOutputId(connector_ids[0]),
1875+ mg::DisplayConfigurationOutputId{0},
1876 mg::DisplayConfigurationCardId{0},
1877 mg::DisplayConfigurationOutputType::composite,
1878 {},
1879@@ -567,7 +511,7 @@
1880 {}
1881 },
1882 {
1883- mg::DisplayConfigurationOutputId(connector_ids[1]),
1884+ mg::DisplayConfigurationOutputId{0},
1885 mg::DisplayConfigurationCardId{0},
1886 mg::DisplayConfigurationOutputType::vga,
1887 {},
1888@@ -593,7 +537,7 @@
1889 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =
1890 {
1891 {
1892- mg::DisplayConfigurationOutputId(connector_ids[0]),
1893+ mg::DisplayConfigurationOutputId{0},
1894 mg::DisplayConfigurationCardId{0},
1895 mg::DisplayConfigurationOutputType::composite,
1896 {},
1897@@ -615,7 +559,7 @@
1898 {}
1899 },
1900 {
1901- mg::DisplayConfigurationOutputId(connector_ids[1]),
1902+ mg::DisplayConfigurationOutputId{0},
1903 mg::DisplayConfigurationCardId{0},
1904 mg::DisplayConfigurationOutputType::vga,
1905 {},
1906@@ -664,16 +608,7 @@
1907
1908 auto conf = display->configuration();
1909
1910- size_t output_count{0};
1911-
1912- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1913- {
1914- ASSERT_LT(output_count, expected_outputs_before.size());
1915- EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count;
1916- ++output_count;
1917- });
1918-
1919- EXPECT_EQ(expected_outputs_before.size(), output_count);
1920+ EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_before));
1921
1922 /* Reset DRM resources and check again */
1923 resources.reset();
1924@@ -703,16 +638,7 @@
1925
1926 conf = display->configuration();
1927
1928- output_count = 0;
1929-
1930- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1931- {
1932- ASSERT_LT(output_count, expected_outputs_after.size());
1933- EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count;
1934- ++output_count;
1935- });
1936-
1937- EXPECT_EQ(expected_outputs_after.size(), output_count);
1938+ EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_after));
1939 }
1940
1941 TEST_F(MesaDisplayConfigurationTest, new_monitor_matches_hardware_state)
1942@@ -727,12 +653,11 @@
1943 geom::Size const connector_physical_sizes_mm_after{512, 642};
1944 std::vector<uint32_t> possible_encoder_ids_empty;
1945 uint32_t const possible_crtcs_mask_empty{0};
1946- int const noutputs = 1;
1947
1948- mg::DisplayConfigurationOutput const expected_outputs_before[noutputs] =
1949+ std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =
1950 {
1951 {
1952- mg::DisplayConfigurationOutputId(connector_ids[0]),
1953+ mg::DisplayConfigurationOutputId{0},
1954 mg::DisplayConfigurationCardId{0},
1955 mg::DisplayConfigurationOutputType::composite,
1956 {},
1957@@ -755,10 +680,10 @@
1958 },
1959 };
1960
1961- mg::DisplayConfigurationOutput const expected_outputs_after[noutputs] =
1962+ std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =
1963 {
1964 {
1965- mg::DisplayConfigurationOutputId(connector_ids[0]),
1966+ mg::DisplayConfigurationOutputId{0},
1967 mg::DisplayConfigurationCardId{0},
1968 mg::DisplayConfigurationOutputType::composite,
1969 {},
1970@@ -797,14 +722,7 @@
1971
1972 auto display = create_display(create_platform());
1973 auto conf = display->configuration();
1974- int output_count = 0;
1975- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1976- {
1977- ASSERT_LT(output_count, noutputs);
1978- EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count;
1979- ++output_count;
1980- });
1981- EXPECT_EQ(noutputs, output_count);
1982+ EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_before));
1983
1984 // Now simulate a change of monitor, with no CRTC attached (and hence no current mode).
1985 // The configuration should mirror this state.
1986@@ -825,14 +743,7 @@
1987 ASSERT_TRUE(handler_signal.wait_for(10s));
1988
1989 conf = display->configuration();
1990- output_count = 0;
1991- conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
1992- {
1993- ASSERT_LT(output_count, noutputs);
1994- EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count;
1995- ++output_count;
1996- });
1997- EXPECT_EQ(noutputs, output_count);
1998+ EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_after));
1999 }
2000
2001 TEST_F(MesaDisplayConfigurationTest, does_not_query_drm_unnecesarily)
2002@@ -872,6 +783,7 @@
2003 fake_devices.emit_device_changed(syspath);
2004 ASSERT_TRUE(handler_signal.wait_for(10s));
2005
2006- EXPECT_CALL(mock_drm, drmModeGetConnector(_,_)).Times(1);
2007+ // It needs to query DRM at least once; we don't really care how many times it does, though.
2008+ EXPECT_CALL(mock_drm, drmModeGetConnector(_,_)).Times(AtLeast(1));
2009 display->configuration();
2010 }
2011
2012=== modified file 'tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp'
2013--- tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2017-03-13 08:12:52 +0000
2014+++ tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2017-03-20 02:44:46 +0000
2015@@ -135,19 +135,6 @@
2016
2017 }
2018
2019-TEST_F(RealKMSOutputTest, construction_queries_connector)
2020-{
2021- using namespace testing;
2022-
2023- setup_outputs_connected_crtc();
2024-
2025- EXPECT_CALL(mock_drm, drmModeGetConnector(_,connector_ids[0]))
2026- .Times(1);
2027-
2028- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2029- mt::fake_shared(null_page_flipper)};
2030-}
2031-
2032 TEST_F(RealKMSOutputTest, operations_use_existing_crtc)
2033 {
2034 using namespace testing;
2035@@ -177,8 +164,10 @@
2036 .Times(1);
2037 }
2038
2039- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2040- mt::fake_shared(mock_page_flipper)};
2041+ mgm::RealKMSOutput output{
2042+ mock_drm.fake_drm.fd(),
2043+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2044+ mt::fake_shared(mock_page_flipper)};
2045
2046 auto fb = output.fb_for(fake_bo, 1920, 1024);
2047
2048@@ -217,8 +206,10 @@
2049
2050 append_fb_id(fb_id);
2051
2052- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2053- mt::fake_shared(mock_page_flipper)};
2054+ mgm::RealKMSOutput output{
2055+ mock_drm.fake_drm.fd(),
2056+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2057+ mt::fake_shared(mock_page_flipper)};
2058
2059 auto fb = output.fb_for(fake_bo, 1920, 756);
2060
2061@@ -252,16 +243,18 @@
2062 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, _, _, _, _, _, _, _))
2063 .Times(0);
2064 }
2065+
2066 append_fb_id(fb_id);
2067
2068 mgm::RealKMSOutput output{
2069 mock_drm.fake_drm.fd(),
2070- connector_ids[0],
2071+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2072 mt::fake_shared(mock_page_flipper)};
2073
2074 auto fb = output.fb_for(fake_bo, 1280, 1024);
2075
2076 EXPECT_FALSE(output.set_crtc(*fb));
2077+
2078 EXPECT_NO_THROW({
2079 EXPECT_FALSE(output.schedule_page_flip(*fb));
2080 });
2081@@ -276,8 +269,10 @@
2082
2083 setup_outputs_connected_crtc();
2084
2085- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2086- mt::fake_shared(mock_page_flipper)};
2087+ mgm::RealKMSOutput output{
2088+ mock_drm.fake_drm.fd(),
2089+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2090+ mt::fake_shared(mock_page_flipper)};
2091
2092 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))
2093 .Times(1)
2094@@ -302,8 +297,10 @@
2095
2096 resources.prepare();
2097
2098- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2099- mt::fake_shared(mock_page_flipper)};
2100+ mgm::RealKMSOutput output{
2101+ mock_drm.fake_drm.fd(),
2102+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2103+ mt::fake_shared(mock_page_flipper)};
2104
2105 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, _, 0, 0, 0, nullptr, 0, nullptr))
2106 .Times(0);
2107@@ -321,8 +318,10 @@
2108
2109 setup_outputs_connected_crtc();
2110
2111- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2112- mt::fake_shared(mock_page_flipper)};
2113+ mgm::RealKMSOutput output{
2114+ mock_drm.fake_drm.fd(),
2115+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2116+ mt::fake_shared(mock_page_flipper)};
2117
2118 auto fb = output.fb_for(fake_bo, 1292, 222);
2119
2120@@ -347,8 +346,10 @@
2121
2122 setup_outputs_connected_crtc();
2123
2124- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2125- mt::fake_shared(mock_page_flipper)};
2126+ mgm::RealKMSOutput output{
2127+ mock_drm.fake_drm.fd(),
2128+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2129+ mt::fake_shared(mock_page_flipper)};
2130
2131 auto fb = output.fb_for(fake_bo, 1292, 222);
2132
2133@@ -374,8 +375,10 @@
2134
2135 setup_outputs_connected_crtc();
2136
2137- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2138- mt::fake_shared(mock_page_flipper)};
2139+ mgm::RealKMSOutput output{
2140+ mock_drm.fake_drm.fd(),
2141+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2142+ mt::fake_shared(mock_page_flipper)};
2143
2144 auto fb = output.fb_for(fake_bo, 1292, 222);
2145
2146@@ -393,8 +396,10 @@
2147
2148 setup_outputs_connected_crtc();
2149
2150- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2151- mt::fake_shared(mock_page_flipper)};
2152+ mgm::RealKMSOutput output{
2153+ mock_drm.fake_drm.fd(),
2154+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2155+ mt::fake_shared(mock_page_flipper)};
2156
2157 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))
2158 .Times(1)
2159@@ -413,8 +418,10 @@
2160
2161 setup_outputs_connected_crtc();
2162
2163- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2164- mt::fake_shared(mock_page_flipper)};
2165+ mgm::RealKMSOutput output{
2166+ mock_drm.fake_drm.fd(),
2167+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2168+ mt::fake_shared(mock_page_flipper)};
2169
2170 mg::GammaCurves gamma{{1}, {2}, {3}};
2171
2172@@ -442,8 +449,10 @@
2173
2174 setup_outputs_connected_crtc();
2175
2176- mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
2177- mt::fake_shared(mock_page_flipper)};
2178+ mgm::RealKMSOutput output{
2179+ mock_drm.fake_drm.fd(),
2180+ mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
2181+ mt::fake_shared(mock_page_flipper)};
2182
2183 mg::GammaCurves gamma{{1}, {2}, {3}};
2184

Subscribers

People subscribed via source and target branches