Merge lp:~afrantzis/unity-system-compositor/dbus-active-outputs into lp:unity-system-compositor

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: 307
Merged at revision: 307
Proposed branch: lp:~afrantzis/unity-system-compositor/dbus-active-outputs
Merge into: lp:unity-system-compositor
Diff against target: 1233 lines (+794/-86)
13 files modified
src/com.canonical.Unity.Display.xml (+31/-1)
src/mir_screen.cpp (+153/-19)
src/mir_screen.h (+32/-6)
src/screen.h (+23/-2)
src/server.cpp (+6/-1)
src/unity_display_service.cpp (+179/-12)
src/unity_display_service.h (+9/-2)
tests/include/usc/test/mock_screen.h (+3/-2)
tests/include/usc/test/stub_display_configuration.h (+55/-7)
tests/integration-tests/test_unity_display_service.cpp (+148/-9)
tests/integration-tests/unity_display_dbus_client.cpp (+47/-2)
tests/integration-tests/unity_display_dbus_client.h (+6/-2)
tests/unit-tests/test_mir_screen.cpp (+102/-21)
To merge this branch: bzr merge lp:~afrantzis/unity-system-compositor/dbus-active-outputs
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Unity System Compositor Development Team Pending
Review via email: mp+316443@code.launchpad.net

Commit message

Update the Unity.Display DBus interface to support multiple outputs better

Description of the change

Update the Unity.Display DBus interface to support multiple outputs better

To post a comment you must log in.
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 :

PASSED: Continuous integration, rev:307
https://mir-jenkins.ubuntu.com/job/usc-ci/53/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-usc/75
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3988
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3978
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3978
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3978
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=amd64,release=xenial+overlay/79
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=amd64,release=xenial+overlay/79/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=amd64,release=zesty/79
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=amd64,release=zesty/79/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=armhf,release=vivid+overlay/79
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=armhf,release=vivid+overlay/79/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=i386,release=xenial+overlay/79
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=i386,release=xenial+overlay/79/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=i386,release=zesty/79
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-usc/arch=i386,release=zesty/79/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/com.canonical.Unity.Display.xml'
2--- src/com.canonical.Unity.Display.xml 2016-04-15 08:40:54 +0000
3+++ src/com.canonical.Unity.Display.xml 2017-02-06 11:51:56 +0000
4@@ -2,9 +2,39 @@
5 <node>
6 <interface name='com.canonical.Unity.Display'>
7 <method name='TurnOn'>
8+ <arg type="s" name="what" direction="in"/>
9 </method>
10 <method name='TurnOff'>
11+ <arg type="s" name="what" direction="in"/>
12+ </method>
13+ <property name='ActiveOutputs' type='(ii)' access='read'/>
14+ </interface>
15+
16+ <interface name="org.freedesktop.DBus.Properties">
17+ <method name="Get">
18+ <arg type="s" name="interface_name" direction="in"/>
19+ <arg type="s" name="property_name" direction="in"/>
20+ <arg type="v" name="value" direction="out"/>
21+ </method>
22+ <method name="GetAll">
23+ <arg type="s" name="interface_name" direction="in"/>
24+ <arg type="a{sv}" name="properties" direction="out"/>
25+ </method>
26+ <method name="Set">
27+ <arg type="s" name="interface_name" direction="in"/>
28+ <arg type="s" name="property_name" direction="in"/>
29+ <arg type="v" name="value" direction="in"/>
30+ </method>
31+ <signal name="PropertiesChanged">
32+ <arg type="s" name="interface_name"/>
33+ <arg type="a{sv}" name="changed_properties"/>
34+ <arg type="as" name="invalidated_properties"/>
35+ </signal>
36+ </interface>
37+
38+ <interface name="org.freedesktop.DBus.Introspectable">
39+ <method name="Introspect">
40+ <arg type="s" name="xml_data" direction="out"/>
41 </method>
42 </interface>
43 </node>
44-
45
46=== modified file 'src/mir_screen.cpp'
47--- src/mir_screen.cpp 2016-08-12 14:08:22 +0000
48+++ src/mir_screen.cpp 2017-02-06 11:51:56 +0000
49@@ -42,6 +42,82 @@
50 mir::report_exception(buffer);
51 mir::log(::mir::logging::Severity::warning, "usc::MirScreen", warning_format, func, buffer.str().c_str());
52 }
53+
54+bool is_external(mir::graphics::DisplayConfigurationOutputType type)
55+{
56+ using mir::graphics::DisplayConfigurationOutputType;
57+
58+ return type == DisplayConfigurationOutputType::vga ||
59+ type == DisplayConfigurationOutputType::dvii ||
60+ type == DisplayConfigurationOutputType::dvid ||
61+ type == DisplayConfigurationOutputType::dvia ||
62+ type == DisplayConfigurationOutputType::composite ||
63+ type == DisplayConfigurationOutputType::svideo ||
64+ type == DisplayConfigurationOutputType::component ||
65+ type == DisplayConfigurationOutputType::ninepindin ||
66+ type == DisplayConfigurationOutputType::displayport ||
67+ type == DisplayConfigurationOutputType::hdmia ||
68+ type == DisplayConfigurationOutputType::hdmib ||
69+ type == DisplayConfigurationOutputType::tv;
70+}
71+
72+usc::ActiveOutputs count_active_outputs(
73+ mir::graphics::DisplayConfiguration const& display_configuration)
74+{
75+ usc::ActiveOutputs active_outputs{};
76+
77+ display_configuration.for_each_output(
78+ [&active_outputs](mir::graphics::DisplayConfigurationOutput const& output)
79+ {
80+ if (output.connected &&
81+ output.used &&
82+ output.power_mode == MirPowerMode::mir_power_mode_on)
83+ {
84+ if (is_external(output.type))
85+ ++active_outputs.external;
86+ else
87+ ++active_outputs.internal;
88+ }
89+ });
90+
91+ return active_outputs;
92+}
93+
94+bool has_active_outputs(
95+ mir::graphics::DisplayConfiguration const& display_configuration)
96+{
97+ auto const active_outputs = count_active_outputs(display_configuration);
98+ return active_outputs.internal + active_outputs.external > 0;
99+}
100+
101+bool all_outputs_filter(mir::graphics::UserDisplayConfigurationOutput const&)
102+{
103+ return true;
104+}
105+
106+bool internal_outputs_filter(mir::graphics::UserDisplayConfigurationOutput const& output)
107+{
108+ return !is_external(output.type);
109+}
110+
111+bool external_outputs_filter(mir::graphics::UserDisplayConfigurationOutput const& output)
112+{
113+ return is_external(output.type);
114+}
115+
116+auto get_power_mode_filter_for_output_filter(usc::OutputFilter output_filter)
117+{
118+ switch (output_filter)
119+ {
120+ case usc::OutputFilter::all: return all_outputs_filter;
121+ case usc::OutputFilter::internal: return internal_outputs_filter;
122+ case usc::OutputFilter::external: return external_outputs_filter;
123+ default: return all_outputs_filter;
124+ }
125+
126+ return all_outputs_filter;
127+}
128+
129 }
130
131 usc::MirScreen::MirScreen(
132@@ -49,7 +125,7 @@
133 std::shared_ptr<mir::graphics::Display> const& display)
134 : compositor{compositor},
135 display{display},
136- current_power_mode{MirPowerMode::mir_power_mode_on}
137+ active_outputs_handler{[](ActiveOutputs const&){}}
138 {
139 try
140 {
141@@ -69,27 +145,87 @@
142
143 usc::MirScreen::~MirScreen() = default;
144
145-void usc::MirScreen::turn_on()
146-{
147- set_power_mode(MirPowerMode::mir_power_mode_on);
148-}
149-
150-void usc::MirScreen::turn_off()
151-{
152- set_power_mode(MirPowerMode::mir_power_mode_off);
153-}
154-
155-void usc::MirScreen::set_power_mode(MirPowerMode mode)
156+void usc::MirScreen::turn_on(OutputFilter output_filter)
157+{
158+ auto const filter_func = get_power_mode_filter_for_output_filter(output_filter);
159+ set_power_mode(MirPowerMode::mir_power_mode_on, filter_func);
160+}
161+
162+void usc::MirScreen::turn_off(OutputFilter output_filter)
163+{
164+ auto const filter_func = get_power_mode_filter_for_output_filter(output_filter);
165+ set_power_mode(MirPowerMode::mir_power_mode_off, filter_func);
166+}
167+
168+void usc::MirScreen::register_active_outputs_handler(
169+ ActiveOutputsHandler const& handler)
170+{
171+ // It's not ideal to call the handler under lock, but we need this to
172+ // guarantee that after this function returns no invocation of the old
173+ // handler will be in progress. Alternatively, we would need to implement
174+ // an event loop.
175+ std::lock_guard<std::mutex> lock{active_outputs_mutex};
176+ active_outputs_handler = handler;
177+ active_outputs_handler(active_outputs);
178+}
179+
180+void usc::MirScreen::initial_configuration(
181+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const& display_configuration)
182+{
183+ std::lock_guard<std::mutex> lock{active_outputs_mutex};
184+ active_outputs = count_active_outputs(*display_configuration);
185+ active_outputs_handler(active_outputs);
186+}
187+
188+void usc::MirScreen::configuration_applied(
189+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const& display_configuration)
190+{
191+ std::lock_guard<std::mutex> lock{active_outputs_mutex};
192+ active_outputs = count_active_outputs(*display_configuration);
193+ active_outputs_handler(active_outputs);
194+}
195+
196+void usc::MirScreen::base_configuration_updated(
197+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&)
198+{
199+}
200+
201+void usc::MirScreen::session_configuration_applied(
202+ std::shared_ptr<mir::frontend::Session> const&,
203+ std::shared_ptr<mir::graphics::DisplayConfiguration> const&)
204+{
205+}
206+
207+void usc::MirScreen::session_configuration_removed(
208+ std::shared_ptr<mir::frontend::Session> const&)
209+{
210+}
211+
212+void usc::MirScreen::configuration_failed(
213+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&,
214+ std::exception const&)
215+{
216+}
217+
218+void usc::MirScreen::catastrophic_configuration_error(
219+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&,
220+ std::exception const&)
221+{
222+}
223+
224+void usc::MirScreen::set_power_mode(MirPowerMode mode, SetPowerModeFilter const& filter)
225 try
226 {
227- if (current_power_mode == mode)
228- return;
229-
230 std::shared_ptr<mg::DisplayConfiguration> displayConfig = display->configuration();
231
232 displayConfig->for_each_output(
233 [&](const mg::UserDisplayConfigurationOutput displayConfigOutput) {
234- displayConfigOutput.power_mode = mode;
235+ if (displayConfigOutput.connected &&
236+ displayConfigOutput.used &&
237+ filter(displayConfigOutput))
238+ {
239+ displayConfigOutput.power_mode = mode;
240+ }
241 }
242 );
243
244@@ -97,10 +233,8 @@
245
246 display->configure(*displayConfig.get());
247
248- if (mode == MirPowerMode::mir_power_mode_on)
249+ if (has_active_outputs(*displayConfig))
250 compositor->start();
251-
252- current_power_mode = mode;
253 }
254 catch (std::exception const&)
255 {
256
257=== modified file 'src/mir_screen.h'
258--- src/mir_screen.h 2016-08-12 14:08:22 +0000
259+++ src/mir_screen.h 2017-02-06 11:51:56 +0000
260@@ -17,6 +17,7 @@
261 #ifndef USC_MIR_SCREEN_H_
262 #define USC_MIR_SCREEN_H_
263
264+#include <mir/graphics/display_configuration_observer.h>
265 #include "screen.h"
266
267 #include <chrono>
268@@ -26,29 +27,54 @@
269 namespace mir
270 {
271 namespace compositor { class Compositor; }
272-namespace graphics {class Display;}
273+namespace graphics {class Display; struct UserDisplayConfigurationOutput;}
274 }
275
276 namespace usc
277 {
278
279-class MirScreen: public Screen
280+class MirScreen: public Screen, public mir::graphics::DisplayConfigurationObserver
281 {
282 public:
283 MirScreen(std::shared_ptr<mir::compositor::Compositor> const& compositor,
284 std::shared_ptr<mir::graphics::Display> const& display);
285 ~MirScreen();
286
287- void turn_on();
288- void turn_off();
289+ // From Screen
290+ void turn_on(OutputFilter output_filter) override;
291+ void turn_off(OutputFilter output_filter) override;
292+ void register_active_outputs_handler(ActiveOutputsHandler const& handler) override;
293+
294+ // From DisplayConfigurationObserver
295+ void initial_configuration(
296+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const& display_configuration) override;
297+ void configuration_applied(
298+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const& display_configuration) override;
299+
300+ void base_configuration_updated(
301+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&) override;
302+ void session_configuration_applied(
303+ std::shared_ptr<mir::frontend::Session> const&,
304+ std::shared_ptr<mir::graphics::DisplayConfiguration> const&) override;
305+ void session_configuration_removed(
306+ std::shared_ptr<mir::frontend::Session> const&) override;
307+ void configuration_failed(
308+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&,
309+ std::exception const&) override;
310+ void catastrophic_configuration_error(
311+ std::shared_ptr<mir::graphics::DisplayConfiguration const> const&,
312+ std::exception const&) override;
313
314 private:
315- void set_power_mode(MirPowerMode mode);
316+ using SetPowerModeFilter = bool(*)(mir::graphics::UserDisplayConfigurationOutput const&);
317+ void set_power_mode(MirPowerMode mode, SetPowerModeFilter const& filter);
318
319 std::shared_ptr<mir::compositor::Compositor> const compositor;
320 std::shared_ptr<mir::graphics::Display> const display;
321
322- MirPowerMode current_power_mode;
323+ std::mutex active_outputs_mutex;
324+ ActiveOutputsHandler active_outputs_handler;
325+ ActiveOutputs active_outputs;
326 };
327
328 }
329
330=== modified file 'src/screen.h'
331--- src/screen.h 2016-04-15 08:40:54 +0000
332+++ src/screen.h 2017-02-06 11:51:56 +0000
333@@ -23,13 +23,34 @@
334 namespace usc
335 {
336
337+struct ActiveOutputs
338+{
339+ ActiveOutputs(int i, int e) : internal{i}, external{e} {}
340+ ActiveOutputs() : ActiveOutputs{0, 0} {}
341+
342+ bool operator==(ActiveOutputs const& other) const
343+ {
344+ return internal == other.internal &&
345+ external == other.external;
346+ }
347+
348+ int internal;
349+ int external;
350+};
351+
352+using ActiveOutputsHandler = std::function<void(ActiveOutputs const&)>;
353+
354+enum class OutputFilter { all, internal, external };
355+
356 class Screen
357 {
358 public:
359 virtual ~Screen() = default;
360
361- virtual void turn_on() = 0;
362- virtual void turn_off() = 0;
363+ virtual void turn_on(OutputFilter filter) = 0;
364+ virtual void turn_off(OutputFilter filter) = 0;
365+ virtual void register_active_outputs_handler(
366+ ActiveOutputsHandler const& handler) = 0;
367
368 protected:
369 Screen() = default;
370
371=== modified file 'src/server.cpp'
372--- src/server.cpp 2016-11-09 19:12:16 +0000
373+++ src/server.cpp 2017-02-06 11:51:56 +0000
374@@ -43,6 +43,7 @@
375 #include <mir/log.h>
376 #include <mir/abnormal_exit.h>
377 #include <mir/main_loop.h>
378+#include <mir/observer_registrar.h>
379
380 #include <boost/exception/all.hpp>
381
382@@ -292,9 +293,13 @@
383 return screen(
384 [this]
385 {
386- return std::make_shared<MirScreen>(
387+ auto mir_screen = std::make_shared<MirScreen>(
388 the_compositor(),
389 the_display());
390+
391+ the_display_configuration_observer_registrar()->register_interest(mir_screen);
392+
393+ return mir_screen;
394 });
395 }
396
397
398=== modified file 'src/unity_display_service.cpp'
399--- src/unity_display_service.cpp 2016-04-15 08:40:54 +0000
400+++ src/unity_display_service.cpp 2017-02-06 11:51:56 +0000
401@@ -32,6 +32,48 @@
402 char const* const dbus_display_path = "/com/canonical/Unity/Display";
403 char const* const dbus_display_service_name = "com.canonical.Unity.Display";
404
405+void usc_dbus_message_iter_append_active_outputs_variant(
406+ DBusMessageIter* iter, usc::ActiveOutputs const& active_outputs)
407+{
408+ DBusMessageIter iter_variant;
409+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "(ii)", &iter_variant);
410+
411+ {
412+ DBusMessageIter iter_struct;
413+ dbus_message_iter_open_container(&iter_variant, DBUS_TYPE_STRUCT, nullptr, &iter_struct);
414+
415+ dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_INT32, &active_outputs.internal);
416+ dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_INT32, &active_outputs.external);
417+
418+ dbus_message_iter_close_container(&iter_variant, &iter_struct);
419+ }
420+
421+ dbus_message_iter_close_container(iter, &iter_variant);
422+}
423+
424+void usc_dbus_message_iter_append_active_outputs_dict_entry(
425+ DBusMessageIter* iter, usc::ActiveOutputs const& active_outputs)
426+{
427+ char const* key = "ActiveOutputs";
428+ DBusMessageIter iter_entry;
429+ dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, nullptr, &iter_entry);
430+
431+ dbus_message_iter_append_basic(&iter_entry, DBUS_TYPE_STRING, &key);
432+ usc_dbus_message_iter_append_active_outputs_variant(&iter_entry, active_outputs);
433+
434+ dbus_message_iter_close_container(iter, &iter_entry);
435+}
436+
437+usc::OutputFilter output_filter_from_string(std::string const& filter_str)
438+{
439+ if (filter_str == "internal")
440+ return usc::OutputFilter::internal;
441+ else if (filter_str == "external")
442+ return usc::OutputFilter::external;
443+
444+ return usc::OutputFilter::all;
445+}
446+
447 }
448
449 usc::UnityDisplayService::UnityDisplayService(
450@@ -45,6 +87,22 @@
451 loop->add_connection(connection);
452 connection->request_name(dbus_display_service_name);
453 connection->add_filter(handle_dbus_message_thunk, this);
454+
455+ screen->register_active_outputs_handler(
456+ [this] (ActiveOutputs const& active_outputs_arg)
457+ {
458+ this->loop->enqueue(
459+ [this, active_outputs_arg]
460+ {
461+ active_outputs = active_outputs_arg;
462+ dbus_emit_ActiveOutputs();
463+ });
464+ });
465+}
466+
467+usc::UnityDisplayService::~UnityDisplayService()
468+{
469+ screen->register_active_outputs_handler([](ActiveOutputs const&){});
470 }
471
472 ::DBusHandlerResult usc::UnityDisplayService::handle_dbus_message_thunk(
473@@ -70,16 +128,69 @@
474 }
475 else if (dbus_message_is_method_call(message, dbus_display_interface, "TurnOn"))
476 {
477- dbus_TurnOn();
478+ char const* filter{""};
479+ dbus_message_get_args(
480+ message, &args_error,
481+ DBUS_TYPE_STRING, &filter,
482+ DBUS_TYPE_INVALID);
483+
484+ // For backward compatibility
485+ if (args_error)
486+ filter = "all";
487+
488+ dbus_TurnOn(filter);
489
490 DBusMessageHandle reply{dbus_message_new_method_return(message)};
491 dbus_connection_send(connection, reply, nullptr);
492 }
493 else if (dbus_message_is_method_call(message, dbus_display_interface, "TurnOff"))
494 {
495- dbus_TurnOff();
496-
497- DBusMessageHandle reply{dbus_message_new_method_return(message)};
498+ char const* filter{""};
499+
500+ dbus_message_get_args(
501+ message, &args_error,
502+ DBUS_TYPE_STRING, &filter,
503+ DBUS_TYPE_INVALID);
504+
505+ // For backward compatibility
506+ if (args_error)
507+ filter = "all";
508+
509+ dbus_TurnOff(filter);
510+
511+ DBusMessageHandle reply{dbus_message_new_method_return(message)};
512+ dbus_connection_send(connection, reply, nullptr);
513+ }
514+ else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get"))
515+ {
516+ char const* interface{""};
517+ char const* property{""};
518+ dbus_message_get_args(
519+ message, &args_error,
520+ DBUS_TYPE_STRING, &interface,
521+ DBUS_TYPE_STRING, &property,
522+ DBUS_TYPE_INVALID);
523+
524+ DBusMessageHandle reply{dbus_message_new_method_return(message)};
525+
526+ if (!args_error && std::string{interface} == dbus_display_interface)
527+ dbus_properties_Get(reply, property);
528+
529+ dbus_connection_send(connection, reply, nullptr);
530+ }
531+ else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll"))
532+ {
533+ char const* interface{""};
534+ dbus_message_get_args(
535+ message, &args_error,
536+ DBUS_TYPE_STRING, &interface,
537+ DBUS_TYPE_INVALID);
538+
539+ DBusMessageHandle reply{dbus_message_new_method_return(message)};
540+
541+ if (!args_error && std::string{interface} == dbus_display_interface)
542+ dbus_properties_GetAll(reply);
543+
544 dbus_connection_send(connection, reply, nullptr);
545 }
546 else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
547@@ -101,12 +212,68 @@
548 return DBUS_HANDLER_RESULT_HANDLED;
549 }
550
551-void usc::UnityDisplayService::dbus_TurnOn()
552-{
553- screen->turn_on();
554-}
555-
556-void usc::UnityDisplayService::dbus_TurnOff()
557-{
558- screen->turn_off();
559+void usc::UnityDisplayService::dbus_TurnOn(std::string const& filter)
560+{
561+ screen->turn_on(output_filter_from_string(filter));
562+}
563+
564+void usc::UnityDisplayService::dbus_TurnOff(std::string const& filter)
565+{
566+ screen->turn_off(output_filter_from_string(filter));
567+}
568+
569+void usc::UnityDisplayService::dbus_emit_ActiveOutputs()
570+{
571+ DBusMessageHandle signal{
572+ dbus_message_new_signal(
573+ dbus_display_path,
574+ "org.freedesktop.DBus.Properties",
575+ "PropertiesChanged")};
576+
577+ DBusMessageIter iter;
578+ dbus_message_iter_init_append(signal, &iter);
579+
580+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &dbus_display_interface);
581+
582+ {
583+ DBusMessageIter iter_dict;
584+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
585+
586+ usc_dbus_message_iter_append_active_outputs_dict_entry(&iter_dict, active_outputs);
587+
588+ dbus_message_iter_close_container(&iter, &iter_dict);
589+ }
590+
591+ {
592+ DBusMessageIter iter_array;
593+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iter_array);
594+ dbus_message_iter_close_container(&iter, &iter_array);
595+ }
596+
597+ dbus_connection_send(*connection, signal, nullptr);
598+ dbus_connection_flush(*connection);
599+}
600+
601+void usc::UnityDisplayService::dbus_properties_Get(DBusMessage* reply, std::string const& property)
602+{
603+ DBusMessageIter iter;
604+ dbus_message_iter_init_append(reply, &iter);
605+
606+ if (property == "ActiveOutputs")
607+ usc_dbus_message_iter_append_active_outputs_variant(&iter, active_outputs);
608+}
609+
610+void usc::UnityDisplayService::dbus_properties_GetAll(DBusMessage* reply)
611+{
612+ DBusMessageIter iter;
613+ dbus_message_iter_init_append(reply, &iter);
614+
615+ {
616+ DBusMessageIter iter_dict;
617+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
618+
619+ usc_dbus_message_iter_append_active_outputs_dict_entry(&iter_dict, active_outputs);
620+
621+ dbus_message_iter_close_container(&iter, &iter_dict);
622+ }
623 }
624
625=== modified file 'src/unity_display_service.h'
626--- src/unity_display_service.h 2016-04-15 08:40:54 +0000
627+++ src/unity_display_service.h 2017-02-06 11:51:56 +0000
628@@ -20,8 +20,10 @@
629 #define USC_UNITY_DISPLAY_SERVICE_H_
630
631 #include "dbus_connection_handle.h"
632+#include "screen.h"
633
634 #include <memory>
635+#include <string>
636
637 namespace usc
638 {
639@@ -35,6 +37,7 @@
640 std::shared_ptr<usc::DBusEventLoop> const& loop,
641 std::string const& address,
642 std::shared_ptr<usc::Screen> const& screen);
643+ ~UnityDisplayService();
644
645 private:
646 static ::DBusHandlerResult handle_dbus_message_thunk(
647@@ -42,12 +45,16 @@
648 ::DBusHandlerResult handle_dbus_message(
649 DBusConnection* connection, DBusMessage* message, void* user_data);
650
651- void dbus_TurnOn();
652- void dbus_TurnOff();
653+ void dbus_TurnOn(std::string const& filter);
654+ void dbus_TurnOff(std::string const& filter);
655+ void dbus_emit_ActiveOutputs();
656+ void dbus_properties_Get(DBusMessage* reply, std::string const& property);
657+ void dbus_properties_GetAll(DBusMessage* reply);
658
659 std::shared_ptr<usc::Screen> const screen;
660 std::shared_ptr<DBusEventLoop> const loop;
661 std::shared_ptr<DBusConnectionHandle> connection;
662+ ActiveOutputs active_outputs;
663 };
664
665 }
666
667=== modified file 'tests/include/usc/test/mock_screen.h'
668--- tests/include/usc/test/mock_screen.h 2016-04-18 14:58:36 +0000
669+++ tests/include/usc/test/mock_screen.h 2017-02-06 11:51:56 +0000
670@@ -31,8 +31,9 @@
671
672 struct MockScreen : usc::Screen
673 {
674- MOCK_METHOD0(turn_on, void());
675- MOCK_METHOD0(turn_off, void());
676+ MOCK_METHOD1(turn_on, void(OutputFilter));
677+ MOCK_METHOD1(turn_off, void(OutputFilter));
678+ MOCK_METHOD1(register_active_outputs_handler, void(ActiveOutputsHandler const&));
679 };
680
681 }
682
683=== modified file 'tests/include/usc/test/stub_display_configuration.h'
684--- tests/include/usc/test/stub_display_configuration.h 2015-11-12 14:21:24 +0000
685+++ tests/include/usc/test/stub_display_configuration.h 2017-02-06 11:51:56 +0000
686@@ -28,12 +28,36 @@
687 {
688 StubDisplayConfiguration()
689 {
690- conf_output.power_mode = MirPowerMode::mir_power_mode_on;
691+ internal_active_conf_output.power_mode = MirPowerMode::mir_power_mode_on;
692+ internal_active_conf_output.type = mir::graphics::DisplayConfigurationOutputType::lvds;
693+ internal_active_conf_output.used = true;
694+ internal_active_conf_output.connected = true;
695+
696+ external_active_conf_output.power_mode = MirPowerMode::mir_power_mode_on;
697+ external_active_conf_output.type = mir::graphics::DisplayConfigurationOutputType::dvid;
698+ external_active_conf_output.used = true;
699+ external_active_conf_output.connected = true;
700+
701+ inactive_conf_output.power_mode = MirPowerMode::mir_power_mode_off;
702+ inactive_conf_output.used = false;
703+ inactive_conf_output.connected = false;
704+ }
705+
706+ StubDisplayConfiguration(
707+ int num_internal_active_outputs,
708+ int num_external_active_outputs,
709+ int num_inactive_outputs)
710+ : StubDisplayConfiguration{}
711+ {
712+ this->num_internal_active_outputs = num_internal_active_outputs;
713+ this->num_external_active_outputs = num_external_active_outputs;
714+ this->num_inactive_outputs = num_inactive_outputs;
715 }
716
717 StubDisplayConfiguration(mir::graphics::DisplayConfigurationOutput const& output)
718- : conf_output(output)
719+ : StubDisplayConfiguration{}
720 {
721+ internal_active_conf_output = output;
722 }
723
724 void for_each_card(std::function<void(mir::graphics::DisplayConfigurationCard const&)>) const override
725@@ -42,21 +66,45 @@
726
727 void for_each_output(std::function<void(mir::graphics::DisplayConfigurationOutput const&)> f) const override
728 {
729- f(conf_output);
730+ for (int i = 0; i < num_internal_active_outputs; ++i)
731+ f(internal_active_conf_output);
732+ for (int i = 0; i < num_external_active_outputs; ++i)
733+ f(external_active_conf_output);
734+ for (int i = 0; i < num_inactive_outputs; ++i)
735+ f(inactive_conf_output);
736 }
737
738 void for_each_output(std::function<void(mir::graphics::UserDisplayConfigurationOutput&)> f)
739 {
740- mir::graphics::UserDisplayConfigurationOutput user{conf_output};
741- f(user);
742+ for (int i = 0; i < num_internal_active_outputs; ++i)
743+ {
744+ mir::graphics::UserDisplayConfigurationOutput user{internal_active_conf_output};
745+ f(user);
746+ }
747+ for (int i = 0; i < num_external_active_outputs; ++i)
748+ {
749+ mir::graphics::UserDisplayConfigurationOutput user{external_active_conf_output};
750+ f(user);
751+ }
752+ for (int i = 0; i < num_inactive_outputs; ++i)
753+ {
754+ mir::graphics::UserDisplayConfigurationOutput user{inactive_conf_output};
755+ f(user);
756+ }
757 }
758
759 std::unique_ptr<mir::graphics::DisplayConfiguration> clone() const override
760 {
761- return std::make_unique<StubDisplayConfiguration>(conf_output);
762+ return std::make_unique<StubDisplayConfiguration>(internal_active_conf_output);
763 }
764
765- mir::graphics::DisplayConfigurationOutput conf_output;
766+ int num_internal_active_outputs{1};
767+ int num_external_active_outputs{0};
768+ int num_inactive_outputs{0};
769+
770+ mir::graphics::DisplayConfigurationOutput internal_active_conf_output;
771+ mir::graphics::DisplayConfigurationOutput external_active_conf_output;
772+ mir::graphics::DisplayConfigurationOutput inactive_conf_output;
773 };
774
775 }
776
777=== modified file 'tests/integration-tests/test_unity_display_service.cpp'
778--- tests/integration-tests/test_unity_display_service.cpp 2016-04-18 14:58:36 +0000
779+++ tests/integration-tests/test_unity_display_service.cpp 2017-02-06 11:51:56 +0000
780@@ -40,16 +40,34 @@
781 namespace
782 {
783
784+struct FakeScreen : ut::MockScreen
785+{
786+ void register_active_outputs_handler(usc::ActiveOutputsHandler const& handler)
787+ {
788+ std::lock_guard<std::mutex> lock{active_outputs_mutex};
789+ active_outputs_handler = handler;
790+ }
791+
792+ void notify_active_outputs(usc::ActiveOutputs const& active_outputs)
793+ {
794+ std::lock_guard<std::mutex> lock{active_outputs_mutex};
795+ active_outputs_handler(active_outputs);
796+ }
797+
798+ std::mutex active_outputs_mutex;
799+ usc::ActiveOutputsHandler active_outputs_handler{[](usc::ActiveOutputs const&){}};
800+};
801+
802 struct AUnityDisplayService : testing::Test
803 {
804 ut::DBusBus bus;
805
806- std::shared_ptr<ut::MockScreen> const mock_screen =
807- std::make_shared<testing::NiceMock<ut::MockScreen>>();
808+ std::shared_ptr<FakeScreen> const fake_screen =
809+ std::make_shared<testing::NiceMock<FakeScreen>>();
810 ut::UnityDisplayDBusClient client{bus.address()};
811 std::shared_ptr<usc::DBusEventLoop> const dbus_loop =
812 std::make_shared<usc::DBusEventLoop>();
813- usc::UnityDisplayService service{dbus_loop, bus.address(), mock_screen};
814+ usc::UnityDisplayService service{dbus_loop, bus.address(), fake_screen};
815 std::shared_ptr<usc::DBusConnectionThread> const dbus_thread =
816 std::make_shared<usc::DBusConnectionThread>(dbus_loop);
817 };
818@@ -66,16 +84,137 @@
819
820 TEST_F(AUnityDisplayService, forwards_turn_on_request)
821 {
822- EXPECT_CALL(*mock_screen, turn_on());
823-
824- client.request_turn_on();
825+ using namespace testing;
826+
827+ InSequence s;
828+ EXPECT_CALL(*fake_screen, turn_on(usc::OutputFilter::all));
829+ EXPECT_CALL(*fake_screen, turn_on(usc::OutputFilter::internal));
830+ EXPECT_CALL(*fake_screen, turn_on(usc::OutputFilter::external));
831+
832+ client.request_turn_on("all");
833+ client.request_turn_on("internal");
834+ client.request_turn_on("external");
835 }
836
837 TEST_F(AUnityDisplayService, forwards_turn_off_request)
838 {
839- EXPECT_CALL(*mock_screen, turn_off());
840-
841- client.request_turn_off();
842+ using namespace testing;
843+
844+ InSequence s;
845+ EXPECT_CALL(*fake_screen, turn_off(usc::OutputFilter::all));
846+ EXPECT_CALL(*fake_screen, turn_off(usc::OutputFilter::internal));
847+ EXPECT_CALL(*fake_screen, turn_off(usc::OutputFilter::external));
848+
849+ client.request_turn_off("all");
850+ client.request_turn_off("internal");
851+ client.request_turn_off("external");
852+}
853+
854+TEST_F(AUnityDisplayService, emits_active_outputs_property_change)
855+{
856+ using namespace testing;
857+
858+ usc::ActiveOutputs expected_active_outputs;
859+ expected_active_outputs.internal = 2;
860+ expected_active_outputs.external = 3;
861+
862+ fake_screen->notify_active_outputs(expected_active_outputs);
863+ // Received messages are queued at the destination, so it doesn't
864+ // matter that we start listening after the signal has been sent
865+ auto message = client.listen_for_properties_changed();
866+
867+ DBusMessageIter iter;
868+ dbus_message_iter_init(message, &iter);
869+ dbus_message_iter_next(&iter);
870+ DBusMessageIter iter_properties;
871+ dbus_message_iter_recurse(&iter, &iter_properties);
872+ DBusMessageIter iter_property;
873+ dbus_message_iter_recurse(&iter_properties, &iter_property);
874+
875+ char const* property_name{""};
876+ usc::ActiveOutputs active_outputs{-1, -1};
877+
878+ dbus_message_iter_get_basic(&iter_property, &property_name);
879+
880+ dbus_message_iter_next(&iter_property);
881+ DBusMessageIter iter_variant;
882+ DBusMessageIter iter_values;
883+ dbus_message_iter_recurse(&iter_property, &iter_variant);
884+ dbus_message_iter_recurse(&iter_variant, &iter_values);
885+
886+ dbus_message_iter_get_basic(&iter_values, &active_outputs.internal);
887+ dbus_message_iter_next(&iter_values);
888+ dbus_message_iter_get_basic(&iter_values, &active_outputs.external);
889+
890+ EXPECT_THAT(property_name, StrEq("ActiveOutputs"));
891+ EXPECT_THAT(active_outputs, Eq(expected_active_outputs));
892+}
893+
894+TEST_F(AUnityDisplayService, returns_active_outputs_property)
895+{
896+ using namespace testing;
897+
898+ usc::ActiveOutputs expected_active_outputs;
899+ expected_active_outputs.internal = 2;
900+ expected_active_outputs.external = 3;
901+
902+ fake_screen->notify_active_outputs(expected_active_outputs);
903+
904+ auto message = client.request_active_outputs_property().get();
905+
906+ DBusMessageIter iter;
907+ dbus_message_iter_init(message, &iter);
908+
909+ DBusMessageIter iter_variant;
910+ dbus_message_iter_recurse(&iter, &iter_variant);
911+ DBusMessageIter iter_values;
912+ dbus_message_iter_recurse(&iter_variant, &iter_values);
913+
914+ usc::ActiveOutputs active_outputs{-1, -1};
915+
916+ dbus_message_iter_get_basic(&iter_values, &active_outputs.internal);
917+ dbus_message_iter_next(&iter_values);
918+ dbus_message_iter_get_basic(&iter_values, &active_outputs.external);
919+
920+ EXPECT_THAT(active_outputs, Eq(expected_active_outputs));
921+}
922+
923+TEST_F(AUnityDisplayService, returns_all_properties)
924+{
925+ using namespace testing;
926+
927+ usc::ActiveOutputs expected_active_outputs;
928+ expected_active_outputs.internal = 2;
929+ expected_active_outputs.external = 3;
930+
931+ fake_screen->notify_active_outputs(expected_active_outputs);
932+
933+ auto message = client.request_all_properties().get();
934+
935+ DBusMessageIter iter;
936+ dbus_message_iter_init(message, &iter);
937+ DBusMessageIter iter_properties;
938+ dbus_message_iter_recurse(&iter, &iter_properties);
939+ DBusMessageIter iter_property;
940+ dbus_message_iter_recurse(&iter_properties, &iter_property);
941+
942+ char const* property_name{""};
943+ usc::ActiveOutputs active_outputs{-1, -1};
944+
945+ dbus_message_iter_get_basic(&iter_property, &property_name);
946+
947+ dbus_message_iter_next(&iter_property);
948+ DBusMessageIter iter_variant;
949+ DBusMessageIter iter_values;
950+ dbus_message_iter_recurse(&iter_property, &iter_variant);
951+ dbus_message_iter_recurse(&iter_variant, &iter_values);
952+
953+ dbus_message_iter_get_basic(&iter_values, &active_outputs.internal);
954+ dbus_message_iter_next(&iter_values);
955+ dbus_message_iter_get_basic(&iter_values, &active_outputs.external);
956+
957+ EXPECT_THAT(property_name, StrEq("ActiveOutputs"));
958+ EXPECT_THAT(active_outputs, Eq(expected_active_outputs));
959 }
960
961 TEST_F(AUnityDisplayService, returns_error_reply_for_unsupported_method)
962
963=== modified file 'tests/integration-tests/unity_display_dbus_client.cpp'
964--- tests/integration-tests/unity_display_dbus_client.cpp 2016-04-18 14:58:36 +0000
965+++ tests/integration-tests/unity_display_dbus_client.cpp 2017-02-06 11:51:56 +0000
966@@ -17,6 +17,7 @@
967 */
968
969 #include "unity_display_dbus_client.h"
970+#include "src/dbus_message_handle.h"
971
972 namespace ut = usc::test;
973
974@@ -26,6 +27,9 @@
975 "com.canonical.Unity.Display",
976 "/com/canonical/Unity/Display"}
977 {
978+ connection.add_match(
979+ "type='signal',"
980+ "interface='org.freedesktop.DBus.Properties'");
981 }
982
983 ut::DBusAsyncReplyString ut::UnityDisplayDBusClient::request_introspection()
984@@ -35,17 +39,25 @@
985 DBUS_TYPE_INVALID);
986 }
987
988-ut::DBusAsyncReplyVoid ut::UnityDisplayDBusClient::request_turn_on()
989+ut::DBusAsyncReplyVoid ut::UnityDisplayDBusClient::request_turn_on(
990+ std::string const& filter)
991 {
992+ auto const filter_cstr = filter.c_str();
993+
994 return invoke_with_reply<ut::DBusAsyncReplyVoid>(
995 unity_display_interface, "TurnOn",
996+ DBUS_TYPE_STRING, &filter_cstr,
997 DBUS_TYPE_INVALID);
998 }
999
1000-ut::DBusAsyncReplyVoid ut::UnityDisplayDBusClient::request_turn_off()
1001+ut::DBusAsyncReplyVoid ut::UnityDisplayDBusClient::request_turn_off(
1002+ std::string const& filter)
1003 {
1004+ auto const filter_cstr = filter.c_str();
1005+
1006 return invoke_with_reply<ut::DBusAsyncReplyVoid>(
1007 unity_display_interface, "TurnOff",
1008+ DBUS_TYPE_STRING, &filter_cstr,
1009 DBUS_TYPE_INVALID);
1010 }
1011
1012@@ -54,3 +66,36 @@
1013 return invoke_with_reply<ut::DBusAsyncReply>(
1014 unity_display_interface, "invalidMethod", DBUS_TYPE_INVALID);
1015 }
1016+
1017+ut::DBusAsyncReply ut::UnityDisplayDBusClient::request_active_outputs_property()
1018+{
1019+ char const* const active_outputs_cstr = "ActiveOutputs";
1020+
1021+ return invoke_with_reply<ut::DBusAsyncReply>(
1022+ "org.freedesktop.DBus.Properties", "Get",
1023+ DBUS_TYPE_STRING, &unity_display_interface,
1024+ DBUS_TYPE_STRING, &active_outputs_cstr,
1025+ DBUS_TYPE_INVALID);
1026+}
1027+
1028+ut::DBusAsyncReply ut::UnityDisplayDBusClient::request_all_properties()
1029+{
1030+ return invoke_with_reply<ut::DBusAsyncReply>(
1031+ "org.freedesktop.DBus.Properties", "GetAll",
1032+ DBUS_TYPE_STRING, &unity_display_interface,
1033+ DBUS_TYPE_INVALID);
1034+}
1035+
1036+usc::DBusMessageHandle ut::UnityDisplayDBusClient::listen_for_properties_changed()
1037+{
1038+ while (true)
1039+ {
1040+ dbus_connection_read_write(connection, 1);
1041+ auto msg = usc::DBusMessageHandle{dbus_connection_pop_message(connection)};
1042+
1043+ if (msg && dbus_message_is_signal(msg, "org.freedesktop.DBus.Properties", "PropertiesChanged"))
1044+ {
1045+ return msg;
1046+ }
1047+ }
1048+}
1049
1050=== modified file 'tests/integration-tests/unity_display_dbus_client.h'
1051--- tests/integration-tests/unity_display_dbus_client.h 2016-04-18 14:58:36 +0000
1052+++ tests/integration-tests/unity_display_dbus_client.h 2017-02-06 11:51:56 +0000
1053@@ -32,10 +32,14 @@
1054 UnityDisplayDBusClient(std::string const& address);
1055
1056 DBusAsyncReplyString request_introspection();
1057- DBusAsyncReplyVoid request_turn_on();
1058- DBusAsyncReplyVoid request_turn_off();
1059+ DBusAsyncReplyVoid request_turn_on(std::string const& filter);
1060+ DBusAsyncReplyVoid request_turn_off(std::string const& filter);
1061+ DBusAsyncReply request_active_outputs_property();
1062+ DBusAsyncReply request_all_properties();
1063 DBusAsyncReply request_invalid_method();
1064
1065+ DBusMessageHandle listen_for_properties_changed();
1066+
1067 char const* const unity_display_interface = "com.canonical.Unity.Display";
1068 };
1069
1070
1071=== modified file 'tests/unit-tests/test_mir_screen.cpp'
1072--- tests/unit-tests/test_mir_screen.cpp 2016-11-09 19:12:16 +0000
1073+++ tests/unit-tests/test_mir_screen.cpp 2017-02-06 11:51:56 +0000
1074@@ -19,6 +19,8 @@
1075 #include "src/mir_screen.h"
1076
1077 #include "usc/test/mock_display.h"
1078+#include "usc/test/stub_display_configuration.h"
1079+#include "fake_shared.h"
1080
1081 #include <mir/compositor/compositor.h>
1082 #include <mir/graphics/display_configuration.h>
1083@@ -40,17 +42,31 @@
1084 MOCK_METHOD0(stop, void());
1085 };
1086
1087+struct MockDisplayWithExternalOutputs : ut::MockDisplay
1088+{
1089+ std::unique_ptr<mir::graphics::DisplayConfiguration> configuration() const override
1090+ {
1091+ return std::make_unique<usc::test::StubDisplayConfiguration>(2, 3, 1);
1092+ }
1093+};
1094+
1095 struct AMirScreen : testing::Test
1096 {
1097- void turn_screen_off()
1098- {
1099- mir_screen.turn_off();
1100- verify_and_clear_expectations();
1101- }
1102-
1103- void turn_screen_on()
1104- {
1105- mir_screen.turn_on();
1106+ void turn_all_displays_off()
1107+ {
1108+ mir_screen->turn_off(usc::OutputFilter::all);
1109+ verify_and_clear_expectations();
1110+ }
1111+
1112+ void turn_all_displays_on()
1113+ {
1114+ mir_screen->turn_on(usc::OutputFilter::all);
1115+ verify_and_clear_expectations();
1116+ }
1117+
1118+ void turn_internal_displays_off()
1119+ {
1120+ mir_screen->turn_off(usc::OutputFilter::internal);
1121 verify_and_clear_expectations();
1122 }
1123
1124@@ -60,14 +76,33 @@
1125 Mock::VerifyAndClearExpectations(compositor.get());
1126 }
1127
1128+ void use_mir_screen_with_external_outputs()
1129+ {
1130+ display = std::make_shared<testing::NiceMock<MockDisplayWithExternalOutputs>>();
1131+ mir_screen = std::make_shared<usc::MirScreen>(compositor, display);
1132+ }
1133+
1134 std::shared_ptr<MockCompositor> compositor{
1135 std::make_shared<testing::NiceMock<MockCompositor>>()};
1136 std::shared_ptr<ut::MockDisplay> display{
1137 std::make_shared<testing::NiceMock<ut::MockDisplay>>()};
1138
1139- usc::MirScreen mir_screen{
1140- compositor,
1141- display};
1142+ usc::ActiveOutputs const config_active_outputs{1, 3};
1143+ int const config_inactive_outputs = 2;
1144+ ut::StubDisplayConfiguration stub_display_configuration{
1145+ config_active_outputs.internal,
1146+ config_active_outputs.external,
1147+ config_inactive_outputs};
1148+
1149+ usc::ActiveOutputs active_outputs{-1,-1};
1150+ usc::ActiveOutputsHandler active_outputs_handler =
1151+ [this] (usc::ActiveOutputs const& active_outputs_arg)
1152+ {
1153+ active_outputs = active_outputs_arg;
1154+ };
1155+
1156+ std::shared_ptr<usc::MirScreen> mir_screen{
1157+ std::make_shared<usc::MirScreen>(compositor, display)};
1158 };
1159
1160 }
1161@@ -78,17 +113,63 @@
1162 EXPECT_CALL(*compositor, stop());
1163 EXPECT_CALL(*display, configure(_));
1164
1165- turn_screen_off();
1166+ turn_all_displays_off();
1167 }
1168
1169 TEST_F(AMirScreen, starts_compositing_and_turns_on_display_when_turning_on)
1170 {
1171- turn_screen_off();
1172-
1173- InSequence s;
1174- EXPECT_CALL(*compositor, stop());
1175- EXPECT_CALL(*display, configure(_));
1176- EXPECT_CALL(*compositor, start());
1177-
1178- turn_screen_on();
1179+ turn_all_displays_off();
1180+
1181+ InSequence s;
1182+ EXPECT_CALL(*compositor, stop());
1183+ EXPECT_CALL(*display, configure(_));
1184+ EXPECT_CALL(*compositor, start());
1185+
1186+ turn_all_displays_on();
1187+}
1188+
1189+TEST_F(AMirScreen, stops_compositing_and_turns_off_internal_when_only_internal)
1190+{
1191+ InSequence s;
1192+ EXPECT_CALL(*compositor, stop());
1193+ EXPECT_CALL(*display, configure(_));
1194+
1195+ turn_internal_displays_off();
1196+}
1197+
1198+TEST_F(AMirScreen, restarts_compositing_after_turn_off_internal_if_active_outputs_remain)
1199+{
1200+ use_mir_screen_with_external_outputs();
1201+
1202+ InSequence s;
1203+ EXPECT_CALL(*compositor, stop());
1204+ EXPECT_CALL(*display, configure(_));
1205+ EXPECT_CALL(*compositor, start());
1206+
1207+ turn_internal_displays_off();
1208+}
1209+
1210+TEST_F(AMirScreen, registered_handler_is_called_immediately)
1211+{
1212+ mir_screen->register_active_outputs_handler(active_outputs_handler);
1213+
1214+ EXPECT_THAT(active_outputs, Eq(usc::ActiveOutputs{}));
1215+}
1216+
1217+TEST_F(AMirScreen, initial_configuration_calls_handler)
1218+{
1219+ mir_screen->register_active_outputs_handler(active_outputs_handler);
1220+
1221+ mir_screen->initial_configuration(ut::fake_shared(stub_display_configuration));
1222+
1223+ EXPECT_THAT(active_outputs, Eq(config_active_outputs));
1224+}
1225+
1226+TEST_F(AMirScreen, configuration_applied_calls_handler)
1227+{
1228+ mir_screen->register_active_outputs_handler(active_outputs_handler);
1229+
1230+ mir_screen->configuration_applied(ut::fake_shared(stub_display_configuration));
1231+
1232+ EXPECT_THAT(active_outputs, Eq(config_active_outputs));
1233 }

Subscribers

People subscribed via source and target branches